Compare commits

...
Sign in to create a new pull request.

57 commits

Author SHA1 Message Date
Cara Salter
7255ccebfd Re-add Discord link to participant dashboard 2025-01-03 19:52:13 -05:00
Cara Salter
02eb9b4220 update static site 2025-01-03 19:35:55 -05:00
Cara Salter
59266eb865 force MAX_BEFORE_WAITLIST to be an int 2024-10-31 14:41:00 -04:00
Cara Salter
fb2dd9032b fix resume uploading 2024-10-31 14:36:07 -04:00
Cara Salter
97f072ddb8 open registration 2024-10-31 13:37:25 -04:00
Cara Salter
cf725a2206 Add MLH logistics checkbox 2024-10-31 13:37:25 -04:00
Cara Salter
0f4ef0b690 use .env for config from now on
can't believe it took us this long
2024-10-31 13:37:25 -04:00
Cara Salter
b0a1e142cd Update migrations and homepage for 2025 2024-10-31 13:37:25 -04:00
ngolp
c73184060c Changed back the SQL query in /mail route 2024-10-31 13:37:25 -04:00
ngolp
29cdcf1899 See Github Issue #10. Added separation of admins and users on admin dashboard.
Refactored a bit of the original admin.home endpoint into a helper function to accompany two routes that use the same template without having to copy+paste code.
2024-10-31 13:37:25 -04:00
warren yun
3c40553b45 update user schema 2024-10-31 13:37:25 -04:00
warren yun
36bb39a8a0 more fields 2024-10-31 13:37:25 -04:00
warren yun
fa55e10e5b countries 2024-10-31 13:37:25 -04:00
warren yun
35c0197a40 schools dropdown 2024-10-31 13:37:25 -04:00
warren yun
1857e15791 countdown (hot) 2024-10-31 13:37:25 -04:00
warren yun
f6b03460d9 modified theme 2024-10-31 13:37:25 -04:00
warren yun
8a94de44c0 playing around with styling and items 2024-10-31 13:37:25 -04:00
Cara Salter
3dff046e84 Make unauth handler redirect to login 2024-10-31 13:37:25 -04:00
Cara Salter
0b3480dd52 Support editing event category in modals 2024-10-31 13:37:25 -04:00
Cara Salter
b7ca654bf1 Enable editing/creating/deleting events
Wholly through modals, yay!
2024-10-31 13:37:25 -04:00
Cara Salter
c47a46c204 Input event modal and create some supporting infra
update form to split date/time as well
2024-10-31 13:37:25 -04:00
Cara Salter
3a41dc6559
Add post_upgrade 2024-04-27 09:11:38 +10:00
Cara Salter
9dd8c98c51
Merge pull request #25 from wpi-acm/rewrite
UI rewrite
2024-03-18 09:01:16 +11:00
Cara Salter
fa6a9ee148
Merge remote-tracking branch 'origin/master' into rewrite 2024-02-23 16:32:05 -05:00
Cara Salter
6b48c30395
Re-add index page 2024-02-23 16:30:35 -05:00
Cara Salter
476966bef5
fix checkin/waitlist 2024-02-23 14:47:14 -05:00
Cara Salter
24384b2a35
Update bulk mailer tool
Use new admin site theme, allow for tinyMCE api key to be input to
config.py
2024-02-23 14:40:07 -05:00
Cara Salter
f1640493ec
Completely rewrite all backend pages 2024-01-12 17:19:17 -05:00
Cara Salter
9144ad89b0
Merge pull request #24 from wpi-acm/meal-checkin
Make qrcode generator create external URLs
2024-01-11 14:28:26 -05:00
Cara Salter
ede69a3be6
Make qrcode generator create external URLs 2024-01-11 14:27:47 -05:00
Cara Salter
cd5e58fa59
Start admin site rewrite
aaaaaa
2024-01-11 14:25:21 -05:00
Cara Salter
02b5ffa481
update static site 2023-12-15 08:12:27 -05:00
William Ryan
6ba1313ea6
Merge pull request #21 from wpi-acm/2024
2024 Registration Changes
2023-12-14 21:20:59 -05:00
Cara Salter
f82aa08085
Update static site 2023-12-14 21:14:50 -05:00
Cara Salter
a2e640f8f7
Update CLI
Add the following user commands:
- list
- autopromote

List will list all users as a table, useful for debugging things
without pulling out the database or the admin page

Autopromote will automatically promote and email users off the
waitlist until capacity is reached
2023-12-14 19:05:58 -05:00
Cara Salter
18a1dcb0d1
Enable setting discord link via config
Also makes it super easy to turn on/off the discord button, if the
DISCORD_LINK configuration option is undefined, it'll show a little
message that discord is still under construction
2023-12-14 18:32:09 -05:00
Cara Salter
c863b4064f
Merge branch 'master' into rewrite 2023-12-03 15:21:26 -05:00
William Ryan
62a4efca71
Merge pull request #19 from wpi-acm/meal-checkin
Allow for workshop/meal checkins
2023-12-01 17:30:15 -05:00
Cara Salter
dfd0c33be7
Enable creating and editing of events, and QR codes 2023-12-01 17:05:54 -05:00
Cara Salter
9d08c27135
Editing and creating events! 2023-12-01 16:25:32 -05:00
Cara Salter
74394abdfe
Scaffold event blueprint
Only has /events/checkin/<id> for now, but that's progress!
2023-12-01 14:21:48 -05:00
Cara Salter
006f54255f
Fix migration for event table
Didn't autogenerate, oops
2023-12-01 13:31:17 -05:00
Cara Salter
1432d0d7fb
Update makefile
Remove spurious 'txt'
2023-12-01 13:26:57 -05:00
Cara Salter
cc4fe8cea4
Scaffold event models
Will be used to check people into workshops and meals to track event
attendance over time, could be used to generate a dynamic schedule

Signed-Off-By: Cara Salter <cara@devcara.com>
2023-12-01 13:26:06 -05:00
Cara Salter
b2c8f3a77a
bootstrap5-ify the dashboard and participant-facing pages
except login

don't merge to master yet!
2023-01-13 18:06:16 -05:00
Cara Salter
05b70dcaa7
admin: Add registration time to dashboard table
Does this because "last_login" isn't actually set when people log in. It
should be fixed for next year.
2023-01-06 14:18:09 -05:00
William Ryan
de8277c970
Merge pull request #12 from wpi-acm/password-reset
Implement Password Reset
2023-01-03 20:42:45 -05:00
Cara Salter
5cb4b7582d
registration: Amend password reset email to include expiration 2023-01-03 18:01:23 -05:00
Cara Salter
e49e329f68
registration: Expire password requests after 30 minutes
Need to amend email to make this clear
2023-01-03 18:00:41 -05:00
Cara Salter
60953074e7
registration: Basic password reset
I think expiration should be pending on a proper way to do recurring
tasks -- I'm personally in favor of a CLI command that can be run from a
cronjob or systemd timer that will do things like auto-expire password
reset requests and send the daily registration reports.

Now that I'm thinking about it, this does need at least a rudimentary
system to make sure that it actually expires. If the expiration is
invalid at the time of reset, then the request can just be invalidated
and deleted. There's no pressing need for automatic removal until it's
implemented.

Thoughts @willhockey20?
2023-01-03 17:43:17 -05:00
Cara Salter
952df13136
meta: Add ulid dependency 2022-12-30 14:38:01 -05:00
Cara Salter
41bad2b8b0
models: Add PwResetRequest model 2022-12-30 14:35:39 -05:00
William Ryan
87a071b878
Merge pull request #11 from wpi-acm/rewrite
Send registration emails
2022-12-30 14:13:30 -05:00
Cara Salter
d1045226a1
registration: Add code to actually send registration emails 2022-12-30 14:05:03 -05:00
Cara Salter
4f06eab26a
Remove broken backgrounds 2022-12-23 14:42:05 -05:00
Cara Salter
8cc4a41492
Fix email registration template 2022-12-23 14:27:52 -05:00
William Ryan
eedfa91f35
Merge pull request #6 from wpi-acm/adding-sponsor-and-conduct
Added routing for sponsor package and code of conduct
2022-12-20 20:15:05 -05:00
49 changed files with 3957 additions and 1222 deletions

3
.gitignore vendored
View file

