app   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 274
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 189
dl 0
loc 274
rs 8.8
c 0
b 0
f 0
wmc 45

10 Functions

Rating   Name   Duplication   Size   Complexity  
C edit_page() 0 41 10
A logout() 0 6 2
B dashboard_page() 0 35 6
A legal_page() 0 3 1
A delete_account_page() 0 16 5
A search_user() 0 10 2
B register_page() 0 33 8
A index_page() 0 3 1
A delete_user() 0 23 4
B login_page() 0 23 6

How to fix   Complexity   

Complexity

Complex classes like app 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
import re
2
import secrets
3
from datetime import datetime
4
5
from flask import Flask, render_template, redirect, url_for, request, session
6
from flask_sqlalchemy import SQLAlchemy
7
8
from src.security import sha512
9
10
app = Flask(__name__)
11
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///bday.db'
12
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
13
db = SQLAlchemy(app)
14
15
app.config.from_object(__name__)
16
app.secret_key = secrets.token_urlsafe(32)
17
18
USERNAME_PATTERN = r'^([\w\d-]){4,32}$'
19
PASSWORD_PATTERN = (
20
    r'^.*(?=.{8,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[-_ @#$%^&+=]).*$'
21
)
22
23
24
# DB Model
25
class User(db.Model):
26
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
27
    pseudo = db.Column(db.String(32))
28
    password = db.Column(db.String(128))
29
    birthday = db.Column(db.String(10), default=None)
30
31
32
class Birthday(db.Model):
33
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
34
    user_id = db.Column(db.Integer)
35
    person_name = db.Column(db.String(32))
36
    person_birthday = db.Column(db.String(10))
37
38
39
db.drop_all()
40
db.create_all()
41
42
dummy_user = User(
43
    pseudo="dummy",
44
    password=(
45
        "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185"
46
        "f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"
47
    ),
48
    birthday="2001-12-11"
49
)
50
db.session.add(dummy_user)
51
db.session.commit()
52
53
54
@app.route('/', methods=('GET', 'POST'))
55
def index_page():
56
    return render_template('index.jinja2')
57
58
59
@app.route('/auth/login', methods=('GET', 'POST'))
60
def login_page():
61
    if session.get('user'):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable session does not seem to be defined.
Loading history...
62
        return redirect(url_for('dashboard_page'))
63
    if request.method == 'POST':
64
        username = request.form['username']
65
        password = request.form['password']
66
67
        if username and password:
68
            login = User.query.filter_by(
69
                pseudo=username,
70
                password=sha512(password)
71
            ).first()
72
73
            if login is not None:
74
                session['user'] = {
75
                    'name': username,
76
                    'id': login.id
77
                }
78
79
                return redirect(url_for('dashboard_page'))
80
81
    return render_template('auth/login.jinja2')
82
83
84
@app.route('/auth/register', methods=('GET', 'POST'))
85
def register_page():
86
    if session.get('user'):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable session does not seem to be defined.
Loading history...
87
        return redirect(url_for('dashboard_page'))
88
    if request.method == 'POST':
89
        username = request.form['username']
90
        password = request.form['password']
91
        confirm_password = request.form['confirm_password']
92
        birthday = request.form['date']
93
94
        if (
95
                re.match(USERNAME_PATTERN, username)
96
                and re.match(PASSWORD_PATTERN, password)
97
                and confirm_password
98
                and confirm_password == password
99
                and birthday
100
        ):
101
            new_user = User(
102
                pseudo=username,
103
                password=sha512(password),
104
                birthday=birthday
105
            )
106
107
            db.session.add(new_user)
108
            db.session.commit()
109
110
            session['user'] = {'name': username}
111
            return redirect(url_for('dashboard_page'))
112
113
    return render_template(
114
        'auth/register.jinja2',
115
        USERNAME_PATTERN=USERNAME_PATTERN,
116
        PASSWORD_PATTERN=PASSWORD_PATTERN
117
    )
118
119
120
@app.route('/dashboard', methods=('GET', 'POST'))
121
def dashboard_page():
122
    user = session.get('user')
123
124
    if not user:
125
        return redirect(url_for('login_page'))
126
127
    if not user.get('name'):
128
        return redirect(url_for('login_page'))
129
130
    if request.method == 'POST':
131
        username = request.form['username']
132
        date = request.form['date']
133
134
        if username and date:
135
            new_birthday = Birthday(
136
                person_name=username,
137
                person_birthday=date,
138
                user_id=user.get('id')
139
            )
140
141
            db.session.add(new_birthday)
142
            db.session.commit()
143
144
    birthdays = Birthday.query.filter_by(user_id=user.get('id')).all()
145
    now = datetime.now()
146
147
    return render_template(
148
        'dashboard.jinja2',
149
        birthdays=birthdays,
150
        today_birthdays=[
151
            birthday
152
            for birthday in birthdays
153
            if birthday.person_birthday.endswith(
154
                f'-{now.month:02}-{now.day:02}'
155
            )
156
        ]
157
    )
158
159
160
@app.route('/auth/delete', methods=('GET', 'POST'))
161
def delete_account_page():
162
    user = session.get('user')
163
164
    if not user:
165
        return redirect(url_for('login_page'))
166
167
    if not user.get('name'):
168
        return redirect(url_for('login_page'))
169
    if request.method == 'POST' and user.get('name') == request.form.get(
170
            'account_name'
171
    ):
172
        db.session.delete(User.query.filter_by(pseudo=user.get('name')).first())
173
        db.session.commit()
174
        return redirect(url_for('logout'))
175
    return render_template('auth/delete.jinja2')
176
177
178
@app.route('/legal')
179
def legal_page():
180
    return render_template('legal.jinja2')
181
182
183
@app.route('/auth/edit', methods=('GET', 'POST'))
184
def edit_page():
185
    user = session.get('user')
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable session does not seem to be defined.
Loading history...
186
187
    if not user:
188
        return redirect(url_for('login_page'))
189
190
    if not user.get('name'):
191
        return redirect(url_for('login_page'))
192
193
    if request.method == 'POST':
194
        if request.form.get('new_password'):
195
            new_password = request.form['new_password']
196
            confirm_password = request.form['confirm_new_password']
197
            old_password = request.form['old_password']
198
199
            if (
200
                    User.query.filter_by(
201
                        pseudo=user.get('name'), password=sha512(old_password)
202
                    ).first()
203
                    and new_password == confirm_password
204
            ):
205
                user = User.query.filter_by(pseudo=user.get('name')).first()
206
                user.password = sha512(new_password)
207
                db.session.commit()
208
209
        elif request.form.get('new_username'):
210
            new_username = request.form['new_username']
211
            confirm_username = request.form['confirm_new_username']
212
213
            if (
214
                    new_username == confirm_username
215
                    and not User.query.filter_by(pseudo=new_username).first()
216
            ):
217
                user = User.query.filter_by(pseudo=user.get('name')).first()
218
                user.pseudo = new_username
219
                db.session.commit()
220
221
                session['user'] = {'name': new_username}
222
223
    return render_template('auth/edit.jinja2')
224
225
226
@app.route('/delete/<index>')
227
def delete_user(index):
228
    user = session.get('user')
229
230
    if not user:
231
        return {}
232
233
    if not user.get('name'):
234
        return {}
235
236
    if not index.isdigit():
237
        return {}
238
239
    db.session.delete(
240
        Birthday.query.filter_by(
241
            user_id=user.get('id'),
242
            id=int(index)
243
        ).first()
244
    )
245
246
    db.session.commit()
247
248
    return {}
249
250
251
@app.route('/api/search/<user>')
252
def search_user(user):
253
    found_user: User = User.query.filter_by(pseudo=user).first()
254
    if not found_user:
255
        return {}
256
257
    return {
258
        'id': found_user.id,
259
        'name': found_user.pseudo,
260
        'birthday': found_user.birthday
261
    }
262
263
264
@app.route('/logout/')
265
def logout():
266
    if session.get('user'):
267
        session.pop('user')
268
269
    return redirect(url_for('index_page'))
270
271
272
if __name__ == '__main__':
273
    app.run(debug=True)
274