Obihoernchen /
ChaosWG-Manager
| 1 | from datetime import datetime |
||
| 2 | |||
| 3 | from flask import Flask, render_template, request, redirect, jsonify |
||
| 4 | from flask_babel import Babel |
||
| 5 | from flask_bootstrap import Bootstrap, WebCDN |
||
| 6 | from flask_login import LoginManager, login_user, logout_user, login_required, current_user |
||
| 7 | |||
| 8 | from chaoswg.admin import init_admin |
||
| 9 | from chaoswg.forms import RegisterForm, LoginForm, CreateTaskForm, CustomTaskForm |
||
| 10 | from chaoswg.helpers import format_datetime_custom, format_timedelta_custom |
||
| 11 | from chaoswg.models import init_database, create_tables, User, Task, History |
||
| 12 | from chaoswg.scheduler import TaskScheduler |
||
| 13 | |||
| 14 | # from test.testdata import insert_testdata |
||
| 15 | |||
| 16 | # init app and load config |
||
| 17 | app = Flask(__name__) |
||
|
0 ignored issues
–
show
|
|||
| 18 | # read ../default-config.py |
||
| 19 | app.config.from_pyfile('../default-config.py') |
||
| 20 | # overwrite with custom config in ../custom-config.py |
||
| 21 | app.config.from_pyfile('../custom-config.py', silent=True) |
||
| 22 | |||
| 23 | # init DB |
||
| 24 | database = init_database(app) |
||
|
0 ignored issues
–
show
The name
database does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 25 | create_tables() |
||
| 26 | # Comment out in production |
||
| 27 | # insert_testdata(database) |
||
| 28 | |||
| 29 | # init login manager |
||
| 30 | login_manager = LoginManager() |
||
|
0 ignored issues
–
show
The name
login_manager does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 31 | login_manager.init_app(app) |
||
| 32 | |||
| 33 | # Enable bootstrap support |
||
| 34 | Bootstrap(app) |
||
| 35 | # jQuery 3 instead of 1 |
||
| 36 | app.extensions['bootstrap']['cdns']['jquery'] = WebCDN( |
||
| 37 | '//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/' |
||
| 38 | ) |
||
| 39 | |||
| 40 | # Enable babel support |
||
| 41 | app.babel = Babel(app, default_timezone='Europe/Berlin') |
||
| 42 | # set datetime filter for jinja2 |
||
| 43 | app.jinja_env.filters['datetime'] = format_datetime_custom |
||
|
0 ignored issues
–
show
|
|||
| 44 | app.jinja_env.filters['timedelta'] = format_timedelta_custom |
||
|
0 ignored issues
–
show
|
|||
| 45 | |||
| 46 | # init admin interface |
||
| 47 | init_admin(app) |
||
| 48 | |||
| 49 | # Create the task scheduler thread |
||
| 50 | task_scheduler = TaskScheduler() |
||
|
0 ignored issues
–
show
The name
task_scheduler does not conform to the constant naming conventions ((([A-Z_][A-Z0-9_]*)|(__.*__))$).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 51 | |||
| 52 | |||
| 53 | @app.before_first_request |
||
| 54 | def start_task_scheduler(): |
||
| 55 | # Start the task scheduler thread only once even if app is in debug mode |
||
| 56 | task_scheduler.start() |
||
| 57 | |||
| 58 | |||
| 59 | @app.route('/') |
||
| 60 | def index(): |
||
| 61 | return render_template('index.html', usernames=User.get_usernames()) |
||
| 62 | |||
| 63 | |||
| 64 | @app.route('/register', methods=['GET', 'POST']) |
||
| 65 | def register(): |
||
| 66 | form = RegisterForm() |
||
| 67 | |||
| 68 | if form.validate_on_submit(): |
||
| 69 | if app.config['INVITE_KEY'] != form.invite_key.data: |
||
| 70 | # TODO return error message, wrong invite key |
||
|
0 ignored issues
–
show
|
|||
| 71 | return '', 403 |
||
| 72 | if User.register(form.name.data, form.password.data): |
||
|
0 ignored issues
–
show
|
|||
| 73 | # TODO return success message |
||
|
0 ignored issues
–
show
|
|||
| 74 | return redirect('/login') |
||
| 75 | else: |
||
| 76 | # TODO return error message, user exists |
||
|
0 ignored issues
–
show
|
|||
| 77 | return '', 403 |
||
| 78 | |||
| 79 | return render_template('register.html', form=form) |
||
| 80 | |||
| 81 | |||
| 82 | @app.route('/login', methods=['GET', 'POST']) |
||
| 83 | def login(): |
||
| 84 | form = LoginForm() |
||
| 85 | |||
| 86 | if form.validate_on_submit(): |
||
| 87 | user = User.get_by_name(form.name.data) |
||
| 88 | if user and user.check_password(form.password.data): |
||
| 89 | login_user(user, remember=True) |
||
| 90 | return redirect('/tasks') |
||
| 91 | |||
| 92 | return render_template('login.html', form=form) |
||
| 93 | |||
| 94 | |||
| 95 | @login_manager.user_loader |
||
| 96 | def load_user(user_id): |
||
| 97 | return User.get(User.id == user_id) |
||
| 98 | |||
| 99 | |||
| 100 | @login_manager.unauthorized_handler |
||
| 101 | def unauthorized(): |
||
| 102 | return redirect('/login') |
||
| 103 | |||
| 104 | |||
| 105 | @app.route('/logout') |
||
| 106 | @login_required |
||
| 107 | def logout(): |
||
| 108 | logout_user() |
||
| 109 | return redirect('/') |
||
| 110 | |||
| 111 | |||
| 112 | @app.route('/users') |
||
| 113 | @login_required |
||
| 114 | def get_users(): |
||
| 115 | users = User.get_all() |
||
| 116 | usernames = set() |
||
| 117 | for user in users: |
||
| 118 | usernames.add(user['username']) |
||
| 119 | return render_template('users.html', users=users, usernames=usernames) |
||
| 120 | |||
| 121 | |||
| 122 | @app.route('/history') |
||
| 123 | @login_required |
||
| 124 | def get_history(): |
||
| 125 | return render_template('history.html', usernames=User.get_usernames()) |
||
| 126 | |||
| 127 | |||
| 128 | @app.route('/history/<username>') |
||
| 129 | @login_required |
||
| 130 | def get_history_user(username): |
||
| 131 | return render_template('history_user.html', userhist=History.get_user_history(username), username=username, |
||
| 132 | usernames=User.get_usernames()) |
||
| 133 | |||
| 134 | |||
| 135 | @app.route('/create_task', methods=['GET', 'POST']) |
||
| 136 | @login_required |
||
| 137 | def create_task(): |
||
| 138 | form = CreateTaskForm() |
||
| 139 | |||
| 140 | if form.validate_on_submit(): |
||
| 141 | task, created = Task.get_or_create(task=form.task.data, defaults={'base_points': form.base_points.data, |
||
| 142 | 'time_factor': form.time_factor.data, |
||
| 143 | 'schedule_days': form.schedule_days.data}) |
||
| 144 | if not created: |
||
| 145 | # TODO return error message, task exists |
||
|
0 ignored issues
–
show
|
|||
| 146 | return '', 403 |
||
| 147 | |||
| 148 | # TODO return success message |
||
|
0 ignored issues
–
show
|
|||
| 149 | return redirect('/tasks') |
||
| 150 | |||
| 151 | return render_template('create_task.html', form=form) |
||
| 152 | |||
| 153 | |||
| 154 | @app.route('/do_custom_task', methods=['GET', 'POST']) |
||
| 155 | @login_required |
||
| 156 | def do_custom_task(): |
||
| 157 | form = CustomTaskForm() |
||
| 158 | |||
| 159 | if form.validate_on_submit(): |
||
| 160 | # do custom onetime task |
||
| 161 | Task.do_custom_task(form.task.data, form.points.data, current_user.get_id()) |
||
| 162 | return redirect('/tasks') |
||
| 163 | |||
| 164 | return render_template('do_custom_task.html', form=form) |
||
| 165 | |||
| 166 | |||
| 167 | @app.route('/tasks') |
||
| 168 | @login_required |
||
| 169 | def get_tasks(): |
||
| 170 | tasks = Task.get_all() |
||
| 171 | backlog = [t for t in tasks if t.state == Task.BACKLOG] |
||
| 172 | todo = [t for t in tasks if t.state == Task.TODO] |
||
| 173 | done = [t for t in tasks if t.state == Task.DONE] |
||
| 174 | |||
| 175 | progress = { |
||
| 176 | 'backlog': len(backlog) / len(tasks) * 100, |
||
| 177 | 'todo': len(todo) / len(tasks) * 100, |
||
| 178 | 'done': len(done) / len(tasks) * 100 |
||
| 179 | } |
||
| 180 | |||
| 181 | return render_template('tasks.html', backlog=backlog, todo=todo, done=done, progress=progress, |
||
| 182 | usernames=User.get_usernames()) |
||
| 183 | |||
| 184 | |||
| 185 | @app.route('/set_task_state', methods=['POST']) |
||
| 186 | @login_required |
||
| 187 | def set_task_state(): |
||
| 188 | user_id = current_user.get_id() |
||
| 189 | task_id = request.form.get('id', type=int) |
||
| 190 | state = request.form.get('state', type=int) |
||
| 191 | if None not in (task_id, state, user_id) and state in (0, 1, 2): |
||
| 192 | Task.set_state(task_id, state, user_id) |
||
| 193 | else: |
||
| 194 | # TODO message |
||
|
0 ignored issues
–
show
|
|||
| 195 | return '', 403 |
||
| 196 | # New state was set |
||
| 197 | return '', 204 |
||
| 198 | |||
| 199 | |||
| 200 | ######################### |
||
| 201 | # JSON endpoints for JS # |
||
| 202 | ######################### |
||
| 203 | |||
| 204 | @app.route('/json/history/<username>') |
||
| 205 | @login_required |
||
| 206 | def get_history_user_json(username): |
||
| 207 | return jsonify(History.get_user_history(username)) |
||
| 208 | |||
| 209 | |||
| 210 | @app.route('/json/users') |
||
| 211 | @login_required |
||
| 212 | def get_users_json(): |
||
| 213 | return jsonify(User.get_all()) |
||
| 214 | |||
| 215 | |||
| 216 | @app.route('/json/history') |
||
| 217 | @login_required |
||
| 218 | def get_history_json(): |
||
| 219 | hist = History.get_full_history() |
||
| 220 | result = {} |
||
| 221 | for h in hist: |
||
|
0 ignored issues
–
show
The name
h does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).
This check looks for invalid names for a range of different identifiers. You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements. If your project includes a Pylint configuration file, the settings contained in that file take precedence. To find out more about Pylint, please refer to their site. Loading history...
|
|||
| 222 | if h['username'] not in result: |
||
| 223 | # initial 0 points |
||
| 224 | result[h['username']] = [{ |
||
| 225 | 'time': h['time'], |
||
| 226 | 'points': 0 |
||
| 227 | }] |
||
| 228 | result[h['username']].append({ |
||
| 229 | 'time': h['time'], |
||
| 230 | 'points': h['points'] |
||
| 231 | }) |
||
| 232 | |||
| 233 | # same point count till today |
||
| 234 | for user in result: |
||
| 235 | result[user].append({ |
||
| 236 | 'time': datetime.utcnow(), |
||
| 237 | 'points': 0 |
||
| 238 | }) |
||
| 239 | |||
| 240 | return jsonify(result) |
||
| 241 |
This check looks for invalid names for a range of different identifiers.
You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.
If your project includes a Pylint configuration file, the settings contained in that file take precedence.
To find out more about Pylint, please refer to their site.