@ -1,7 +1,6 @@
resumes*/
resumes*.zip
config_hackWPI.py
config.py
admin/*.json
admin/*.csv
@ -13,6 +12,8 @@ goathacks/config.py
sqldmp
.vscode/
.DS_Store
### Bower ###
bower_components

2
.gitmodules vendored
View file

@ -1,4 +1,4 @@
[submodule "goathacks/templates/home"]
path = goathacks/templates/home
url = https://github.com/WPI-ACM/Hack-WPI-Static
branch = 2023-dev
branch = master

View file

@ -20,7 +20,7 @@ init_env:
source .venv/bin/activate && pip3 install -r requirements.txt txt
upgrade_env:
source .venv/bin/activate && pip3 install --upgrade -r requirements.txt txt
source .venv/bin/activate && pip3 install --upgrade -r requirements.txt
make_migrations:
source .venv/bin/activate && flask db migrate
@ -32,8 +32,14 @@ daemon:
@echo "--- STARTING UWSGI DAEMON ---"
@echo ""
@echo ""
source .venv/bin/activate && flask run
source .venv/bin/activate && flask --debug run
@echo ""
@echo ""
@echo "--- STARTING UWSGI DAEMON ---"
post_upgrade: upgrade_env run_migrations
# Make sure a tmp directory exists
@mkdir -p acmsite/tmp
# Create upload directory
@mkdir -p acmsite/uploads

30
config.py Normal file
View file

@ -0,0 +1,30 @@
import os
from dotenv import load_dotenv, dotenv_values
basedir = os.path.abspath(os.path.dirname(__file__))
load_dotenv(os.path.join(basedir, '.env'))
class Config():
TESTING = dotenv_values().get("TESTING") or False
DEBUG = dotenv_values().get("DEBUG") or False
SQLALCHEMY_DATABASE_URI = dotenv_values().get("SQLALCHEMY_DATABASE_URI") or "postgresql://localhost/goathacks"
MAX_BEFORE_WAITLIST = int(dotenv_values().get("MAX_BEFORE_WAITLIST") or 1)
MCE_API_KEY = dotenv_values().get("MCE_API_KEY")
SECRET_KEY = dotenv_values().get("SECRET_KEY") or "bad-key-change-me"
UPLOAD_FOLDER = dotenv_values().get("UPLOAD_FOLDER") or "./uploads/"
DISCORD_LINK = dotenv_values().get("DISCORD_LINK") or None
# Mail server settings
MAIL_SERVER = dotenv_values().get("MAIL_SERVER") or "localhost"
MAIL_PORT = dotenv_values().get("MAIL_PORT") or 25
MAIL_USE_TLS = dotenv_values().get("MAIL_USE_TLS") or False
MAIL_USE_SSL = dotenv_values().get("MAIL_USE_SSL") or False
MAIL_USERNAME = dotenv_values().get("MAIL_USERNAME") or "dummy"
MAIL_PASSWORD = dotenv_values().get("MAIL_PASSWORD") or "dummy"
MAIL_DEFAULT_SENDER = dotenv_values().get("MAIL_DEFAULT_SENDER") or "GoatHacks Team <hack@wpi.edu>"
MAIL_SUPPRESS_SEND = dotenv_values().get("MAIL_SUPPRESS_SEND") or TESTING

View file

@ -4,7 +4,13 @@ from flask_migrate import Migrate
from flask_login import LoginManager
from flask_assets import Bundle, Environment
from flask_cors import CORS
from flask_mail import Mail
from flask_mail import Mail, email_dispatched
from flask_bootstrap import Bootstrap5
from flask_font_awesome import FontAwesome
from flask_qrcode import QRcode
from config import Config
db = SQLAlchemy()
@ -13,11 +19,14 @@ login = LoginManager()
environment = Environment()
cors = CORS()
mail = Mail()
bootstrap = Bootstrap5()
font_awesome = FontAwesome()
qrcode = QRcode()
def create_app():
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_pyfile("config.py")
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
@ -25,6 +34,9 @@ def create_app():
environment.init_app(app)
cors.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
qrcode.init_app(app)
font_awesome.init_app(app)
scss = Bundle('css/style.scss', filters='scss',
output='css/style.css')
@ -35,10 +47,12 @@ def create_app():
from . import registration
from . import dashboard
from . import admin
from . import events
app.register_blueprint(registration.bp)
app.register_blueprint(dashboard.bp)
app.register_blueprint(admin.bp)
app.register_blueprint(events.bp)
from goathacks import cli
@ -70,11 +84,20 @@ def create_app():
def index():
return render_template("home/index.html")
@app.route("/index2.html")
def index2():
return render_template("home/index2.html")
# homepage assets
@app.route("/assets/<path:path>")
def assets(path):
return send_from_directory('templates/home/assets', path)
def log_message(message, app):
app.logger.debug(message)
email_dispatched.connect(log_message)
return app

View file

@ -1,4 +1,4 @@
from flask import Blueprint, jsonify, redirect, render_template, request, url_for
from flask import Blueprint, current_app, jsonify, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from flask_mail import Message
@ -7,10 +7,11 @@ from goathacks.models import User
bp = Blueprint("admin", __name__, url_prefix="/admin")
from goathacks import db, mail as app_mail
from goathacks.admin import events
@bp.route("/")
@login_required
def home():
# Helper function for admin.home and admin.admin_list to render list of users.
# This function was abstracted to promote code reuse.
def render_user_list(admin_list):
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
male_count = 0
@ -20,7 +21,10 @@ def home():
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()
if(admin_list):
hackers = db.session.execute(db.select(User).where(User.is_admin)).scalars().all()
else:
hackers = db.session.execute(db.select(User).where(User.is_admin == False)).scalars().all()
schools = {}
for h in hackers:
@ -54,6 +58,17 @@ def home():
female_count=female_count, nb_count=nb_count,
check_in_count=check_in_count, schools=schools)
@bp.route("/")
@login_required
def home():
return render_user_list(False) # list users (not admins)
@bp.route("/admin_list")
@login_required
def admin_list():
return render_user_list(True) # list users (admins)
@bp.route("/mail")
@login_required
def mail():
@ -61,8 +76,20 @@ def mail():
return redirect(url_for("dashboard.home"))
total_count = len(db.session.execute(db.select(User)).scalars().all())
api_key = current_app.config["MCE_API_KEY"]
return render_template("mail.html", NUM_HACKERS=total_count)
return render_template("mail.html", NUM_HACKERS=total_count,
MCE_API_KEY=api_key)
@bp.route("/users")
@login_required
def users():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
users = User.query.all()
return render_template("users.html", users=users)
@bp.route("/send", methods=["POST"])
@login_required

173
goathacks/admin/events.py Normal file
View file

@ -0,0 +1,173 @@
import flask
from flask import Response, render_template, redirect, request, url_for, flash, current_app
from flask_login import current_user, login_required
from goathacks.admin import bp, forms
from goathacks import db
from goathacks.models import Event
import io, qrcode, datetime
import qrcode.image.pure
@bp.route("/events")
@login_required
def list_events():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
events = Event.query.all()
form = forms.EventForm()
return render_template("events/list.html", events=events, form=form)
@bp.route("/event/<int:id>/delete")
@login_required
def delete_event(id):
if not current_user.is_admin:
return {"status": "error", "message": "Unauthorized"}
event = Event.query.filter_by(id=id).first()
if event is None:
return {"status": "error", "message": "Invalid event ID"}
db.session.delete(event)
db.session.commit()
return {"status": "success"}
@bp.route("/event/<int:id>")
@login_required
def event(id):
if not current_user.is_admin:
return {"status": "error", "message": "Unauthorized"}
event = Event.query.filter_by(id=id).first()
if event is None:
return {"status": "error", "message": "Invalid event ID"}
return event.create_json()
@bp.route("/event/<int:id>", methods=["POST"])
@login_required
def update_create_event(id):
if not current_user.is_admin:
flash("Unauthorized")
return redirect(url_for("dashboard.home"))
name = request.form.get('name')
description = request.form.get('description')
location = request.form.get('location')
start_day = request.form.get('start_day')
start_time = request.form.get('start_time')
end_day = request.form.get('end_day')
end_time = request.form.get('end_time')
start = datetime.datetime.combine(datetime.date.fromisoformat(start_day),
datetime.time.fromisoformat(start_time))
end = datetime.datetime.combine(datetime.date.fromisoformat(end_day),
datetime.time.fromisoformat(end_time))
category = request.form.get("category")
if id == 0:
# new event
e = Event(
name=name,
description=description,
location=location,
start_time=start,
category=category,
end_time=end)
db.session.add(e)
db.session.commit()
current_app.logger.info(f"{current_user} is creating a new event: {e.name}")
else:
e = Event.query.filter_by(id=id).first()
if e is None:
return {"status": "error", "message": "Invalid event ID"}
e.name = name
e.description = description
e.location = location
e.start_time = start
e.end_time = end
e.category=category
db.session.commit()
current_app.logger.info(f"{current_user} is updating an existing event: {e.name}")
return redirect(url_for("admin.list_events"))
@bp.route("/events/events.json")
@login_required
def events_json():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
events = Event.query.all()
return Event.create_json_output(events)
@bp.route("/events/new", methods=["GET", "POST"])
@login_required
def new_event():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
form = forms.EventForm(request.form)
if request.method == 'POST':
name = request.form.get("name")
description = request.form.get("description")
location = request.form.get("location")
start_time = request.form.get("start_time")
end_time = request.form.get("end_time")
category = request.form.get("category")
event = Event(
name = name,
description = description,
location = location,
start_time = start_time,
end_time = end_time,
category = category
)
db.session.add(event)
db.session.commit()
flash("Created event")
return redirect(url_for("admin.list_events"))
return render_template("events/new_event.html", form=form)
@bp.route("/events/edit/<int:id>", methods=["GET", "POST"])
@login_required
def edit_event(id):
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
event = Event.query.filter_by(id=id).one()
if event is None:
flash("Event does not exist")
return redirect(url_for("admin.list_events"))
form = forms.EventForm(request.form)
if request.method == 'POST':
form.populate_obj(event)
db.session.commit()
flash("Updated event")
return redirect(url_for("admin.list_events"))
else:
form = forms.EventForm(obj=event)
return render_template("events/new_event.html", form=form)
@bp.route("/events/qrcode/<int:id>")
@login_required
def qrcode_event(id):
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
event = Event.query.filter_by(id=id).first()
if event is None:
flash("Event does not exist")
return redirect(url_for("admin.list_events"))
return render_template("events/qrcode.html", event=event)

14
goathacks/admin/forms.py Normal file
View file

@ -0,0 +1,14 @@
from flask_wtf import FlaskForm
from wtforms import StringField, DateField, TimeField, SubmitField, TextAreaField
from wtforms.validators import DataRequired
class EventForm(FlaskForm):
name = StringField("Name", validators=[DataRequired()])
description = TextAreaField("Description")
location = StringField("Location", validators=[DataRequired()])
start_day = DateField("Start Day", validators=[DataRequired()])
start_time = TimeField("Start Time", validators=[DataRequired()])
end_day = DateField("End Day", validators=[DataRequired()])
end_time = TimeField("End Time", validators=[DataRequired()])
category = StringField("Category")
submit = SubmitField("Submit")

View file

@ -9,6 +9,8 @@ from goathacks.registration import bp
from goathacks import db, mail
from goathacks.models import User
from tabulate import tabulate
gr = AppGroup("user")
@gr.command('create')
@ -21,8 +23,10 @@ gr = AppGroup("user")
@click.option("--school", prompt=True)
@click.option("--phone", prompt=True)
@click.option("--gender", prompt=True)
@click.option("--country", prompt=True)
@click.option("--age", prompt=True)
def create_user(email, first_name, last_name, password, school, phone, gender,
admin):
admin,age, country):
"""
Creates a user
"""
@ -46,7 +50,9 @@ def create_user(email, first_name, last_name, password, school, phone, gender,
school=school,
phone=phone,
gender=gender,
is_admin=admin
is_admin=admin,
country=country,
age=age
)
db.session.add(user)
db.session.commit()
@ -122,3 +128,56 @@ def drop_user(email, confirm):
db.session.commit()
click.echo(f"Dropped {user.first_name}'s registration")
@gr.command("list")
def list_users():
"""
Gets a list of all users
"""
users = User.query.all()
def make_table_content(user):
return [user.email, f"{user.first_name} {user.last_name}", user.waitlisted, user.is_admin]
table = map(make_table_content, users)
print(tabulate(table, headers=["Email", "Name", "Waitlisted", "Admin"]))
@gr.command("autopromote")
def autopromote_users():
"""
Runs through and automatically promotes users up to the waitlist limit
"""
WAITLIST_LIMIT = current_app.config['MAX_BEFORE_WAITLIST']
num_confirmed = db.session.query(User).filter(User.waitlisted == False).count()
click.echo(f"Got {num_confirmed} confirmed attendees")
num_waitlisted = db.session.query(User).filter(User.waitlisted == True).count()
click.echo(f"Got {num_waitlisted} waitlisted attendees")
num_to_promote = WAITLIST_LIMIT - num_confirmed
if num_to_promote > num_waitlisted:
num_to_promote = num_waitlisted
click.echo(f"About to promote {str(num_to_promote)} attendees from waitlist")
users = db.session.query(User).filter(User.waitlisted == True).all()
num_promoted = 0
num_to_promote_orig = num_to_promote
for u in users:
if num_to_promote > 0:
click.echo(f"Attempting to promote {u.email} ({u.id})")
u.waitlisted = False
db.session.commit()
msg = Message("Waitlist Promotion")
msg.add_recipient(u.email)
msg.sender = ("GoatHacks Team", "hack@wpi.edu")
msg.body = render_template("emails/waitlist_promotion.txt", user=u)
mail.send(msg)
num_promoted += 1
num_to_promote -= 1
click.echo(f"Promoted {num_promoted}/{num_to_promote_orig} attendees off the waitlist!")

View file

@ -1,16 +0,0 @@
SQLALCHEMY_DATABASE_URI="postgresql://localhost/goathacks"
MAX_BEFORE_WAITLIST=1
SECRET_KEY="bad-key-change-me"
UPLOAD_FOLDER="./uploads/"
# Mail settings
MAIL_SERVER="localhost"
MAIL_PORT=25
MAIL_USE_TLS=False
MAIL_USE_SSL=False
MAIL_USERNAME="dummy"
MAIL_PASSWORD="dummy"
MAIL_DEFAULT_SENDER="GoatHacks Team <hack@wpi.edu>"

View file

@ -1,4 +1,4 @@
from flask import Blueprint, current_app, flash, jsonify, render_template, request
from flask import Blueprint, current_app, flash, jsonify, redirect, render_template, request, url_for
from flask_login import current_user, login_required
from werkzeug.utils import secure_filename
@ -19,6 +19,8 @@ def home():
current_user.accomodations = request.form.get('accomodations')
db.session.commit()
flash("Updated successfully")
else:
form = forms.ShirtAndAccomForm(obj=current_user)
return render_template("dashboard.html", form=form, resform=resform)
@bp.route("/resume", methods=["POST"])
@ -45,9 +47,17 @@ def resume():
filename = current_user.first_name.lower() + '_' + current_user.last_name.lower() + '_' + str(
current_user.id) + '.' + resume.filename.split('.')[-1].lower()
filename = secure_filename(filename)
if not os.path.exists(current_app.config['UPLOAD_FOLDER']):
try:
os.makedirs(current_app.config['UPLOAD_FOLDER'])
except Exception:
flash("Error saving resume. Contact acm-sysadmin@wpi.edu")
return redirect(url_for("dashboard.home"))
resume.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename))
return 'Resume uploaded! <a href="/dashboard">Return to dashboard</a>'
return "Something went wrong. If this keeps happening, contact hack@wpi.edu for assistance"
flash("Resume uploaded!")
return redirect(url_for("dashboard.home"))
flash("Something went wrong. If this keeps happening, contact hack@wpi.edu for assistance")
return redirect(url_for("dashboard.home"))
def allowed_file(filename):

View file

@ -1,16 +1,19 @@
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import RadioField, TextAreaField
from wtforms import SelectField, TextAreaField, SubmitField
from wtforms.validators import DataRequired
class ShirtAndAccomForm(FlaskForm):
shirt_size = RadioField("Shirt size", choices=["XS", "S", "M", "L", "XL",
shirt_size = SelectField("Shirt size", choices=["XS", "S", "M", "L", "XL",
"None"],
validators=[DataRequired()])
accomodations = TextAreaField("Special needs and/or Accomodations")
submit = SubmitField("Save")
class ResumeForm(FlaskForm):
resume = FileField("Resume", validators=[FileRequired(),
FileAllowed(['pdf', 'docx', 'doc',
'txt', 'rtf'],
"Documents only!")])
submit = SubmitField("Submit")

View file

@ -0,0 +1,31 @@
from flask import Blueprint, current_app, flash, redirect, url_for
from flask_login import current_user, login_required
from goathacks.models import Event, EventCheckins
from goathacks import db
bp = Blueprint("events", __name__, url_prefix="/events")
@bp.route("/checkin/<int:id>")
@login_required
def workshop_checkin(id):
event = Event.query.filter_by(id=id).one()
if event is None:
flash("That event does not exist!")
return redirect(url_for("dashboard.home"))
checkin = EventCheckins.query.filter_by(event_id=id,
user_id=current_user.id).first()
if checkin is not None:
flash("You've already checked into this event!")
return redirect(url_for("dashboard.home"))
checkin = EventCheckins(
user_id=current_user.id,
event_id=id
)
db.session.add(checkin)
db.session.commit()
flash("You've successfully checked in!")
return redirect(url_for("dashboard.home"))

View file

@ -1,6 +1,6 @@
from flask import flash, redirect, url_for
from flask_login import UserMixin
from sqlalchemy import Boolean, Column, DateTime, Integer, String
from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, Integer, String
from . import db
from . import login
@ -20,7 +20,13 @@ class User(db.Model, UserMixin):
school = Column(String, nullable=True)
phone = Column(String, nullable=True)
gender = Column(String, nullable=True)
newsletter = Column(Boolean, nullable=False, default=False)
country = Column(String, nullable=False)
age = Column(Integer, nullable=False)
dietary_restrictions = Column(String, nullable=True)
def __str__(self):
return f"{self.first_name} {self.last_name} ({self.email})"
def create_json_output(lis):
hackers = []
@ -41,7 +47,6 @@ class User(db.Model, UserMixin):
return hackers
@login.user_loader
def user_loader(user_id):
return User.query.filter_by(id=user_id).first()
@ -49,4 +54,55 @@ def user_loader(user_id):
@login.unauthorized_handler
def unauth():
flash("Please login first")
return redirect(url_for("registration.register"))
return redirect(url_for("registration.login"))
class PwResetRequest(db.Model):
id = Column(String, primary_key=True)
user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
expires = Column(DateTime, nullable=False)
"""
Represents an event within the hackathon, that can be checked into
"""
class Event(db.Model):
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
description = Column(String, nullable=True)
location = Column(String, nullable=False)
start_time = Column(DateTime, nullable=False)
end_time = Column(DateTime, nullable=False)
category = Column(String, nullable=True)
def create_json_output(lis):
events = []
for e in lis:
events.append(e.create_json())
return events
def create_json(self):
return {
"id": self.id,
"name": self.name,
"description": self.description,
"location": self.location,
"start_time": self.start_time.isoformat(),
"end_time": self.end_time.isoformat(),
"category": self.category
}
def get_checkins(self):
checkins = EventCheckins.query.filter_by(event_id=self.id).all()
return checkins
class EventCheckins(db.Model):
__tablename__ = "event_checkins"
id = Column(Integer, primary_key=True)
event_id = Column(Integer, ForeignKey('event.id'), nullable=False)
user_id = Column(Integer, ForeignKey('user.id'), nullable=False)

View file

@ -1,12 +1,14 @@
from datetime import datetime
from flask import Blueprint, config, current_app, flash, redirect, render_template, request, url_for
from datetime import datetime, timedelta
from flask import Blueprint, abort, config, current_app, flash, redirect, render_template, request, url_for
import flask_login
from flask_login import current_user
from goathacks.registration.forms import LoginForm, RegisterForm
from flask_login import current_user, login_required
from goathacks.registration.forms import LoginForm, PwResetForm, RegisterForm, ResetForm
from werkzeug.security import check_password_hash, generate_password_hash
from flask_mail import Message
import ulid
from goathacks import db
from goathacks.models import User
from goathacks import db, mail as app_mail
from goathacks.models import PwResetRequest, User
bp = Blueprint('registration', __name__, url_prefix="/registration")
@ -28,7 +30,10 @@ def register():
school = request.form.get('school')
phone = request.form.get('phone_number')
gender = request.form.get('gender')
country = request.form.get('country')
age = request.form.get('age')
dietary_restrictions = request.form.get('dietary_restrictions')
newsletter = request.form.get('newsletter')
if password == password_c:
# Passwords match!
@ -49,12 +54,26 @@ def register():
waitlisted=waitlisted,
school=school,
phone=phone,
gender=gender
gender=gender,
country=country,
age=age,
dietary_restrictions=dietary_restrictions,
newsletter=newsletter
)
db.session.add(user)
db.session.commit()
flask_login.login_user(user)
if waitlisted:
msg = Message("Goathacks - Waitlist Confirmation")
else:
msg = Message("GoatHacks - Registration Confirmation")
msg.add_recipient(user.email)
msg.sender = ("GoatHacks Team", "hack@wpi.edu")
msg.body = render_template("emails/registration.txt", user=user)
app_mail.send(msg)
return redirect(url_for("dashboard.home"))
else:
flash("Passwords do not match")
@ -70,6 +89,9 @@ def login():
password = request.form.get('password')
user = User.query.filter_by(email=email).first()
if user == None:
flash("Email or password incorrect")
return render_template("login.html", form=form)
if check_password_hash(user.password, password):
flask_login.login_user(user)
@ -81,3 +103,76 @@ def login():
flash("Incorrect password")
return render_template("login.html", form=form)
@bp.route("/logout")
@login_required
def logout():
flask_login.logout_user()
flash("See you later!")
return redirect(url_for("registration.login"))
@bp.route("/reset", methods=["GET", "POST"])
def reset():
form = ResetForm(request.form)
if request.method == 'POST':
email = request.form.get('email')
user = User.query.filter_by(email=email).first()
if user == None:
flash("If that email has an account here, we've just sent it a link to reset your password.")
return redirect(url_for("registration.login"))
else:
r = PwResetRequest(
id=str(ulid.ulid()),
user_id=user.id,
expires=datetime.now() + timedelta(minutes=30)
)
db.session.add(r)
db.session.commit()
msg = Message("GoatHacks - Password Reset Request")
msg.add_recipient(user.email)
msg.body = render_template("emails/password_reset.txt", code=r.id)
app_mail.send(msg)
flash("If that email has an account here, we've just sent it a link to reset your password.")
return redirect(url_for("registration.login"))
else:
return render_template("pw_reset.html", form=form)
@bp.route("/reset/complete/<string:id>", methods=["GET", "POST"])
def do_reset(id):
form = PwResetForm(request.form)
req = PwResetRequest.query.filter_by(id=id).first()
if req == None:
flash("Invalid request")
return redirect(url_for("registration.login"))
if req.expires < datetime.now():
db.session.delete(req)
db.session.commit()
flash("Invalid request")
return redirect(url_for("registration.login"))
if request.method == "POST":
password = request.form.get("password")
password_c = request.form.get("password_confirm")
if password == password_c:
user = User.query.filter_by(id=req.user_id).first()
if user == None:
flash("Invalid user")
return redirect(url_for("registration.login"))
user.password = generate_password_hash(password)
db.session.delete(req)
db.session.commit()
flash("Password successfully reset")
return redirect(url_for("registration.login"))
else:
flash("Passwords do not match!")
return render_template("password_reset.html", form=form)
else:
return render_template("password_reset.html", form=form)

View file

@ -0,0 +1,249 @@
United States of America,US,USA,840,ISO 3166-2:US,Americas,Northern America,"",019,021,""
Afghanistan,AF,AFG,004,ISO 3166-2:AF,Asia,Southern Asia,"",142,034,""
Åland Islands,AX,ALA,248,ISO 3166-2:AX,Europe,Northern Europe,"",150,154,""
Albania,AL,ALB,008,ISO 3166-2:AL,Europe,Southern Europe,"",150,039,""
Algeria,DZ,DZA,012,ISO 3166-2:DZ,Africa,Northern Africa,"",002,015,""
American Samoa,AS,ASM,016,ISO 3166-2:AS,Oceania,Polynesia,"",009,061,""
Andorra,AD,AND,020,ISO 3166-2:AD,Europe,Southern Europe,"",150,039,""
Angola,AO,AGO,024,ISO 3166-2:AO,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Anguilla,AI,AIA,660,ISO 3166-2:AI,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Antarctica,AQ,ATA,010,ISO 3166-2:AQ,"","","","","",""
Antigua and Barbuda,AG,ATG,028,ISO 3166-2:AG,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Argentina,AR,ARG,032,ISO 3166-2:AR,Americas,Latin America and the Caribbean,South America,019,419,005
Armenia,AM,ARM,051,ISO 3166-2:AM,Asia,Western Asia,"",142,145,""
Aruba,AW,ABW,533,ISO 3166-2:AW,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Australia,AU,AUS,036,ISO 3166-2:AU,Oceania,Australia and New Zealand,"",009,053,""
Austria,AT,AUT,040,ISO 3166-2:AT,Europe,Western Europe,"",150,155,""
Azerbaijan,AZ,AZE,031,ISO 3166-2:AZ,Asia,Western Asia,"",142,145,""
Bahamas,BS,BHS,044,ISO 3166-2:BS,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Bahrain,BH,BHR,048,ISO 3166-2:BH,Asia,Western Asia,"",142,145,""
Bangladesh,BD,BGD,050,ISO 3166-2:BD,Asia,Southern Asia,"",142,034,""
Barbados,BB,BRB,052,ISO 3166-2:BB,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Belarus,BY,BLR,112,ISO 3166-2:BY,Europe,Eastern Europe,"",150,151,""
Belgium,BE,BEL,056,ISO 3166-2:BE,Europe,Western Europe,"",150,155,""
Belize,BZ,BLZ,084,ISO 3166-2:BZ,Americas,Latin America and the Caribbean,Central America,019,419,013
Benin,BJ,BEN,204,ISO 3166-2:BJ,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Bermuda,BM,BMU,060,ISO 3166-2:BM,Americas,Northern America,"",019,021,""
Bhutan,BT,BTN,064,ISO 3166-2:BT,Asia,Southern Asia,"",142,034,""
"Bolivia, Plurinational State of",BO,BOL,068,ISO 3166-2:BO,Americas,Latin America and the Caribbean,South America,019,419,005
"Bonaire, Sint Eustatius and Saba",BQ,BES,535,ISO 3166-2:BQ,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Bosnia and Herzegovina,BA,BIH,070,ISO 3166-2:BA,Europe,Southern Europe,"",150,039,""
Botswana,BW,BWA,072,ISO 3166-2:BW,Africa,Sub-Saharan Africa,Southern Africa,002,202,018
Bouvet Island,BV,BVT,074,ISO 3166-2:BV,Americas,Latin America and the Caribbean,South America,019,419,005
Brazil,BR,BRA,076,ISO 3166-2:BR,Americas,Latin America and the Caribbean,South America,019,419,005
British Indian Ocean Territory,IO,IOT,086,ISO 3166-2:IO,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Brunei Darussalam,BN,BRN,096,ISO 3166-2:BN,Asia,South-eastern Asia,"",142,035,""
Bulgaria,BG,BGR,100,ISO 3166-2:BG,Europe,Eastern Europe,"",150,151,""
Burkina Faso,BF,BFA,854,ISO 3166-2:BF,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Burundi,BI,BDI,108,ISO 3166-2:BI,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Cabo Verde,CV,CPV,132,ISO 3166-2:CV,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Cambodia,KH,KHM,116,ISO 3166-2:KH,Asia,South-eastern Asia,"",142,035,""
Cameroon,CM,CMR,120,ISO 3166-2:CM,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Canada,CA,CAN,124,ISO 3166-2:CA,Americas,Northern America,"",019,021,""
Cayman Islands,KY,CYM,136,ISO 3166-2:KY,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Central African Republic,CF,CAF,140,ISO 3166-2:CF,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Chad,TD,TCD,148,ISO 3166-2:TD,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Chile,CL,CHL,152,ISO 3166-2:CL,Americas,Latin America and the Caribbean,South America,019,419,005
China,CN,CHN,156,ISO 3166-2:CN,Asia,Eastern Asia,"",142,030,""
Christmas Island,CX,CXR,162,ISO 3166-2:CX,Oceania,Australia and New Zealand,"",009,053,""
Cocos (Keeling) Islands,CC,CCK,166,ISO 3166-2:CC,Oceania,Australia and New Zealand,"",009,053,""
Colombia,CO,COL,170,ISO 3166-2:CO,Americas,Latin America and the Caribbean,South America,019,419,005
Comoros,KM,COM,174,ISO 3166-2:KM,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Congo,CG,COG,178,ISO 3166-2:CG,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
"Congo, Democratic Republic of the",CD,COD,180,ISO 3166-2:CD,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Cook Islands,CK,COK,184,ISO 3166-2:CK,Oceania,Polynesia,"",009,061,""
Costa Rica,CR,CRI,188,ISO 3166-2:CR,Americas,Latin America and the Caribbean,Central America,019,419,013
Côte d'Ivoire,CI,CIV,384,ISO 3166-2:CI,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Croatia,HR,HRV,191,ISO 3166-2:HR,Europe,Southern Europe,"",150,039,""
Cuba,CU,CUB,192,ISO 3166-2:CU,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Curaçao,CW,CUW,531,ISO 3166-2:CW,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Cyprus,CY,CYP,196,ISO 3166-2:CY,Asia,Western Asia,"",142,145,""
Czechia,CZ,CZE,203,ISO 3166-2:CZ,Europe,Eastern Europe,"",150,151,""
Denmark,DK,DNK,208,ISO 3166-2:DK,Europe,Northern Europe,"",150,154,""
Djibouti,DJ,DJI,262,ISO 3166-2:DJ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Dominica,DM,DMA,212,ISO 3166-2:DM,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Dominican Republic,DO,DOM,214,ISO 3166-2:DO,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Ecuador,EC,ECU,218,ISO 3166-2:EC,Americas,Latin America and the Caribbean,South America,019,419,005
Egypt,EG,EGY,818,ISO 3166-2:EG,Africa,Northern Africa,"",002,015,""
El Salvador,SV,SLV,222,ISO 3166-2:SV,Americas,Latin America and the Caribbean,Central America,019,419,013
Equatorial Guinea,GQ,GNQ,226,ISO 3166-2:GQ,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Eritrea,ER,ERI,232,ISO 3166-2:ER,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Estonia,EE,EST,233,ISO 3166-2:EE,Europe,Northern Europe,"",150,154,""
Eswatini,SZ,SWZ,748,ISO 3166-2:SZ,Africa,Sub-Saharan Africa,Southern Africa,002,202,018
Ethiopia,ET,ETH,231,ISO 3166-2:ET,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Falkland Islands (Malvinas),FK,FLK,238,ISO 3166-2:FK,Americas,Latin America and the Caribbean,South America,019,419,005
Faroe Islands,FO,FRO,234,ISO 3166-2:FO,Europe,Northern Europe,"",150,154,""
Fiji,FJ,FJI,242,ISO 3166-2:FJ,Oceania,Melanesia,"",009,054,""
Finland,FI,FIN,246,ISO 3166-2:FI,Europe,Northern Europe,"",150,154,""
France,FR,FRA,250,ISO 3166-2:FR,Europe,Western Europe,"",150,155,""
French Guiana,GF,GUF,254,ISO 3166-2:GF,Americas,Latin America and the Caribbean,South America,019,419,005
French Polynesia,PF,PYF,258,ISO 3166-2:PF,Oceania,Polynesia,"",009,061,""
French Southern Territories,TF,ATF,260,ISO 3166-2:TF,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Gabon,GA,GAB,266,ISO 3166-2:GA,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Gambia,GM,GMB,270,ISO 3166-2:GM,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Georgia,GE,GEO,268,ISO 3166-2:GE,Asia,Western Asia,"",142,145,""
Germany,DE,DEU,276,ISO 3166-2:DE,Europe,Western Europe,"",150,155,""
Ghana,GH,GHA,288,ISO 3166-2:GH,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Gibraltar,GI,GIB,292,ISO 3166-2:GI,Europe,Southern Europe,"",150,039,""
Greece,GR,GRC,300,ISO 3166-2:GR,Europe,Southern Europe,"",150,039,""
Greenland,GL,GRL,304,ISO 3166-2:GL,Americas,Northern America,"",019,021,""
Grenada,GD,GRD,308,ISO 3166-2:GD,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Guadeloupe,GP,GLP,312,ISO 3166-2:GP,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Guam,GU,GUM,316,ISO 3166-2:GU,Oceania,Micronesia,"",009,057,""
Guatemala,GT,GTM,320,ISO 3166-2:GT,Americas,Latin America and the Caribbean,Central America,019,419,013
Guernsey,GG,GGY,831,ISO 3166-2:GG,Europe,Northern Europe,"",150,154,""
Guinea,GN,GIN,324,ISO 3166-2:GN,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Guinea-Bissau,GW,GNB,624,ISO 3166-2:GW,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Guyana,GY,GUY,328,ISO 3166-2:GY,Americas,Latin America and the Caribbean,South America,019,419,005
Haiti,HT,HTI,332,ISO 3166-2:HT,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Heard Island and McDonald Islands,HM,HMD,334,ISO 3166-2:HM,Oceania,Australia and New Zealand,"",009,053,""
Holy See,VA,VAT,336,ISO 3166-2:VA,Europe,Southern Europe,"",150,039,""
Honduras,HN,HND,340,ISO 3166-2:HN,Americas,Latin America and the Caribbean,Central America,019,419,013
Hong Kong,HK,HKG,344,ISO 3166-2:HK,Asia,Eastern Asia,"",142,030,""
Hungary,HU,HUN,348,ISO 3166-2:HU,Europe,Eastern Europe,"",150,151,""
Iceland,IS,ISL,352,ISO 3166-2:IS,Europe,Northern Europe,"",150,154,""
India,IN,IND,356,ISO 3166-2:IN,Asia,Southern Asia,"",142,034,""
Indonesia,ID,IDN,360,ISO 3166-2:ID,Asia,South-eastern Asia,"",142,035,""
"Iran, Islamic Republic of",IR,IRN,364,ISO 3166-2:IR,Asia,Southern Asia,"",142,034,""
Iraq,IQ,IRQ,368,ISO 3166-2:IQ,Asia,Western Asia,"",142,145,""
Ireland,IE,IRL,372,ISO 3166-2:IE,Europe,Northern Europe,"",150,154,""
Isle of Man,IM,IMN,833,ISO 3166-2:IM,Europe,Northern Europe,"",150,154,""
Israel,IL,ISR,376,ISO 3166-2:IL,Asia,Western Asia,"",142,145,""
Italy,IT,ITA,380,ISO 3166-2:IT,Europe,Southern Europe,"",150,039,""
Jamaica,JM,JAM,388,ISO 3166-2:JM,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Japan,JP,JPN,392,ISO 3166-2:JP,Asia,Eastern Asia,"",142,030,""
Jersey,JE,JEY,832,ISO 3166-2:JE,Europe,Northern Europe,"",150,154,""
Jordan,JO,JOR,400,ISO 3166-2:JO,Asia,Western Asia,"",142,145,""
Kazakhstan,KZ,KAZ,398,ISO 3166-2:KZ,Asia,Central Asia,"",142,143,""
Kenya,KE,KEN,404,ISO 3166-2:KE,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Kiribati,KI,KIR,296,ISO 3166-2:KI,Oceania,Micronesia,"",009,057,""
"Korea, Democratic People's Republic of",KP,PRK,408,ISO 3166-2:KP,Asia,Eastern Asia,"",142,030,""
"Korea, Republic of",KR,KOR,410,ISO 3166-2:KR,Asia,Eastern Asia,"",142,030,""
Kuwait,KW,KWT,414,ISO 3166-2:KW,Asia,Western Asia,"",142,145,""
Kyrgyzstan,KG,KGZ,417,ISO 3166-2:KG,Asia,Central Asia,"",142,143,""
Lao People's Democratic Republic,LA,LAO,418,ISO 3166-2:LA,Asia,South-eastern Asia,"",142,035,""
Latvia,LV,LVA,428,ISO 3166-2:LV,Europe,Northern Europe,"",150,154,""
Lebanon,LB,LBN,422,ISO 3166-2:LB,Asia,Western Asia,"",142,145,""
Lesotho,LS,LSO,426,ISO 3166-2:LS,Africa,Sub-Saharan Africa,Southern Africa,002,202,018
Liberia,LR,LBR,430,ISO 3166-2:LR,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Libya,LY,LBY,434,ISO 3166-2:LY,Africa,Northern Africa,"",002,015,""
Liechtenstein,LI,LIE,438,ISO 3166-2:LI,Europe,Western Europe,"",150,155,""
Lithuania,LT,LTU,440,ISO 3166-2:LT,Europe,Northern Europe,"",150,154,""
Luxembourg,LU,LUX,442,ISO 3166-2:LU,Europe,Western Europe,"",150,155,""
Macao,MO,MAC,446,ISO 3166-2:MO,Asia,Eastern Asia,"",142,030,""
Madagascar,MG,MDG,450,ISO 3166-2:MG,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Malawi,MW,MWI,454,ISO 3166-2:MW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Malaysia,MY,MYS,458,ISO 3166-2:MY,Asia,South-eastern Asia,"",142,035,""
Maldives,MV,MDV,462,ISO 3166-2:MV,Asia,Southern Asia,"",142,034,""
Mali,ML,MLI,466,ISO 3166-2:ML,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Malta,MT,MLT,470,ISO 3166-2:MT,Europe,Southern Europe,"",150,039,""
Marshall Islands,MH,MHL,584,ISO 3166-2:MH,Oceania,Micronesia,"",009,057,""
Martinique,MQ,MTQ,474,ISO 3166-2:MQ,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Mauritania,MR,MRT,478,ISO 3166-2:MR,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Mauritius,MU,MUS,480,ISO 3166-2:MU,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Mayotte,YT,MYT,175,ISO 3166-2:YT,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Mexico,MX,MEX,484,ISO 3166-2:MX,Americas,Latin America and the Caribbean,Central America,019,419,013
"Micronesia, Federated States of",FM,FSM,583,ISO 3166-2:FM,Oceania,Micronesia,"",009,057,""
"Moldova, Republic of",MD,MDA,498,ISO 3166-2:MD,Europe,Eastern Europe,"",150,151,""
Monaco,MC,MCO,492,ISO 3166-2:MC,Europe,Western Europe,"",150,155,""
Mongolia,MN,MNG,496,ISO 3166-2:MN,Asia,Eastern Asia,"",142,030,""
Montenegro,ME,MNE,499,ISO 3166-2:ME,Europe,Southern Europe,"",150,039,""
Montserrat,MS,MSR,500,ISO 3166-2:MS,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Morocco,MA,MAR,504,ISO 3166-2:MA,Africa,Northern Africa,"",002,015,""
Mozambique,MZ,MOZ,508,ISO 3166-2:MZ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Myanmar,MM,MMR,104,ISO 3166-2:MM,Asia,South-eastern Asia,"",142,035,""
Namibia,NA,NAM,516,ISO 3166-2:NA,Africa,Sub-Saharan Africa,Southern Africa,002,202,018
Nauru,NR,NRU,520,ISO 3166-2:NR,Oceania,Micronesia,"",009,057,""
Nepal,NP,NPL,524,ISO 3166-2:NP,Asia,Southern Asia,"",142,034,""
"Netherlands, Kingdom of the",NL,NLD,528,ISO 3166-2:NL,Europe,Western Europe,"",150,155,""
New Caledonia,NC,NCL,540,ISO 3166-2:NC,Oceania,Melanesia,"",009,054,""
New Zealand,NZ,NZL,554,ISO 3166-2:NZ,Oceania,Australia and New Zealand,"",009,053,""
Nicaragua,NI,NIC,558,ISO 3166-2:NI,Americas,Latin America and the Caribbean,Central America,019,419,013
Niger,NE,NER,562,ISO 3166-2:NE,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Nigeria,NG,NGA,566,ISO 3166-2:NG,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Niue,NU,NIU,570,ISO 3166-2:NU,Oceania,Polynesia,"",009,061,""
Norfolk Island,NF,NFK,574,ISO 3166-2:NF,Oceania,Australia and New Zealand,"",009,053,""
North Macedonia,MK,MKD,807,ISO 3166-2:MK,Europe,Southern Europe,"",150,039,""
Northern Mariana Islands,MP,MNP,580,ISO 3166-2:MP,Oceania,Micronesia,"",009,057,""
Norway,NO,NOR,578,ISO 3166-2:NO,Europe,Northern Europe,"",150,154,""
Oman,OM,OMN,512,ISO 3166-2:OM,Asia,Western Asia,"",142,145,""
Pakistan,PK,PAK,586,ISO 3166-2:PK,Asia,Southern Asia,"",142,034,""
Palau,PW,PLW,585,ISO 3166-2:PW,Oceania,Micronesia,"",009,057,""
"Palestine, State of",PS,PSE,275,ISO 3166-2:PS,Asia,Western Asia,"",142,145,""
Panama,PA,PAN,591,ISO 3166-2:PA,Americas,Latin America and the Caribbean,Central America,019,419,013
Papua New Guinea,PG,PNG,598,ISO 3166-2:PG,Oceania,Melanesia,"",009,054,""
Paraguay,PY,PRY,600,ISO 3166-2:PY,Americas,Latin America and the Caribbean,South America,019,419,005
Peru,PE,PER,604,ISO 3166-2:PE,Americas,Latin America and the Caribbean,South America,019,419,005
Philippines,PH,PHL,608,ISO 3166-2:PH,Asia,South-eastern Asia,"",142,035,""
Pitcairn,PN,PCN,612,ISO 3166-2:PN,Oceania,Polynesia,"",009,061,""
Poland,PL,POL,616,ISO 3166-2:PL,Europe,Eastern Europe,"",150,151,""
Portugal,PT,PRT,620,ISO 3166-2:PT,Europe,Southern Europe,"",150,039,""
Puerto Rico,PR,PRI,630,ISO 3166-2:PR,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Qatar,QA,QAT,634,ISO 3166-2:QA,Asia,Western Asia,"",142,145,""
Réunion,RE,REU,638,ISO 3166-2:RE,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Romania,RO,ROU,642,ISO 3166-2:RO,Europe,Eastern Europe,"",150,151,""
Russian Federation,RU,RUS,643,ISO 3166-2:RU,Europe,Eastern Europe,"",150,151,""
Rwanda,RW,RWA,646,ISO 3166-2:RW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Saint Barthélemy,BL,BLM,652,ISO 3166-2:BL,Americas,Latin America and the Caribbean,Caribbean,019,419,029
"Saint Helena, Ascension and Tristan da Cunha",SH,SHN,654,ISO 3166-2:SH,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Saint Kitts and Nevis,KN,KNA,659,ISO 3166-2:KN,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Saint Lucia,LC,LCA,662,ISO 3166-2:LC,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Saint Martin (French part),MF,MAF,663,ISO 3166-2:MF,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Saint Pierre and Miquelon,PM,SPM,666,ISO 3166-2:PM,Americas,Northern America,"",019,021,""
Saint Vincent and the Grenadines,VC,VCT,670,ISO 3166-2:VC,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Samoa,WS,WSM,882,ISO 3166-2:WS,Oceania,Polynesia,"",009,061,""
San Marino,SM,SMR,674,ISO 3166-2:SM,Europe,Southern Europe,"",150,039,""
Sao Tome and Principe,ST,STP,678,ISO 3166-2:ST,Africa,Sub-Saharan Africa,Middle Africa,002,202,017
Saudi Arabia,SA,SAU,682,ISO 3166-2:SA,Asia,Western Asia,"",142,145,""
Senegal,SN,SEN,686,ISO 3166-2:SN,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Serbia,RS,SRB,688,ISO 3166-2:RS,Europe,Southern Europe,"",150,039,""
Seychelles,SC,SYC,690,ISO 3166-2:SC,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Sierra Leone,SL,SLE,694,ISO 3166-2:SL,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Singapore,SG,SGP,702,ISO 3166-2:SG,Asia,South-eastern Asia,"",142,035,""
Sint Maarten (Dutch part),SX,SXM,534,ISO 3166-2:SX,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Slovakia,SK,SVK,703,ISO 3166-2:SK,Europe,Eastern Europe,"",150,151,""
Slovenia,SI,SVN,705,ISO 3166-2:SI,Europe,Southern Europe,"",150,039,""
Solomon Islands,SB,SLB,090,ISO 3166-2:SB,Oceania,Melanesia,"",009,054,""
Somalia,SO,SOM,706,ISO 3166-2:SO,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
South Africa,ZA,ZAF,710,ISO 3166-2:ZA,Africa,Sub-Saharan Africa,Southern Africa,002,202,018
South Georgia and the South Sandwich Islands,GS,SGS,239,ISO 3166-2:GS,Americas,Latin America and the Caribbean,South America,019,419,005
South Sudan,SS,SSD,728,ISO 3166-2:SS,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Spain,ES,ESP,724,ISO 3166-2:ES,Europe,Southern Europe,"",150,039,""
Sri Lanka,LK,LKA,144,ISO 3166-2:LK,Asia,Southern Asia,"",142,034,""
Sudan,SD,SDN,729,ISO 3166-2:SD,Africa,Northern Africa,"",002,015,""
Suriname,SR,SUR,740,ISO 3166-2:SR,Americas,Latin America and the Caribbean,South America,019,419,005
Svalbard and Jan Mayen,SJ,SJM,744,ISO 3166-2:SJ,Europe,Northern Europe,"",150,154,""
Sweden,SE,SWE,752,ISO 3166-2:SE,Europe,Northern Europe,"",150,154,""
Switzerland,CH,CHE,756,ISO 3166-2:CH,Europe,Western Europe,"",150,155,""
Syrian Arab Republic,SY,SYR,760,ISO 3166-2:SY,Asia,Western Asia,"",142,145,""
"Taiwan, Province of China",TW,TWN,158,ISO 3166-2:TW,,,,,,
Tajikistan,TJ,TJK,762,ISO 3166-2:TJ,Asia,Central Asia,"",142,143,""
"Tanzania, United Republic of",TZ,TZA,834,ISO 3166-2:TZ,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Thailand,TH,THA,764,ISO 3166-2:TH,Asia,South-eastern Asia,"",142,035,""
Timor-Leste,TL,TLS,626,ISO 3166-2:TL,Asia,South-eastern Asia,"",142,035,""
Togo,TG,TGO,768,ISO 3166-2:TG,Africa,Sub-Saharan Africa,Western Africa,002,202,011
Tokelau,TK,TKL,772,ISO 3166-2:TK,Oceania,Polynesia,"",009,061,""
Tonga,TO,TON,776,ISO 3166-2:TO,Oceania,Polynesia,"",009,061,""
Trinidad and Tobago,TT,TTO,780,ISO 3166-2:TT,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Tunisia,TN,TUN,788,ISO 3166-2:TN,Africa,Northern Africa,"",002,015,""
Türkiye,TR,TUR,792,ISO 3166-2:TR,Asia,Western Asia,"",142,145,""
Turkmenistan,TM,TKM,795,ISO 3166-2:TM,Asia,Central Asia,"",142,143,""
Turks and Caicos Islands,TC,TCA,796,ISO 3166-2:TC,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Tuvalu,TV,TUV,798,ISO 3166-2:TV,Oceania,Polynesia,"",009,061,""
Uganda,UG,UGA,800,ISO 3166-2:UG,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Ukraine,UA,UKR,804,ISO 3166-2:UA,Europe,Eastern Europe,"",150,151,""
United Arab Emirates,AE,ARE,784,ISO 3166-2:AE,Asia,Western Asia,"",142,145,""
United Kingdom of Great Britain and Northern Ireland,GB,GBR,826,ISO 3166-2:GB,Europe,Northern Europe,"",150,154,""
United States Minor Outlying Islands,UM,UMI,581,ISO 3166-2:UM,Oceania,Micronesia,"",009,057,""
Uruguay,UY,URY,858,ISO 3166-2:UY,Americas,Latin America and the Caribbean,South America,019,419,005
Uzbekistan,UZ,UZB,860,ISO 3166-2:UZ,Asia,Central Asia,"",142,143,""
Vanuatu,VU,VUT,548,ISO 3166-2:VU,Oceania,Melanesia,"",009,054,""
"Venezuela, Bolivarian Republic of",VE,VEN,862,ISO 3166-2:VE,Americas,Latin America and the Caribbean,South America,019,419,005
Viet Nam,VN,VNM,704,ISO 3166-2:VN,Asia,South-eastern Asia,"",142,035,""
Virgin Islands (British),VG,VGB,092,ISO 3166-2:VG,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Virgin Islands (U.S.),VI,VIR,850,ISO 3166-2:VI,Americas,Latin America and the Caribbean,Caribbean,019,419,029
Wallis and Futuna,WF,WLF,876,ISO 3166-2:WF,Oceania,Polynesia,"",009,061,""
Western Sahara,EH,ESH,732,ISO 3166-2:EH,Africa,Northern Africa,"",002,015,""
Yemen,YE,YEM,887,ISO 3166-2:YE,Asia,Western Asia,"",142,145,""
Zambia,ZM,ZMB,894,ISO 3166-2:ZM,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
Zimbabwe,ZW,ZWE,716,ISO 3166-2:ZW,Africa,Sub-Saharan Africa,Eastern Africa,002,202,014
1 United States of America US USA 840 ISO 3166-2:US Americas Northern America 019 021
2 Afghanistan AF AFG 004 ISO 3166-2:AF Asia Southern Asia 142 034
3 Åland Islands AX ALA 248 ISO 3166-2:AX Europe Northern Europe 150 154
4 Albania AL ALB 008 ISO 3166-2:AL Europe Southern Europe 150 039
5 Algeria DZ DZA 012 ISO 3166-2:DZ Africa Northern Africa 002 015
6 American Samoa AS ASM 016 ISO 3166-2:AS Oceania Polynesia 009 061
7 Andorra AD AND 020 ISO 3166-2:AD Europe Southern Europe 150 039
8 Angola AO AGO 024 ISO 3166-2:AO Africa Sub-Saharan Africa Middle Africa 002 202 017
9 Anguilla AI AIA 660 ISO 3166-2:AI Americas Latin America and the Caribbean Caribbean 019 419 029
10 Antarctica AQ ATA 010 ISO 3166-2:AQ
11 Antigua and Barbuda AG ATG 028 ISO 3166-2:AG Americas Latin America and the Caribbean Caribbean 019 419 029
12 Argentina AR ARG 032 ISO 3166-2:AR Americas Latin America and the Caribbean South America 019 419 005
13 Armenia AM ARM 051 ISO 3166-2:AM Asia Western Asia 142 145
14 Aruba AW ABW 533 ISO 3166-2:AW Americas Latin America and the Caribbean Caribbean 019 419 029
15 Australia AU AUS 036 ISO 3166-2:AU Oceania Australia and New Zealand 009 053
16 Austria AT AUT 040 ISO 3166-2:AT Europe Western Europe 150 155
17 Azerbaijan AZ AZE 031 ISO 3166-2:AZ Asia Western Asia 142 145
18 Bahamas BS BHS 044 ISO 3166-2:BS Americas Latin America and the Caribbean Caribbean 019 419 029
19 Bahrain BH BHR 048 ISO 3166-2:BH Asia Western Asia 142 145
20 Bangladesh BD BGD 050 ISO 3166-2:BD Asia Southern Asia 142 034
21 Barbados BB BRB 052 ISO 3166-2:BB Americas Latin America and the Caribbean Caribbean 019 419 029
22 Belarus BY BLR 112 ISO 3166-2:BY Europe Eastern Europe 150 151
23 Belgium BE BEL 056 ISO 3166-2:BE Europe Western Europe 150 155
24 Belize BZ BLZ 084 ISO 3166-2:BZ Americas Latin America and the Caribbean Central America 019 419 013
25 Benin BJ BEN 204 ISO 3166-2:BJ Africa Sub-Saharan Africa Western Africa 002 202 011
26 Bermuda BM BMU 060 ISO 3166-2:BM Americas Northern America 019 021
27 Bhutan BT BTN 064 ISO 3166-2:BT Asia Southern Asia 142 034
28 Bolivia, Plurinational State of BO BOL 068 ISO 3166-2:BO Americas Latin America and the Caribbean South America 019 419 005
29 Bonaire, Sint Eustatius and Saba BQ BES 535 ISO 3166-2:BQ Americas Latin America and the Caribbean Caribbean 019 419 029
30 Bosnia and Herzegovina BA BIH 070 ISO 3166-2:BA Europe Southern Europe 150 039
31 Botswana BW BWA 072 ISO 3166-2:BW Africa Sub-Saharan Africa Southern Africa 002 202 018
32 Bouvet Island BV BVT 074 ISO 3166-2:BV Americas Latin America and the Caribbean South America 019 419 005
33 Brazil BR BRA 076 ISO 3166-2:BR Americas Latin America and the Caribbean South America 019 419 005
34 British Indian Ocean Territory IO IOT 086 ISO 3166-2:IO Africa Sub-Saharan Africa Eastern Africa 002 202 014
35 Brunei Darussalam BN BRN 096 ISO 3166-2:BN Asia South-eastern Asia 142 035
36 Bulgaria BG BGR 100 ISO 3166-2:BG Europe Eastern Europe 150 151
37 Burkina Faso BF BFA 854 ISO 3166-2:BF Africa Sub-Saharan Africa Western Africa 002 202 011
38 Burundi BI BDI 108 ISO 3166-2:BI Africa Sub-Saharan Africa Eastern Africa 002 202 014
39 Cabo Verde CV CPV 132 ISO 3166-2:CV Africa Sub-Saharan Africa Western Africa 002 202 011
40 Cambodia KH KHM 116 ISO 3166-2:KH Asia South-eastern Asia 142 035
41 Cameroon CM CMR 120 ISO 3166-2:CM Africa Sub-Saharan Africa Middle Africa 002 202 017
42 Canada CA CAN 124 ISO 3166-2:CA Americas Northern America 019 021
43 Cayman Islands KY CYM 136 ISO 3166-2:KY Americas Latin America and the Caribbean Caribbean 019 419 029
44 Central African Republic CF CAF 140 ISO 3166-2:CF Africa Sub-Saharan Africa Middle Africa 002 202 017
45 Chad TD TCD 148 ISO 3166-2:TD Africa Sub-Saharan Africa Middle Africa 002 202 017
46 Chile CL CHL 152 ISO 3166-2:CL Americas Latin America and the Caribbean South America 019 419 005
47 China CN CHN 156 ISO 3166-2:CN Asia Eastern Asia 142 030
48 Christmas Island CX CXR 162 ISO 3166-2:CX Oceania Australia and New Zealand 009 053
49 Cocos (Keeling) Islands CC CCK 166 ISO 3166-2:CC Oceania Australia and New Zealand 009 053
50 Colombia CO COL 170 ISO 3166-2:CO Americas Latin America and the Caribbean South America 019 419 005
51 Comoros KM COM 174 ISO 3166-2:KM Africa Sub-Saharan Africa Eastern Africa 002 202 014
52 Congo CG COG 178 ISO 3166-2:CG Africa Sub-Saharan Africa Middle Africa 002 202 017
53 Congo, Democratic Republic of the CD COD 180 ISO 3166-2:CD Africa Sub-Saharan Africa Middle Africa 002 202 017
54 Cook Islands CK COK 184 ISO 3166-2:CK Oceania Polynesia 009 061
55 Costa Rica CR CRI 188 ISO 3166-2:CR Americas Latin America and the Caribbean Central America 019 419 013
56 Côte d'Ivoire CI CIV 384 ISO 3166-2:CI Africa Sub-Saharan Africa Western Africa 002 202 011
57 Croatia HR HRV 191 ISO 3166-2:HR Europe Southern Europe 150 039
58 Cuba CU CUB 192 ISO 3166-2:CU Americas Latin America and the Caribbean Caribbean 019 419 029
59 Curaçao CW CUW 531 ISO 3166-2:CW Americas Latin America and the Caribbean Caribbean 019 419 029
60 Cyprus CY CYP 196 ISO 3166-2:CY Asia Western Asia 142 145
61 Czechia CZ CZE 203 ISO 3166-2:CZ Europe Eastern Europe 150 151
62 Denmark DK DNK 208 ISO 3166-2:DK Europe Northern Europe 150 154
63 Djibouti DJ DJI 262 ISO 3166-2:DJ Africa Sub-Saharan Africa Eastern Africa 002 202 014
64 Dominica DM DMA 212 ISO 3166-2:DM Americas Latin America and the Caribbean Caribbean 019 419 029
65 Dominican Republic DO DOM 214 ISO 3166-2:DO Americas Latin America and the Caribbean Caribbean 019 419 029
66 Ecuador EC ECU 218 ISO 3166-2:EC Americas Latin America and the Caribbean South America 019 419 005
67 Egypt EG EGY 818 ISO 3166-2:EG Africa Northern Africa 002 015
68 El Salvador SV SLV 222 ISO 3166-2:SV Americas Latin America and the Caribbean Central America 019 419 013
69 Equatorial Guinea GQ GNQ 226 ISO 3166-2:GQ Africa Sub-Saharan Africa Middle Africa 002 202 017
70 Eritrea ER ERI 232 ISO 3166-2:ER Africa Sub-Saharan Africa Eastern Africa 002 202 014
71 Estonia EE EST 233 ISO 3166-2:EE Europe Northern Europe 150 154
72 Eswatini SZ SWZ 748 ISO 3166-2:SZ Africa Sub-Saharan Africa Southern Africa 002 202 018
73 Ethiopia ET ETH 231 ISO 3166-2:ET Africa Sub-Saharan Africa Eastern Africa 002 202 014
74 Falkland Islands (Malvinas) FK FLK 238 ISO 3166-2:FK Americas Latin America and the Caribbean South America 019 419 005
75 Faroe Islands FO FRO 234 ISO 3166-2:FO Europe Northern Europe 150 154
76 Fiji FJ FJI 242 ISO 3166-2:FJ Oceania Melanesia 009 054
77 Finland FI FIN 246 ISO 3166-2:FI Europe Northern Europe 150 154
78 France FR FRA 250 ISO 3166-2:FR Europe Western Europe 150 155
79 French Guiana GF GUF 254 ISO 3166-2:GF Americas Latin America and the Caribbean South America 019 419 005
80 French Polynesia PF PYF 258 ISO 3166-2:PF Oceania Polynesia 009 061
81 French Southern Territories TF ATF 260 ISO 3166-2:TF Africa Sub-Saharan Africa Eastern Africa 002 202 014
82 Gabon GA GAB 266 ISO 3166-2:GA Africa Sub-Saharan Africa Middle Africa 002 202 017
83 Gambia GM GMB 270 ISO 3166-2:GM Africa Sub-Saharan Africa Western Africa 002 202 011
84 Georgia GE GEO 268 ISO 3166-2:GE Asia Western Asia 142 145
85 Germany DE DEU 276 ISO 3166-2:DE Europe Western Europe 150 155
86 Ghana GH GHA 288 ISO 3166-2:GH Africa Sub-Saharan Africa Western Africa 002 202 011
87 Gibraltar GI GIB 292 ISO 3166-2:GI Europe Southern Europe 150 039
88 Greece GR GRC 300 ISO 3166-2:GR Europe Southern Europe 150 039
89 Greenland GL GRL 304 ISO 3166-2:GL Americas Northern America 019 021
90 Grenada GD GRD 308 ISO 3166-2:GD Americas Latin America and the Caribbean Caribbean 019 419 029
91 Guadeloupe GP GLP 312 ISO 3166-2:GP Americas Latin America and the Caribbean Caribbean 019 419 029
92 Guam GU GUM 316 ISO 3166-2:GU Oceania Micronesia 009 057
93 Guatemala GT GTM 320 ISO 3166-2:GT Americas Latin America and the Caribbean Central America 019 419 013
94 Guernsey GG GGY 831 ISO 3166-2:GG Europe Northern Europe 150 154
95 Guinea GN GIN 324 ISO 3166-2:GN Africa Sub-Saharan Africa Western Africa 002 202 011
96 Guinea-Bissau GW GNB 624 ISO 3166-2:GW Africa Sub-Saharan Africa Western Africa 002 202 011
97 Guyana GY GUY 328 ISO 3166-2:GY Americas Latin America and the Caribbean South America 019 419 005
98 Haiti HT HTI 332 ISO 3166-2:HT Americas Latin America and the Caribbean Caribbean 019 419 029
99 Heard Island and McDonald Islands HM HMD 334 ISO 3166-2:HM Oceania Australia and New Zealand 009 053
100 Holy See VA VAT 336 ISO 3166-2:VA Europe Southern Europe 150 039
101 Honduras HN HND 340 ISO 3166-2:HN Americas Latin America and the Caribbean Central America 019 419 013
102 Hong Kong HK HKG 344 ISO 3166-2:HK Asia Eastern Asia 142 030
103 Hungary HU HUN 348 ISO 3166-2:HU Europe Eastern Europe 150 151
104 Iceland IS ISL 352 ISO 3166-2:IS Europe Northern Europe 150 154
105 India IN IND 356 ISO 3166-2:IN Asia Southern Asia 142 034
106 Indonesia ID IDN 360 ISO 3166-2:ID Asia South-eastern Asia 142 035
107 Iran, Islamic Republic of IR IRN 364 ISO 3166-2:IR Asia Southern Asia 142 034
108 Iraq IQ IRQ 368 ISO 3166-2:IQ Asia Western Asia 142 145
109 Ireland IE IRL 372 ISO 3166-2:IE Europe Northern Europe 150 154
110 Isle of Man IM IMN 833 ISO 3166-2:IM Europe Northern Europe 150 154
111 Israel IL ISR 376 ISO 3166-2:IL Asia Western Asia 142 145
112 Italy IT ITA 380 ISO 3166-2:IT Europe Southern Europe 150 039
113 Jamaica JM JAM 388 ISO 3166-2:JM Americas Latin America and the Caribbean Caribbean 019 419 029
114 Japan JP JPN 392 ISO 3166-2:JP Asia Eastern Asia 142 030
115 Jersey JE JEY 832 ISO 3166-2:JE Europe Northern Europe 150 154
116 Jordan JO JOR 400 ISO 3166-2:JO Asia Western Asia 142 145
117 Kazakhstan KZ KAZ 398 ISO 3166-2:KZ Asia Central Asia 142 143
118 Kenya KE KEN 404 ISO 3166-2:KE Africa Sub-Saharan Africa Eastern Africa 002 202 014
119 Kiribati KI KIR 296 ISO 3166-2:KI Oceania Micronesia 009 057
120 Korea, Democratic People's Republic of KP PRK 408 ISO 3166-2:KP Asia Eastern Asia 142 030
121 Korea, Republic of KR KOR 410 ISO 3166-2:KR Asia Eastern Asia 142 030
122 Kuwait KW KWT 414 ISO 3166-2:KW Asia Western Asia 142 145
123 Kyrgyzstan KG KGZ 417 ISO 3166-2:KG Asia Central Asia 142 143
124 Lao People's Democratic Republic LA LAO 418 ISO 3166-2:LA Asia South-eastern Asia 142 035
125 Latvia LV LVA 428 ISO 3166-2:LV Europe Northern Europe 150 154
126 Lebanon LB LBN 422 ISO 3166-2:LB Asia Western Asia 142 145
127 Lesotho LS LSO 426 ISO 3166-2:LS Africa Sub-Saharan Africa Southern Africa 002 202 018
128 Liberia LR LBR 430 ISO 3166-2:LR Africa Sub-Saharan Africa Western Africa 002 202 011
129 Libya LY LBY 434 ISO 3166-2:LY Africa Northern Africa 002 015
130 Liechtenstein LI LIE 438 ISO 3166-2:LI Europe Western Europe 150 155
131 Lithuania LT LTU 440 ISO 3166-2:LT Europe Northern Europe 150 154
132 Luxembourg LU LUX 442 ISO 3166-2:LU Europe Western Europe 150 155
133 Macao MO MAC 446 ISO 3166-2:MO Asia Eastern Asia 142 030
134 Madagascar MG MDG 450 ISO 3166-2:MG Africa Sub-Saharan Africa Eastern Africa 002 202 014
135 Malawi MW MWI 454 ISO 3166-2:MW Africa Sub-Saharan Africa Eastern Africa 002 202 014
136 Malaysia MY MYS 458 ISO 3166-2:MY Asia South-eastern Asia 142 035
137 Maldives MV MDV 462 ISO 3166-2:MV Asia Southern Asia 142 034
138 Mali ML MLI 466 ISO 3166-2:ML Africa Sub-Saharan Africa Western Africa 002 202 011
139 Malta MT MLT 470 ISO 3166-2:MT Europe Southern Europe 150 039
140 Marshall Islands MH MHL 584 ISO 3166-2:MH Oceania Micronesia 009 057
141 Martinique MQ MTQ 474 ISO 3166-2:MQ Americas Latin America and the Caribbean Caribbean 019 419 029
142 Mauritania MR MRT 478 ISO 3166-2:MR Africa Sub-Saharan Africa Western Africa 002 202 011
143 Mauritius MU MUS 480 ISO 3166-2:MU Africa Sub-Saharan Africa Eastern Africa 002 202 014
144 Mayotte YT MYT 175 ISO 3166-2:YT Africa Sub-Saharan Africa Eastern Africa 002 202 014
145 Mexico MX MEX 484 ISO 3166-2:MX Americas Latin America and the Caribbean Central America 019 419 013
146 Micronesia, Federated States of FM FSM 583 ISO 3166-2:FM Oceania Micronesia 009 057
147 Moldova, Republic of MD MDA 498 ISO 3166-2:MD Europe Eastern Europe 150 151
148 Monaco MC MCO 492 ISO 3166-2:MC Europe Western Europe 150 155
149 Mongolia MN MNG 496 ISO 3166-2:MN Asia Eastern Asia 142 030
150 Montenegro ME MNE 499 ISO 3166-2:ME Europe Southern Europe 150 039
151 Montserrat MS MSR 500 ISO 3166-2:MS Americas Latin America and the Caribbean Caribbean 019 419 029
152 Morocco MA MAR 504 ISO 3166-2:MA Africa Northern Africa 002 015
153 Mozambique MZ MOZ 508 ISO 3166-2:MZ Africa Sub-Saharan Africa Eastern Africa 002 202 014
154 Myanmar MM MMR 104 ISO 3166-2:MM Asia South-eastern Asia 142 035
155 Namibia NA NAM 516 ISO 3166-2:NA Africa Sub-Saharan Africa Southern Africa 002 202 018
156 Nauru NR NRU 520 ISO 3166-2:NR Oceania Micronesia 009 057
157 Nepal NP NPL 524 ISO 3166-2:NP Asia Southern Asia 142 034
158 Netherlands, Kingdom of the NL NLD 528 ISO 3166-2:NL Europe Western Europe 150 155
159 New Caledonia NC NCL 540 ISO 3166-2:NC Oceania Melanesia 009 054
160 New Zealand NZ NZL 554 ISO 3166-2:NZ Oceania Australia and New Zealand 009 053
161 Nicaragua NI NIC 558 ISO 3166-2:NI Americas Latin America and the Caribbean Central America 019 419 013
162 Niger NE NER 562 ISO 3166-2:NE Africa Sub-Saharan Africa Western Africa 002 202 011
163 Nigeria NG NGA 566 ISO 3166-2:NG Africa Sub-Saharan Africa Western Africa 002 202 011
164 Niue NU NIU 570 ISO 3166-2:NU Oceania Polynesia 009 061
165 Norfolk Island NF NFK 574 ISO 3166-2:NF Oceania Australia and New Zealand 009 053
166 North Macedonia MK MKD 807 ISO 3166-2:MK Europe Southern Europe 150 039
167 Northern Mariana Islands MP MNP 580 ISO 3166-2:MP Oceania Micronesia 009 057
168 Norway NO NOR 578 ISO 3166-2:NO Europe Northern Europe 150 154
169 Oman OM OMN 512 ISO 3166-2:OM Asia Western Asia 142 145
170 Pakistan PK PAK 586 ISO 3166-2:PK Asia Southern Asia 142 034
171 Palau PW PLW 585 ISO 3166-2:PW Oceania Micronesia 009 057
172 Palestine, State of PS PSE 275 ISO 3166-2:PS Asia Western Asia 142 145
173 Panama PA PAN 591 ISO 3166-2:PA Americas Latin America and the Caribbean Central America 019 419 013
174 Papua New Guinea PG PNG 598 ISO 3166-2:PG Oceania Melanesia 009 054
175 Paraguay PY PRY 600 ISO 3166-2:PY Americas Latin America and the Caribbean South America 019 419 005
176 Peru PE PER 604 ISO 3166-2:PE Americas Latin America and the Caribbean South America 019 419 005
177 Philippines PH PHL 608 ISO 3166-2:PH Asia South-eastern Asia 142 035
178 Pitcairn PN PCN 612 ISO 3166-2:PN Oceania Polynesia 009 061
179 Poland PL POL 616 ISO 3166-2:PL Europe Eastern Europe 150 151
180 Portugal PT PRT 620 ISO 3166-2:PT Europe Southern Europe 150 039
181 Puerto Rico PR PRI 630 ISO 3166-2:PR Americas Latin America and the Caribbean Caribbean 019 419 029
182 Qatar QA QAT 634 ISO 3166-2:QA Asia Western Asia 142 145
183 Réunion RE REU 638 ISO 3166-2:RE Africa Sub-Saharan Africa Eastern Africa 002 202 014
184 Romania RO ROU 642 ISO 3166-2:RO Europe Eastern Europe 150 151
185 Russian Federation RU RUS 643 ISO 3166-2:RU Europe Eastern Europe 150 151
186 Rwanda RW RWA 646 ISO 3166-2:RW Africa Sub-Saharan Africa Eastern Africa 002 202 014
187 Saint Barthélemy BL BLM 652 ISO 3166-2:BL Americas Latin America and the Caribbean Caribbean 019 419 029
188 Saint Helena, Ascension and Tristan da Cunha SH SHN 654 ISO 3166-2:SH Africa Sub-Saharan Africa Western Africa 002 202 011
189 Saint Kitts and Nevis KN KNA 659 ISO 3166-2:KN Americas Latin America and the Caribbean Caribbean 019 419 029
190 Saint Lucia LC LCA 662 ISO 3166-2:LC Americas Latin America and the Caribbean Caribbean 019 419 029
191 Saint Martin (French part) MF MAF 663 ISO 3166-2:MF Americas Latin America and the Caribbean Caribbean 019 419 029
192 Saint Pierre and Miquelon PM SPM 666 ISO 3166-2:PM Americas Northern America 019 021
193 Saint Vincent and the Grenadines VC VCT 670 ISO 3166-2:VC Americas Latin America and the Caribbean Caribbean 019 419 029
194 Samoa WS WSM 882 ISO 3166-2:WS Oceania Polynesia 009 061
195 San Marino SM SMR 674 ISO 3166-2:SM Europe Southern Europe 150 039
196 Sao Tome and Principe ST STP 678 ISO 3166-2:ST Africa Sub-Saharan Africa Middle Africa 002 202 017
197 Saudi Arabia SA SAU 682 ISO 3166-2:SA Asia Western Asia 142 145
198 Senegal SN SEN 686 ISO 3166-2:SN Africa Sub-Saharan Africa Western Africa 002 202 011
199 Serbia RS SRB 688 ISO 3166-2:RS Europe Southern Europe 150 039
200 Seychelles SC SYC 690 ISO 3166-2:SC Africa Sub-Saharan Africa Eastern Africa 002 202 014
201 Sierra Leone SL SLE 694 ISO 3166-2:SL Africa Sub-Saharan Africa Western Africa 002 202 011
202 Singapore SG SGP 702 ISO 3166-2:SG Asia South-eastern Asia 142 035
203 Sint Maarten (Dutch part) SX SXM 534 ISO 3166-2:SX Americas Latin America and the Caribbean Caribbean 019 419 029
204 Slovakia SK SVK 703 ISO 3166-2:SK Europe Eastern Europe 150 151
205 Slovenia SI SVN 705 ISO 3166-2:SI Europe Southern Europe 150 039
206 Solomon Islands SB SLB 090 ISO 3166-2:SB Oceania Melanesia 009 054
207 Somalia SO SOM 706 ISO 3166-2:SO Africa Sub-Saharan Africa Eastern Africa 002 202 014
208 South Africa ZA ZAF 710 ISO 3166-2:ZA Africa Sub-Saharan Africa Southern Africa 002 202 018
209 South Georgia and the South Sandwich Islands GS SGS 239 ISO 3166-2:GS Americas Latin America and the Caribbean South America 019 419 005
210 South Sudan SS SSD 728 ISO 3166-2:SS Africa Sub-Saharan Africa Eastern Africa 002 202 014
211 Spain ES ESP 724 ISO 3166-2:ES Europe Southern Europe 150 039
212 Sri Lanka LK LKA 144 ISO 3166-2:LK Asia Southern Asia 142 034
213 Sudan SD SDN 729 ISO 3166-2:SD Africa Northern Africa 002 015
214 Suriname SR SUR 740 ISO 3166-2:SR Americas Latin America and the Caribbean South America 019 419 005
215 Svalbard and Jan Mayen SJ SJM 744 ISO 3166-2:SJ Europe Northern Europe 150 154
216 Sweden SE SWE 752 ISO 3166-2:SE Europe Northern Europe 150 154
217 Switzerland CH CHE 756 ISO 3166-2:CH Europe Western Europe 150 155
218 Syrian Arab Republic SY SYR 760 ISO 3166-2:SY Asia Western Asia 142 145
219 Taiwan, Province of China TW TWN 158 ISO 3166-2:TW
220 Tajikistan TJ TJK 762 ISO 3166-2:TJ Asia Central Asia 142 143
221 Tanzania, United Republic of TZ TZA 834 ISO 3166-2:TZ Africa Sub-Saharan Africa Eastern Africa 002 202 014
222 Thailand TH THA 764 ISO 3166-2:TH Asia South-eastern Asia 142 035
223 Timor-Leste TL TLS 626 ISO 3166-2:TL Asia South-eastern Asia 142 035
224 Togo TG TGO 768 ISO 3166-2:TG Africa Sub-Saharan Africa Western Africa 002 202 011
225 Tokelau TK TKL 772 ISO 3166-2:TK Oceania Polynesia 009 061
226 Tonga TO TON 776 ISO 3166-2:TO Oceania Polynesia 009 061
227 Trinidad and Tobago TT TTO 780 ISO 3166-2:TT Americas Latin America and the Caribbean Caribbean 019 419 029
228 Tunisia TN TUN 788 ISO 3166-2:TN Africa Northern Africa 002 015
229 Türkiye TR TUR 792 ISO 3166-2:TR Asia Western Asia 142 145
230 Turkmenistan TM TKM 795 ISO 3166-2:TM Asia Central Asia 142 143
231 Turks and Caicos Islands TC TCA 796 ISO 3166-2:TC Americas Latin America and the Caribbean Caribbean 019 419 029
232 Tuvalu TV TUV 798 ISO 3166-2:TV Oceania Polynesia 009 061
233 Uganda UG UGA 800 ISO 3166-2:UG Africa Sub-Saharan Africa Eastern Africa 002 202 014
234 Ukraine UA UKR 804 ISO 3166-2:UA Europe Eastern Europe 150 151
235 United Arab Emirates AE ARE 784 ISO 3166-2:AE Asia Western Asia 142 145
236 United Kingdom of Great Britain and Northern Ireland GB GBR 826 ISO 3166-2:GB Europe Northern Europe 150 154
237 United States Minor Outlying Islands UM UMI 581 ISO 3166-2:UM Oceania Micronesia 009 057
238 Uruguay UY URY 858 ISO 3166-2:UY Americas Latin America and the Caribbean South America 019 419 005
239 Uzbekistan UZ UZB 860 ISO 3166-2:UZ Asia Central Asia 142 143
240 Vanuatu VU VUT 548 ISO 3166-2:VU Oceania Melanesia 009 054
241 Venezuela, Bolivarian Republic of VE VEN 862 ISO 3166-2:VE Americas Latin America and the Caribbean South America 019 419 005
242 Viet Nam VN VNM 704 ISO 3166-2:VN Asia South-eastern Asia 142 035
243 Virgin Islands (British) VG VGB 092 ISO 3166-2:VG Americas Latin America and the Caribbean Caribbean 019 419 029
244 Virgin Islands (U.S.) VI VIR 850 ISO 3166-2:VI Americas Latin America and the Caribbean Caribbean 019 419 029
245 Wallis and Futuna WF WLF 876 ISO 3166-2:WF Oceania Polynesia 009 061
246 Western Sahara EH ESH 732 ISO 3166-2:EH Africa Northern Africa 002 015
247 Yemen YE YEM 887 ISO 3166-2:YE Asia Western Asia 142 145
248 Zambia ZM ZMB 894 ISO 3166-2:ZM Africa Sub-Saharan Africa Eastern Africa 002 202 014
249 Zimbabwe ZW ZWE 716 ISO 3166-2:ZW Africa Sub-Saharan Africa Eastern Africa 002 202 014

View file

@ -1,8 +1,13 @@
from flask_wtf import FlaskForm
from wtforms import BooleanField, PasswordField, SelectField, StringField, SubmitField, widgets
from wtforms import BooleanField, IntegerField, PasswordField, SelectField, StringField, SubmitField, widgets
from wtforms.validators import DataRequired
import os
class RegisterForm(FlaskForm):
__location__ = os.path.realpath(os.path.join(os.getcwd(), os.path.dirname(__file__)))
schools_list = open(os.path.join(__location__, 'schools.txt')).read().split("\n")
countries_list = open(os.path.join(__location__, 'countries.csv')).read().split("\n")
email = StringField("Email", validators=[DataRequired()])
first_name = StringField("Preferred First Name",
validators=[DataRequired()])
@ -10,12 +15,18 @@ class RegisterForm(FlaskForm):
password = PasswordField("Password", validators=[DataRequired()])
password_confirm = PasswordField("Confirm Password",
validators=[DataRequired()])
school = StringField("School/University", validators=[DataRequired()])
school = SelectField("School", choices=[(school, school) for school in schools_list], widget=widgets.Select())
phone_number = StringField("Phone number", validators=[DataRequired()])
age = IntegerField("Age", validators=[DataRequired()])
dietary_restrictions = StringField("Dietary Restrictions (Optional)")
gender = SelectField("Gender", choices=[("F", "Female"), ("M", "Male"),
("NB", "Non-binary/Other")],
widget=widgets.Select())
country = SelectField("Country", choices=[(country.split(",")[0], country.split(",")[0]) for country in countries_list], widget=widgets.Select())
newsletter = BooleanField("Subscribe to the MLH newsletter?")
agree_coc = BooleanField("I confirm that I have read and agree to the Code of Conduct", validators=[DataRequired()])
logistics = BooleanField("I authorize you to share my application/registration with Major League Hacking for event administration, ranking, and MLH administration in-line with the MLH privacy policy.I further agree to the terms of both the MLH Contest Terms and Conditions and the MLH Privacy Policy.", validators=[DataRequired()])
submit = SubmitField("Register")
class LoginForm(FlaskForm):
@ -23,3 +34,11 @@ class LoginForm(FlaskForm):
password = PasswordField("Password", validators=[DataRequired()])
submit = SubmitField("Sign in")
class ResetForm(FlaskForm):
email = StringField("Email", validators=[DataRequired()])
submit = SubmitField("Request reset")
class PwResetForm(FlaskForm):
password = PasswordField("Password")
password_confirm = PasswordField("Confirm Password")
submit = SubmitField("Submit")

File diff suppressed because it is too large Load diff

View file

@ -1,185 +1,18 @@
@font-face {
font-family: "Krungthep";
src: url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.eot");
src: url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.woff") format("woff"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.svg#Krungthep") format("svg");
.navbar-dark, .modal-header, .table-header {
background-color: #974355;
color: #FFFFFF;
}
html {
height: 100%;
.modal-header, .table-header {
color: #FFFFFF;
}
.container {
min-height: 100vh;
position: relative;
}
body {
background-color: #003049;
font-family: 'Montserrat', sans-serif;
font-size: 1.1rem;
color: #eee;
position: relative;
min-height: 100%;
}
p {
line-height: 2rem;
}
#logo-container {
display: flex;
justify-content: center;
flex-direction: row;
padding-top: 5px;
padding-bottom: 5px;
height: 100%;
width: 100%;
}
#goat {
height: 100%;
}
.button-collapse {
color: #26a69a;
}
.parallax-container {
min-height: 380px;
line-height: 0;
height: auto;
color: rgba(255, 255, 255, 0.9);
}
.parallax-container .section {
width: 100%;
}
label {
color: white !important;
}
@media only screen and (max-width: 992px) {
.parallax-container .section {
top: 40%;
}
#index-banner .section {
top: 10%;
}
}
@media only screen and (max-width: 600px) {
.parallax-container .section {
height: auto;
overflow: auto;
}
.container {
height: auto;
}
#index-banner .section {
top: 0;
}
}
#tagline {
font-weight: 600;
}
#event-info {
font-weight: 400;
}
#registration-banner {
min-height: 100px;
max-height: 150px;
}
#registration-banner .section {
top: auto;
}
.icon-block {
padding: 0 15px;
}
.icon-block .material-icons {
font-size: inherit;
}
.parallax img {
display: inherit;
max-width: 200%;
}
#mlh-trust-badge {
display: block;
max-width: 100px;
min-width: 60px;
position: fixed;
right: 50px;
top: 0;
width: 10%;
z-index: 10000;
}
nav {
line-height: normal !important;
font-family: "Jost", sans-serif;
font-weight: 700;
}
/*
.navbar-brand {
} */
.footer-nav {
position: absolute;
bottom: 0;
width: 100%;
}
label {
padding-bottom: 0.5rem;
}
form input {
border-radius: 5px;
}
form input[type="submit"] {
background: #26a69a;
border-radius: 10px;
border-color: #26a69a;
}
form input[type="radio"] {
padding-right: 5px;
}
form input[type="checkbox"]:checked {
visibility: visible;
left: unset;
position: unset;
}
form label {
font-size: 1.1rem;
padding-right: 10px;
padding-left: 25px !important;
}
form select {
display: unset;
background: #974355;
max-width: 11rem;
}
.flashes {
list-style-type: none;
display: flex;
justify-content: center;
}
.message {
width: 80%;
justify-content: center;
border: 1px solid #eee;
background-color: #26a69a;
padding: 0.2rem;
font-size: large;
color: #eee;
min-height: 100vh;
background-color: #000000;
}

