Completed
Push — master ( d2aebd...b0ae7e )
by
unknown
01:01
created

terminal()   F

Complexity

Conditions 13

Size

Total Lines 69

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 13
dl 0
loc 69
rs 2.4914

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like terminal() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
from pyramid.events import subscriber
2
from pyramid.events import BeforeRender
3
from pyramid.httpexceptions import HTTPFound
4
from pyramid.renderers import render
5
from pyramid.renderers import render_to_response
6
from pyramid.response import Response
7
from pyramid.view import view_config, forbidden_view_config
8
9
from pyramid.i18n import TranslationStringFactory, get_localizer
10
_ = TranslationStringFactory('betty')
11
12
from sqlalchemy.sql import func
13
from sqlalchemy.exc import DBAPIError
14
from sqlalchemy.orm.exc import NoResultFound
15
16
from .models import *
17
from .models.model import *
18
from .models import user as __user
19
from .models.user import User
20
from .models.item import Item
21
from .models.box import Box
22
from .models.transaction import Transaction, BTCDeposit, PurchaseLineItem
23
from .models.account import Account, VirtualAccount, CashAccount
24
from .models.event import Event
25
from .models.announcement import Announcement
26
from .models.btcdeposit import BtcPendingDeposit
27
from .models.pool import Pool
28
from .models.tag import Tag
29
30
from .utility import user_password_reset
31
from .utility import send_email
32
33
from pyramid.security import Allow, Everyone, remember, forget
34
35
import chezbetty.datalayer as datalayer
36
import transaction
37
38
import math
39
40
# Custom exception
41
class DepositException(Exception):
42
    pass
43
44
45
# Check for a valid user by UMID.
46
#
47
# Input: form encoded umid=11223344
48
#
49
# Note: will not create new user if this user does not exist.
50
@view_config(route_name='terminal_umid_check',
51
             request_method='POST',
52
             renderer='json',
53
             permission='service')
54
def terminal_umid_check(request):
55
    try:
56
        User.from_umid(request.POST['umid'])
57
        return {}
58
    except:
59
        return {'error': get_localizer(request).translate(
60
                    _('mcard-keypad-error',
61
                      default='First time using Betty? You need to swipe your M-Card the first time you log in.'))}
62
63
64
## Main terminal page with purchase/cash deposit.
65
@view_config(route_name='terminal',
66
             renderer='templates/terminal/terminal.jinja2',
67
             permission='service')
68
def terminal(request):
69
    try:
70
        if len(request.matchdict['umid']) != 8:
71
            raise __user.InvalidUserException()
72
73
        with transaction.manager:
74
            user = User.from_umid(request.matchdict['umid'], create_if_never_seen=True)
75
        user = DBSession.merge(user)
76
        if not user.enabled:
77
            request.session.flash(get_localizer(request).translate(_(
78
                'user-not-enabled',
79
                default='User is not enabled. Please contact ${email}.',
80
                mapping={'email':request.registry.settings['chezbetty.email']},
81
                )), 'error')
82
            return HTTPFound(location=request.route_url('index'))
83
84
        # Handle re-instating archived user
85
        if user.archived:
86
            if user.archived_balance != 0:
87
                datalayer.adjust_user_balance(user,
88
                                              user.archived_balance,
89
                                              'Reinstated archived user.',
90
                                              request.user)
91
            user.balance = user.archived_balance
92
            user.archived = False
93
94
        # For Demo mode:
95
        items = DBSession.query(Item)\
96
                         .filter(Item.enabled == True)\
97
                         .order_by(Item.name)\
98
                         .limit(6).all()
99
100
        # Determine initial wall-of-shame fee (if applicable)
101
        purchase_fee_percent = Decimal(0)
102
        if user.balance <= Decimal('-5.0'):
103
            purchase_fee_percent = 5 + math.floor((user.balance+5) / -5)
104
105
        # Figure out if any pools can be used to pay for this purchase
106
        purchase_pools = []
107
        deposit_pools = []
108
        for pool in Pool.all_by_owner(user, True):
109
            deposit_pools.append(pool)
110
            if pool.balance > (pool.credit_limit * -1):
111
                purchase_pools.append(pool)
112
113
        for pu in user.pools:
114
            deposit_pools.append(pu.pool)
115
            if pu.pool.enabled and pu.pool.balance > (pu.pool.credit_limit * -1):
116
                purchase_pools.append(pu.pool)
117
118
        # Get the list of tags that have items without barcodes in them
119
        tags_with_nobarcode_items = Tag.get_tags_with_nobarcode_items();
120
121
        return {'user': user,
122
                'items': items,
123
                'purchase_pools': purchase_pools,
124
                'deposit_pools': deposit_pools,
125
                'purchase_fee_percent': purchase_fee_percent,
126
                'tags_with_nobarcode_items': tags_with_nobarcode_items}
127
128
    except __user.InvalidUserException as e:
129
        request.session.flash(get_localizer(request).translate(_(
130
            'mcard-error',
131
            default='Failed to read M-Card. Please try swiping again.',
132
            )), 'error')
133
        return HTTPFound(location=request.route_url('index'))
134
135
136
## Get all items without barcodes in a tag
137
@view_config(route_name='terminal_purchase_tag',
138
             renderer='json',
139
             permission='service')
140
def terminal_purchase_tag(request):
141
    try:
142
        tag_id = int(request.matchdict['tag_id'])
143
        tag = Tag.from_id(tag_id)
144
    except:
145
        if request.matchdict['tag_id'] == 'other':
146
            tag = {'name': 'other',
147
                   'nobarcode_items': Item.get_nobarcode_notag_items()}
148
        else:
149
            return {'error': 'Unable to parse TAG ID'}
150
151
    item_array = render('templates/terminal/purchase_nobarcode_items.jinja2',
152
                        {'tag': tag})
153
154
    return {'items_html': item_array}
155
156
157
## Add a cash deposit.
158
@view_config(route_name='terminal_deposit',
159
             request_method='POST',
160
             renderer='json',
161
             permission='service')
162
def terminal_deposit(request):
163
    try:
164
        user = User.from_umid(request.POST['umid'])
165
        amount = Decimal(request.POST['amount'])
166
        account = request.POST['account']
167
168
        # Check if the deposit amount is too great.
169
        # This if block could be tighter, but this is easier to understand
170
        if amount > 100.0:
171
            # Anything above 100 is blocked
172
            raise DepositException('Deposit amount of ${:,.2f} exceeds the limit'.format(amount))
173
174
        if amount < 100.0 and amount > 20.0 and (user.total_deposits < 10.0 or user.total_purchases < 10.0):
175
            # If the deposit is between 20 and 100 and the user hasn't done much
176
            # with betty. Block the deposit. We do allow deposits up to 100 for
177
            # customers that have shown they know how to scan/purchase and
178
            # deposit
179
            raise DepositException('Deposit amount of ${:,.2f} exceeds the limit'.format(amount))
180
181
        if amount <= 0.0:
182
            raise DepositException('Deposit amount must be greater than $0.00')
183
184
        # At this point the deposit can go through
185
        ret = {}
186
187
        if account == 'user':
188
            deposit = datalayer.deposit(user, user, amount)
189
        elif account == 'pool':
190
            pool = Pool.from_id(request.POST['pool_id'])
191
            deposit = datalayer.deposit(user, pool, amount)
192
            ret['pool_name'] = pool.name
193
            ret['pool_balance'] = float(pool.balance)
194
            ret['pool_id'] = pool.id
195
196
        # Return a JSON blob of the transaction ID so the client can redirect to
197
        # the deposit success page
198
        ret['amount'] = float(deposit['amount'])
199
        ret['event_id'] = deposit['event'].id
200
        ret['user_balance'] = float(user.balance)
201
        return ret
202
203
    except __user.InvalidUserException as e:
204
        request.session.flash('Invalid user error. Please try again.', 'error')
205
        return {'error': 'Error finding user.'}
206
207
    except ValueError as e:
208
        return {'error': 'Error understanding deposit amount.'}
209
210
    except DepositException as e:
211
        return {'error': str(e)}
212
213
    except Exception as e:
214
        return {'error': str(e)}
215
216
217
## Delete a just completed transaction.
218
@view_config(route_name='terminal_deposit_delete',
219
             request_method='POST',
220
             renderer='json',
221
             permission='service')
