Initial Commit

This commit is contained in:
binamkayastha 2017-12-11 13:48:40 -05:00
commit be23511299
37 changed files with 8856 additions and 0 deletions

824
.gitignore vendored Normal file
View file

@ -0,0 +1,824 @@
config_hackWPI.py
config.py
# Created by https://www.gitignore.io/api/web,vim,git,macos,linux,bower,grunt,python,pycharm,windows,eclipse,webstorm,intellij,jetbrains,virtualenv,visualstudio,visualstudiocode
### Bower ###
bower_components
.bower-cache
.bower-registry
.bower-tmp
### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# PyDev specific (Python IDE for Eclipse)
*.pydevproject
# CDT-specific (C/C++ Development Tooling)
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific (PHP Development Tools)
.buildpath
# sbteclipse plugin
.target
# Tern plugin
.tern-project
# TeXlipse plugin
.texlipse
# STS (Spring Tool Suite)
.springBeans
# Code Recommenders
.recommenders/
# Scala IDE specific (Scala & Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet
### Git ###
*.orig
### grunt ###
# Grunt usually compiles files inside this directory
dist/
# Grunt usually preprocesses files such as coffeescript, compass... inside the .tmp directory
.tmp/
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
# Gradle:
.idea/**/gradle.xml
.idea/**/libraries
# Mongo Explorer plugin:
.idea/**/mongoSettings.xml
## File-based project format:
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
.idea/sonarlint
### JetBrains ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
# Sensitive or high-churn files:
# Gradle:
# Mongo Explorer plugin:
## File-based project format:
## Plugin-specific files:
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
### JetBrains Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
### macOS ###
*.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
# Sensitive or high-churn files:
# Gradle:
# Mongo Explorer plugin:
## File-based project format:
## Plugin-specific files:
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
### PyCharm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# dotenv
.env
# virtualenv
.venv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject
### Vim ###
# swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]
# session
Session.vim
# temporary
.netrwhist
# auto-generated tag files
tags
### VirtualEnv ###
# Virtualenv
# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/
[Bb]in
[Ii]nclude
[Ll]ib
[Ll]ib64
[Ll]ocal
[Ss]cripts
pyvenv.cfg
pip-selfcheck.json
### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
### Web ###
*.asp
*.cer
*.csr
*.htm
*.jsp
*.php
*.rss
*.xhtml
### WebStorm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
# Sensitive or high-churn files:
# Gradle:
# Mongo Explorer plugin:
## File-based project format:
## Plugin-specific files:
# IntelliJ
# mpeltonen/sbt-idea plugin
# JIRA plugin
# Cursive Clojure plugin
# Crashlytics plugin (for Android Studio and IntelliJ)
### WebStorm Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr
# Sonarlint plugin
### Windows ###
# Windows thumbnail cache files
Thumbs.db
ehthumbs.db
ehthumbs_vista.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp_proj
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# End of https://www.gitignore.io/api/web,vim,git,macos,linux,bower,grunt,python,pycharm,windows,eclipse,webstorm,intellij,jetbrains,virtualenv,visualstudio,visualstudiocode
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Kevin Bohinski
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
README.md Normal file
View file

@ -0,0 +1,20 @@
# Hack@WPI 2018 Website
Used chronicel's registration system as a base but removed the need of mailchimp.
Rest is from their repo:
## Setup:
- Clone repo
- `pip3 install -r requirements.txt`
- Fill in all config files!
- Database:
```sh
python3
```
```python
from flask_app import db
db.create_all()
```
- Automatic waitlist management setup: Setup your favorite cron like tool to run `python3 manage_waitlist.py` nightly!
- `python3 flask_app.py`
- 🎉 🔥 🙌 💃 👌 💯

BIN
admin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

1
dependency_ubuntu Normal file
View file

@ -0,0 +1 @@
sudo apt-get install python-mysqldb

540
flask_app.py Normal file
View file