View file

@ -1,184 +1,21 @@
$color-bg: #003049;
$color-fg: #eee;
$color-section-bg: #974355;
$color-accent: #26a69a;
@font-face {font-family: "Krungthep"; src: url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.eot"); src: url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.eot?#iefix") format("embedded-opentype"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.woff2") format("woff2"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.woff") format("woff"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.ttf") format("truetype"), url("//db.onlinewebfonts.com/t/736cf5b08b01082a3645e14038868e20.svg#Krungthep") format("svg"); }
$color-nav-bg: #974355;
$color-bg: #000000;
html {
height: 100%;
.navbar-dark, .modal-header, .table-header {
background-color: $color-nav-bg;
color: #FFFFFF;
}
.modal-header, .table-header {
color: #FFFFFF;
}
.container {
min-height: 100vh;
position: relative;
}
body {
min-height: 100vh;
background-color: $color-bg;
font-family: 'Montserrat', sans-serif;
font-size: 1.1rem;
color: $color-fg;
position: relative;
min-height: 100%;
}
p {
line-height: 2rem;
}
#logo-container {
display: flex;
justify-content: center;
flex-direction: row;
padding-top: 5px;
padding-bottom: 5px;
height: 100%;
width: 100%;
}
#goat {
height: 100%;
}
.button-collapse {
color: $color-accent;
}
.parallax-container {
min-height: 380px;
line-height: 0;
height: auto;
color: rgba(255,255,255,.9);
}
.parallax-container .section {
width: 100%;
}
label {
color: white !important;
}
@media only screen and (max-width : 992px) {
.parallax-container .section {
top: 40%;
}
#index-banner .section {
top: 10%;
}
}
@media only screen and (max-width : 600px) {
.parallax-container .section {
height: auto;
overflow: auto;
}
.container {
height: auto;
}
#index-banner .section {
top: 0;
}
}
#tagline {
font-weight: 600;
}
#event-info {
font-weight: 400;
}
#registration-banner {
min-height: 100px;
max-height: 150px;
}
#registration-banner .section{
top: auto;
}
.icon-block {
padding: 0 15px;
}
.icon-block .material-icons {
font-size: inherit;
}
.parallax img {
display: inherit;
max-width: 200%;
}
#mlh-trust-badge {
display: block;
max-width: 100px;
min-width: 60px;
position: fixed;
right: 50px;
top: 0;
width: 10%;
z-index: 10000;
}
nav {
line-height: normal !important;
font-family: "Jost", sans-serif;
font-weight: 700;
}
/*
.navbar-brand {
} */
.footer-nav {
position: absolute;
bottom: 0;
width: 100%;
}
// Forms
label {
padding-bottom: 0.5rem;
}
form {
input {
border-radius: 5px;
}
input[type="submit"] {
background: $color-accent;
border-radius: 10px;
border-color: $color-accent;
}
input[type="radio"] {
padding-right: 5px;
}
input[type="checkbox"]:checked {
visibility: visible;
left: unset;
position: unset;
}
label {
font-size: 1.1rem;
padding-right: 10px;
padding-left: 25px !important;
}
select {
display: unset;
background: $color-section-bg;
max-width: 11rem;
}
}
// Flashed messages
.flashes {
list-style-type: none;
display: flex;
justify-content: center;
}
.message {
width: 80%;
justify-content: center;
border: 1px solid $color-fg;
background-color: $color-accent;
padding: 0.2rem;
font-size: large;
color: $color-fg;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,39 @@
{% extends 'layout.html' %}
{% block navbar %}
<nav class="navbar navbar-dark mb-4 navbar-expand-lg">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="true"
aria-label="Toggle
navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-header">
<a class="navbar-brand" href="{{ url_for('admin.home') }}">
<img src="{{url_for('static', filename='img/logo.png')}}"
alt="Logo" width="35" height="35" class="d-inline-block
align-text-middle mx-2">GoatHacks Admin</a>
</div>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="nav navbar-nav me-auto mb-2 mb-lg-0">
{{ render_nav_item('admin.home', 'User List')}}
{{ render_nav_item('admin.admin_list', 'Admin List')}}
{{ render_nav_item('admin.list_events', 'Event List')}}
{{ render_nav_item('admin.mail', 'Bulk Mail Tool')}}
</ul>
<ul class="nav navbar-nav">
{% if current_user.is_authenticated %}
{{ render_nav_item('dashboard.home', '<- Back To Site') }}
{{ render_nav_item('registration.logout', 'Logout') }}
{% else %}
{{ render_nav_item('registration.register', 'Registration') }}
{{ render_nav_item('registration.login', 'Login') }}
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock %}

