app.main   F
last analyzed

Complexity

Total Complexity 66

Size/Duplication

Total Lines 514
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 372
dl 0
loc 514
rs 3.12
c 0
b 0
f 0
wmc 66

30 Functions

Rating   Name   Duplication   Size   Complexity  
A clear_holds() 0 9 1
B changepw() 0 22 6
A schedule_delete() 0 13 2
A appleicon() 0 4 1
A balance() 0 15 2
A holds_delete() 0 13 2
A skips_delete() 0 13 2
A clear_skips() 0 9 1
A favicon() 0 4 1
B signups() 0 30 5
A export_csv() 0 12 1
A transactions() 0 8 1
B update_user() 0 26 5
A delete_user() 0 13 2
B update() 0 30 6
A addhold() 0 12 1
A refresh() 0 5 1
A import_csv() 0 12 2
A users() 0 7 1
A holds() 0 8 1
A serve_manifest() 0 3 1
A serve_sw() 0 3 1
A schedule() 0 7 1
A create_user() 0 25 4
A settings() 0 8 1
A profile() 0 8 2
A email() 0 39 3
A index() 0 32 3
A create() 0 29 3
A addskip() 0 19 3

How to fix   Complexity   

Complexity

Complex classes like app.main 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 flask import request, redirect, url_for, send_from_directory, flash, send_file, Response
2
from flask_login import login_required, current_user
3
from flask import Blueprint, render_template
4
from .models import Schedule, Balance, User, Settings, Email, Hold, Skip
5
from app import db
6
from datetime import datetime
7
import os
8
from sqlalchemy import desc, extract, asc
9
from werkzeug.security import generate_password_hash, check_password_hash
10
from .cashflow import update_cash, plot_cash
11
from .auth import admin_required
12
from .files import export, upload, version
13
14
15
main = Blueprint('main', __name__)
16
17
18
@main.route('/', methods=('GET', 'POST'))
19
@login_required
20
def index():
21
    # get today's date
22
    todaydate = datetime.today().strftime('%A, %B %d, %Y')
23
24
    # query the latest balance information
25
    balance = Balance.query.order_by(desc(Balance.date), desc(Balance.id)).first()
26
27
    try:
28
        float(balance.amount)
29
        db.session.query(Balance).delete()
30
        balance = Balance(amount=balance.amount, date=datetime.today())
31
        db.session.add(balance)
32
        db.session.commit()
33
    except:
34
        balance = Balance(amount='0',
35
                          date=datetime.today())
36
        db.session.add(balance)
37
        db.session.commit()
38
39
    trans, run = update_cash(float(balance.amount))
40
41
    # plot cash flow results
42
    minbalance, graphJSON = plot_cash(run)
43
44
    if current_user.admin:
45
        return render_template('index.html', title='Index', todaydate=todaydate, balance=balance.amount,
46
                           minbalance=minbalance, graphJSON=graphJSON)
47
    else:
48
        return render_template('index_guest.html', title='Index', todaydate=todaydate, balance=balance.amount,
49
                           minbalance=minbalance, graphJSON=graphJSON)
50
51
52
@main.route('/refresh')
53
@login_required
54
def refresh():
55
56
    return redirect(url_for('main.index'))
57
58
59
@main.route('/profile')
60
@login_required
61
def profile():
62
63
    if current_user.admin:
64
        return render_template('profile.html')
65
    else:
66
        return render_template('profile_guest.html')
67
68
69
@main.route('/settings')
70
@login_required
71
@admin_required
72
def settings():
73
    # get about info
74
    about = version()
75
76
    return render_template('settings.html', about=about)
77
78
79
@main.route('/schedule')
80
@login_required
81
@admin_required
82
def schedule():
83
    schedule = Schedule.query.order_by(asc(extract('day', Schedule.startdate)))
84
85
    return render_template('schedule_table.html', title='Schedule Table', schedule=schedule)
86
87
88
@main.route('/holds')
89
@login_required
90
@admin_required
91
def holds():
92
    hold = Hold.query
93
    skip = Skip.query
94
95
    return render_template('holds_table.html', title='Holds Table', hold=hold, skip=skip)
96
97
98
@main.route('/create', methods=('GET', 'POST'))
99
@login_required
100
@admin_required
101
def create():
102
    # create a new schedule item
103
    format = '%Y-%m-%d'