222
def terminal_deposit_delete(request):
223
    try:
224
        user = User.from_umid(request.POST['umid'])
225
        old_event = Event.from_id(request.POST['old_event_id'])
226
227
        if old_event.type != 'deposit' or \
228
           old_event.transactions[0].type != 'cashdeposit' or \
229
           (old_event.transactions[0].to_account_virt_id != user.id and \
230
            old_event.user_id != user.id):
231
           # Something went wrong, can't undo this deposit
232
           raise DepositException('Cannot undo that deposit')
233
234
        # Now undo old deposit
235
        datalayer.undo_event(old_event, user)
236
237
        purchase_pools = []
238
        for pool in Pool.all_by_owner(user, True):
239
            if pool.balance > (pool.credit_limit * -1):
240
                purchase_pools.append({'id': pool.id, 'balance': float(pool.balance)})
241
242
        for pu in user.pools:
243
            if pu.pool.enabled and pu.pool.balance > (pu.pool.credit_limit * -1):
244
                purchase_pools.append({'id': pu.pool.id, 'balance': float(pu.pool.balance)})
245
246
        return {'user_balance': float(user.balance),
247
                'pools': purchase_pools}
248
249
    except __user.InvalidUserException as e:
250
        return {'error': 'Invalid user error. Please try again.'}
251
252
    except DepositException as e:
253
        return {'error': str(e)}
254
255
    except Exception as e:
256
        if request.debug: raise(e)
257
        return {'error': 'Error.'}
258
259
260
## Add an item to a shopping cart.
261
@view_config(route_name='terminal_item_barcode',
262
             renderer='json',
263
             permission='service')
264
def terminal_item_barcode(request):
265
    try:
266
        item = Item.from_barcode(request.matchdict['barcode'])
267
    except:
268
        # Could not find the item. Check to see if the user scanned a box
269
        # instead. This could lead to two cases: a) the box only has 1 item in it
270
        # in which case we just add that item to the cart. This likely occurred
271
        # because the individual items do not have barcodes so we just use
272
        # the box. b) The box has multiple items in it in which case we throw
273
        # an error for now.
274
        try:
275
            box = Box.from_barcode(request.matchdict['barcode'])
276
            if box.subitem_number == 1:
277
                item = box.items[0].item
278
            else:
279
                return {'error': 'Cannot add that entire box to your order. Please scan an individual item.'}
280
        except:
281
            return {'error': 'Could not find that item.'}
282
283
    if not item.enabled:
284
        return {'error': 'That product is not currently for sale.'}
285
286
    item_html = render('templates/terminal/purchase_item_row.jinja2', {'item': item})
287
    return {'id':item.id,
288
            'item_row_html': item_html}
289
290
291
## Add an item to a shopping cart.
292
@view_config(route_name='terminal_item_id',
293
             renderer='json',
294
             permission='service')
295
def terminal_item_id(request):
296
    try:
297
        item = Item.from_id(request.matchdict['item_id'])
298
    except:
299
        return {'error': 'Could not find that item.'}
300
301
    if not item.enabled:
302
        return {'error': 'That product is not currently for sale.'}
303
304
    item_html = render('templates/terminal/purchase_item_row.jinja2', {'item': item})
305
    return {'id': item.id,
306
            'item_row_html': item_html}
307
308
309
## Make a purchase from the terminal.
310
@view_config(route_name='terminal_purchase',
311
             request_method='POST',
312
             renderer='json',
313
             permission='service')
314
def terminal_purchase(request):
315
    try:
316
        user = User.from_umid(request.POST['umid'])
317
318
        ignored_keys = ['umid', 'pool_id']
319
320
        # Bundle all purchase items
321
        items = {}
322
        for item_id,quantity in request.POST.items():
323
            if item_id in ignored_keys:
324
                continue
325
            item = Item.from_id(int(item_id))
326
            items[item] = int(quantity)
327
328
        # What should pay for this?
329
        # Note: should do a bunch of checking to make sure all of this
330
        # is kosher. But given our locked down single terminal, we're just
331
        # going to skip all of that.
332
        if 'pool_id' in request.POST:
333
            pool = Pool.from_id(int(request.POST['pool_id']))
334
            purchase = datalayer.purchase(user, pool, items)
335
        else:
336
            purchase = datalayer.purchase(user, user, items)
337
338
        # Create a order complete view
339
        order = {'total': purchase.amount,
340
                 'discount': purchase.discount,
341
                 'items': []}
342
        for subtrans in purchase.subtransactions:
343
            item = {}
344
            item['name'] = subtrans.item.name
345
            item['quantity'] = subtrans.quantity
346
            item['price'] = subtrans.item.price
347
            item['total_price'] = subtrans.amount
348
            order['items'].append(item)
349
350
        if purchase.fr_account_virt_id == user.id:
351
            account_type = 'user'
352
            pool = None
353
        else:
354
            account_type = 'pool'
355
            pool = Pool.from_id(purchase.fr_account_virt_id)
356
357
        summary = render('templates/terminal/purchase_complete.jinja2',
358
            {'user': user,
359
             'event': purchase.event,
360
             'order': order,
361
             'transaction': purchase,
362
             'account_type': account_type,
363
             'pool': pool})
364
365
        # Return the committed transaction ID
366
        return {'order_table': summary,
367
                'user_balance': float(user.balance)}
368
369
    except __user.InvalidUserException as e:
370
        return {'error': get_localizer(request).translate(_('invalid-user-error',
371
                           default='Invalid user error. Please try again.'))
372
               }
373
374
    except ValueError as e:
375
        return {'error': 'Unable to parse Item ID or quantity'}
376
377
    except NoResultFound as e:
378
        # Could not find an item
379
        return {'error': 'Unable to identify an item.'}
380
381
382
## Delete a just completed purchase.
383
@view_config(route_name='terminal_purchase_delete',
384
             request_method='POST',
385
             renderer='json',
386
             permission='service')
387
def terminal_purchase_delete(request):
388
    try:
389
        user = User.from_umid(request.POST['umid'])
390
        old_event = Event.from_id(request.POST['old_event_id'])
391
392
        if old_event.type != 'purchase' or \
393
           old_event.transactions[0].type != 'purchase' or \
394
           (old_event.transactions[0].fr_account_virt_id != user.id and \
395
            old_event.user_id != user.id):
396
           # Something went wrong, can't undo this purchase
397
           raise DepositException('Cannot undo that purchase')
398
399
        # Now undo old deposit
400
        datalayer.undo_event(old_event, user)
401
402
        return {'user_balance': float(user.balance)}
403
404
    except __user.InvalidUserException as e:
405
        return {'error': 'Invalid user error. Please try again.'}
406
407
    except DepositException as e:
408
        return {'error': str(e)}