View file

@ -1,213 +1,29 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hack @ WPI</title>
<link rel="icon" href="../static/favicon.png" type="image/png">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="https://opensource.keycdn.com/fontawesome/4.7.0/font-awesome.min.css" integrity="sha384-dNpIIXE8U05kAbPhy3G1cz+yZmTzA6CY8Vg/u2L9xRnHjJiAK76m2BIEaSEV+/aU" crossorigin="anonymous">
<!--[if lt IE 9]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script><![endif]--><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
{% extends 'admin-layout.html' %}
{% block head %}
{{super()}}
<script src="http://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
<script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.4.3.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
<script src="{{url_for('static', filename='js/admin.js')}}"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
</head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="{{url_for('static', filename='js/admin.js')}}"></script>
{% endblock %}
<style>
.table-striped > tbody > tr:nth-of-type(odd) {
background-color: rgba(255, 255, 255, 0.15);
}
.table-hover > tbody > tr:hover {
background-color: rgba(255, 255, 255, 0.25);
}
table.sortable th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
content: " \25B4\25BE"
}
canvas {
max-width: 500px;
max-height: 500px;
}
</style>
<div class="contact-section" style="height: auto; background-repeat: repeat; background-size: contain;">
<div class="container-fluid" style="margin-left: 3%; margin-right: 3%;">
<div class="row" style="margin-top: 10%;">
<h5>JSON object of users from MLH (Including dropped applications):</h5>
<p><a href="{{ mlh_url }}"><b>Do NOT share this URL.</b></a></p>
<h5>Get registered hackers only:</h5>
<p><a href="{{url_for('admin.hackers')}}">JSON</a> <a
href="{{url_for('admin.hackers_csv')}}">CSV</a></p>
</div>
<div class="row">
<div class="col-md-4">
<h2 style="">Gender:</h2>
<canvas id="genderCanvas" width="400" height="400"></canvas>
<script>
let genderCtx = document.getElementById('genderCanvas')
let genderChart = new Chart(genderCtx, {
type: 'doughnut',
data: {
labels: ['Female', 'Male', 'Non-Binary/Other'],
datasets: [
{
data: [{{ female_count }}, {{ male_count }}, {{ nb_count }}]
}
]
},
options: {
title: {
display: false
},
legend: {
display: false
},
labels: {
display: false
}
}
})
</script>
</div>
<div class="col-md-4">
<h2 style="">Schools:</h2>
<canvas id="schoolCanvas" width="400" height="400"></canvas>
<script>
let schoolNames = []
let schoolNums = []
{% for school in schools %}
schoolNames.push('{{ school }}')
schoolNums.push({{ schools[school] }})
{% endfor %}
let schoolCtx = document.getElementById('schoolCanvas')
let schoolChart = new Chart(schoolCtx, {
type: 'doughnut',
data: {
labels: schoolNames,
datasets: [
{
data: schoolNums
}
]
},
options: {
title: {
display: false
},
legend: {
display: false
},
labels: {
display: false
}
}
})
</script>
</div>
<div class="col-md-4">
<h2 style="">Majors:</h2>
<canvas id="majorCanvas" width="400" height="400"></canvas>
<script>
let majorNames = []
let majorNums = []
{% for major in majors %}
majorNames.push('{{ major }}')
majorNums.push({{ majors[major] }})
{% endfor %}
let majorCtx = document.getElementById('majorCanvas')
let majorChart = new Chart(majorCtx, {
type: 'doughnut',
data: {
labels: majorNames,
datasets: [
{
data: majorNums
}
]
},
options: {
title: {
display: false
},
legend: {
display: false
},
labels: {
display: false
}
}
})
</script>
</div>
</div>
<div class="row" style="">
<h2 style="">Counts:</h2>
<table id="counts" class="table table-striped table-hover table-condensed sortable">
<thead>
<tr>
<th>Total</th>
<th>Attendees</th>
<th>Waitlist</th>
<th>Checked In</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ total_count }}</td>
<td>{{ (total_count - waitlist_count) }}</td>
<td>{{ waitlist_count }}</td>
<td>{{ check_in_count }}</td>
</tr>
</tbody>
</table>
<h2 style="margin-top: 2%;">Shirts:</h2>
<table id="shirts" class="table table-striped table-hover table-condensed sortable">
<thead>
<tr>
<th>XXS</th>
<th>XS</th>
<th>S</th>
<th>M</th>
<th>L</th>
<th>XL</th>
<th>XXL</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ shirt_count['XXS'] }}</td>
<td>{{ shirt_count['XS'] }}</td>
<td>{{ shirt_count['S'] }}</td>
<td>{{ shirt_count['M'] }}</td>
<td>{{ shirt_count['L'] }}</td>
<td>{{ shirt_count['XL'] }}</td>
<td>{{ shirt_count['XXL'] }}</td>
</tr>
</tbody>
</table>
<h2 style="margin-top: 2%;">Hackers:</h2>
<table id="hackers" class="table table-striped table-hover table-condensed sortable">
{% block app_content %}
<div class="card text-center">
<div class="card-body">
<h1 class="h3 mb-3 fw-normal">Registered Users</h1>
<table id="hackers" class="table table-striped">
<thead>
<tr>
<th>Options</th>
<th>Checked In?</th>
<th>Waitlisted?</th>
<th>Admin</th>
<th>MLH ID</th>
<th>Time Registered</th>
<th>User ID</th>
<th>Registration Time</th>
<th>Email</th>
<th>Name</th>
<th>Phone</th>
@ -218,63 +34,61 @@
</thead>
<tbody>
{% for hacker in hackers %}
<tr id="{{ hacker.id }}-row">
<tr id="{{hacker.id}}-row">
<td>
<div class="btn-group">
<a href="#" class="btn btn-primary dropdown-toggle" data-toggle="dropdown"><span
<div class="dropdown">
<a href="#" class="btn btn-primary dropdown-toggle"
data-bs-toggle="dropdown"><span
class="caret"></span></a>
<ul class="dropdown-menu">
{% if not hacker.checked_in %}
<li><a class="check_in" id="{{ hacker.id }}-check_in" href="#">Check In</a>
</li>
<li><a class="dropdown-item check_in" id="{{
hacker.id}}-check_in"
href="#">Check
In</a></li>
{% endif %}
{% if hacker.waitlisted and not hacker.checked_in %}
<li><a class="promote_from_waitlist"
id="{{ hacker['id'] }}-promote_from_waitlist"
href="#">Promote From Waitlist</a></li>
{% if hacker.waitlisted and not
hacker.checked_in %}
<li><a class="dropdown-item
promote_from_waitlist" id="{{
hacker.id}}-promote_from_waitlist"
href="#">Promote From
Waitlist</a></li>
{% endif %}
<li class="divider"></li>
<li><hr class="dropdown-divider"></li>
{% if not hacker.checked_in %}
<li><a class="drop" id="{{ hacker['id'] }}-drop" href="#">Drop Application</a>
</li>
<li><a class="dropdown-item drop"
id="{{hacker.id}}-drop"
href="#">Drop Registration</a></li>
{% endif %}
{% if hacker.is_admin %}
<li><a class="demote_admin" id="{{ hacker['id'] }}-demote_admin" href="#">Demote
Admin</a>
</li>
<li><a class="dropdown-item demote_admin"
id="{{hacker.id}}-demote_admin"
href="#">Demote Admin</a></li>
{% else %}
<li><a class="promote_admin" id="{{ hacker['id'] }}-promote_admin" href="#">Promote
Admin</a>
</li>
<li><a class="dropdown-item promote_admin"
id="{{hacker.id}}-promote_admin"
href="#">Promote Admin</a></li>
{% endif %}
</ul>
</div>
</td>
<td id="{{ hacker['id'] }}-checked_in">{{ hacker['checked_in'] }}</td>
<td id="{{ hacker['id'] }}-waitlisted">{{ hacker['waitlisted'] }}</td>
<td id="{{hacker.id}}-checked_in">{{ hacker.checked_in }}</td>
<td id="{{hacker.id}}-waitlisted">{{ hacker.waitlisted }}</td>
<td>{{ hacker.is_admin }}</td>
<td>{{ hacker['id'] }}</td>
<td>{{ hacker['registration_time'] }}</td>
<td>{{ hacker['email'] }}</td>
<td>{{ hacker['first_name'] + ' ' + hacker['last_name'] }}</td>
<td>{{ hacker['phone'] }}</td>
<td>{{ hacker['shirt_size'] }}</td>
<td>{{ hacker['accomodations'] }}</td>
<td>{{ hacker['school'] }}</td>
<td>{{ hacker.id }}</td>
<td>{{ hacker.last_login }}</td>
<td>{{ hacker.email }}</td>
<td>{{ hacker.first_name + ' ' + hacker.last_name }}</td>
<td>{{ hacker.phone }}</td>
<td>{{ hacker.shirt_size }}</td>
<td>{{ hacker.accomodations }}</td>
<td>{{ hacker.school }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}
<script>
$(document).ready(() => {
setTimeout('let myTh = document.getElementsByTagName("th")[14]; sorttable.innerSortFunction.apply(myTh, []); sorttable.innerSortFunction.apply(myTh, []);', 50)
})
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<script src="../static/js/jquery.easing.min.js"></script>
</body>
</html>

