Tracking PR for registration rewrite #5

Merged
Muirrum merged 32 commits from rewrite into master 2022-12-15 18:32:08 -05:00
14 changed files with 178 additions and 53 deletions
Showing only changes of commit 89513af939 - Show all commits

View file

@ -4,6 +4,7 @@ from flask_migrate import Migrate
from flask_login import LoginManager from flask_login import LoginManager
from flask_assets import Bundle, Environment from flask_assets import Bundle, Environment
from flask_cors import CORS from flask_cors import CORS
from flask_mail import Mail
db = SQLAlchemy() db = SQLAlchemy()
@ -11,6 +12,7 @@ migrate = Migrate()
login = LoginManager() login = LoginManager()
environment = Environment() environment = Environment()
cors = CORS() cors = CORS()
mail = Mail()
def create_app(): def create_app():
app = Flask(__name__) app = Flask(__name__)
@ -22,6 +24,7 @@ def create_app():
login.init_app(app) login.init_app(app)
environment.init_app(app) environment.init_app(app)
cors.init_app(app) cors.init_app(app)
mail.init_app(app)
scss = Bundle('css/style.scss', filters='scss', scss = Bundle('css/style.scss', filters='scss',
output='css/style.css') output='css/style.css')
@ -38,3 +41,5 @@ def create_app():
app.register_blueprint(admin.bp) app.register_blueprint(admin.bp)
return app return app

View file

@ -1,11 +1,12 @@
from flask import Blueprint, jsonify, redirect, render_template, url_for from flask import Blueprint, jsonify, redirect, render_template, request, url_for
from flask_login import current_user, login_required from flask_login import current_user, login_required
from flask_mail import Message
from goathacks.models import User from goathacks.models import User
bp = Blueprint("admin", __name__, url_prefix="/admin") bp = Blueprint("admin", __name__, url_prefix="/admin")
from goathacks import db from goathacks import db,mail
@bp.route("/") @bp.route("/")
@login_required @login_required
@ -53,6 +54,45 @@ def home():
female_count=female_count, nb_count=nb_count, female_count=female_count, nb_count=nb_count,
check_in_count=check_in_count, schools=schools) check_in_count=check_in_count, schools=schools)
@bp.route("/mail")
@login_required
def mail():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
total_count = len(db.session.execute(db.select(User)).scalars().all())
return render_template("mail.html", NUM_HACKERS=total_count)
@bp.route("/send", methods=["POST"])
@login_required
def send():
if not current_user.is_admin:
return {"status": "error"}
json = request.json
users = User.query.all()
to = []
if json["recipients"] == "org":
to = ["hack@wpi.edu"]
elif json['recipients'] == 'admin':
to = ["acm-sysadmin@wpi.edu"]
elif json['recipients'] == "all":
to = [x['email'] for x in users]
with mail.connect() as conn:
for e in to:
msg = Message(json['subject'])
msg.add_recipient(e)
msg.html = json['html']
msg.body = json['text']
conn.send(msg)
return {"status": "success"}
@bp.route("/check_in/<int:id>") @bp.route("/check_in/<int:id>")
@login_required @login_required
def check_in(id): def check_in(id):
@ -79,6 +119,11 @@ def drop(id):
if user.checked_in: if user.checked_in:
return {"status": "error", "msg": "Hacker is already checked in"} return {"status": "error", "msg": "Hacker is already checked in"}
msg = Message("Application Dropped")
msg.add_recipient(user.email)
msg.sender = ("GoatHacks Team", "hack@wpi.edu")
msg.body = render_template("emails/dropped.txt", user=user)
db.session.delete(user) db.session.delete(user)
db.session.commit() db.session.commit()
@ -122,6 +167,11 @@ def promote_waitlist(id):
user.waitlisted = False user.waitlisted = False
db.session.commit() db.session.commit()
msg = Message("Waitlist Promotion")
msg.add_recipient(user.email)
msg.sender = ("GoatHacks Team", "hack@wpi.edu")
msg.body = render_template("emails/waitlist_promotion.txt", user=user)
return {"status": "success"} return {"status": "success"}
@bp.route("/hackers.csv") @bp.route("/hackers.csv")

View file

