From 72624a3f4e486be1cea54213f3a31b80339b31f6 Mon Sep 17 00:00:00 2001 From: Cara Salter Date: Tue, 6 Dec 2022 12:03:13 -0500 Subject: [PATCH] admin: Fix admin page --- goathacks/__init__.py | 5 + goathacks/admin/__init__.py | 182 +++++++++++++++++++++++++++ goathacks/models.py | 23 ++++ goathacks/static/js/admin.js | 8 +- goathacks/templates/admin.html | 41 +++--- migrations/versions/311c62fe5f49_.py | 32 +++++ migrations/versions/8d6ae751ec62_.py | 32 +++++ migrations/versions/a14a95ec57b0_.py | 32 +++++ requirements.txt | 1 + 9 files changed, 333 insertions(+), 23 deletions(-) create mode 100644 goathacks/admin/__init__.py create mode 100644 migrations/versions/311c62fe5f49_.py create mode 100644 migrations/versions/8d6ae751ec62_.py create mode 100644 migrations/versions/a14a95ec57b0_.py diff --git a/goathacks/__init__.py b/goathacks/__init__.py index 2a05ecc..d6967a9 100644 --- a/goathacks/__init__.py +++ b/goathacks/__init__.py @@ -3,12 +3,14 @@ from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate from flask_login import LoginManager from flask_assets import Bundle, Environment +from flask_cors import CORS db = SQLAlchemy() migrate = Migrate() login = LoginManager() environment = Environment() +cors = CORS() def create_app(): app = Flask(__name__) @@ -19,6 +21,7 @@ def create_app(): migrate.init_app(app, db) login.init_app(app) environment.init_app(app) + cors.init_app(app) scss = Bundle('css/style.scss', filters='scss', output='css/style.css') @@ -28,8 +31,10 @@ def create_app(): from . import registration from . import dashboard + from . import admin app.register_blueprint(registration.bp) app.register_blueprint(dashboard.bp) + app.register_blueprint(admin.bp) return app diff --git a/goathacks/admin/__init__.py b/goathacks/admin/__init__.py new file mode 100644 index 0000000..89f035f --- /dev/null +++ b/goathacks/admin/__init__.py @@ -0,0 +1,182 @@ +from flask import Blueprint, jsonify, redirect, render_template, url_for +from flask_login import current_user, login_required + +from goathacks.models import User + +bp = Blueprint("admin", __name__, url_prefix="/admin") + +from goathacks import db + +@bp.route("/") +@login_required +def home(): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + male_count = 0 + female_count = 0 + nb_count = 0 + check_in_count = 0 + waitlist_count = 0 + total_count = 0 + shirt_count = {'XS': 0, 'S': 0, 'M': 0, 'L': 0, 'XL': 0} + hackers = db.session.execute(db.select(User)).scalars().all() + schools = {} + + for h in hackers: + if h.waitlisted: + waitlist_count += 1 + + if h.checked_in: + check_in_count += 1 + + if h.gender == 'F': + female_count += 1 + elif h.gender == 'M': + male_count += 1 + else: + nb_count += 1 + + total_count += 1 + + if h.school not in schools: + schools[h.school] = 1 + else: + schools[h.school] += 1 + + if h.shirt_size not in shirt_count: + shirt_count[h.shirt_size] = 1 + else: + shirt_count[h.shirt_size] += 1 + return render_template("admin.html", waitlist_count=waitlist_count, + total_count=total_count, shirt_count=shirt_count, + hackers=hackers, male_count=male_count, + female_count=female_count, nb_count=nb_count, + check_in_count=check_in_count, schools=schools) + +@bp.route("/check_in/") +@login_required +def check_in(id): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + + user = User.query.filter_by(id=id).one() + if user is None: + return {"status": "error", "msg": "No user found"} + user.checked_in = True + db.session.commit() + return {"status": "success"} + +@bp.route("/drop/") +@login_required +def drop(id): + if not current_user.is_admin and not current_user.id == id: + return redirect(url_for("dashboard.home")) + + user = User.query.filter_by(id=id).one() + if user is None: + return {"status": "error", "msg": "user not found"} + + if user.checked_in: + return {"status": "error", "msg": "Hacker is already checked in"} + + db.session.delete(user) + db.session.commit() + + return {"status": "success"} + +@bp.route("/change_admin//") +@login_required +def change_admin(id, action): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + + user = User.query.filter_by(id=id).one() + if user is None: + return {"status": "error", "msg": "user not found"} + + + + valid_actions = ['promote', 'demote'] + if action not in valid_actions: + return {"status": "error", "msg": "invalid action"} + + if action == "promote": + user.is_admin = True + else: + user.is_admin = False + + db.session.commit() + + return {"status": "success"} + +@bp.route("/promote_from_waitlist/") +@login_required +def promote_waitlist(id): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + + user = User.query.filter_by(id=id).one() + if user is None: + return {"status": "error", "msg": "user not found"} + + user.waitlisted = False + db.session.commit() + + return {"status": "success"} + +@bp.route("/hackers.csv") +@login_required +def hackers_csv(): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + + users = User.query.all() + return json_to_csv(User.create_json_output(users)) + +@bp.route("/hackers") +@login_required +def hackers(): + if not current_user.is_admin: + return redirect(url_for("dashboard.home")) + + users = User.query.all() + return User.create_json_output(users) + +import json +import csv +from io import StringIO + + +def json_to_csv(data): + # Opening JSON file and loading the data + # into the variable data + + json_data=[] + if(type(data) is json): + json_data=data + elif(type(data) is str): + json_data=json.loads(data) + else: + json_data = json.loads(json.dumps(data)) + # now we will open a file for writing + csv_out = StringIO("") + + # create the csv writer object + csv_writer = csv.writer(csv_out) + + # Counter variable used for writing + # headers to the CSV file + count = 0 + + for e in json_data: + if count == 0: + + # Writing headers of CSV file + header = e.keys() + csv_writer.writerow(header) + count += 1 + + # Writing data of CSV file + csv_writer.writerow(e.values()) + csv_out.seek(0) + return csv_out.read() diff --git a/goathacks/models.py b/goathacks/models.py index a3ef755..b368749 100644 --- a/goathacks/models.py +++ b/goathacks/models.py @@ -17,6 +17,29 @@ class User(db.Model, UserMixin): shirt_size = Column(String, nullable=True) accomodations = Column(String, nullable=True) checked_in = Column(Boolean, nullable=False, default=False) + school = Column(String, nullable=True) + phone = Column(String, nullable=True) + gender = Column(String, nullable=True) + + def create_json_output(lis): + hackers = [] + + for u in lis: + hackers.append({ + 'checked_in': u.checked_in, + 'waitlisted': u.waitlisted, + 'admin': u.is_admin, + 'id': u.id, + 'email': u.email, + 'first_name': u.first_name, + 'last_name': u.last_name, + 'phone_number': u.phone, + 'shirt_size': u.shirt_size, + 'special_needs': u.accomodations, + 'school': u.school + }) + + return hackers @login.user_loader diff --git a/goathacks/static/js/admin.js b/goathacks/static/js/admin.js index 0ccdd37..45b2515 100644 --- a/goathacks/static/js/admin.js +++ b/goathacks/static/js/admin.js @@ -82,7 +82,7 @@ const promoteFromWaitlist = (id) => { confirmButtonText: 'Yes, promote!', confirmButtonColor: successColor }, () => { - $.get('/promote_from_waitlist?mlh_id=' + id, (data) => { + $.get('/admin/promote_from_waitlist/' + id, (data) => { let title = '' let msg = '' let type = '' @@ -110,7 +110,7 @@ const changeAdmin = (id, action) => { confirmButtonText: 'Yes, ' + action + '!', confirmButtonColor: errColor }, () => { - $.get('/change_admin?mlh_id=' + id + '&action=' + action, (data) => { + $.get('/admin/change_admin/' + id + '/' + action, (data) => { let title = '' let msg = '' let type = '' @@ -138,7 +138,7 @@ const drop = (id) => { confirmButtonText: 'Yes, drop!', confirmButtonColor: errColor }, () => { - $.get('/drop?mlh_id=' + id, (data) => { + $.get('/admin/drop/' + id, (data) => { let title = '' let msg = '' let type = '' @@ -166,7 +166,7 @@ const checkIn = (id) => { confirmButtonText: 'Yes, check in!', confirmButtonColor: successColor }, () => { - $.get('/check_in?mlh_id=' + id, (data) => { + $.get('/admin/check_in/' + id, (data) => { let title = '' let msg = '' let type = '' diff --git a/goathacks/templates/admin.html b/goathacks/templates/admin.html index 0d9c36b..dc936ea 100644 --- a/goathacks/templates/admin.html +++ b/goathacks/templates/admin.html @@ -13,7 +13,7 @@ - + @@ -43,7 +43,8 @@
JSON object of users from MLH (Including dropped applications):

Do NOT share this URL.

Get registered hackers only:
-

JSON CSV

+

JSON CSV

@@ -81,10 +82,12 @@