104
    if request.method == 'POST':
105
        name = request.form['name']
106
        amount = request.form['amount']
107
        frequency = request.form['frequency']
108
        startdate = request.form['startdate']
109
        type = request.form['type']
110
        schedule = Schedule(name=name,
111
                            type=type,
112
                            amount=amount,
113
                            frequency=frequency,
114
                            startdate=datetime.strptime(startdate, format).date(),
115
                            firstdate=datetime.strptime(startdate, format).date())
116
        existing = Schedule.query.filter_by(name=name).first()
117
        if existing:
118
            flash("Schedule already exists")
119
            return redirect(url_for('main.schedule'))
120
        db.session.add(schedule)
121
        db.session.commit()
122
        flash("Added Successfully")
123
124
        return redirect(url_for('main.schedule'))
125
126
    return redirect(url_for('main.schedule'))
127
128
129
@main.route('/update', methods=['GET', 'POST'])
130
@login_required
131
@admin_required
132
def update():
133
    # update an existing schedule item
134
    format = '%Y-%m-%d'
135
136
    if request.method == 'POST':
137
        current = Schedule.query.filter_by(id=request.form['id']).first()
138
        existing = Schedule.query.filter_by(name=request.form['name']).first()
139
        if existing:
140
            if current.name != request.form['name']:
141
                flash("Schedule name already exists")
142
                return redirect(url_for('main.schedule'))
143
        my_data = Schedule.query.get(request.form.get('id'))
144
        my_data.name = request.form['name']
145
        my_data.amount = request.form['amount']
146
        my_data.type = request.form['type']
147
        my_data.frequency = request.form['frequency']
148
        startdate = request.form['startdate']
149
        if (datetime.strptime(startdate, format).date() != my_data.startdate and my_data.startdate.day !=
150
                datetime.strptime(startdate, format).day):
151
            my_data.firstdate = datetime.strptime(startdate, format).date()
152
        my_data.startdate = datetime.strptime(startdate, format).date()
153
        db.session.commit()
154
        flash("Updated Successfully")
155
156
        return redirect(url_for('main.schedule'))
157
158
    return redirect(url_for('main.schedule'))
159
160
161
@main.route('/addhold/<id>')
162
@login_required
163
@admin_required
164
def addhold(id):
165
    # add a hold item from the schedule
166
    schedule = Schedule.query.filter_by(id=id).first()
167
    hold = Hold(name=schedule.name, type=schedule.type, amount=schedule.amount)
168
    db.session.add(hold)
169
    db.session.commit()
170
    flash("Added Hold")
171
172
    return redirect(url_for('main.schedule'))
173
174
175
@main.route('/addskip/<id>')
176
@login_required
177
@admin_required
178
def addskip(id):
179
    # add a skip item from the schedule
180
    balance = Balance.query.order_by(desc(Balance.date), desc(Balance.id)).first()
181
    trans , run = update_cash(float(balance.amount))
182
    transaction = trans.loc[int(id)]
183
    trans_type = ""
184
    if transaction[1] == "Expense":
185
        trans_type = "Income"
186
    elif transaction[1] == "Income":
187
        trans_type = "Expense"
188
    skip = Skip(name=transaction[0] + " (SKIP)", type=trans_type, amount=transaction[2], date=transaction[3])
189
    db.session.add(skip)
190
    db.session.commit()
191
    flash("Added Skip")
192
193
    return redirect(url_for('main.transactions'))
194
195
196
@main.route('/deletehold/<id>')
197
@login_required
198
@admin_required
199
def holds_delete(id):
200
    # delete a hold item
201
    hold = Hold.query.filter_by(id=id).first()
202
203
    if hold:
204
        db.session.delete(hold)
205
        db.session.commit()
206
        flash("Deleted Successfully")
207
208
    return redirect(url_for('main.holds'))
209
210
211
@main.route('/deleteskip/<id>')
212
@login_required
213
@admin_required
214
def skips_delete(id):
215
    # delete a skip item
216
    skip = Skip.query.filter_by(id=id).first()
217
218
    if skip:
219
        db.session.delete(skip)
220
        db.session.commit()
221
        flash("Deleted Successfully")
222
223
    return redirect(url_for('main.holds'))
224
225
226
@main.route('/clearholds')
227
@login_required
228
@admin_required
229
def clear_holds():
230
    # clear holds
