def sign_up(): form = VolunteerSignUpForm() # On sign up give user 'volunteer' permission (+ managers etc.) if current_user.is_anonymous and form.validate_on_submit(): create_current_user(form.email.data, form.name.data) if form.validate_on_submit(): new_volunteer = VolunteerUser() new_volunteer.user_id = current_user.id new_volunteer = update_volunteer_from_form(new_volunteer, form) db.session.add(new_volunteer) db.session.commit() app.logger.info('Add volunteer: %s', new_volunteer) return redirect(url_for('.choose_role')) elif not current_user.is_anonymous: form.email.data = current_user.email form.name.data = current_user.name form.phone_number.data = current_user.phone return render_template('volunteer/sign-up.html', user=current_user, form=form)
def create_volunteer_data(self, user): vol = Volunteer() vol.user = user vol.missing_shifts_opt_in = randombool(0.5) vol.banned = randombool(0.05) vol.volunteer_phone = self.fake.phone_number() vol.over_18 = randombool(0.2) vol.allow_comms_during_event = randombool(0.8) vol.volunteer_email = user.email vol.nickname = user.name db.session.add(vol)
def _toggle_shift_entry(user, shift): res = {} shift_entry = ShiftEntry.query.filter_by(user_id=user.id, shift_id=shift.id).first() if shift.role == Role.get_by_name('Bar') and \ Role.get_by_name('Bar') not in Volunteer.get_for_user(current_user).trained_roles: return { 'warning': 'Missing required training', 'message': 'You must complete bar training before you can sign up for this shift' } if shift_entry: db.session.delete(shift_entry) res['operation'] = 'delete' res['message'] = 'Cancelled %s shift' % shift.role.name else: for v_shift in user.shift_entries: if shift.is_clash(v_shift.shift): res['warning'] = "WARNING: Clashes with an existing shift" shift.entries.append(ShiftEntry(user=user, shift=shift)) res['operation'] = 'add' res['message'] = 'Signed up for %s shift' % shift.role.name return res
def role(role_id): role = Role.query.get_or_404(role_id) current_volunteer = VolunteerUser.get_for_user(current_user) current_role_ids = [r.id for r in current_volunteer.interested_roles] if request.method == "POST": if int(role_id) in current_role_ids: role_name = Role.query.get(role_id).name flash( f"You are already signed up to the {role_name} role. If you " "would like to remove it, please use the form below." ) else: current_volunteer.interested_roles.append(role) db.session.commit() flash("Your role list has been updated", "info") return redirect(url_for(".choose_role")) return render_template( "volunteer/role.html", description=role.full_description, role=role, current_volunteer=current_volunteer, )
def choose_role(): form = RoleSignupForm() current_volunteer = VolunteerUser.get_for_user(current_user) if not current_volunteer.over_18: roles = Role.query.filter_by(over_18_only=False) else: roles = Role.query form.add_roles(roles.order_by(Role.name).all()) if form.validate_on_submit(): current_role_ids = [r.id for r in current_volunteer.interested_roles] for r in form.roles: r_id = r._role.id if r.signup.data and r_id not in current_role_ids: current_volunteer.interested_roles.append(r._role) elif not r.signup.data and r_id in current_role_ids: current_volunteer.interested_roles.remove(r._role) db.session.commit() flash("Your role list has been updated", 'info') return redirect(url_for('.choose_role')) current_roles = current_volunteer.interested_roles.all() if current_roles: role_ids = [r.id for r in current_roles] form.select_roles(role_ids) return render_template('volunteer/choose_role.html', form=form)
def role(role_id): role = Role.query.get_or_404(role_id) current_volunteer = VolunteerUser.get_for_user(current_user) if request.method == "POST": if role_id in current_volunteer.interested_roles: current_volunteer.interested_roles.remove(role) else: current_volunteer.interested_roles.append(role) db.session.commit() flash("Your role list has been updated", "info") return redirect(url_for(".choose_role")) role_description_file = role_name_to_markdown_file(role.name) if path.exists(role_description_file): content = open(role_description_file, "r").read() description = Markup( markdown.markdown(content, extensions=["markdown.extensions.nl2br"]) ) else: description = None return render_template( "volunteer/role.html", description=description, role=role, current_volunteer=current_volunteer, )
def _toggle_shift_entry(user, shift): res = {} shift_entry = ShiftEntry.query.filter_by(user_id=user.id, shift_id=shift.id).first() if (shift.role.requires_training and shift.role not in Volunteer.get_for_user(current_user).trained_roles): return { "warning": "Missing required training", "message": "You must complete training before you can sign up for this shift", } if shift_entry: db.session.delete(shift_entry) res["operation"] = "delete" res["message"] = "Cancelled %s shift" % shift.role.name else: for v_shift in user.shift_entries: if shift.is_clash(v_shift.shift): res["warning"] = "WARNING: Clashes with an existing shift" res["message"] = "You've not been signed up for this shift" return res if shift.current_count >= shift.max_needed: res["warning"] = "WARNING: Shift is already full" res["message"] = "You've not been signed up for this shift" return res shift.entries.append(ShiftEntry(user=user, shift=shift)) res["operation"] = "add" res["message"] = "Signed up for %s shift" % shift.role.name return res
def bar_training(): bar = Role.query.filter_by(name="Bar").one() volunteer = Volunteer.get_for_user(current_user) trained = bar in volunteer.trained_roles params = {"token": current_user.bar_training_token, "name": current_user.name} url = app.config["BAR_TRAINING_FORM"] + "?" + urlencode(params) return render_template( "volunteer/training/bar-training.html", url=url, trained=trained )
def bar_training(): bar = Role.query.filter_by(name="Bar").one() volunteer = Volunteer.get_for_user(current_user) trained = (bar in volunteer.trained_roles) params = { 'token': current_user.bar_training_token, 'name': current_user.name, } url = app.config['BAR_TRAINING_FORM'] + '?' + urlencode(params) return render_template('volunteer/training/bar-training.html', url=url, trained=trained)
def _get_interested_roles(user): roles = Role.get_all() volunteer = Volunteer.get_for_user(user) res = [] for r in roles: to_add = r.to_dict() to_add["is_interested"] = r in volunteer.interested_roles to_add["is_trained"] = r in volunteer.trained_roles res.append(to_add) return res
def shift_json(shift_id): shift = Shift.query.get_or_404(shift_id) if request.method == "POST": override_user_id = request.args.get("override_user", default=None) if (current_user.has_permission("volunteer:admin") and override_user_id is not None): override_user = User.query.get_or_404(override_user_id) msg = _toggle_shift_entry(override_user, shift) msg["user"] = Volunteer.get_for_user(override_user).nickname else: msg = _toggle_shift_entry(current_user, shift) db.session.commit() return jsonify(msg) return jsonify(shift)
def account(): volunteer = VolunteerUser.get_for_user(current_user) if volunteer is None: return redirect(url_for('.sign_up')) form = VolunteerSignUpForm() if form.validate_on_submit(): update_volunteer_from_form(volunteer, form) db.session.commit() return redirect(url_for('.account')) form.name.data = volunteer.nickname form.email.data = volunteer.volunteer_email form.phone_number.data = volunteer.volunteer_phone form.age.data = volunteer.age form.arrival.data = volunteer.planned_arrival form.departure.data = volunteer.planned_departure return render_template('volunteer/sign-up.html', user=current_user, form=form)
def choose_role(): form = RoleSignupForm() current_volunteer = VolunteerUser.get_for_user(current_user) if not current_volunteer.over_18: roles = Role.query.filter_by(over_18_only=False) else: roles = Role.query form.add_roles(roles.order_by(Role.name).all()) if form.validate_on_submit(): current_role_ids = [r.id for r in current_volunteer.interested_roles] for r in form.roles: r_id = r._role.id if r.signup.data and r_id not in current_role_ids: current_volunteer.interested_roles.append(r._role) elif not r.signup.data and r_id in current_role_ids: current_volunteer.interested_roles.remove(r._role) db.session.commit() flash("Your role list has been updated", "info") return redirect(url_for(".choose_role")) current_roles = current_volunteer.interested_roles.all() if current_roles: role_ids = [r.id for r in current_roles] form.select_roles(role_ids) if uninterested_roles := [ se.shift.role for se in current_user.shift_entries if se.shift.role not in current_roles ]: ui_roles_str = ", ".join([uir.name for uir in uninterested_roles]) flash( f"You are still signed up for shifts for {ui_roles_str}. " + "Please cancel them from Shift sign-up if you don't want to do them." )
def train_users(role_id): role = Role.get_by_id(role_id) form = TrainingForm() form.add_volunteers(Volunteer.get_all()) if form.validate_on_submit(): changes = 0 for v in form.volunteers: if v.trained.data and v._volunteer not in role.trained_volunteers: changes += 1 role.trained_volunteers.append(v._volunteer) elif not v.trained.data and v._volunteer in role.trained_volunteers: changes += 1 role.trained_volunteers.remove(v._volunteer) db.session.commit() flash("Trained %d volunteers" % changes) app.logger.info("Trained %d volunteers" % changes) return redirect(url_for(".train_users", role_id=role_id)) for v in role.trained_volunteers: for f in form.volunteers: if f.volunteer_id.data == v.id: f.trained.data = True break # Sort people who've been trained to the top then by nickname form.volunteers = sorted( form.volunteers, key=lambda f: (-1 if f.trained.data else 1, f._volunteer.nickname), ) return render_template("volunteer/training/train_users.html", role=role, form=form)
def role(role_id): role = Role.query.get_or_404(role_id) current_volunteer = VolunteerUser.get_for_user(current_user) if request.method == "POST": if role_id in current_volunteer.interested_roles: current_volunteer.interested_roles.remove(role) else: current_volunteer.interested_roles.append(role) db.session.commit() flash("Your role list has been updated", "info") return redirect(url_for('.choose_role')) role_description_file = role_name_to_markdown_file(role.name) if path.exists(role_description_file): content = open(role_description_file, 'r').read() description = Markup(markdown.markdown(content, extensions=["markdown.extensions.nl2br"])) else: description = None return render_template('volunteer/role.html', description=description, role=role, current_volunteer=current_volunteer)
def bar_training_check(): volunteer = Volunteer.get_for_user(current_user) bar = Role.query.filter_by(name="Bar").one() return json.dumps(bar in volunteer.trained_roles)
def bar_training_webhook(tag): if not hmac.compare_digest(get_auth_tag(), tag): abort(401) if request.method == "GET": return ("", 200) app.logger.debug("Bar training webhook called with %s", request.data) json_data = json.loads(request.data.decode("utf-8")) if json_data.get("event_type") != "form_response": # Don't care about this event type return ("", 200) response = json_data["form_response"] form_id = response["form_id"] if form_id != app.config["BAR_TRAINING_FORM"].rsplit("/", 1)[1]: return ("", 200) app.logger.info("Received form with hidden parameters %s", response["hidden"]) token = response["hidden"].get("token") if not token: return ("", 200) user = User.get_by_bar_training_token(token) if not user.volunteer: return ("", 200) assert response["definition"]["id"] == form_id # response['calculated']['score'] is the number of answered questions # the "correct" answers have just been implemented as flow redirects answers = response["answers"] actual_answers = { "IqrW3FemheSD": "More than 0.5%", "tp0LL9XwEu41": "Protection of the environment", "tb9aGkLhJCJ0": "In day-to-day control of a particular licensed premises", "yB9izDMlu8N6": "No", "xy8AJqMKZAOH": "PASS hologram", "YHOch7ksAKax": "£90", "YX1fDJQOKOmQ": "60 minutes", "z5RRMJhZiYJG": "Details of the licensable activities to be held at the premises", "bmfSCzY4xQHe": "Outside the times stated in the premises licence", "ODyrmHU3XR2f": "2", "lToop6d3nun2": "18", "iZeKUVN9n33f": "Staggering or an inability to walk", } answers = {} for answer in response["answers"]: if answer["type"] == "choice": answers[answer["field"]["id"]] = answer["choice"]["label"] correct_answers = [ id for id, a in answers.items() if actual_answers[id] == a ] if len(correct_answers) == len(actual_answers): app.logger.info("%s passed the training", user) bar = Role.query.filter_by(name="Bar").one() bar.trained_volunteers.append(Volunteer.get_for_user(user)) db.session.commit() app.logger.info("%s failed the training", user) return ("", 200)
def bar_training_webhook(tag): if not hmac.compare_digest(get_auth_tag(), tag): abort(401) if request.method == 'GET': return ('', 200) app.logger.debug('Bar training webhook called with %s', request.data) json_data = json.loads(request.data.decode('utf-8')) if json_data.get('event_type') != 'form_response': # Don't care about this event type return ('', 200) response = json_data['form_response'] form_id = response['form_id'] if form_id != app.config['BAR_TRAINING_FORM'].rsplit('/', 1)[1]: return ('', 200) app.logger.info("Received form with hidden parameters %s", response['hidden']) token = response['hidden'].get('token') if not token: return ('', 200) user = User.get_by_bar_training_token(token) if not user.volunteer: return ('', 200) assert response['definition']['id'] == form_id # response['calculated']['score'] is the number of answered questions # the "correct" answers have just been implemented as flow redirects answers = response['answers'] actual_answers = { 'IqrW3FemheSD': "More than 0.5%", 'tp0LL9XwEu41': "Protection of the environment", 'tb9aGkLhJCJ0': "In day-to-day control of a particular licensed premises", 'yB9izDMlu8N6': "No", 'xy8AJqMKZAOH': "PASS hologram", 'YHOch7ksAKax': "£90", 'YX1fDJQOKOmQ': "60 minutes", 'z5RRMJhZiYJG': "Details of the licensable activities to be held at the premises", 'bmfSCzY4xQHe': "Outside the times stated in the premises licence", 'ODyrmHU3XR2f': "2", 'lToop6d3nun2': "18", 'iZeKUVN9n33f': "Staggering or an inability to walk", } answers = {} for answer in response['answers']: if answer['type'] == 'choice': answers[answer['field']['id']] = answer['choice']['label'] correct_answers = [ id for id, a in answers.items() if actual_answers[id] == a ] if len(correct_answers) == len(actual_answers): app.logger.info("%s passed the training", user) bar = Role.query.filter_by(name="Bar").one() bar.trained_volunteers.append(Volunteer.get_for_user(user)) db.session.commit() app.logger.info("%s failed the training", user) return ('', 200)
def bar_training_webhook(tag): if not hmac.compare_digest(get_auth_tag(), tag): abort(401) if request.method == 'GET': return ('', 200) app.logger.debug('Bar training webhook called with %s', request.data) json_data = json.loads(request.data.decode('utf-8')) if json_data.get('event_type') != 'form_response': # Don't care about this event type return ('', 200) response = json_data['form_response'] form_id = response['form_id'] if form_id != app.config['BAR_TRAINING_FORM'].rsplit('/', 1)[1]: return ('', 200) app.logger.info("Received form with hidden parameters %s", response['hidden']) token = response['hidden'].get('token') if not token: return ('', 200) user = User.get_by_bar_training_token(token) if not user.volunteer: return ('', 200) assert response['definition']['id'] == form_id # response['calculated']['score'] is the number of answered questions # the "correct" answers have just been implemented as flow redirects answers = response['answers'] actual_answers = { 'IqrW3FemheSD': "More than 0.5%", 'tp0LL9XwEu41': "Protection of the environment", 'tb9aGkLhJCJ0': "In day-to-day control of a particular licensed premises", 'yB9izDMlu8N6': "No", 'xy8AJqMKZAOH': "PASS hologram", 'YHOch7ksAKax': "£90", 'YX1fDJQOKOmQ': "60 minutes", 'z5RRMJhZiYJG': "Details of the licensable activities to be held at the premises", 'bmfSCzY4xQHe': "Outside the times stated in the premises licence", 'ODyrmHU3XR2f': "2", 'lToop6d3nun2': "18", 'iZeKUVN9n33f': "Staggering or an inability to walk", } answers = {} for answer in response['answers']: if answer['type'] == 'choice': answers[answer['field']['id']] = answer['choice']['label'] correct_answers = [id for id, a in answers.items() if actual_answers[id] == a] if len(correct_answers) == len(actual_answers): app.logger.info("%s passed the training", user) bar = Role.query.filter_by(name="Bar").one() bar.trained_volunteers.append(Volunteer.get_for_user(user)) db.session.commit() app.logger.info("%s failed the training", user) return ('', 200)
def bar_training(): bar = Role.query.filter_by(name="Bar").one_or_none() cybar = Role.query.filter_by(name="Cybar").one_or_none() if bar is None or cybar is None: abort(404) volunteer = Volunteer.get_for_user(current_user) trained = bar in volunteer.trained_roles training_json_path = app.config.get("BAR_TRAINING_JSON", "models/fixtures/training/bar.json") training_json = load_training_json(training_json_path) if training_json is None: # Error loading training data app.logger.error( f"Bar training failed -- unable to load JSON: '{training_json_path}'" ) abort(404) global question_data # Otherwise we can't access it in the form validator question_data = build_questions(training_json) form = TrainingForm() form.add_questions(question_data) if form.validate_on_submit(): if trained: # The user might be re-doing the traing, no need to rewrite DB flash("You answered all the questions correctly!") else: app.logger.info(f"{str(current_user)} passed the bar training.") bar.trained_volunteers.append(volunteer) cybar.trained_volunteers.append(volunteer) db.session.commit() flash("Your completion of bar training has been saved.") return redirect(url_for(".bar_training")) # The template takes a list of pages which it will build sequentially, start # building this list now. pages = [] page_num = 0 for json_page in training_json["pages"]: page = {} page_num += 1 page["number"] = page_num page["content"] = load_training_markdown(json_page["content"]) if page["content"] is None: app.logger.error( f"Bar training failed -- unable to load Markdown: '{json_page['content']}'" ) abort(404) page["questions"] = json_page["questions"] pages.append(page) return render_template( "volunteer/training/bar-training.html", trained=trained, form=form, pages=pages, last_page=len(pages), volunteer=volunteer, )