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

transaction_history_queries()   F

Complexity

Conditions 11

Size

Total Lines 50

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 11
dl 0
loc 50
rs 3.375

How to fix   Complexity   

Complexity

Complex classes like transaction_history_queries() 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.response import FileResponse
8
from pyramid.view import view_config, forbidden_view_config
9
10
from sqlalchemy.sql import func
11
from sqlalchemy.exc import DBAPIError, IntegrityError
12
from sqlalchemy.orm.exc import NoResultFound
13
14
from . import views_data
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.box_item import BoxItem
23
from .models.transaction import Transaction, Deposit, CashDeposit, CCDeposit, BTCDeposit, Purchase
24
from .models.transaction import Inventory, InventoryLineItem
25
from .models.transaction import PurchaseLineItem, SubTransaction, SubSubTransaction
26
from .models.account import Account, VirtualAccount, CashAccount
27
from .models.event import Event
28
from .models import event as __event
29
from .models.vendor import Vendor
30
from .models.item_vendor import ItemVendor
31
from .models.box_vendor import BoxVendor
32
from .models.request import Request
33
from .models.announcement import Announcement
34
from .models.btcdeposit import BtcPendingDeposit
35
from .models.receipt import Receipt
36
from .models.pool import Pool
37
from .models.pool_user import PoolUser
38
39
from .utility import post_stripe_payment
40
41
from pyramid.security import Allow, Everyone, remember, forget
42
43
import chezbetty.datalayer as datalayer
44
from .btc import Bitcoin, BTCException
45
46
import uuid
47
import math
48
import pytz
49
import traceback
50
import arrow
51
52
###
53
### User Admin
54
###
55
56
57
### Common helper code
58
59
def transaction_history_queries(request, user_or_pool):
60
    # Web library native date format: 2015/06/19 19:00
61
    # DO NOT CHANGE: The datetimepicker has a bug and tries to parse the
62
    #                prepopulated value before reading the format string,
63
    #                which means you have to use its native format string
64
    TS_FORMAT = 'YYYY/MM/DD HH:mm'
65
    if 'history-end' not in request.GET:
66
        request.GET['history-end'] =\
67
                arrow.now()\
68
                .replace(hours=+1)\
69
                .floor('hour')\
70
                .format(TS_FORMAT)
71
    if 'history-start' not in request.GET:
72
        request.GET['history-start'] =\
73
                arrow.get(request.GET['history-end'], TS_FORMAT)\
74
                .replace(months=-1)\
75
                .format(TS_FORMAT)
76
77
    start = arrow.get(request.GET['history-start'], TS_FORMAT)
78
    end   = arrow.get(request.GET['history-end'],   TS_FORMAT)
79
    start = start.replace(tzinfo='US/Eastern')
80
    end   = end  .replace(tzinfo='US/Eastern')
81
    start = start.to('utc')
82
    end   = end  .to('utc')
83
84
    query = user_or_pool.get_transactions_query()
85
    query = query\
86
            .filter(event.Event.timestamp > start.datetime)\
87
            .filter(event.Event.timestamp < end.datetime)
88
89
    for t in ('purchase', 'adjustment'):
90
        if 'history-filter-'+t in request.GET:
91
            query = query.filter(event.Event.type!=t)
92
    for t in ('cashdeposit', 'ccdeposit', 'btcdeposit'):
93
        if 'history-filter-'+t in request.GET:
94
            query = query.filter(Transaction.type!=t)
95
    transactions = query.all()
96
97
    withdrawls  = query.filter(event.Event.type=='purchase').all()
98
    deposits    = query.filter(event.Event.type=='deposit').all()
99
    adjustments = query.filter(event.Event.type=='adjustment').all()
100
101
    withdrawls  = sum(w.amount for w in withdrawls)
102
    deposits    = sum(d.amount for d in deposits)
103
    adjustments = sum(a.amount for a in adjustments) if len(adjustments) else None
104
105
    return {'transactions': transactions,
106
            'withdrawls': withdrawls,
107
            'deposits': deposits,
108
            'adjustments': adjustments,
109
            }
110
111
112
113
@view_config(route_name='user_ajax_bool',
114
             permission='user')
115
def user_ajax_bool(request):
116
    obj_str = request.matchdict['object']
117
    obj_id  = int(request.matchdict['id'])
118
    obj_field = request.matchdict['field']
119
    obj_state = request.matchdict['state'].lower() == 'true'
120
121
    if obj_str == 'pool':
122
        obj = Pool.from_id(obj_id)
123
        if obj.owner != request.user.id:
124
            request.response.status = 502
125
            return request.response
126
    elif obj_str == 'pool_user':
127
        obj = PoolUser.from_id(obj_id)
128
        if obj.pool.owner != request.user.id:
129
            request.response.status = 502
130
            return request.response
131
    else:
132
        # Return an error, object type not recognized
133
        request.response.status = 502
134
        return request.response
135
136
    setattr(obj, obj_field, obj_state)
137
    DBSession.flush()
138
139
    return request.response
140
141
@view_config(route_name='user_index',
142
             renderer='templates/user/index.jinja2',
143
             permission='user')
144
def user_index(request):
145
    r = transaction_history_queries(request, request.user)
146
    r['user'] = request.user
147
    r['my_pools'] = Pool.all_by_owner(request.user)
148
149
    return r
150
151
@view_config(route_name='user_index_slash',
152
             renderer='templates/user/index.jinja2',
153
             permission='user')
154
def user_index_slash(request):
155
    return HTTPFound(location=request.route_url('user_index'))
156
157
@view_config(route_name='user_deposit_cc',
158
             renderer='templates/user/deposit_cc.jinja2',
159
             permission='user')
160
def user_deposit_cc(request):
161
    pools = Pool.all_accessable(request.user, True)
162
    pool = None
163
    if 'acct' in request.GET:
164
        account = request.GET['acct']
165
        if account != 'user':
166
            pool = Pool.from_id(account.split('-')[1])
167
    else:
168
        account = 'user'
169
    return {'user': request.user,
170
            'account': account,
171
            'pool': pool,
172
            'pools': pools,
173
            'stripe_pk': request.registry.settings['stripe.publishable_key'],
174
            }
175
176
@view_config(route_name='user_deposit_cc_custom',
177
             renderer='templates/user/deposit_cc_custom.jinja2',
178
             permission='user')
179
def user_deposit_cc_custom(request):
180
    account = request.GET['betty_to_account']
181
    if account != 'user':
182
        pool = Pool.from_id(account.split('-')[1])
183
    else:
184
        pool = None
185
    return {'user': request.user,
186
            'stripe_pk': request.registry.settings['stripe.publishable_key'],
187
            'amount': round(Decimal(request.GET['deposit-amount']), 2),
188
            'account': account,
189
            'pool': pool,
190
            }
191
192
@view_config(route_name='user_deposit_cc_submit',
193
             request_method='POST',
194
             permission='user')
195
def user_deposit_cc_submit(request):
196
    token = request.POST['stripeToken']
197
    amount = Decimal(request.POST['betty_amount'])
198
    total_cents = int(request.POST['betty_total_cents'])
199
    to_account = request.POST['betty_to_account']
200
201
    try:
202
        if to_account != 'user':
203
            pool = Pool.from_id(to_account.split('-')[1])
204
            if pool.enabled == False:
205
                print("to_account:", to_account)
206
                raise NotImplementedError
207
            if pool.owner != request.user.id:
208
                if pool not in map(lambda pu: getattr(pu, 'pool'), request.user.pools):
209
                    print("to_account:", to_account)
210
                    raise NotImplementedError
211
    except Exception as e:
212
        traceback.print_exc()
213
        request.session.flash('Unexpected error processing transaction. Card NOT charged.', 'error')
214
        return HTTPFound(location=request.route_url('user_index'))
215
216
    post_stripe_payment(
217
            datalayer,
218
            request,
219
            token,
220
            amount,
221
            total_cents,
222
            request.user,
223
            request.user if to_account == 'user' else pool,
224
            )
225
226
    return HTTPFound(location=request.route_url('user_index'))
227
228
@view_config(route_name='user_pools',
229
             renderer='templates/user/pools.jinja2',
230
             permission='user')
231
def user_pools(request):
232
    return {'user': request.user,
233
            'my_pools': Pool.all_by_owner(request.user)}
234
235
236
@view_config(route_name='user_pools_new_submit',
237
             request_method='POST',
238
             permission='user')
239
def user_pools_new_submit(request):
240
    try:
241
        pool_name = request.POST['pool-name'].strip()
242
        if len(pool_name) > 255:
243
            pool_name = pool_name[0:255]
244
        if len(pool_name) < 5:
245
            request.session.flash('Pool names must be at least 5 letters long', 'error')
246
            return HTTPFound(location=request.route_url('user_pools'))
247
248
        pool = Pool(request.user, pool_name)
249
        DBSession.add(pool)
250
        DBSession.flush()
251
252
        request.session.flash('Pool created.', 'succcess')