@ -0,0 +1,540 @@
import hashlib
import json
import os
import random
import string
import smtplib
import time
from datetime import datetime
import requests
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from dateutil.relativedelta import relativedelta
from flask import Flask, render_template, redirect, url_for, request, session, jsonify
from flask_sqlalchemy import SQLAlchemy
from mailchimp3 import MailChimp
from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub
from werkzeug.utils import secure_filename
from config_hackWPI import api_keys, WAITLIST_LIMIT, HACKATHON_TIME, ALLOWED_EXTENSIONS
app = Flask(__name__)
app.config.from_pyfile('config.py')
db = SQLAlchemy(app)
pnconfig = PNConfiguration()
pnconfig.publish_key = api_keys['pubnub']['pub']
pnconfig.subscribe_key = api_keys['pubnub']['sub']
pnconfig.ssl = True
pn = PubNub(pnconfig)
class Hacker(db.Model):
__tablename__ = 'hackers'
mlh_id = db.Column(db.Integer, primary_key=True)
registration_time = db.Column(db.Integer)
checked_in = db.Column(db.Boolean)
waitlisted = db.Column(db.Boolean)
admin = db.Column(db.Boolean)
first_name = db.Column(db.String(100))
last_name = db.Column(db.String(100))
email = db.Column(db.String(100))
class AutoPromoteKeys(db.Model):
__tablename__ = 'AutoPromoteKeys'
id = db.Column(db.Integer, primary_key=True)
key = db.Column(db.String(4096))
val = db.Column(db.String(4096))
@app.route('/')
def root():
print("Someone visited!.")
return render_template('index.html')
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
# Register a hacker...
if is_logged_in() and db.session.query(
db.exists().where(Hacker.mlh_id == session['mymlh']['id'])).scalar():
# Already logged in, take them to dashboard
return redirect(url_for('dashboard'))
if request.args.get('code') is None:
# Get info from MyMLH
return redirect(
'https://my.mlh.io/oauth/authorize?client_id=' + api_keys['mlh']['client_id'] + '&redirect_uri=' +
api_keys['mlh'][
'callback'] + '&response_type=code&scope=email+phone_number+demographics+birthday+education+event')
if is_logged_in():
return render_template('register.html', name=session['mymlh']['first_name'])
code = request.args.get('code')
oauth_redirect = requests.post(
'https://my.mlh.io/oauth/token?client_id=' + api_keys['mlh']['client_id'] + '&client_secret=' +
api_keys['mlh'][
'secret'] + '&code=' + code + '&redirect_uri=' + api_keys['mlh'][
'callback'] + '&grant_type=authorization_code')
if oauth_redirect.status_code == 200:
access_token = json.loads(oauth_redirect.text)['access_token']
user_info_request = requests.get('https://my.mlh.io/api/v2/user.json?access_token=' + access_token)
if user_info_request.status_code == 200:
user = json.loads(user_info_request.text)['data']
session['mymlh'] = user
if db.session.query(db.exists().where(Hacker.mlh_id == user['id'])).scalar():
# User already exists in db, log them in
return redirect(url_for('dashboard'))
return render_template('register.html', name=user['first_name'])
return redirect(url_for('register'))
if request.method == 'POST':
if not is_logged_in() or db.session.query(
db.exists().where(Hacker.mlh_id == session['mymlh']['id'])).scalar():
# Request flow == messed up somehow, restart them
return redirect(url_for('register'))
if 'resume' not in request.files:
# No file?
return redirect(url_for('register'))
resume = request.files['resume']
if resume.filename == '':
resume = False
# No file selected
#return redirect(url_for('register'))
if resume and not allowed_file(resume.filename):
resume = False
#return jsonify(
# {'status': 'error', 'action': 'register',
# 'more_info': 'Invalid file type... Accepted types are txt pdf doc docx and rtf...'})
if resume and allowed_file(resume.filename):
# Good file!
filename = session['mymlh']['first_name'].lower() + '_' + session['mymlh']['last_name'].lower() + '_' + str(
session['mymlh']['id']) + '.' + resume.filename.split('.')[-1].lower()
filename = secure_filename(filename)
resume.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
# Determine if hacker should be placed on waitlist
waitlist = False
if db.session.query(Hacker).count() + 1 > WAITLIST_LIMIT:
print(session['mymlh']['first_name'] + " put on waitlist.")
waitlist = True
else:
print(session['mymlh']['first_name'] + " put on registered.")
first_name = session['mymlh']['first_name']
last_name = session['mymlh']['last_name']
email = session['mymlh']['email']
# Add the user to the database
print(Hacker(mlh_id=session['mymlh']['id'], registration_time=int(time.time()),
checked_in=False, waitlisted=waitlist, admin=False))
db.session.add(
Hacker(mlh_id=session['mymlh']['id'], registration_time=int(time.time()),
checked_in=False, waitlisted=waitlist, admin=False,
first_name=first_name, last_name=last_name, email=email))
db.session.commit()
print(session['mymlh']['first_name'] + " put on database successfully.")
# Send a welcome email
msg = 'Hey ' + session['mymlh']['first_name'] + '\n\n'
msg += 'Thanks for applying to Hack@WPI!\n'
if waitlist:
msg += 'Sorry! We have hit our registration capacity. You have been placed on the waitlist.\n'
msg += 'We will let you know if space opens up.\n'
else:
msg += 'You are fully registered! We will send you more info closer to the hackathon.\n'
send_email(session['mymlh']['email'], 'Hack@WPI - Thanks for applying', msg)
pn.publish().channel('hackWPI-admin').message({'action': 'new_user'}).sync()
# Finally, send them to their dashboard
return redirect(url_for('dashboard'))
@app.route('/admin', methods=['GET'])
def admin():
# Displays total registration information...
# As Firebase could not be used with MyMLH, use PubNub to simulate the realtime database...
if not is_admin():
return redirect(url_for('register'))
waitlist_count = 0
total_count = 0
check_in_count = 0
shirt_count = {'xs': 0, 's': 0, 'm': 0, 'l': 0, 'xl': 0, }
male_count = 0
female_count = 0
schools = {}
majors = {}
mlh_info = get_mlh_users()
hackers = []
result = db.session.query(Hacker)
for hacker in mlh_info:
obj = result.filter(Hacker.mlh_id == hacker['id']).one_or_none()
if obj is None:
continue
if obj.waitlisted:
waitlist_count += 1
if obj.checked_in:
check_in_count += 1
if hacker['gender'] == 'Male':
male_count += 1
else:
female_count += 1
total_count += 1
if hacker['school']['name'] not in schools:
schools[hacker['school']['name']] = 1
else:
schools[hacker['school']['name']] += 1
if hacker['major'] not in majors:
majors[hacker['major']] = 1
else:
majors[hacker['major']] += 1
shirt_count[hacker['shirt_size'].split(' - ')[1].lower()] += 1
hackers.append({
'checked_in': obj.checked_in,
'waitlisted': obj.waitlisted,
'admin': obj.admin,
'registration_time': obj.registration_time,
'id': hacker['id'],
'email': hacker['email'],
'first_name': hacker['first_name'],
'last_name': hacker['last_name'],
'phone_number': hacker['phone_number'],
'dietary_restrictions': hacker['dietary_restrictions'],
'special_needs': hacker['special_needs'],
'school': hacker['school']
})
return render_template('admin.html', hackers=hackers, total_count=total_count, waitlist_count=waitlist_count,
check_in_count=check_in_count, shirt_count=shirt_count, female_count=female_count,
male_count=male_count, schools=schools, majors=majors,
mlh_url='https://my.mlh.io/api/v2/users.json?client_id=' + api_keys['mlh'][
'client_id'] + '&secret=' + api_keys['mlh'][
'secret'])
@app.route('/change_admin', methods=['GET'])
def change_admin():
# Promote or drop a given hacker to/from admin status...
if not is_admin():
return jsonify({'status': 'error', 'action': 'modify_permissions',
'more_info': 'You do not have permissions to perform this action...'})
if request.args.get('mlh_id') is None or request.args.get('action') is None:
return jsonify({'status': 'error', 'action': 'change_admin', 'more_info': 'Missing required field...'})
valid_actions = ['promote', 'demote']
if request.args.get('action') not in valid_actions:
return jsonify({'status': 'error', 'action': 'change_admin', 'more_info': 'Invalid action...'})
if request.args.get('action') == 'promote':
db.session.query(Hacker).filter(Hacker.mlh_id == request.args.get('mlh_id')).update({'admin': True})
elif request.args.get('action') == 'demote':
db.session.query(Hacker).filter(Hacker.mlh_id == request.args.get('mlh_id')).update({'admin': False})
db.session.commit()
pn.publish().channel('hackWPI-admin').message(
{'status': 'success', 'action': 'change_admin:' + request.args.get('action'), 'more_info': '',
'id': request.args.get('mlh_id')}).sync()
return jsonify({'status': 'success', 'action': 'change_admin:' + request.args.get('action'), 'more_info': '',
'id': request.args.get('mlh_id')})
@app.route('/check_in', methods=['GET'])
def check_in():
# Check in a hacker...
if not is_admin():
return jsonify({'status': 'error', 'action': 'check_in',
'more_info': 'You do not have permissions to perform this action...'})
if request.args.get('mlh_id') is None:
return jsonify({'status': 'error', 'action': 'check_in', 'more_info': 'Missing required field...'})
# See if hacker was already checked in...
checked_in = db.session.query(Hacker.checked_in).filter(
Hacker.mlh_id == request.args.get('mlh_id')).one_or_none()
print(db.session.query(Hacker.checked_in).filter(Hacker.mlh_id == request.args.get('mlh_id')))
print(checked_in)
if checked_in and checked_in[0]:
return jsonify({'status': 'error', 'action': 'check_in', 'more_info': 'Hacker already checked in!'})
# Update db...
db.session.query(Hacker).filter(Hacker.mlh_id == request.args.get('mlh_id')).update({'checked_in': True})
db.session.commit()
mlh_info = get_mlh_user(request.args.get('mlh_id'))
# Send a welcome email...
msg = 'Hey ' + mlh_info['first_name'] + ',\n\n'
msg += 'Thanks for checking in!\n'
msg += 'We will start shortly, please check your dashboard for updates!\n'
send_email(mlh_info['email'], 'HackWPI - Thanks for checking in', msg)
pn.publish().channel('hackWPI-admin').message(
{'status': 'success', 'action': 'check_in', 'more_info': '',
'id': request.args.get('mlh_id')}).sync()
return jsonify(
{'status': 'success', 'action': 'check_in', 'more_info': '', 'id': request.args.get('mlh_id')})
@app.route('/drop', methods=['GET'])
def drop():
# Drop a hacker's registration...
if request.args.get('mlh_id') is None:
return jsonify({'status': 'error', 'action': 'drop', 'more_info': 'Missing required field...'})
if not is_admin() and not is_self(request.args.get('mlh_id')):
return jsonify({'status': 'error', 'action': 'drop',
'more_info': 'You do not have permissions to perform this action...'})
row = db.session.query(Hacker.checked_in, Hacker.waitlisted).filter(
Hacker.mlh_id == request.args.get('mlh_id')).one_or_none()
if row is None:
return jsonify({'status': 'error', 'action': 'drop',
'more_info': 'Could not find hacker...'})
(checked_in, waitlisted) = row
if checked_in:
return jsonify({'status': 'error', 'action': 'drop', 'more_info': 'Cannot drop, already checked in...'})
mlh_info = get_mlh_user(request.args.get('mlh_id'))
print(mlh_info['first_name'] + " trying to drop.")
# Delete from db...
row = db.session.query(Hacker).filter(Hacker.mlh_id == request.args.get('mlh_id')).first()
db.session.delete(row)
db.session.commit()
# Delete resume...
for ext in ALLOWED_EXTENSIONS:
filename = mlh_info['first_name'].lower() + '_' + mlh_info['last_name'].lower() + '_' + request.args.get(
'mlh_id') + '.' + ext
try:
os.remove(app.config['UPLOAD_FOLDER'] + '/' + filename)
except OSError:
pass
# Send a goodbye email...
msg = 'Dear ' + mlh_info['first_name'] + ',\n\n'
msg += 'Your application was dropped, sorry to see you go.\n'
send_email(mlh_info['email'], 'Hack@WPI - Application Dropped', msg)
pn.publish().channel('hackWPI-admin').message(
{'status': 'success', 'action': 'drop', 'more_info': '', 'id': request.args.get('mlh_id')}).sync()
if is_self(request.args.get('mlh_id')):
session.clear()
print(mlh_info['first_name'] + " dropped successfully.")
return jsonify({'status': 'success', 'action': 'drop', 'more_info': '', 'id': request.args.get('mlh_id')})
@app.route('/promote_from_waitlist', methods=['GET'])
def promote_from_waitlist():
# Promote a hacker from the waitlist...
if request.args.get('mlh_id') is None:
return jsonify({'status': 'error', 'action': 'promote_from_waitlist', 'more_info': 'Missing required field...'})
(key, val) = get_auto_promote_keys()
if request.args.get(key) is None:
if not is_admin():
return jsonify({'status': 'error', 'action': 'promote_from_waitlist',
'more_info': 'You do not have permissions to perform this action...'})
else:
if request.args.get(key) != val:
return jsonify({'status': 'error', 'action': 'promote_from_waitlist',
'more_info': 'Invalid auto promote keys...'})
(checked_in, waitlisted) = db.session.query(Hacker.checked_in, Hacker.waitlisted).filter(
Hacker.mlh_id == request.args.get('mlh_id')).one_or_none()
if checked_in:
return jsonify(
{'status': 'error', 'action': 'promote_from_waitlist',
'more_info': 'Cannot promote, already checked in...'})
if not waitlisted:
return jsonify(
{'status': 'error', 'action': 'promote_from_waitlist',
'more_info': 'Cannot promote, user is not waitlisted...'})
# Update db...
db.session.query(Hacker).filter(Hacker.mlh_id == request.args.get('mlh_id')).update({'waitlisted': False})
db.session.commit()
mlh_info = get_mlh_user(request.args.get('mlh_id'))
# Send a welcome email...
msg = 'Dear ' + mlh_info['first_name'] + ',\n\n'
msg += 'You are off the waitlist!\n'
msg += 'Room has opened up, and you are now welcome to come, we look forward to seeing you!\n'
msg += 'If you cannot make it, please remove yourself at hack.wpi.edu\dashboard.\n'
send_email(mlh_info['email'], "Hack@WPI - You're off the Waitlist!", msg)
pn.publish().channel('hackWPI-admin').message(
{'status': 'success', 'action': 'promote_from_waitlist', 'more_info': '',
'id': request.args.get('mlh_id')}).sync()
print(mlh_info['first_name'] + "is off the waitlist!")
return jsonify(
{'status': 'success', 'action': 'promote_from_waitlist', 'more_info': '', 'id': request.args.get('mlh_id')})
@app.route('/dashboard', methods=['GET'])
def dashboard():
# Display's a hacker's options...
if not is_logged_in():
return redirect(url_for('register'))
return render_template('dashboard.html', name=session['mymlh']['first_name'], id=session['mymlh']['id'],
admin=is_admin())
def is_logged_in():
if session is None:
return False
if 'mymlh' not in session:
return False
return True
def is_admin():
if not is_logged_in():
return False
user_admin = db.session.query(Hacker.admin).filter(Hacker.mlh_id == session['mymlh']['id']).one_or_none()
if user_admin is None:
return False
if not user_admin[0]:
return False
return True
def is_self(mlh_id):
mlh_id = int(mlh_id)
if not is_logged_in():
return False
if session['mymlh']['id'] != mlh_id:
return False
return True
def send_email(to, subject, body):
print("Email sent to: " + to)
body += '\nPlease let your friends know about the event as well!.\n'
body += 'To update your status, you can go to hack.wpi.edu/dashboard\n'
body += '\nThanks Again!\nThe HackWPI Team\nhttps://twitter.com/hackwpi?lang=en'
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
sender = api_keys['smtp_email']['user']
server.login(sender, api_keys['smtp_email']['pass'])
msg = _create_MIMEMultipart(subject, sender, to, body)
server.send_message(msg)
print("Sucess! (Email to " + to)
def _create_MIMEMultipart(subject, sender, to, body):
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender
if type(to) == list:
msg['To'] = ", ".join(to)
else:
msg['To'] = to
msg.attach(MIMEText(body, 'plain'))
return msg
def get_mlh_user(mlh_id):
if not isinstance(mlh_id, int):
mlh_id = int(mlh_id)
req = requests.get(
'https://my.mlh.io/api/v2/users.json?client_id=' + api_keys['mlh']['client_id'] + '&secret=' + api_keys['mlh'][
'secret'])
if req.status_code == 200:
hackers = req.json()['data']
for hacker in hackers:
if hacker['id'] == mlh_id:
return hacker
def get_mlh_users():
req = requests.get(
'https://my.mlh.io/api/v2/users.json?client_id=' + api_keys['mlh']['client_id'] + '&secret=' + api_keys['mlh'][
'secret'])
if req.status_code == 200:
return req.json()['data']
def gen_new_auto_promote_keys(n=50):
key = new_key(n)
val = new_key(n)
db.session.add(AutoPromoteKeys(key=key, val=val))
db.session.commit()
return (key, val)
def get_auto_promote_keys():
row = db.session.query(AutoPromoteKeys).one_or_none()
if row is not None:
db.session.delete(row)
db.session.commit()
return (row.key, row.val)
else:
return ('', '')
def new_key(n):
return ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(n))
def allowed_file(filename):
return '.' in filename and \
filename.split('.')[-1].lower() in ALLOWED_EXTENSIONS
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