231
    db.session.query(Hold).delete()
232
    db.session.commit()
233
234
    return redirect(url_for('main.holds'))
235
236
237
@main.route('/clearskips')
238
@login_required
239
@admin_required
240
def clear_skips():
241
    # clear skips
242
    db.session.query(Skip).delete()
243
    db.session.commit()
244
245
    return redirect(url_for('main.holds'))
246
247
248
@main.route('/delete/<id>')
249
@login_required
250
@admin_required
251
def schedule_delete(id):
252
    # delete a schedule item
253
    schedule = Schedule.query.filter_by(id=id).first()
254
255
    if schedule:
256
        db.session.delete(schedule)
257
        db.session.commit()
258
        flash("Deleted Successfully")
259
260
    return redirect(url_for('main.schedule'))
261
262
263
@main.route('/favicon')
264
def favicon():
265
    return send_from_directory(os.path.join(main.root_path, 'static'),
266
                               'favicon.ico', mimetype='image/vnd.microsoft.icon')
267
268
269
@main.route('/appleicon')
270
def appleicon():
271
    return send_from_directory(os.path.join(main.root_path, 'static'),
272
                               'apple-touch-icon.png', mimetype='image/png')
273
274
275
@main.route('/balance', methods=('GET', 'POST'))
276
@login_required
277
@admin_required
278
def balance():
279
    # manually update the balance from the balance button
280
    format = '%Y-%m-%d'
281
    if request.method == 'POST':
282
        amount = request.form['amount']
283
        dateentry = request.form['date']
284
        balance = Balance(amount=amount,
285
                          date=datetime.strptime(dateentry, format).date())
286
        db.session.add(balance)
287
        db.session.commit()
288
289
        return redirect(url_for('main.index'))
290
291
292
@main.route('/changepw', methods=('GET', 'POST'))
293
@login_required
294
def changepw():
295
    # change the users password from the profile page
296
    if request.method == 'POST':
297
        curr_user = current_user.id
298
        my_user = User.query.filter_by(id=curr_user).first()
299
        current = request.form['current']
300
        password = request.form['password']
301
        password2 = request.form['password2']
302
        if password == password2 and check_password_hash(my_user.password, current):
303
            my_user.password = generate_password_hash(password, method='scrypt')
304
            db.session.commit()
305
            flash('Password change successful')
306
        elif password != password2:
307
            flash('Passwords do not match')
308
        elif not check_password_hash(my_user.password, current):
309
            flash('Incorrect password')
310
311
        return redirect(url_for('main.profile'))
312
313
    return redirect(url_for('main.profile'))
314
315
316
@main.route('/signups', methods=('GET', 'POST'))
317
@login_required
318
@admin_required
319
def signups():
320
    # set the settings options, in this case disable signups, from the profile page
321
    if request.method == 'POST':
322
        signupsettingname = Settings.query.filter_by(name='signup').first()
323
324
        if signupsettingname:
325
            if request.form['signupvalue'] == "True":
326
                signupvalue = True
327
            else:
328
                signupvalue = False
329
            signupsettingname.value = signupvalue
330
            db.session.commit()
331
332
            return redirect(url_for('main.settings'))
333
334
        # store the signup option value in the database to check when the user clicks signup
335
        if request.form['signupvalue'] == "True":
336
            signupvalue = True
337
        else:
338
            signupvalue = False
339
        settings = Settings(name="signup", value=signupvalue)
340
        db.session.add(settings)
341
        db.session.commit()
342
343
        return redirect(url_for('main.settings'))
344
345
    return redirect(url_for('main.settings'))
346
347
348
@main.route('/transactions')
349
@login_required
350
@admin_required
351
def transactions():
352
    balance = Balance.query.order_by(desc(Balance.date), desc(Balance.id)).first()
353
    trans, run = update_cash(float(balance.amount))
354
355
    return render_template('transactions_table.html', total=trans.to_dict(orient='records'))
356
357
358
@main.route('/email', methods=('GET', 'POST'))
359
@login_required
360
@admin_required
361
def email():
362
    # set the users email address, password, and server for the auto email balance update
363
    if request.method == 'POST':
364
        emailsettings = Email.query.filter_by(id=1).first()
365
366
        if emailsettings:
367
            email = request.form['email']
368
            password = request.form['password']
369
            server = request.form['server']