View file

@ -1,20 +0,0 @@
{% include 'header.html' %}
<div class="container">
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul class="flashed-content">
{% for m in messages %}
<li class="message">{{ m }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}
This content block is still being worked on!
{% endblock %}
</div>
{% include 'footer.html' %}

View file

@ -0,0 +1,44 @@
{% from 'bootstrap5/nav.html' import render_nav_item %}
{% from 'bootstrap5/utils.html' import render_messages %}
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
{% if title %}
<title>{{ title }} - GoatHacks</title>
{% else %}
<title>GoatHacks</title>
{% endif %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% block styles %}
<!-- Bootstrap CSS -->
{{ bootstrap.load_css() }}
{% endblock %}
{% endblock %}
</head>
<body>
<!-- Your page content -->
{% block navbar %}{% endblock %}
<!-- Your page content -->
<div class="container">
<div id="outerAlerts">
{{ render_messages(container=False, dismissible=True, dismiss_animate=True) }}
</div>
{% block app_content %}{% endblock %}
</div>
{% block scripts %}
<!-- Optional JavaScript -->
{{ bootstrap.load_js() }}
{% endblock %}
</body>
</html>

View file

@ -1,101 +1,56 @@
{% extends 'base.html' %}
{% extends 'layout.html' %}
{% from 'bootstrap5/form.html' import render_field %}
{% block content %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
<script>
function drop(id) {
if(window.confirm("Are you sure you wish to drop your application? This cannot be undone. (patiently wait after clicking the button)")) {
window.location.href = "/admin/drop/" + id;
}
}
function resumeChange() {
let val = document.getElementById('resume').value;
val = val.split(/\\|\//gi).slice(-1)[0]
document.getElementById('filename').setAttribute('value', val);
}
</script>
<div class="contact-section" style="height: 100%;">
<div class="container">
<div class="row center justify-content-center" style="margin-top: 10%;">
<h1>Hi {{ current_user.first_name }}!</h1>
{% block app_content %}
<div class="card text-center">
<div class="card-body">
<h1 class="h2 mb-3 fw-normal">Hi {{current_user.first_name}}!</h1>
{% if current_user.waitlisted %}
<h2>You are waitlisted, if space opens up we will let you know</h2>
<h4 class="h3 mb-3 fw-normal">You're currently waitlisted. If space
opens up, we'll let you know!</h4>
{% else %}
<h2>You are fully registered! We look forward to seeing you!</h2>
Let us know if you have any questions by sending them to <a href="mailto:hack@wpi.edu">hack@wpi.edu</a>
<br>
<br>
Forgot to upload your resume while registering? No worries, submit it below.
</div>
<h4>You are fully registered! We look forward to seeing you!</h4>
{% endif %}
<p class="card-text">Let us know if you have any questions by sending
them to <a href="mailto:hack@wpi.edu">hack@wpi.edu</a></p>
{% if not current_user.waitlisted and config['DISCORD_LINK'] %}
<p>Make sure to join our Discord to get the latest updates!</p>
<button type="button" class="btn btn-primary mb-3"><a href="{{ config['DISCORD_LINK']
}}" class="link-light">Discord</a></button>
{% endif %}
<div class="row center justify-content-center">
<h5>Make sure to join the Discord and enter your shirt size below!</h5>
<p>(Please note that due to COVID-19 constraints, we can't guarantee that all participants will receive Hack@WPI t-shirts this year but we are trying to find a way!)</p>
<a href="https://discord.gg/G3pseHPRNv" style="margin: 5px;"
class="btn btn-lg btn-primary btn-invert">Discord</a>
</div>
<div class="row center justify-content-center" style="background-color: #974355; padding: 20; margin-left: 20; margin-right: 20; border-radius: 5px;">
<form method="post">
{{ form.csrf_token }}
<br>
<p><b>Optional Info:</b></p>
<div>
<p>Shirt Size (Currently selected: {{current_user.shirt_size}})</p>
{% for subfield in form.shirt_size %}
{{subfield}}{{subfield.label}}
{% endfor %}
<p>Special Needs/Accommodations:</p>
<input type="text" name="accomodations"
id="special_needs" value="{{ current_user.accomodations }}">
{{ form.csrf_token() }}
<p><b>Optional Info</b></p>
<div class="form-floating mb-3 required">
{{ form.shirt_size(class="form-control", selected=current_user.shirt_size) }}
{{ form.shirt_size.label() }}
</div>
<br><br>
<input name="save" class="btn btn-lg btn-primary btn-invert" type="submit" value="Save"/>
<br><br><br>
<div class="form-floating mb-3 required">
{{ form.accomodations(class="form-control") }}
{{ form.accomodations.label() }}
</div>
{{ render_field(form.submit) }}
</form>
</div>
<hr/>
<div class="row center justify-content-center">
<form method="post" action="{{url_for('dashboard.resume')}}" enctype="multipart/form-data">
{{ resform.csrf_token }}
<p><b>If you'd like, add your resume to send to sponsors... </b></p>
<div class="file-field input-field">
<div class="btn">
<span>File</span>
<input id="resume" name="resume" type="file" oninput="resumeChange()"/>
<form method="post" action="{{url_for('dashboard.resume')}}"
enctype="multipart/form-data">
{{ resform.csrf_token() }}
<p><b>If you'd like, add your resume to send to
sponsors...</b></p>
<div class="form mb-3 required">
{{ resform.resume(class="form-control") }}
</div>
<div class="file-path-wrapper white-text">
<input disabled id="filename" class="file-path validate white-text" type="text">
</div>
</div>
<input name="submit" class="btn btn-lg btn-primary btn-invert" type="submit" value="Submit"/>
{{ render_field(resform.submit) }}
</form>
{% endif %}
<br>
</div>
{% if current_user.is_admin %}
<br>
<div class="row justify-content-center">
<a href="/admin"><p class="btn">Admin Dashboard</p></a>
</div>
{% endif %}
<br>
</div>
<br>
<br>
<center><a onclick="drop('{{current_user.id}}')" id="drop-link"><p class="btn">Drop Application if you can't make it :(</p></a></center>
</div>
<script>
let errColor = '#E74C3C'
$(document).ready(() => {
$('#drop-link').click((e) => {
e.preventDefault()
let id = {{ id }}
drop(id)
})
})
</script>
{% endblock %}

View file

@ -0,0 +1,15 @@
Hello!
Someone just requested a password reset for your GoatHacks account. If this
wasn't you, don't worry. Just ignore this request, and nothing will happen.
If this was you, please follow this link: {{url_for("registration.do_reset", id=code, _external=True)}}
This link will expire in 30 minutes.
Happy Hacking!
GoatHacks Team
This is an automated message. Please email hack@wpi.edu with any questions or
concerns

View file

@ -3,7 +3,7 @@ Dear {{ user.first_name }},
Your application for GoatHacks has been confirmed! {% if user.waitlisted
%}You're on the waitlist right now, but we'll send you another email if a spot
opens up.{% else %}You've got a confirmed spot this year! Make sure to look at
the schedule at https://hack.wpi.edu.
the schedule at https://hack.wpi.edu.{% endif %}
{% if not user.waitlisted %}
We'll send another email with more details closer to the event. In the

View file

@ -0,0 +1,199 @@
{% extends 'admin-layout.html' %}
{% block app_content %}
<div class="card text-center">
<div style="height: 100%;">
<h2>Events</h2>
Get a JSON readout of events <a href="{{ url_for('admin.events_json')
}}">here</a>
<table class="table table-striped table-hover table-condensed" style="color:
white;">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Location</th>
<th>Start</th>
<th>End</th>
<th>Category</th>
<th>Checked in</th>
<th>QR Code</th>
<th><a href="#editModal" data-bs-toggle="modal" data-id="0">New</a></th>
</tr>
</thead>
<tbody>
{% for event in events %}
<tr id="{{event.id}}-row">
<td>{{ event.id }}</td>
<td>{{ event.name }}</td>
<td>{{ event.location }}</td>
<td>{{ event.start_time }}</td>
<td>{{ event.end_time }}</td>
<td>{{ event.category }}</td>
<td>{{ event.get_checkins()|length }}</td>
<td><a href='{{ url_for("admin.qrcode_event", id=event.id)
}}'>QR Code</a></td>
<td><a href="#editModal" data-bs-toggle="modal" data-id="{{ event.id}}" >Edit</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<div class="modal" id="editModal" tabindex="-1" aria-labelledby="editModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="editModalLabel">Event</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form class="form" id="edit-form" action="/admin/events/0" role="form" method="post">
<div class="modal-body">
{{ form.csrf_token }}
<div class="form-floating mb-3 required">
{{ form.name(class="form-control") }}
{{ form.name.label() }}
</div>
<div class="form-floating mb-3">
{{ form.description(class="form-control") }}
{{ form.description.label() }}
</div>
<div class="form-floating mb-3 required">
{{ form.location(class="form-control") }}
{{ form.location.label() }}
</div>
<div class="form-floating mb-3">
{{ form.category(class="form-control") }}
{{ form.category.label() }}
</div>
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.start_day(class="form-control") }}
{{ form.start_day.label() }}
</div>
</div>
<div class="col">
<div class="form-floating mb-3 required">
{{ form.start_time(class="form-control") }}
{{ form.start_time.label() }}
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.end_day(class="form-control") }}
{{ form.end_day.label() }}
</div>
</div>
<div class="col">
<div class="form-floating mb-3 required">
{{ form.end_time(class="form-control") }}
{{ form.end_time.label() }}
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-id="0" id="delete">Delete</button>
<button type="submit" class="btn btn-primary" id="edit-save">Save changes</button>
</div>
</form>
</div>
</div>
</div>
<script src="{{ url_for('static', filename='js/jquery-3.6.3.min.js') }}" charset="utf-8"></script>
<script charset="utf-8">
const editButton = document.getElementById("edit-save")
$('#delete').on("click", (event) => {
if (window.confirm("Delete this event?")) {
console.log("Got OK")
deleteButton = document.getElementById("delete")
id = deleteButton.dataset.id
$.get(`/admin/event/${id}/delete`, (data) => {
if (data.status == "error") {
window.alert(`Error: ${data.message}`)
} else {
window.alert("Success")
}
location.reload()
})
}
})
$('#editModal').on('show.bs.modal', function(event) {
var modal = $(this)
modal.find('#name').val('')
modal.find('#location').val('')
modal.find('#description').val('')
modal.find('#start_day').val('')
modal.find('#start_time').val('')
modal.find('#end_day').val('')
modal.find('#end_time').val('')
var button = $(event.relatedTarget)
var name,description,loc,start_time,start_day,end_time,end_day
id = button.data('id')
saveButton = document.getElementById("edit-save")
saveButton.dataset.id = id
deleteButton = document.getElementById("delete")
deleteButton.dataset.id = id
editForm = document.getElementById("edit-form")
editForm.action = "/admin/event/" + id
if (id) {
$.get(`/admin/event/${id}`, (data) => {
if (data.status == "error") {
// This is a new event, do nothing!
} else {
name = data.name,
description = data.description,
loc = data.location,
category = data.category
start = new Date(data.start_time)
var day = ("0" + start.getDate()).slice(-2);
var month = ("0" + (start.getMonth() + 1)).slice(-2);
start_day = start.getFullYear()+"-"+(month)+"-"+(day);
start_time = `${start.getHours()}:${padTwoDigits(start.getMinutes())}`
end = new Date(data.end_time)
var day = ("0" + end.getDate()).slice(-2);
var month = ("0" + (end.getMonth() + 1)).slice(-2);
end_day = end.getFullYear()+"-"+(month)+"-"+(day);
end_time = `${end.getHours()}:${padTwoDigits(end.getMinutes())}`
}
modal.find('#name').val(name)
modal.find('#location').val(loc)
modal.find('#description').val(description)
modal.find('#start_day').val(start_day)
modal.find('#start_time').val(start_time)
modal.find('#end_day').val(end_day)
modal.find('#end_time').val(end_time)
modal.find('#category').val(category)
});
}
})
function padTwoDigits(num) {
return num.toString().padStart(2, '0')
}
</script>
{% endblock %}