BIN
hacker.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

54
manage_waitlist.py Normal file
View file

@ -0,0 +1,54 @@
import requests
from flask_app import db, Hacker, send_email, gen_new_auto_promote_keys
from config_hackWPI import WAITLIST_LIMIT
num_attendees = db.session.query(Hacker).filter(Hacker.waitlisted == False).count()
num_waitlisted = db.session.query(Hacker).filter(Hacker.waitlisted == True).count()
num_to_promote = WAITLIST_LIMIT - num_attendees
if num_to_promote > num_waitlisted:
num_to_promote = num_waitlisted
num_to_promote_copy = num_to_promote
num_promoted = 0
errs = []
mlh_ids = db.session.query(Hacker.mlh_id).filter(Hacker.waitlisted == True).order_by(Hacker.registration_time)
for id in mlh_ids:
if num_to_promote > 0:
print('Attempting to promote: ' + str(id[0]))
(key, val) = gen_new_auto_promote_keys()
url = 'http://75.136.89.196:5000/promote_from_waitlist' + '?mlh_id=' + str(id[0]) + '&' + key + '=' + val
print(url)
req = requests.get(url)
if req.status_code == 500:
errs.append('Server 500')
if not req.status_code == 200 or not req.json()['status'] == 'success':
print(req.status_code)
errs.append(req.json())
num_promoted += 1
num_to_promote -= 1
else:
break
print('\n')
msg = 'Hi, here is your daily waitlist report:\n'
msg += '\nBefore Promotion:\n'
msg += ' Reg Cap: ' + str(WAITLIST_LIMIT) + '\n'
msg += ' Num Attendees: ' + str(num_attendees) + '\n'
msg += ' Num Waitlisted: ' + str(num_waitlisted) + '\n'
msg += ' Num to Promote: ' + str(num_to_promote_copy) + '\n'
msg += '\nAfter Promotion:\n'
msg += ' Num Promoted (Attempted): ' + str(num_promoted) + '\n'
msg += ' Error Count: ' + str(len(errs)) + '\n'
msg += ' Num To Promote: ' + str(num_to_promote) + '\n'
msg += '\nPromotion Error Messages:\n'
msg += ' ' + str(errs) + '\n'
print(msg)
send_email('hack@wpi.edu', 'HackWPI - Daily Waitlist Report!', msg)
send_email('bkayastha@wpi.edu', 'HackWPI - Daily Waitlist Report!', msg)