370
            subjectstr = request.form['subject_str']
371
            startstr = request.form['start_str']
372
            endstr = request.form['end_str']
373
            emailsettings.email = email
374
            emailsettings.password = password
375
            emailsettings.server = server
376
            emailsettings.subjectstr = subjectstr
377
            emailsettings.startstr = startstr
378
            emailsettings.endstr = endstr
379
            db.session.commit()
380
381
            return redirect(url_for('main.settings'))
382
383
        email = request.form['email']
384
        password = request.form['password']
385
        server = request.form['server']
386
        subjectstr = request.form['subject_str']
387
        startstr = request.form['start_str']
388
        endstr = request.form['end_str']
389
        emailentry = Email(email=email, password=password, server=server, subjectstr=subjectstr, startstr=startstr,
390
                           endstr=endstr)
391
        db.session.add(emailentry)
392
        db.session.commit()
393
394
        return redirect(url_for('main.settings'))
395
396
    return redirect(url_for('main.settings'))
397
398
399
@main.route('/users_table')
400
@login_required
401
@admin_required
402
def users():
403
    users = User.query
404
405
    return render_template('users_table.html', title='Users Table', users=users)
406
407
408
@main.route('/update_user', methods=['GET', 'POST'])
409
@login_required
410
@admin_required
411
def update_user():
412
    # update an existing user
413
    if request.method == 'POST':
414
        current = User.query.filter_by(id=request.form['id']).first()
415
        existing = User.query.filter_by(email=request.form['email']).first()
416
        if existing:
417
            if current.email != request.form['email']:
418
                flash("Email already exists")
419
                return redirect(url_for('main.users'))
420
        my_data = User.query.get(request.form.get('id'))
421
        my_data.name = request.form['name']
422
        my_data.email = request.form['email']
423
        if request.form['admin'] == "True":
424
            adminvalue = True
425
        else:
426
            adminvalue = False
427
        my_data.admin = adminvalue
428
        db.session.commit()
429
        flash("Updated Successfully")
430
431
        return redirect(url_for('main.users'))
432
433
    return redirect(url_for('main.users'))
434
435
436
@main.route('/delete_user/<id>')
437
@login_required
438
@admin_required
439
def delete_user(id):
440
    # delete a user
441
    user = User.query.filter_by(id=id).first()
442
443
    if user:
444
        db.session.delete(user)
445
        db.session.commit()
446
        flash("Deleted Successfully")
447
448
    return redirect(url_for('main.users'))
449
450
451
@main.route('/create_user', methods=('GET', 'POST'))
452
@login_required
453
@admin_required
454
def create_user():
455
    # create a new user
456
    if request.method == 'POST':
457
        name = request.form['name']
458
        email = request.form['email']
459
        if request.form['admin'] == "True":
460
            adminvalue = True
461
        else:
462
            adminvalue = False
463
        password = generate_password_hash(request.form['password'], method='scrypt')
464
        user = User(name=name, email=email, admin=adminvalue, password=password)
465
        existing = User.query.filter_by(email=email).first()
466
        if existing:
467
            flash("User already exists")
468
            return redirect(url_for('main.users'))
469
        db.session.add(user)
470
        db.session.commit()
471
        flash("Added Successfully")
472
473
        return redirect(url_for('main.users'))
474
475
    return redirect(url_for('main.users'))
476
477
478
@main.route('/export', methods=('GET', 'POST'))
479
@login_required
480
@admin_required
481
def export_csv():
482
483
    csv_data = export()
484
485
    # Create a direct download response with the CSV data and appropriate headers
486
    response = Response(csv_data, content_type="text/csv")
487
    response.headers["Content-Disposition"] = "attachment; filename=schedule_export.csv"
488
489
    return response
490
491
492
@main.route('/import', methods=('GET', 'POST'))
493
@login_required
494
@admin_required
495
def import_csv():
496
    if request.method == 'POST':
497
        csv_file = request.files.get('file')
498
499
        upload(csv_file)
500
501
        flash("Import Successful")
502
503
    return redirect(url_for('main.schedule'))
504
505
506
@main.route('/manifest.json')
507
def serve_manifest():
508
    return send_file('manifest.json', mimetype='application/manifest+json')
509
510
511
@main.route('/sw.js')
512
def serve_sw():
513
    return send_file('sw.js', mimetype='application/javascript')
514