View file

@ -0,0 +1,49 @@
{% extends 'admin-layout.html' %}
{% block app_content %}
<div class="card text-center">
<div style="height: 100%;">
<div id="registration-banner" class="parallax-container valign-wrapper">
<div class="section">
<h3 class="header-center text-darken-2">Create/Edit Event</h3>
</div>
</div>
</div>
<div class="container">
<form method="post">
{{ form.csrf_token }}
<div class="form-floating mb-3 required">
{{ form.name(class="form-control") }}
{{ form.name.label }}
</div>
<div class="form-floating mb-3">
{{ form.description(class="form-control") }}
{{form.description.label}}
</div>
<div class="form-floating mb-3 required">
{{ form.location(class="form-control")}}
{{form.location.label}}
</div>
<div class="row">
<div class="col">
<div class="form mb-3 required">
{{form.start_time(class="form-control")}}
{{form.start_time.label}}
</div>
</div>
<div class="col">
<div class="form mb-3 required">
{{form.end_time(class="form-control")}}
{{form.end_time.label}}
</div>
</div>
</div>
<div class="form-floating mb-3 required">
{{form.category(class="form-control")}}
{{form.category.label}}
</div>
{{form.submit}}
</form>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,7 @@
<head>
<title>QR Code for {{ event.name }}</title>
</head>
<body>
<img src="{{ qrcode(url_for('events.workshop_checkin', id=event.id,
_external=True)) }}">
</body>