BIN
options.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

7
requirements.txt Normal file
View file

@ -0,0 +1,7 @@
Flask==0.12
Flask_SQLAlchemy==2.1
mailchimp3==2.0.7
pubnub==4.0.6
requests==2.13.0
Werkzeug==0.11.15
python_dateutil==2.6.0

Binary file not shown.

BIN
resumes/hack_wpi_78137.pdf Normal file

Binary file not shown.

16
static/css/materialize.min.css vendored Normal file

File diff suppressed because one or more lines are too long

100
static/css/style.css Normal file
View file

@ -0,0 +1,100 @@
/* Custom Stylesheet */
/**
* Use this file to override Materialize files so you can update
* the core Materialize files in the future
*
* Made By MaterializeCSS.com
*/
nav ul a,
nav .brand-logo {
color: #444;
}
p {
line-height: 2rem;
}
#logo-container {
padding-top: 5px;
padding-bottom: 5px;
height: 100%;
left: 50%;
transform: translateX(-50%);
}
.button-collapse {
color: #26a69a;
}
.sub-button {
margin-top: 5px;
}
.parallax-container {
min-height: 380px;
line-height: 0;
height: auto;
color: rgba(255,255,255,.9);
}
.parallax-container .section {
width: 100%;
}
@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;
}
}
#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;
}
footer.page-footer {
margin: 0;
}
.parallax img {
display: inherit
}
#mlh-trust-badge {
display: block;
max-width: 100px;
min-width: 60px;
position: fixed;
right: 50px;
top: 0;
width: 10%;
z-index: 10000;
}

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 2755 1817" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g><g><path d="M657.652,341.71l113.155,0l0,507.021l-113.155,0l0,-199.471l-201.648,0l0,199.471l-113.155,0l0,-507.021l113.155,0l0,211.803l201.648,0l0,-211.803Z" style="fill:#505050;fill-rule:nonzero;"/><path d="M1256.79,848.731l-47.148,-109.528l-212.528,0l-47.148,109.528l-120.408,0l219.056,-507.021l109.528,0l219.057,507.021l-120.409,0Zm-153.049,-356.147l-63.831,147.246l126.937,0l-63.106,-147.246Z" style="fill:#505050;fill-rule:nonzero;"/><path d="M1668.79,748.633c55.61,0 101.066,-22.003 136.366,-66.007l72.535,74.711c-57.544,64.798 -125.365,97.197 -203.461,97.197c-78.096,0 -142.41,-24.662 -192.943,-73.986c-50.533,-49.324 -75.8,-111.583 -75.8,-186.778c0,-75.195 25.75,-137.938 77.25,-188.229c51.5,-50.291 114.485,-75.436 188.955,-75.436c83.173,0 152.807,31.673 208.901,95.021l-70.359,79.789c-35.784,-44.489 -80.273,-66.733 -133.465,-66.733c-42.554,0 -78.942,13.903 -109.165,41.708c-30.223,27.805 -45.335,65.282 -45.335,112.429c0,47.148 14.265,84.987 42.796,113.518c28.53,28.531 63.105,42.796 103.725,42.796Z" style="fill:#505050;fill-rule:nonzero;"/><path d="M1966.91,341.71l113.155,0l0,208.176l191.492,-208.176l139.993,0l-201.647,224.134c17.408,24.178 49.565,69.271 96.471,135.278c46.906,66.007 81.965,115.21 105.176,147.609l-132.014,0l-148.697,-200.197l-50.774,56.578l0,143.619l-113.155,0l0,-507.021Z" style="fill:#505050;fill-rule:nonzero;"/><path d="M767.849,1341.5c11.25,0 21.146,-8.959 29.688,-26.875c8.541,-17.917 12.812,-40.417 12.812,-67.5c0,-58.75 -16.875,-107.396 -50.625,-145.938c-33.75,-38.542 -78.958,-57.812 -135.625,-57.812c-56.667,0 -104.375,20.208 -143.125,60.625c-38.75,40.416 -58.125,88.854 -58.125,145.312c0,56.458 18.229,103.438 54.688,140.938c36.458,37.5 81.562,56.25 135.312,56.25c42.083,0 79.167,-15.417 111.25,-46.25l25.625,44.375c-16.667,14.583 -37.917,26.354 -63.75,35.312c-25.833,8.958 -50.208,13.438 -73.125,13.438c-69.583,0 -127.812,-23.23 -174.687,-69.688c-46.875,-46.458 -70.313,-104.583 -70.313,-174.375c0,-69.792 24.688,-129.375 74.063,-178.75c49.375,-49.375 108.854,-74.062 178.437,-74.062c69.583,0 127.708,23.75 174.375,71.25c46.667,47.5 70,106.666 70,177.5c0,45.833 -9.479,82.604 -28.437,110.312c-18.959,27.708 -43.855,41.563 -74.688,41.563c-15.833,0 -30.312,-4.792 -43.437,-14.375c-13.125,-9.584 -22.188,-21.667 -27.188,-36.25c-22.917,29.583 -51.979,44.375 -87.187,44.375c-35.209,0 -65.834,-13.334 -91.875,-40c-26.042,-26.667 -39.063,-60.521 -39.063,-101.563c0,-41.042 11.771,-75.625 35.313,-103.75c23.541,-28.125 54.479,-42.187 92.812,-42.187c12.917,0 25.208,2.812 36.875,8.437c11.667,5.625 20,11.146 25,16.563l8.125,8.125l0,-25.625l78.125,0l0,199.375c0,20.833 6.25,31.25 18.75,31.25Zm-112.812,-146.25c-11.042,-13.75 -25.521,-20.625 -43.438,-20.625c-17.917,0 -32.187,6.875 -42.812,20.625c-10.625,13.75 -15.938,30.729 -15.938,50.937c0,20.208 5.313,37.813 15.938,52.813c10.625,15 25.104,22.5 43.437,22.5c18.333,0 32.813,-7.188 43.438,-21.563c10.625,-14.375 15.937,-31.979 15.937,-52.812c0,-20.834 -5.521,-38.125 -16.562,-51.875Z" style="fill:#505050;fill-rule:nonzero;"/><path d="M1197.16,1277.95l89.943,-291.591l118.233,0l89.218,291.591l100.824,-291.591l122.584,0l-176.985,507.021l-84.141,0l-110.979,-352.521l-110.254,352.521l-84.14,0l-176.986,-507.021l122.584,0l100.099,291.591Z" style="fill:#ff3633;fill-rule:nonzero;"/><path d="M2127.43,1031.69c35.542,30.223 53.313,76.646 53.313,139.268c0,62.622 -18.255,108.44 -54.764,137.454c-36.51,29.014 -92.241,43.521 -167.194,43.521l-67.457,0l0,141.444l-113.155,0l0,-507.021l179.162,0c77.854,0 134.552,15.111 170.095,45.334Zm-83.053,199.472c13.54,-15.233 20.31,-37.477 20.31,-66.732c0,-29.256 -8.826,-50.05 -26.476,-62.381c-17.65,-12.331 -45.092,-18.496 -82.327,-18.496l-64.556,0l0,170.457l76.161,0c37.719,0 63.348,-7.616 76.888,-22.848Z" style="fill:#ff3633;fill-rule:nonzero;"/><rect x="2269.23" y="986.358" width="113.155" height="507.021" style="fill:#ff3633;fill-rule:nonzero;"/></g><path d="M2714.7,1776.83l-2675,0l0,-1737.5l2675,0l0,1737.5Zm-2583.33,-1645.83l0,1554.17l2491.67,0l0,-1554.17l-2491.67,0Z" style="fill:#505050;"/></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
static/img/background1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