409
410
411
412
413
# @view_config(route_name='user', renderer='templates/terminal/user.jinja2', permission='service')
414
# def user(request):
415
#     try:
416
#         user = User.from_umid(request.matchdict['umid'])
417
#         if not user.enabled:
418
#             request.session.flash('User not permitted to purchase items.', 'error')
419
#             return HTTPFound(location=request.route_url('index'))
420
421
#         transactions,count = limitable_request(
422
#                 request, user.get_transactions, limit=20, count=True)
423
#         return {'user': user,
424
#                 'transactions': transactions,
425
#                 'transactions_count': count,
426
#                 }
427
428
#     except __user.InvalidUserException as e:
429
#         request.session.flash('Invalid User ID.', 'error')
430
#         return HTTPFound(location=request.route_url('index'))
431
432
433
# @view_config(route_name='deposit', renderer='templates/terminal/deposit.jinja2', permission='service')
434
# def deposit(request):
435
#     try:
436
#         user = User.from_umid(request.matchdict['umid'])
437
438
#         # Record the deposit limit so we can show the user
439
#         if user.total_deposits > 10.0 and user.total_purchases > 10.0:
440
#             user.deposit_limit = 100.0
441
#         else:
442
#             user.deposit_limit = 20.0
443
444
#         try:
445
#             auth_key = binascii.b2a_hex(open("/dev/urandom", "rb").read(32))[:-3].decode("ascii")
446
#             btc_addr = Bitcoin.get_new_address(user.umid, auth_key)
447
#             btc_html = render('templates/terminal/btc.jinja2', {'addr': btc_addr})
448
449
#             e = BtcPendingDeposit(user, auth_key, btc_addr)
450
#             DBSession.add(e)
451
#             DBSession.flush()
452
#         except BTCException as e:
453
#             print('BTC error: %s' % str(e))
454
#             btc_html = ""
455
456
#         # Get pools the user can deposit to
457
#         pools = Pool.all_accessable(user, True)
458
459
#         return {'user' : user,
460
#                 'btc'  : btc_html,
461
#                 'pools': pools}
462
463
#     except __user.InvalidUserException as e:
464
#         request.session.flash('Invalid User ID.', 'error')
465
#         return HTTPFound(location=request.route_url('index'))
466
467
468
# @view_config(route_name='deposit_edit',
469
#              renderer='templates/terminal/deposit_edit.jinja2',
470
#              permission='service')
471
# def deposit_edit(request):
472
#     try:
473
#         user = User.from_umid(request.matchdict['umid'])
474
#         event = Event.from_id(request.matchdict['event_id'])
475
476
#         if event.type != 'deposit' or event.transactions[0].type != 'cashdeposit':
477
#             request.session.flash('Can only edit a cash deposit.', 'error')
478
#             return HTTPFound(location=request.route_url('index'))
479
480
#         # Get pools the user can deposit to
481
#         pools = []
482
#         for pool in Pool.all_by_owner(user, True):
483
#             pools.append(pool)
484
485
#         for pu in user.pools:
486
#             if pu.pool.enabled:
487
#                 pools.append(pu.pool)
488
489
#         return {'user': user,
490
#                 'old_event': event,
491
#                 'old_deposit': event.transactions[0],
492
#                 'pools': pools}
493
494
#     except __user.InvalidUserException as e:
495
#         request.session.flash('Invalid User ID.', 'error')
496
#         return HTTPFound(location=request.route_url('index'))
497
498
#     except Exception as e:
499
#         if request.debug: raise(e)
500
#         request.session.flash('Error.', 'error')
501
#         return HTTPFound(location=request.route_url('index'))
502
503
504
505
# @view_config(route_name='event', permission='service')
506
# def event(request):
507
#     try:
508
#         event = Event.from_id(request.matchdict['event_id'])
509
#         transaction = event.transactions[0]
510
#         user = User.from_id(event.user_id)
511
512
#         # Choose which page to show based on the type of event
513
#         if event.type == 'deposit':
514
#             # View the deposit success page
515
#             prev_balance = user.balance - transaction.amount
516
517
#             if transaction.to_account_virt_id == user.id:
518
#                 account_type = 'user'
519
#                 pool = None
520
#             else:
521
#                 account_type = 'pool'
522
#                 pool = Pool.from_id(transaction.to_account_virt_id)
523
524
#             return render_to_response('templates/terminal/deposit_complete.jinja2',
525
#                 {'deposit': transaction,
526
#                  'user': user,
527
#                  'event': event,
528
#                  'prev_balance': prev_balance,
529
#                  'account_type': account_type,
530
#                  'pool': pool}, request)
531
532
#         elif event.type == 'purchase':
533
#             # View the purchase success page
534
#             order = {'total': transaction.amount,
535
#                      'discount': transaction.discount,
536
#                      'items': []}
537
#             for subtrans in transaction.subtransactions:
538
#                 item = {}
539
#                 item['name'] = subtrans.item.name
540
#                 item['quantity'] = subtrans.quantity
541
#                 item['price'] = subtrans.item.price
542
#                 item['total_price'] = subtrans.amount
543
#                 order['items'].append(item)
544
545
#             if transaction.fr_account_virt_id == user.id:
546
#                 account_type = 'user'
547
#                 pool = None
548
#             else:
549
#                 account_type = 'pool'
550
#                 pool = Pool.from_id(transaction.fr_account_virt_id)
551
552
#             request.session.flash('Success! The purchase was added successfully', 'success')
553
#             return render_to_response('templates/terminal/purchase_complete.jinja2',
554
#                 {'user': user,
555
#                  'event': event,
556
#                  'order': order,
557
#                  'transaction': transaction,
558
#                  'account_type': account_type,
559
#                  'pool': pool}, request)
560
561
#     except NoResultFound as e:
562
#         # TODO: add generic failure page
563
#         pass
564
#     except Exception as e:
565
#         if request.debug: raise(e)
566
#         return HTTPFound(location=request.route_url('index'))
567
568
569
# @view_config(route_name='event_undo', permission='service')
570
# def event_undo(request):
571
#     # Lookup the transaction that the user wants to undo
572
#     try:
573
#         event = Event.from_id(request.matchdict['event_id'])
574
#     except:
575
#         request.session.flash('Error: Could not find transaction to undo.', 'error')
576
#         return HTTPFound(location=request.route_url('index'))
577
578
#     for transaction in event.transactions:
579
580
#         # Make sure transaction is a deposit, the only one the user is allowed
581
#         # to undo
582
#         if transaction.type not in ('cashdeposit', 'purchase'):
583
#             request.session.flash('Error: Only deposits and purchases may be undone.', 'error')
584
#             return HTTPFound(location=request.route_url('index'))
585
586
#         # Make sure that the user who is requesting the deposit was the one who
587
#         # actually placed the deposit.
588
#         try:
589
#             user = User.from_id(event.user_id)
590
#         except:
591
#             request.session.flash('Error: Invalid user for transaction.', 'error')
592
#             return HTTPFound(location=request.route_url('index'))
593
594
#         if user.umid != request.matchdict['umid']:
595
#             request.session.flash('Error: Transaction does not belong to specified user', 'error')
596
#             return HTTPFound(location=request.route_url('user', umid=request.matchdict['umid']))
597
598
#     # If the checks pass, actually revert the transaction
599
#     try:
600
#         line_items = datalayer.undo_event(event, user)
601
#         if event.type == 'deposit':
602
#             request.session.flash('Deposit successfully undone.', 'success')
603
#         elif event.type == 'purchase':
604
#             request.session.flash('Purchase undone. Please edit it as needed.', 'success')
605
#     except:
606
#         request.session.flash('Error: Failed to undo transaction.', 'error')
607
#         return HTTPFound(location=request.route_url('purchase', umid=user.umid))
608
609
#     if event.type == 'deposit':
610
#         return HTTPFound(location=request.route_url('user', umid=user.umid))
611
#     elif event.type == 'purchase':
612
#         return HTTPFound(location=request.route_url('purchase', umid=user.umid, _query=line_items))
613
#     else:
614
#         assert(False and "Should not be able to get here?")
615
616
617
# ###
618
# ### JSON Requests
619
# ###
620
621
# @view_config(route_name='purchase_item_row', renderer='json', permission='service')
622
# def item(request):
623
#     try:
624
#         item = Item.from_barcode(request.matchdict['barcode'])
625
#     except:
626
#         # Could not find the item. Check to see if the user scanned a box
627
#         # instead. This could lead to two cases: a) the box only has 1 item in it
628
#         # in which case we just add that item to the cart. This likely occurred
629
#         # because the individual items do not have barcodes so we just use
630
#         # the box. b) The box has multiple items in it in which case we throw
631
#         # an error for now.
632
#         try:
633
#             box = Box.from_barcode(request.matchdict['barcode'])
634
#             if box.subitem_number == 1:
635
#                 item = box.items[0].item
636
#             else:
637
#                 return {'status': 'scanned_box_with_multiple_items'}
638
#         except:
639
#             return {'status': 'unknown_barcode'}
640
#     if item.enabled:
641
#         status = 'success'
642
#     else:
643
#         status = 'disabled'
644
#     item_html = render('templates/terminal/purchase_item_row.jinja2', {'item': item})
645
#     return {'status': status, 'id':item.id, 'item_row_html' : item_html}
646
647
# ###
648
# ### POST Handlers
649
# ###
650
651
652
653
654
655
# # Handle the POST from coinbase saying Chez Betty got a btc deposit.
656
# # Store the bitcoin record in the DB
657
# @view_config(route_name='btc_deposit', request_method='POST', renderer='json')
658
# def btc_deposit(request):
659
660
#     user = User.from_umid(request.matchdict['umid'])
661
#     auth_key = request.matchdict['auth_key']
662
663
#     addr       = request.json_body['address']
664
#     amount_btc = request.json_body['amount']
665
#     txid       = request.json_body['transaction']['id']
666
#     created_at = request.json_body['transaction']['created_at']
667
#     txhash     = request.json_body['transaction']['hash']
668
669
#     try:
670
#         pending = BtcPendingDeposit.from_auth_key(auth_key)
671
#     except NoResultFound as e:
672
#         print("No result for auth_key %s" % auth_key)
673
#         return {}
674
675
676
#     if (pending.user_id != user.id or pending.address != addr):
677
#         print("Mismatch of BtcPendingDeposit userid or address: (%d/%d), (%s/%s)" % (pending.user_id, user.id, pending.address, addr))
678
#         return {}
679
680
#     #try:
681
#     usd_per_btc = Bitcoin.get_spot_price()
682
#     #except BTCException as e:
683
#     #    # unknown exchange rate?
684
#     #    print('Could not get exchange rate for addr %s txhash %s; failing...' % (addr, txhash))
685
#     #    return {}
686
687
#     amount_usd = Decimal(amount_btc) * usd_per_btc
688
689
#     # round down to nearest cent
690
#     amount_usd = Decimal(int(amount_usd*100))/Decimal(100)
691
692
#     ret = "addr: %s, amount: %s, txid: %s, created_at: %s, txhash: %s, exchange = $%s/BTC"\
693
#            % (addr, amount_btc, txid, created_at, txhash, usd_per_btc)
694
#     datalayer.bitcoin_deposit(user, amount_usd, txhash, addr, amount_btc)
695
#     DBSession.delete(pending)
696
#     print(ret)
697
698
#     return {}
699
700
701
# @view_config(route_name='btc_check', request_method='GET', renderer='json')
702
# def btc_check(request):
703
#     try:
704
#         deposit = BTCDeposit.from_address(request.matchdict['addr'])
705
#         return {"event_id": deposit.event.id}
706
#     except:
707
#         return {}
708
709
710
# @view_config(route_name='deposit_emailinfo',
711
#              renderer='json',
712
#              permission='service')
713
# def deposit_emailinfo(request):
714
#     try:
715
#         user = User.from_id(int(request.matchdict['user_id']))
716
#         if not user.has_password:
717
#             return deposit_password_create(request)
718
#         send_email(TO=user.uniqname+'@umich.edu',
719
#                    SUBJECT='Chez Betty Credit Card Instructions',
720
#                    body=render('templates/terminal/email_userinfo.jinja2', {'user': user}))
721
#         return {'status': 'success',
722
#                 'msg': 'Instructions emailed to {}@umich.edu.'.format(user.uniqname)}
723
#     except NoResultFound:
724
#         return {'status': 'error',
725
#                 'msg': 'Could not find user.'}
726
#     except Exception as e:
727
#         if request.debug: raise(e)
728
#         return {'status': 'error',
729
#                 'msg': 'Error.'}
730
731
732
# @view_config(route_name='deposit_password_create',
733
#              renderer='json',
734
#              permission='service')
735
# def deposit_password_create(request):
736
#     try:
737
#         user = User.from_id(int(request.matchdict['user_id']))
738
#         if user.has_password:
739
#             return {'status': 'error',
740
#                     'msg': 'Error: User already has password.'}
741
#         user_password_reset(user)
742
#         return {'status': 'success',
743
#                 'msg': 'Password set and emailed to {}@umich.edu.'.format(user.uniqname)}
744
#     except NoResultFound:
745
#         return {'status': 'error',
746
#                 'msg': 'Could not find user.'}
747
#     except Exception as e:
748
#         if request.debug: raise(e)
749
#         return {'status': 'error',
750
#                 'msg': 'Error.'}
751
752
# @view_config(route_name='deposit_password_reset',
753
#         renderer='json',
754
#         permission='service')
755
# def deposit_password_reset(request):
756
#     try:
757
#         user = User.from_id(int(request.matchdict['user_id']))
758
#         user_password_reset(user)
759
#         return {'status': 'success',
760
#                 'msg': 'Password set and emailed to {}@umich.edu.'.format(user.uniqname)}
761
#     except NoResultFound:
762
#         return {'status': 'error',
763
#                 'msg': 'Could not find user.'}
764
#     except Exception as e:
765
#         if request.debug: raise(e)
766
#         return {'status': 'error',
767
#                 'msg': 'Error.'}
768
769
770
771
772
773
774