View file

@ -1,16 +0,0 @@
<!-- A hack to make sure the footer bar shows-->
<div style="height: 100px"></div>
<nav class="navbar navbar-inverse footer-nav" style= "margin-top: 30px">
<div class="container-fluid">
<div class="navbar-header">
</div>
<ul class="nav justify-content-end">
<li class="nav-item">
<a class="nav-link" href="mailto:hack@wpi.edu?subject=[Hackathon]">Contact Us</a>
</li>
</ul>
</div>
</nav>
</body>
</html>

View file

@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link href="../static/css/materialize.min.css" rel="stylesheet">
{% assets 'scss' %}
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
{% endassets %}
</head>
<style>
nav {
background-color: #F5665B;
border-color: #E7E4C6;
color: white;
}
</style>
<body>
<nav class="navbar navbar-inverse sticky-top navbar-expand">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="/">Goathacks</a>
</div>
<ul class="nav justify-content-end">
<!-- <li class="nav-item">
{% if registration_open or registration_open is not defined %}
<a class="nav-link active" href="/register">Register</a>
{% else %}
<a class="nav-link active disabled" href="#">Registration is closed</a>
{% endif %}
</li>
<li class="nav-item">
<a class="nav-link" href="../static/assets/Hack@WPI-SponsorshipInformation-2020.pdf">Sponsorship Packet</a>
</li> -->
<li class="nav-item">
<a class="nav-link" href="https://hack.wpi.edu/conduct/conduct.pdf">Code of Conduct</a>
</li>
</ul>
</div>
</nav>

@ -1 +1 @@
Subproject commit fba594664faeb8b6056462a0f8bc79a0d21a3768
Subproject commit db2a7a865f9b3865fa2180b1b53b1c2d2640be81

View file

@ -1,195 +0,0 @@
{% include 'header.html' %}
<div>
<style>
hr {
margin-top: 2rem;
margin-bottom: 2rem;
width: 70%;
border-width: 10px;
border-color: #F5665B;
}
h2 {
text-align: center;
}
h3 {
text-align: center;
}
h4 {
text-align: center;
}
h5 {
text-align: center;
}
h6 {
text-align: center;
margin-right: 100px;
margin-left: 100px;
}
div.main-logo {
margin-top: 16px;
}
.sponsors {
text-align: center;
display: block;
padding: 20px;
}
.sponsors > div {
margin-bottom: 40px;
}
.sponsors > div:last-child {
margin-bottom: 0;
}
.sponsors img {
display: inline-block;
margin-right: 40px;
/* Hack to fix overflow of bose logo*/
max-width: calc(100% - 10px);
}
.title-sponsors img{
max-height: 90px;
}
.silver-sponsors img {
max-height: 25px;
}
a {
color: black;
}
.x{
background-color: #E7E4C6;
margin-right: 100px;
margin-left: 100px;
color: black;
border-radius: 50px 50px 50px 50px;
padding-bottom: 10px;
padding-top:10px;
}
.snow-banner {
background-color: red;
width: 100%;
padding: 10px;
text-align: center;
}
</style>
<div class="snow-banner">
<b>Attention!</b> Due to the road conditions, we have delayed the schedule by an hour. We will still have live presentations, but submissions have been extended until 9am, and judging will take place at 10am.
</div>
<!--Logo + Date+ Hosted By -->
<div class="container">
<div class="row center main-logo">
<img src="../static/img/hackwpilogo.png" alt="logo" style='height: 100%; width: 100%; object-fit: contain'/>
</div>
</div>
<!--"Hackathon for all students" -->
<hr/>
<h2> A HACKATHON FOR ALL COLLEGE STUDENTS </h2>
<hr/>
<!--Location -->
<h3> WPI Campus Center </h3>
<h4>100 Institute Road</h4>
<h4> Worcester, MA 01609</h4>
<hr/>
<!-- Sponsors for 2020 -->
<h2> OUR SPONSORS FOR 2020 </h2>
<div class="container">
<div class="row center sponsors">
<div class="title-sponsors">
<img class="logo" src="../static/img/logos/bose.png"></div>
<div class="platinum-sponsors">
<h1>WPI Computer Science Department</h1>
</div>
<div class="gold-sponsors">
<img class="logo" src="../static/img/logos/iboss.png" style = "height: 150px">
<img class="logo" src="../static/img/logos/kronos.png" style = "height: 75px">
<img class="logo" src="../static/img/logos/paytronix.png" style = "height: 30px">
<img class="logo" src="../static/img/logos/abbvie.png" style = "height: 60px">
</div>
<div class="silver-sponsors">
<img class="logo" src="../static/img/logos/carbonblack.png" >
</div>
<div class="bronze-sponsors">
<img class="logo" src="../static/img/logos/wakefly.png" style = "height: 70px">
<img class="logo" src="../static/img/logos/amazon.png" style = "height: 70px">
<img class="logo" src="../static/img/logos/everquote.png" style = "height: 35px">
<img class="logo" src="../static/img/logos/synopses.png" style = "height: 20px">
<img class="logo" src="../static/img/logos/boozallenhamilton.png" style = "height: 35px">
</div>
</div>
</div>
<!--FAQs -->
<hr/>
<div class = "x">
<h2> FAQs </h2>
<div class = "FAQ">
<h4>Who can attend?</h4>
<h6> Our event is open to any college student! You dont need any experience to participate. However, due to legal issues, anyone under 18 must have a legal guardian present and cannot win prizes </h6>
</div>
<div class = "FAQ">
<h4>What is the max team size?</h4>
<h6>The maximum is 5 people per team. Minimum is 1 :P</h6>
</div>
<div class = "FAQ">
<h4>What are the awards for?</h4>
<h6>The categories this year are: </h6>
<h6>Best Overall Project
<h6>Best Game </h6>
<h6>Best Software </h6>
<h6>Best Hardware </h6>
<h6>Company Favorite </h6>
<h6>Best Use of the Bose API/SDK </h6>
<h6>Best Meme </h6>
<h6>Best Rookie </h6>
</div>
<div class = "FAQ">
<h4>What should I bring?</h4>
<h6>Bring devices, chargers, sleeping bags, toiletries, basically anything you need to be comfortable. We will provide showers at certain time slots during the weekend. Food will also be provided.</h6>
</div>
<div class = "FAQ">
<h4>What if I don't have a team?</h4>
<h6>You dont need a team to register! We will have a team formation session right after the opening ceremony for those who would like to work with a team. If you prefer to stay solo, thats also great. </h6>
</div>
<div class = "FAQ">
<h4>How should I prepare beforehand?</h4>
<h6>Make sure you have travel plans in place, especially in case of random snowfalls. Unfortunately, we do not provide travel reimbursements.
Its also nice to have an idea in mind beforehand. However, we ask that you do not bring any pre-made materials to keep the competition fair. </h6>
</div>
<div class = "FAQ">
<h4>What is the schedule?</h4>
<h6><a href = 'https://docs.google.com/spreadsheets/d/1FSh2UfZwoMwXlgrKnKjnQg2okEcLDVCP30khnPdM1H0/edit?usp=sharing' style= "color: blue">Here it is!</a> Keep in mind that this is tentative. </h6>
</div>
</div>
</div>
{% include 'footer.html' %}