BIN
static/img/background2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
static/img/background3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
static/img/contact_bg.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

BIN
static/img/cosponsors.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
static/img/cosponsors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
static/img/cover.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
static/img/sponsors.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

190
static/js/admin.js Normal file
View file

@ -0,0 +1,190 @@
'use strict'
let pn = new PubNub({
publishKey: '',
subscribeKey: '',
ssl: true
})
pn.subscribe({
channels: ['hackWPI-admin']
})
pn.addListener({
message: (msg) => {
msg = msg.message
console.log(msg)
const id = msg.id
if (msg.action === 'check_in') {
document.getElementById(id + '-check_in').outerHTML = ''
document.getElementById(id + '-checked_in').innerHTML = 'True'
} else if (msg.action === 'drop') {
document.getElementById(id + '-row').outerHTML = ''
} else if (msg.action === 'promote_from_waitlist') {
document.getElementById(id + '-promote_from_waitlist').outerHTML = ''
document.getElementById(id + '-waitlisted').innerHTML = 'False'
} else if (msg.action === 'new_user' || msg.action === 'refresh' || msg.action.split(':')[0] === 'change_admin') {
window.location.reload(true)
}
}
})
let successColor = '#18BC9C'
let errColor = '#E74C3C'
$(document).ready(() => {
let timeout = setTimeout("window.location.reload(true);", 300000)
const resetTimeout = () => {
clearTimeout(timeout)
timeout = setTimeout("window.location.reload(true);", 300000)
}
$('a.check_in').click((e) => {
e.preventDefault()
let id = e.target.id.split('-')[0]
console.log('check in ' + id)
checkIn(id)
})
$('a.drop').click((e) => {
e.preventDefault()
let id = e.target.id.split('-')[0]
console.log('drop ' + id)
drop(id)
})
$('a.demote_admin').click((e) => {
e.preventDefault()
let id = e.target.id.split('-')[0]
console.log('demote admin ' + id)
changeAdmin(id, 'demote')
})
$('a.promote_admin').click((e) => {
e.preventDefault()
let id = e.target.id.split('-')[0]
console.log('promote admin ' + id)
changeAdmin(id, 'promote')
})
$('a.promote_from_waitlist').click((e) => {
e.preventDefault()
let id = e.target.id.split('-')[0]
console.log('promote waitlist ' + id)
promoteFromWaitlist(id)
})
})
const promoteFromWaitlist = (id) => {
swal({
title: 'Promote hacker ' + id + ' off the waitlist?',
text: 'Are you sure you wish to promote this hacker off the waitlist?',
type: 'info',
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: 'Yes, promote!',
confirmButtonColor: successColor
}, () => {
$.get('/promote_from_waitlist?mlh_id=' + id, (data) => {
let title = ''
let msg = ''
let type = ''
if (data.status === 'success') {
title = 'Promoted!'
msg = 'The hacker was successfully promoted off the waitlist!'
type = 'success'
} else {
title = 'Error!'
msg = JSON.stringify(data)
type = 'error'
}
swal(title, msg, type)
})
})
}
const changeAdmin = (id, action) => {
swal({
title: 'Modify:' + action + ' admin prviliges on hacker ' + id + ' ?',
text: 'Are you sure you wish to modify:' + action + ' this hacker\'s administrative privileges?',
type: 'warning',
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: 'Yes, ' + action + '!',
confirmButtonColor: errColor
}, () => {
$.get('/change_admin?mlh_id=' + id + '&action=' + action, (data) => {
let title = ''
let msg = ''
let type = ''
if (data.status === 'success') {
title = 'Modified!'
msg = 'The hacker\'s administrative privileges have been modified:' + action + '!'
type = 'success'
} else {
title = 'Error!'
msg = JSON.stringify(data)
type = 'error'
}
swal({title: title, msg: msg, type: type}, () => window.location.reload(true))
})
})
}
const drop = (id) => {
swal({
title: 'Drop hacker ' + id + ' ?',
text: 'Are you sure you wish to drop this hacker\'s application?',
type: 'warning',
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: 'Yes, drop!',
confirmButtonColor: errColor
}, () => {
$.get('/drop?mlh_id=' + id, (data) => {
let title = ''
let msg = ''
let type = ''
if (data.status === 'success') {
title = 'Dropped!'
msg = 'The hacker\'s application was successfully dropped!'
type = 'success'
} else {
title = 'Error!'
msg = JSON.stringify(data)
type = 'error'
}
swal(title, msg, type)
})
})
}
const checkIn = (id) => {
swal({
title: 'Check in hacker ' + id + ' ?',
text: 'Are you sure you wish to check in this hacker?',
type: 'info',
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: 'Yes, check in!',
confirmButtonColor: successColor
}, () => {
$.get('/check_in?mlh_id=' + id, (data) => {
let title = ''
let msg = ''
let type = ''
if (data.status === 'success') {
title = 'Checked in!'
msg = 'The hacker was checked in!'
type = 'success'
} else {
title = 'Error!'
msg = JSON.stringify(data)
type = 'error'
}
if (data.status === 'success' && data.action === 'check_in' && data.minor === true) {
msg += '\nATTENTION:\nHacker is a minor, please ensure they have the minor consent form!'
}
swal(title, msg, type)
})
})
}