@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from flask import Blueprint, flash, redirect, render_template, request, url_for from flask import Blueprint, config, current_app, flash, redirect, render_template, request, url_for
import flask_login import flask_login
from flask_login import current_user from flask_login import current_user
from goathacks.registration.forms import LoginForm, RegisterForm from goathacks.registration.forms import LoginForm, RegisterForm
@ -14,8 +14,10 @@ bp = Blueprint('registration', __name__, url_prefix="/registration")
def register(): def register():
if current_user.is_authenticated: if current_user.is_authenticated:
flash("You are already registered and logged in!") flash("You are already registered and logged in!")
print("got register") print("got register")
form = RegisterForm(request.form) form = RegisterForm(request.form)
print(vars(form.gender))
if request.method == 'POST': if request.method == 'POST':
print("Got form") print("Got form")
email = request.form.get('email') email = request.form.get('email')
@ -23,22 +25,32 @@ def register():
last_name = request.form.get('last_name') last_name = request.form.get('last_name')
password = request.form.get('password') password = request.form.get('password')
password_c = request.form.get('password_confirm') password_c = request.form.get('password_confirm')
school = request.form.get('school')
phone = request.form.get('phone_number')
gender = request.form.get('gender')
if password == password_c: if password == password_c:
# Passwords match! # Passwords match!
# Count of all non-waitlisted hackers
num_not_waitlisted = len(User.query.filter_by(waitlisted=False).all())
waitlisted = False
print(num_not_waitlisted)
print(current_app.config['MAX_BEFORE_WAITLIST'])
if num_not_waitlisted >= current_app.config['MAX_BEFORE_WAITLIST']:
waitlisted = True
user = User( user = User(
email=email, email=email,
password=generate_password_hash(password), password=generate_password_hash(password),
first_name=first_name, first_name=first_name,
last_name=last_name, last_name=last_name,
last_login=datetime.now(), last_login=datetime.now(),
waitlisted=waitlisted,
school=school,
phone=phone,
gender=gender
) )
# Count of all non-waitlisted hackers
# num_not_waitlisted = len(db.session.execute(db.select(User).filter(waitlisted=False)).scalars().all())
# print(num_not_waitlisted)
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
flask_login.login_user(user) flask_login.login_user(user)
@ -48,3 +60,7 @@ def register():
flash("Passwords do not match") flash("Passwords do not match")
return render_template("register.html", form=form) return render_template("register.html", form=form)
@bp.route("/login", methods=["GET", "POST"])
def login():
return "OK"

View file

@ -1,5 +1,5 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import BooleanField, PasswordField, StringField, SubmitField from wtforms import BooleanField, PasswordField, SelectField, StringField, SubmitField, widgets
from wtforms.validators import DataRequired from wtforms.validators import DataRequired
class RegisterForm(FlaskForm): class RegisterForm(FlaskForm):
@ -10,6 +10,11 @@ class RegisterForm(FlaskForm):
password = PasswordField("Password", validators=[DataRequired()]) password = PasswordField("Password", validators=[DataRequired()])
password_confirm = PasswordField("Confirm Password", password_confirm = PasswordField("Confirm Password",
validators=[DataRequired()]) validators=[DataRequired()])
school = StringField("School/University", validators=[DataRequired()])
phone_number = StringField("Phone number", validators=[DataRequired()])
gender = SelectField("Gender", choices=[("F", "Female"), ("M", "Male"),
("NB", "Non-binary/Other")],
widget=widgets.Select())
agree_coc = BooleanField("I confirm that I have read and agree to the Code of Conduct", validators=[DataRequired()]) agree_coc = BooleanField("I confirm that I have read and agree to the Code of Conduct", validators=[DataRequired()])
submit = SubmitField("Register") submit = SubmitField("Register")

View file

@ -150,8 +150,20 @@ form input[type="radio"] {
padding-right: 5px; padding-right: 5px;
} }
form input[type="checkbox"]:checked {
visibility: visible;
left: unset;
position: unset;
}
form label { form label {
font-size: 1.1rem; font-size: 1.1rem;
padding-right: 10px; padding-right: 10px;
padding-left: 25px !important; padding-left: 25px !important;
} }
form select {
display: unset;
background: #974355;
max-width: 11rem;
}

View file