253
        return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
254
255
    except Exception as e:
256
        if request.debug: raise(e)
257
        request.session.flash('Error creating pool.', 'error')
258
        return HTTPFound(location=request.route_url('user_pools'))
259
260
261 View Code Duplication
@view_config(route_name='user_pool',
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
262
             renderer='templates/user/pool.jinja2',
263
             permission='user')
264
def user_pool(request):
265
    try:
266
        pool = Pool.from_id(request.matchdict['pool_id'])
267
        if pool.owner != request.user.id:
268
            request.session.flash('You do not have permission to view that pool.', 'error')
269
            return HTTPFound(location=request.route_url('user_pools'))
270
271
        r = transaction_history_queries(request, pool)
272
        r['user'] = request.user
273
        r['pool'] = pool
274
275
        return r
276
    except Exception as e:
277
        if request.debug: raise(e)
278
        request.session.flash('Could not load pool.', 'error')
279
        return HTTPFound(location=request.route_url('user_pools'))
280
281
282
@view_config(route_name='user_pool_addmember_submit',
283
             request_method='POST',
284
             permission='user')
285
def user_pool_addmember_submit(request):
286
    try:
287
        pool = Pool.from_id(request.POST['pool-id'])
288
        if pool.owner != request.user.id:
289
            request.session.flash('You do not have permission to view that pool.', 'error')
290
            return HTTPFound(location=request.route_url('user_pools'))
291
292
        # Look up the user that is being added to the pool
293
        user = User.from_uniqname(request.POST['uniqname'].strip(), True)
294
        if user == None:
295
            request.session.flash('Could not find that user.', 'error')
296
            return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
297
298
        # Can't add yourself
299
        if user.id == pool.owner:
300
            request.session.flash('You cannot add yourself to a pool. By owning the pool you are automatically a part of it.', 'error')
301
            return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
302
303
        # Make sure the user isn't already in the pool
304
        for u in pool.users:
305
            if u.user_id == user.id:
306
                request.session.flash('User is already in pool.', 'error')
307
                return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
308
309
        # Add the user to the pool
310
        pooluser = PoolUser(pool, user)
311
        DBSession.add(pooluser)
312
        DBSession.flush()
313
314
        request.session.flash('{} added to the pool.'.format(user.name), 'succcess')
315
        return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
316
317
    except Exception as e:
318
        if request.debug: raise(e)
319
        request.session.flash('Error adding user to pool.', 'error')
320
        return HTTPFound(location=request.route_url('user_pools'))
321
322
323
@view_config(route_name='user_pool_changename_submit',
324
             request_method='POST',
325
             permission='user')
326
def user_pool_changename_submit(request):
327
    try:
328
        pool = Pool.from_id(request.POST['pool-id'])
329
        if pool.owner != request.user.id:
330
            request.session.flash('You do not have permission to view that pool.', 'error')
331
            return HTTPFound(location=request.route_url('user_pools'))
332
333
        pool_name = request.POST['newname'].strip()
334
        if len(pool_name) > 255:
335
            pool_name = pool_name[0:255]
336
        if len(pool_name) < 5:
337
            request.session.flash('Pool names must be at least 5 letters long', 'error')
338
            return HTTPFound(location=request.route_url('user_pool', pool_id=int(pool.id)))
339
340
        pool.name = pool_name
341
342
        request.session.flash('Pool created.', 'succcess')
343
        return HTTPFound(location=request.route_url('user_pool', pool_id=pool.id))
344
    except Exception as e:
345
        if request.debug: raise(e)
346
        request.session.flash('Error changing pool name.', 'error')
347
        return HTTPFound(location=request.route_url('user_pools'))
348
349
350
@view_config(route_name='user_password_edit',
351
             renderer='templates/user/password_edit.jinja2',
352
             permission='user')
353
def user_password_edit(request):
354
    return {}
355
356
357 View Code Duplication
@view_config(route_name='user_password_edit_submit',
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
358
             request_method='POST',
359
             permission='user')
360
def user_password_edit_submit(request):
361
    pwd0 = request.POST['edit-password-0']
362
    pwd1 = request.POST['edit-password-1']
363
    if pwd0 != pwd1:
364
        request.session.flash('Error: Passwords do not match', 'error')
365
        return HTTPFound(location=request.route_url('user_password_edit'))
366
    request.user.password = pwd0
367
    request.session.flash('Password changed successfully.', 'success')
368
    return HTTPFound(location=request.route_url('user_index'))
369
    # check that changing password for actually logged in user
370
371
372