4
static/js/jquery-2.2.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
static/js/jquery.easing.min.js vendored Normal file
View file

@ -0,0 +1 @@
(function(factory){if(typeof define==="function"&&define.amd){define(["jquery"],function($){return factory($)})}else if(typeof module==="object"&&typeof module.exports==="object"){exports=factory(require("jquery"))}else{factory(jQuery)}})(function($){$.easing.jswing=$.easing.swing;var pow=Math.pow,sqrt=Math.sqrt,sin=Math.sin,cos=Math.cos,PI=Math.PI,c1=1.70158,c2=c1*1.525,c3=c1+1,c4=2*PI/3,c5=2*PI/4.5;function bounceOut(x){var n1=7.5625,d1=2.75;if(x<1/d1){return n1*x*x}else if(x<2/d1){return n1*(x-=1.5/d1)*x+.75}else if(x<2.5/d1){return n1*(x-=2.25/d1)*x+.9375}else{return n1*(x-=2.625/d1)*x+.984375}}$.extend($.easing,{def:"easeOutQuad",swing:function(x){return $.easing[$.easing.def](x)},easeInQuad:function(x){return x*x},easeOutQuad:function(x){return 1-(1-x)*(1-x)},easeInOutQuad:function(x){return x<.5?2*x*x:1-pow(-2*x+2,2)/2},easeInCubic:function(x){return x*x*x},easeOutCubic:function(x){return 1-pow(1-x,3)},easeInOutCubic:function(x){return x<.5?4*x*x*x:1-pow(-2*x+2,3)/2},easeInQuart:function(x){return x*x*x*x},easeOutQuart:function(x){return 1-pow(1-x,4)},easeInOutQuart:function(x){return x<.5?8*x*x*x*x:1-pow(-2*x+2,4)/2},easeInQuint:function(x){return x*x*x*x*x},easeOutQuint:function(x){return 1-pow(1-x,5)},easeInOutQuint:function(x){return x<.5?16*x*x*x*x*x:1-pow(-2*x+2,5)/2},easeInSine:function(x){return 1-cos(x*PI/2)},easeOutSine:function(x){return sin(x*PI/2)},easeInOutSine:function(x){return-(cos(PI*x)-1)/2},easeInExpo:function(x){return x===0?0:pow(2,10*x-10)},easeOutExpo:function(x){return x===1?1:1-pow(2,-10*x)},easeInOutExpo:function(x){return x===0?0:x===1?1:x<.5?pow(2,20*x-10)/2:(2-pow(2,-20*x+10))/2},easeInCirc:function(x){return 1-sqrt(1-pow(x,2))},easeOutCirc:function(x){return sqrt(1-pow(x-1,2))},easeInOutCirc:function(x){return x<.5?(1-sqrt(1-pow(2*x,2)))/2:(sqrt(1-pow(-2*x+2,2))+1)/2},easeInElastic:function(x){return x===0?0:x===1?1:-pow(2,10*x-10)*sin((x*10-10.75)*c4)},easeOutElastic:function(x){return x===0?0:x===1?1:pow(2,-10*x)*sin((x*10-.75)*c4)+1},easeInOutElastic:function(x){return x===0?0:x===1?1:x<.5?-(pow(2,20*x-10)*sin((20*x-11.125)*c5))/2:pow(2,-20*x+10)*sin((20*x-11.125)*c5)/2+1},easeInBack:function(x){return c3*x*x*x-c1*x*x},easeOutBack:function(x){return 1+c3*pow(x-1,3)+c1*pow(x-1,2)},easeInOutBack:function(x){return x<.5?pow(2*x,2)*((c2+1)*2*x-c2)/2:(pow(2*x-2,2)*((c2+1)*(x*2-2)+c2)+2)/2},easeInBounce:function(x){return 1-bounceOut(1-x)},easeOutBounce:bounceOut,easeInOutBounce:function(x){return x<.5?(1-bounceOut(1-2*x))/2:(1+bounceOut(2*x-1))/2}})});

6478
static/js/materialize.js vendored Normal file

File diff suppressed because one or more lines are too long

10
static/js/materialize.min.js vendored Normal file

File diff suppressed because one or more lines are too long

272
templates/admin.html Normal file
View file