@ -1,6 +1,6 @@
$color-bg: #003049; $color-bg: #003049;
$color-fg: #eee; $color-fg: #eee;
$color-section-bg: #F5665B; $color-section-bg: #974355;
$color-accent: #26a69a; $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"); } @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"); }
@ -148,9 +148,19 @@ form {
input[type="radio"] { input[type="radio"] {
padding-right: 5px; padding-right: 5px;
} }
input[type="checkbox"]:checked {
visibility: visible;
left: unset;
position: unset;
}
label { label {
font-size: 1.1rem; font-size: 1.1rem;
padding-right: 10px; padding-right: 10px;
padding-left: 25px !important; padding-left: 25px !important;
} }
select {
display: unset;
background: $color-section-bg;
max-width: 11rem;
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,46 +1,10 @@
{% include 'header.html' %} {% include 'header.html' %}
<script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/sweetalert/1.1.3/sweetalert.min.js"></script>
<link href="../static/css/materialize.min.css" rel="stylesheet">
<script> <script>
function drop(id) { 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)")) { 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 = "/drop?mlh_id=" + id; window.location.href = "/admin/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
// )
// }
// })
// })
} }
function resumeChange() { function resumeChange() {
@ -54,8 +18,8 @@
<div class="container"> <div class="container">
<div class="row center justify-content-center" style="margin-top: 10%;"> <div class="row center justify-content-center" style="margin-top: 10%;">
<h1>Hi {{ current_user.first_name }}!</h1> <h1>Hi {{ current_user.first_name }}!</h1>
{% if waitlisted %} {% if current_user.waitlisted %}
<h2>You are waitlisted, if space opens up we will let you know...</h2> <h2>You are waitlisted, if space opens up we will let you know</h2>
{% else %} {% else %}
<h2>You are fully registered! We look forward to seeing you!</h2> <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> Let us know if you have any questions by sending them to <a href="mailto:hack@wpi.edu">hack@wpi.edu</a>
@ -115,7 +79,7 @@
</div> </div>
<br> <br>
<br> <br>
<center><a onclick="drop('{{id}}')" id="drop-link"><p class="btn">Drop Application if you can't make it :(</p></a></center> <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> </div>
<script> <script>

View file

@ -0,0 +1,13 @@
Dear {{ user.first_name }},
Your application has been dropped. We're sorry to see you go!
If this was done in error, you can re-register by going to
https://hack.wpi.edu/registration.
Happy Hacking!
GoatHacks Team
This is an automated message. Please email hack@wpi.edu with any questions or
concerns.

View file

@ -0,0 +1,19 @@
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.
{% if not user.waitlisted %}
We'll send another email with more details closer to the event. In the
meantime, visit your Dashboard (https://hack.wpi.edu/dashboard) to tell us about
your shirt size and any accomodations you may need.
{% endif %}
Happy Hacking!
GoatHacks Team
This is an automated message. Please email hack@wpi.edu with any questions or
concerns.

View file

@ -0,0 +1,18 @@
Hello {{ user.first_name }}!
We're writing to let you know that a spot has opened up in our registrations,
and you've been promoted off of the waitlist! Please visit our website
(https://hack.wpi.edu/dashboard) to complete your registration information and
join our Discord.
If you can no longer make the event, please visit your dashboard and use the
"Drop my registration" link.
Happy Hacking!
GoatHacks Team
This is an automated message. Please email hack@wpi.edu with any questions or
concerns.

View file

@ -34,9 +34,9 @@
<br/> <br/>
<br/> <br/>
Best,<br/> Best,<br/>
<b>Hack@WPI Team</b><br/> <b>GoatHacks Team</b><br/>
<i><a href="mailto:hack@wpi.edu">hack@wpi.edu</a></i><br/> <i><a href="mailto:hack@wpi.edu">hack@wpi.edu</a></i><br/>
<img height="75px" width="75px" src="https://media.discordapp.net/attachments/829437603291856938/930311998057635880/hack317-min.png"> <img height="75px" width="75px" src="{{url_for('static', filename='img/favicon.png')}}">
</textarea> </textarea>
<br/> <br/>
<br/> <br/>
@ -98,4 +98,4 @@
</script> </script>
</body> </body>
</html> </html>

View file

@ -29,6 +29,18 @@
<div> <div>
{{form.last_name}}<br/>{{form.last_name.label}} {{form.last_name}}<br/>{{form.last_name.label}}
</div> </div>
<hr/>
<h3>Miscellaneous Information</h3>
<div>
{{form.phone_number}}<br/>{{form.phone_number.label}}
</div>
<div>
{{form.school}}<br/>{{form.school.label}}
</div>
<div>
{{form.gender.label}}{{form.gender}}
</div>
<hr/>
<div> <div>
<label for="agree_coc">I confirm that I have read and agree to the <label for="agree_coc">I confirm that I have read and agree to the
Code of Conduct</label> Code of Conduct</label>

View file

@ -3,6 +3,7 @@ click==8.1.3
Flask==2.2.2 Flask==2.2.2
Flask-Assets Flask-Assets
Flask-CORS Flask-CORS
Flask-Mail
Flask-Login==0.6.2 Flask-Login==0.6.2
Flask-Migrate==4.0.0 Flask-Migrate==4.0.0
Flask-SQLAlchemy==3.0.2 Flask-SQLAlchemy==3.0.2