View file

@ -0,0 +1,56 @@
{% extends 'bootstrap-base.html' %}
{% block html_attribs %} lang="en"{% endblock %}
{% block title %}{% if title %}{{ title }} - GoatHacks{% else %}GoatHacks{%
endif %}{% endblock %}
{% block head %}
{{super()}}
{{ font_awesome.load_css() }}
{% endblock %}
{% block styles %}
{{super()}}
{% assets 'scss' %}
<link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
{% endassets %}
{% endblock %}
{% block navbar %}
<nav class="navbar navbar-dark mb-4 navbar-expand-lg">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent"
aria-expanded="true"
aria-label="Toggle
navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-header">
<a class="navbar-brand" href="{{ url_for('registration.register') }}">
<img src="{{url_for('static', filename='img/logo.png')}}"
alt="Logo" width="35" height="35" class="d-inline-block
align-text-middle mx-2">GoatHacks</a>
</div>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="nav navbar-nav me-auto mb-2 mb-lg-0">
{{ render_nav_item('registration.register', 'Home')}}
</ul>
<ul class="nav navbar-nav">
{% if current_user.is_authenticated %}
{{ render_nav_item('dashboard.home', 'Dashboard') }}
{% if current_user.is_admin %}
{{ render_nav_item('admin.home', 'Admin Dash') }}
{% endif %}
{{ render_nav_item('registration.logout', 'Logout') }}
{% else %}
{{ render_nav_item('registration.register', 'Registration') }}
{{ render_nav_item('registration.login', 'Login') }}
{% endif %}
</ul>
</div>
</div>
</nav>
{% endblock %}

View file

@ -1,32 +1,34 @@
{% extends 'base.html' %}
{% extends 'layout.html' %}
{% from 'bootstrap5/form.html' import render_field %}
{% block content %}
<div style="height: 100%;">
<div id="registration-banner" class="parallax-container valign-wrapper">
<div class="section">
<h3 class="header-center text-darken-2">Login</h3>
{% block app_content %}
<div class="card text-center">
<div class="card-body">
<img class="mb-4" src="{{ url_for('static', filename='img/banner.png')
}}" alt="GoatHacks 2024 Banner">
<h1 class="h3 mb-3 fw-normal">Welcome back to GoatHacks!</h1>
<p class="card-text">If you've already registered, please use this page
to access your participant dashboard. Otherwise, please use the <a
href="{{
url_for('registration.register')
}}"
>
registration</a> page to register!</p>
<div class="text-center">
<form action="" method="post" class="form" role="form">
{{ form.csrf_token() }}
<div class="form-floating mb-3 required">
{{ form.email(class="form-control") }}
{{ form.email.label() }}
</div>
<div class="parallax"><img src="{{url_for('static', filename='img/background1.jpg')}}"
alt="background"></div>
</div>
</div>
<div class="container">
<div class="section" style="background-color: #974355; padding: 20px;">
<form method="post">
{{ form.csrf_token }}
<div>
{{form.email}}<br/>{{ form.email.label}}
</div>
<div>
{{form.password}}<br/>{{form.password.label}}
</div>
<div>
{{form.submit}}
<div class="form-floating mb-3 required">
{{ form.password(class="form-control") }}
{{ form.password.label() }}
</div>
{{ render_field(form.submit) }}
</form>
<span><p><em>Don't have an account? <a
href="{{url_for('registration.register')}}">Register
here</a>.</em></p></span>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,23 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>🍪CookieMailer</title>
{% extends 'admin-layout.html' %}
{% block head %}
{{super()}}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content="" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<meta name="theme-color" content="">
<link rel="stylesheet" type="text/css" href="/static/css/mail.css" />
<script src="https://cdn.tiny.cloud/1/{{MCE_API_KEY}}/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script></head>
<body>
<div id="loader"></div>
<div class="container">
<div class="row">
<div id="root" class="col">
<h2>🍪CookieMailer</h2>
{% endblock %}
{% block app_content %}
<div class="card">
<div class="card-body">
<h1 class="h3 mb-3 fw-normal">🍪CookieMailer</h1>
<label for="recipients">To: </label>
<br/>
<select id="recipients" name="recipients">
<select class="form-control" id="recipients" name="recipients">
<option value="admin">Test Email (SysAdmin)</option>
<option value="org">Test Email (Organizers)</option>
<!-- <option value="wpi">WPI Only</option> -->
@ -26,7 +23,8 @@
<br/>
<label for="subject">Subject: </label>
<br/>
<input id="subject" name="subject" width="100%" type="text" value="Hack@WPI" />
<input class="form-control" id="subject" name="subject" width="100%"
type="text" value="GoatHacks -- " />
<br/>
<br/>
<textarea id="content" name="content">
@ -40,7 +38,7 @@
</textarea>
<br/>
<br/>
<input type="button" onClick="send()" value="Send"/>
<input class="btn btn-primary" type="button" onClick="send()" value="Send"/>
</div>
</div>
</div>
@ -77,6 +75,7 @@
];
if((rec != "all" && window.confirm("Send test email?")) || (rec == "all" && window.confirm("Send email to {{NUM_HACKERS}} recipients?"))) {
console.log(`Sending email to ${rec}`)
fetch('/admin/send', {method: 'POST', body: JSON.stringify(body), headers: headers}).then(async (res) => {
window.alert(await res.text());
}).catch((err) => {
@ -96,6 +95,7 @@
// })
}
</script>
</body>
</div>
</div>
{% endblock %}
</html>

View file

@ -1,60 +1,112 @@
{% extends 'base.html' %}
{% extends 'layout.html' %}
{% from 'bootstrap5/form.html' import render_field %}
{% block content %}
<div style="height: 100%;">
<div id="registration-banner" class="parallax-container valign-wrapper">
<div class="section">
<h3 class="header-center text-darken-2">Registration</h3>
{% block app_content %}
<div class="card text-center">
<div class="card-body">
<img class="mb-4" src="{{ url_for('static', filename='img/banner.png')
}}" alt="GoatHacks Banner">
<h1 class="h3 mb-3 fw-normal">Welcome to GoatHacks!</h1>
<p class="card-text">Please use this page to register for this year's
Hackathon. Accounts from prior years are not carried over!</p>
<div class="text-center">
<form action="" method="post" class="form" role="form">
{{ form.csrf_token() }}
<div class="form-floating mb-3 required">
{{ form.email(class="form-control") }}
{{ form.email.label() }}
</div>
<div class="parallax"><img src="{{url_for('static', filename='img/background1.jpg')}}"
alt="background"></div>
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.password(class="form-control") }}
{{ form.password.label() }}
</div>
</div>
<div class="container">
<div class="section" style="background-color: #974355; padding: 20px;">
<form method="post">
{{ form.csrf_token }}
<div>
{{ form.email}}<br/> {{ form.email.label }}
</div>
<div>
{{ form.password}}<br/>{{form.password.label}}
<div class="col">
<div class="form-floating mb-3 required">
{{ form.password_confirm(class="form-control") }}
{{ form.password_confirm.label() }}
</div>
<div>
{{ form.password_confirm}}<br/>{{form.password_confirm.label}}
</div>
<div>
{{form.first_name}}<br/>{{form.first_name.label}}
</div>
<div>
{{form.last_name}}<br/>{{form.last_name.label}}
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.first_name(class="form-control") }}
{{ form.first_name.label() }}
</div>
<hr/>
<h3>Miscellaneous Information</h3>
<div>
{{form.phone_number}}<br/>{{form.phone_number.label}}
</div>
<div>
{{form.school}}<br/>{{form.school.label}}
<div class="col">
<div class="form-floating mb-3 required">
{{ form.last_name(class="form-control") }}
{{ form.last_name.label() }}
</div>
<div>
{{form.gender.label}}{{form.gender}}
</div>
<hr/>
<div>
<label for="agree_coc">I confirm that I have read and agree to the
Code of Conduct</label>
<input type="checkbox" id="agree_coc" name="agree_coc">
</div>
<div>
{{form.submit}}
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.school(class="form-control") }}
{{ form.school.label() }}
</div>
</div>
<div class="col">
<div class="form-floating mb-3 required">
{{ form.country(class="form-control") }}
{{ form.country.label() }}
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-floating mb-3 required">
{{ form.phone_number(class="form-control") }}
{{ form.phone_number.label() }}
</div>
</div>
<div class="col">
<div class="form-floating mb-3 required">
{{ form.age(class="form-control") }}
{{ form.age.label() }}
</div>
</div>
</div>
<div class="form-floating mb-3 required">
{{ form.gender(class="form-control") }}
{{ form.gender.label() }}
</div>
<div class="form-floating mb-3">
{{ form.dietary_restrictions(class="form-control") }}
{{ form.dietary_restrictions.label() }}
</div>
<div class="form-check mb-3 required">
{{ form.agree_coc }}
I confirm that I have read and agree to the <a href="https://static.mlh.io/docs/mlh-code-of-conduct.pdf" target="_blank">MLH Code of Conduct</a>
</div>
<div class="form-check mb-3 required">
{{ form.logistics }}
I authorize you to share my application/registration with Major League Hacking
for event administration, ranking, and MLH administration in-line with the MLH
privacy policy. I further agree to the terms of both the <a
href="https://github.com/MLH/mlh-policies/blob/main/contest-terms.md">MLH
Contest Terms
and
Conditions</a>
and the <a
href="https://github.com/MLH/mlh-policies/blob/main/privacy-policy.md">MLH
Privacy
Policy</a>.
</div>
<div class="form-check mb-3">
{{ form.newsletter }}
Subscribe to the MLH newsletter?
</div>
{{ render_field(form.submit) }}
</form>
<span><p><em>You may also want to <a
href="{{url_for('registration.login')}}">
log in</a>.</em></p></span>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -1,69 +0,0 @@
{% include 'header.html' %}
<link href="../static/css/materialize.min.css" rel="stylesheet">
<style>
a{
color: skyblue;
}
</style>
<script>
function resumeChange() {
let val = document.getElementById('resume').value;
val = val.split(/\\|\//gi).slice(-1)[0]
document.getElementById('filename').setAttribute('value', val);
}
</script>
<div style="height: 100%;">
<div id="registration-banner" class="parallax-container valign-wrapper">
<div class="section">
<h3 class="header center text-darken-2">Registration</h3>
</div>
<div class="parallax"><img src="../static/img/background1.jpg" alt="background"></div>
</div>
<div class="container">
<div class="container">
<Center><h2>Hi {{ name }}, just a few more steps!</h2></Center>
<div class="section" style="background-color: #974355; padding: 20px;">
<form method="post" action="/register" enctype="multipart/form-data">
<div>
<p><b>If you'd like, add your resume to send to sponsors... </b></p>
<div class="file-field input-field">
<div class="btn">
<span>File</span>
<input id="resume" name="resume" type="file" oninput="resumeChange()"/>
</div>
<div class="file-path-wrapper white-text">
<input disabled id="filename" class="file-path validate white-text" type="text">
</div>
</div>
</div>
<p><i>No worries if you don't have one on hand, you can come back and upload it later via the Hack@WPI dashboard!</i></p>
<p>Please take a moment to review the following policies and check all the boxes below so we can finalize your registration:</p>
<p>
<center><a href="/tos">Hack@WPI's Terms and Conditions</a></center>
</p>
<p>
<center><a href="https://github.com/MLH/mlh-policies/blob/master/data-sharing.md">MLH's Data Sharing Notice</a></center>
</p>
<p>
<center><a href="https://mlh.io/privacy">MLH's Privacy Policy</a></center>
</p>
<p>
<center><a href="https://github.com/MLH/mlh-policies/blob/master/prize-terms-and-conditions/contest-terms.md">MLH's
Contest Terms and Conditions</a></center>
</p>
<p>
<center><a href="https://static.mlh.io/docs/mlh-code-of-conduct.pdf">MLH's Code of Conduct</a></center>
</p>
<br>
<input type="checkbox" name="tos" id="checkboxid" required><label for="checkboxid"><b style="color:white;">*I have read and agree to the Hack@WPI Terms and Conditions</b></label>
<input type="checkbox" name="mlh1" id="checkboxid1" required><label for="checkboxid1"><b style="color:white;">*I have read and agree to the MLH Code of Conduct</b></label>
<input type="checkbox" name="mlh2" id="checkboxid2" required><label for="checkboxid2"><b style="color:white;">*I authorize you to share my application/registration information with Major League Hacking for event administration, ranking, and MLH administration in-line with the MLH Privacy Policy. I further agree to the terms of both the MLH Contest Terms and Conditions and the MLH Privacy Policy.</b></label>
<input type="checkbox" name="mlh3" id="checkboxid3" required><label for="checkboxid3"><b style="color:white;">*I authorize MLH to send me pre- and post-event information emails, which contain free credit and opportunities from their partners.</b></label>
<input type="checkbox" name="mlh3" id="checkboxid4" required><label for="checkboxid4"><b style="color:white;">*I understand that in-person participation is limited to WPI students that are part of the testing protocol only.</b></label>
<br><br>
<center><input name="submit" class="btn btn-lg btn-primary btn-invert" type="submit" value="Submit"/></center>
</form>
</div>
</div>
</div>
{% include 'footer.html' %}

View file

@ -1,16 +0,0 @@
{% include 'header.html' %}
<link href="../static/css/materialize.min.css" rel="stylesheet">
<div style="margin: 16px">
<p>
I agree to hold harmless the organizers of HACK@WPI and WPIs Chapter of the Association of Computing Machinery (ACM) from any and all claims, lawsuits, demands, causes of action, liability, loss, damage and/or injury of any kind whatsoever (including without limitation all claims for monetary loss, property damager, equitable relief, personal injury and/or wrongful death), whether brought by an individual or other entity. The indemnification applies to and includes, without limitation, the payment of all penalties, fines, judgments, awards, decrees, attorneys fees, and related costs or expenses, and any reimbursements to ACM for all legal fees, expenses, and costs incurred by it.
</p>
<p>
I also acknowledge the organizers of HACK@WPI will be recording the event using video, photographs, audio recordings, and other media and give them permission to use said media in marketing/promotional materials.
</p>
</div>
<!-- This is stupid but it works -->
<div style="position: absolute; bottom: 0; width: 100%;">
{% include 'footer.html' %}
</div>

View file

@ -0,0 +1,32 @@
"""empty message
Revision ID: 261c004968a4
Revises: 8a0c9c00f04c
Create Date: 2023-01-03 17:58:35.801660
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '261c004968a4'
down_revision = '8a0c9c00f04c'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('pw_reset_request', schema=None) as batch_op:
batch_op.add_column(sa.Column('expires', sa.DateTime(), nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('pw_reset_request', schema=None) as batch_op:
batch_op.drop_column('expires')
# ### end Alembic commands ###

View file

@ -0,0 +1,46 @@
"""create_event_table
Revision ID: 858e0d45876f
Revises: 261c004968a4
Create Date: 2023-12-01 13:31:00.955470
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '858e0d45876f'
down_revision = '261c004968a4'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('event',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=False),
sa.Column('description', sa.String(), nullable=True),
sa.Column('location', sa.String(), nullable=False),
sa.Column('start_time', sa.DateTime(), nullable=False),
sa.Column('end_time', sa.DateTime(), nullable=False),
sa.Column('category', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
op.create_table('event_checkins',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('event_id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['event_id'], ['event.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('event_checkins')
op.drop_table('event')
# ### end Alembic commands ###

View file

@ -0,0 +1,34 @@
"""empty message
Revision ID: 8a0c9c00f04c
Revises: db38c3deb0b9
Create Date: 2023-01-03 16:59:23.201953
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8a0c9c00f04c'
down_revision = 'db38c3deb0b9'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('pw_reset_request', schema=None) as batch_op:
batch_op.add_column(sa.Column('user_id', sa.Integer(), nullable=False))
batch_op.create_foreign_key(None, 'user', ['user_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('pw_reset_request', schema=None) as batch_op:
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('user_id')
# ### end Alembic commands ###

View file

@ -0,0 +1,31 @@
"""empty message
Revision ID: db38c3deb0b9
Revises: a14a95ec57b0
Create Date: 2022-12-30 14:35:27.652423
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'db38c3deb0b9'
down_revision = 'a14a95ec57b0'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('pw_reset_request',
sa.Column('id', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('pw_reset_request')
# ### end Alembic commands ###

View file

@ -0,0 +1,38 @@
"""empty message
Revision ID: f5b70c6e73eb
Revises: 858e0d45876f
Create Date: 2024-10-31 13:04:48.500263
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'f5b70c6e73eb'
down_revision = '858e0d45876f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.add_column(sa.Column('newsletter', sa.Boolean(), nullable=False))
batch_op.add_column(sa.Column('country', sa.String(), nullable=False))
batch_op.add_column(sa.Column('age', sa.Integer(), nullable=False))
batch_op.add_column(sa.Column('dietary_restrictions', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('user', schema=None) as batch_op:
batch_op.drop_column('dietary_restrictions')
batch_op.drop_column('age')
batch_op.drop_column('country')
batch_op.drop_column('newsletter')
# ### end Alembic commands ###

View file

@ -1,6 +1,7 @@
alembic==1.8.1
click==8.1.3
Flask==2.2.2
Flask-QRCode
Flask-Assets
Flask-CORS
Flask-Mail
@ -8,7 +9,7 @@ Flask-Login==0.6.2
Flask-Migrate==4.0.0
Flask-SQLAlchemy==3.0.2
Flask-WTF==1.0.1
greenlet==2.0.1
greenlet
itsdangerous==2.1.2
Jinja2==3.1.2
Mako==1.2.4
@ -18,6 +19,11 @@ psycopg2==2.9.5
pynvim==0.4.3
python-dotenv==0.21.0
SQLAlchemy==1.4.44
uWSGI==2.0.21
uWSGI
Werkzeug==2.2.2
WTForms==3.0.1
ulid
bootstrap-flask
Font-Awesome-Flask
tabulate
markupsafe