@ -0,0 +1,272 @@
<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>
<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="../static/js/admin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
</head>
<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%;">
<h2><a href="{{ mlh_url }}">Get JSON object of users from MLH. <b>Do NOT share this URL.</b></a></h2>
</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'],
datasets: [
{
data: [{{ female_count }}, {{ male_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>XS</th>
<th>S</th>
<th>M</th>
<th>L</th>
<th>XL</th>
</tr>
</thead>
<tbody>
<tr>
<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>
</tr>
</tbody>
</table>
<h2 style="margin-top: 2%;">Hackers:</h2>
<table id="hackers" class="table table-striped table-hover table-condensed sortable">
<thead>
<tr>
<th>Options</th>
<th>Checked In?</th>
<th>Waitlisted?</th>
<th>Admin</th>
<th>MLH ID</th>
<th>Time Registered</th>
<th>Email</th>
<th>Name</th>
<th>Phone</th>
<th>Diet</th>
<th>Special</th>
<th>School</th>
</tr>
</thead>
<tbody>
{% for hacker in hackers %}
<tr id="{{ hacker['id'] }}-row">
<td>
<div class="btn-group">
<a href="#" class="btn btn-primary dropdown-toggle" data-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>
{% 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>
{% endif %}
<li class="divider"></li>
{% if not hacker['checked_in'] %}
<li><a class="drop" id="{{ hacker['id'] }}-drop" href="#">Drop Application</a>
</li>
{% endif %}
{% if hacker['admin'] %}
<li><a class="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>
{% endif %}
</ul>
</div>
</td>
<td id="{{ hacker['id'] }}-checked_in">{{ hacker['checked_in'] }}</td>
<td id="{{ hacker['id'] }}-waitlisted">{{ hacker['waitlisted'] }}</td>
<td>{{ hacker['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_number'] }}</td>
<td>{{ hacker['dietary_restrictions'] }}</td>
<td>{{ hacker['special_needs'] }}</td>
<td>{{ hacker['school']['name'] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<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>

77
templates/dashboard.html Normal file
View file

@ -0,0 +1,77 @@
{% include 'header.html' %}
<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">
<div class="contact-section" style="height: 100%;">
<div class="container">
<div class="row" style="margin-top: 10%;">
<h1>Hi {{ name }}!</h1>
{% if waitlisted %}
<h1>You are waitlisted, if space opens up we ill let you know...</h1>
{% else %}
<h1>You are fully registered! We look forward to seeing you!</h1>
{% endif %}
<br>
<h2>Here are your available actions... </h2>
<a href="#" id="drop-link"><p>Drop Application :(</p></a>
{% if admin %}
<a href="/admin"><p>Admin Dashboard</p></a>
{% endif %}
Please sit tight, while we improve the UI of this page :P
</div>
</div>
</div>
<script>
let errColor = '#E74C3C'
$(document).ready(() => {
$('#drop-link'
).click((e) => {
e.preventDefault()
let id = {{ id }}
drop(id)
})
})
const drop = (id) =>
{
swal({
title: 'Drop your application?',
text: 'Are you sure you wish to drop your application? This cannot be undone. (patiently wait after clicking the button)',
type: 'warning',
showCancelButton: true,
closeOnConfirm: false,
confirmButtonText: 'Yes, drop!',
confirmButtonColor: errColor
}, () => {
$.get('/drop?mlh_id=' + id, (data) => {
let title = ''
let msg = ''
let type = ''
if (data.status === 'success'
)
{
title = 'Dropped!'
msg = 'Your application was successfully dropped!'
type = 'success'
}
else
{
title = 'Error!'
msg = JSON.stringify(data)
type = 'error'
}
swal(title, msg, type)
if (data.status === 'success') {
setTimeout(() => {window.location = '/'
},
5000
)
}
})
})
}
</script>
{% include 'footer.html' %}

6
templates/footer.html Normal file
View file

@ -0,0 +1,6 @@
<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-2.2.4.min.js"></script>
<script src="../static/js/jquery.easing.min.js"></script>
<script src="../static/js/materialize.min.js"></script>
</body>
</html>

27
templates/header.html Normal file
View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="HackTCNJ">
<meta name="author" content="bohinsk1@tcnj.edu">
<title>Hack @ WPI</title>
<link rel="icon" href="../static/favicon.png" type="image/png">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link href="../static/css/materialize.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
<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>
</head>
<body>
<nav class="white" role="navigation" style="height: 80px">
<div class="nav-wrapper container">
<a id="logo-container" href="#" class="red-text text-darken-4 brand-logo">
<img src="../static/img/hackwpi_longlogo.png" alt="hack@WPI" height="100%">
</a>
</div>
<a id="mlh-trust-badge" style="display:block;max-width:100px;min-width:60px;position:fixed;right:50px;top:0;width:10%;z-index:10000" href="https://mlh.io/seasons/na-2018/events?utm_source=na-2018&utm_medium=TrustBadge&utm_campaign=na-2018&utm_content=black" target="_blank"><img src="https://s3.amazonaws.com/logged-assets/trust-badge/2018/black.svg" alt="Major League Hacking 2018 Hackathon Season" style="width:100%"></a>
</nav>
{% include 'footer.html' %}

164
templates/index.html Executable file
View file

@ -0,0 +1,164 @@
{% include 'header.html' %}
<link href="../static/css/materialize.min.css" rel="stylesheet">
<div>
<div id="index-banner" class="parallax-container valign-wrapper">
<div class="section no-pad-bot">
<div class="container">
<br><br>
<h2 class="header center red-text text-darken-2">Learn. Hack. Compete.</h2>
<div class="row center">
<h5 class="header col s12 light">An open for all WPI Hackathon, for both hardware and software!</h5>
</div>
<div class="row center">
<h5 class="header col s6 m5 light" style="margin-top:0; float: left; display: block">
<i class="small material-icons">today</i> Jan. 12 - 14th
</h5>
<h5 class="header col s6 m5 light" style="margin-top:0; float: right; display: block">
<i class="small material-icons">room</i> WPI Odeum
</h5>
<!--<%= link_to "Registration", "/pages/registration",
class: "col s12 m2 btn-large waves-effect waves-light red darken-1",
style: "float: left; display: inline-block" %>-->
<!--<%= link_to 'Click here to apply!'.html_safe, questionnaires_path,
class: "col s12 m2 btn-large waves-effect waves-light red darken-1",
style: "float: left; display: inline-block" %>-->
<a class="col s12 m2 btn-large waves-effect waves-light red darken-1"
style= "float: left; display: inline-block; text-decoration: none;"
href= "/register">Register Here!</a>
</div>
<div class="row center" style="margin:0">
<a id="cocbutton" class="sub-button col s6 push-s3 m2 push-m5 btn waves-effect waves-light grey darken-1"
href="https://static.mlh.io/docs/mlh-code-of-conduct.pdf" data-mode="1" target="_blank">MLH Code</a>
</div>
<div class="row center">
<div class="col s12 m6">
<a class="sub-button col s6 push-s3 m4 push-m4 btn waves-effect waves-light grey darken-1"
href="" data-mode="1" target="_blank">Slack Chatroom (TBA)</a>
</div>
<div class="col s12 m6">
<a class="sub-button col s6 push-s3 m4 push-m4 btn waves-effect waves-light grey darken-1"
href="" data-mode="1" target="_blank">Devpost Link (TBA)</a>
</div>
</div>
<br>
</div>
</div>
<div class="parallax"><img src="../static/img/background1.jpg" alt="background image"></div>
</div>
<div class="container">
<div class="section">
<!-- Icpathon Section -->
<div class="row">
<div class="col s12 m4">
<div class="icon-block">
<h2 class="center brown-text"><i class="material-icons">grade</i></h2>
<h5 class="center">Learn</h5>
<p class="light">Participate in the workshops taught by experienced students and learn everything you need to take your project from idea to reality. Ask currently employed Mentors for help when needed.</p>
</div>
</div>
<div class="col s12 m4">
<div class="icon-block">
<h2 class="center brown-text"><i class="material-icons">settings</i></h2>
<h5 class="center">Hack</h5>
<p class="light">Work all weekend long to build and hack your project together. We provide food, snacks, drinks, and entertainment; everything you need to stay focused and have fun for the whole event.</p>
</div>
</div>
<div class="col s12 m4">
<div class="icon-block">
<h2 class="center brown-text"><i class="material-icons">group</i></h2>
<h5 class="center">Compete</h5>
<p class="light">Present your project at the end of the hackathon to our panel of judges, and win various prizes! Grand prizes are available for the top three teams, the best overall, the best software, and the best hardware project. Additionally, smaller prizes will also be available for various categories.</p>
</div>
</div>
</div>
</div>
</div>
<div class="parallax-container valign-wrapper">
<div class="section no-pad-bot">
<div class="container">
<div class="row center">
<img src="../static/img/cosponsors.png" alt="cosponsors" style='height: 100%; width: 100%; object-fit: contain'/>
</div>
</div>
</div>
<div class="parallax">
<img src="../static/img/background2.png" alt="background image">
</div>
</div>
<div class="container">
<div class="section">
<div class="row">
<div class="col s12 center" style="margin-top: -40px; margin-bottom: -50px;">
<div class="icon-block">
<h1 class="center brown-text"><strong>&#11014;</strong></h1>
<h5 class="center">Organizing Club</h5>
<p class="light">Hack @ WPI is run by the local ACM chapter. The event would not be possible without their support!</p>
<div class="divider"></div>
<h5 class="center">Sponsoring Companies</h5>
<p class="light">Hack @ WPI is proud to announce that the companies below are all part of making the event a reality.</p>
<h1 class="center brown-text"><strong>&#11015;</strong></h1>
</div>
</div>
</div>
</div>
</div>
<div class="parallax-container valign-wrapper">
<div class="section no-pad-bot">
<div class="container">
<div class="row center">
<img src="../static/img/sponsors.png" alt="sponsors" class="responsive-img">
</div>
</div>
</div>
<div class="parallax">
<img src="../static/img/background3.png" alt="background" >
</div>
</div>
<footer class="page-footer red darken-2">
<div class="container">
<div class="row">
<h5 class="white-text">Interested in Sponsoring or Co-Sponsoring?</h5>
<div class="col l6 s12">
<p class="grey-text text-lighten-4">For any inquiries regarding sponsoring the event as a company or co-sponsoring the event as an on campus club, please email <a class="white-text" href="mailto:hack@wpi.edu?subject=[Hackathon]" target="_top"><u>hack@wpi.edu</u></a>, and we will happily discuss it with you!</p>
</div>
</div>
<div class="row">
<h4 class="white-text">FAQ</h4>
<div class="col l6 s6">
<h5 class="white-text">When does registration close?</h5>
<p class="grey-text text-lighten-4">Registration officially closes on Monday, January 10th, 11:59pm.</p>
</div>
<div class="col l6 s6">
<h5 class="white-text">Will there be travel reimbursements?</h5>
<p class="grey-text text-lighten-4">We are attempting to partner with some nearby schools to run a bus from there's to ours, but may not have the budget. If you're interested in providing a bus to our hackathon, let us know. At the moment we cannot provide travel reimburstments.</p>
</div>
<div class="col l6 s6">
<h5 class="white-text">How do I know I'm registered?</h5>
<p class="grey-text text-lighten-4">You should be automatically notified whether or not you're registered. If you're on the waitlist and a spot opens, you will get an email saying you got in.</p>
</div>
<div class="col l6 s6">
<h5 class="white-text">I am officially registered but now I want to unregister, how do I do that?</h5>
<p class="grey-text text-lighten-4">Go to hack.wpi.edu/dashboard, there is a link on the bottom to drop your registration</p>
</div>
</div>
</div>
<div class="footer-copyright">
<div class="container">
For issues, please contact <a class="white-text" href="mailto:acm@wpi.edu?subject=[Site]" target="_top"><u>acm@wpi.edu</u></a>
</div>
</div>
</footer>
</div>
{% include 'footer.html' %}

43
templates/register.html Normal file
View file

@ -0,0 +1,43 @@
{% include 'header.html' %}
<link href="../static/css/materialize.min.css" rel="stylesheet">
<div style="height: 100%;">
<div id="registration-banner" class="parallax-container valign-wrapper">
<div class="section">
<h2 class="header center red-text text-darken-2">Registration</h2>
</div>
<div class="parallax"><img src="../static/img/background1.jpg" alt="background"></div>
</div>
<div class="container">
<a id="mlh-trust-badge" href="https://mlh.io/seasons/na-2017/events?utm_source=na-2017&utm_medium=TrustBadge&utm_campaign=na-2017&utm_content=black" target="_blank"><img src="https://s3.amazonaws.com/logged-assets/trust-badge/2017/black.svg" alt="Major League Hacking 2017 Hackathon Season" style="width:100%"></a>
<div class="container">
<h1>Hi {{ name }}, thanks for logging in with MyMLH!</h1>
<h2>If you'd like, add your resume to send to sponsors... </h2>
<div class="section">
<form method="post" action="/register" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>File</span>
<input id="resume" name="resume" type="file"/>
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text">
</div>
</div>
<p>By registering & attending, you agree to the following policies:</p>
<p><a href="https://github.com/MLH/mlh-policies/blob/master/data-sharing.md">MLH's Data Sharing
Notice</a>
</p>
<p><a href="https://mlh.io/privacy">MLH's Privacy Policy</a></p>
<p>
<a href="https://github.com/MLH/mlh-policies/blob/master/prize-terms-and-conditions/contest-terms.md">MLH's
Contest Terms and Conditions</a></p>
<p><a href="https://static.mlh.io/docs/mlh-code-of-conduct.pdf">MLH's Code of Conduct</a></p>
<br>
<input type="checkbox" id="checkboxid" required><label for="checkboxid">Do you agree with MLH Rules?</label><br>
<input name="submit" class="btn btn-lg btn-primary btn-invert" type="submit" value="Submit"/>
</form>
</div>
</div>
</div>
{% include 'footer.html' %}