Tracking PR for registration rewrite #5
14 changed files with 178 additions and 53 deletions
|
@ -4,6 +4,7 @@ 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
|
||||
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
@ -11,6 +12,7 @@ migrate = Migrate()
|
|||
login = LoginManager()
|
||||
environment = Environment()
|
||||
cors = CORS()
|
||||
mail = Mail()
|
||||
|
||||
def create_app():
|
||||
app = Flask(__name__)
|
||||
|
@ -22,6 +24,7 @@ def create_app():
|
|||
login.init_app(app)
|
||||
environment.init_app(app)
|
||||
cors.init_app(app)
|
||||
mail.init_app(app)
|
||||
|
||||
scss = Bundle('css/style.scss', filters='scss',
|
||||
output='css/style.css')
|
||||
|
@ -38,3 +41,5 @@ def create_app():
|
|||
app.register_blueprint(admin.bp)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
|
|
@ -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_mail import Message
|
||||
|
||||
from goathacks.models import User
|
||||
|
||||
bp = Blueprint("admin", __name__, url_prefix="/admin")
|
||||
|
||||
from goathacks import db
|
||||
from goathacks import db,mail
|
||||
|
||||
@bp.route("/")
|
||||
@login_required
|
||||
|
@ -53,6 +54,45 @@ def home():
|
|||
female_count=female_count, nb_count=nb_count,
|
||||
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>")
|
||||
@login_required
|
||||
def check_in(id):
|
||||
|
@ -79,6 +119,11 @@ def drop(id):
|
|||
if user.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.commit()
|
||||
|
||||
|
@ -122,6 +167,11 @@ def promote_waitlist(id):
|
|||
user.waitlisted = False
|
||||
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"}
|
||||
|
||||
@bp.route("/hackers.csv")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
from flask_login import current_user
|
||||
from goathacks.registration.forms import LoginForm, RegisterForm
|
||||
|
@ -14,8 +14,10 @@ bp = Blueprint('registration', __name__, url_prefix="/registration")
|
|||
def register():
|
||||
if current_user.is_authenticated:
|
||||
flash("You are already registered and logged in!")
|
||||
|
||||
print("got register")
|
||||
form = RegisterForm(request.form)
|
||||
print(vars(form.gender))
|
||||
if request.method == 'POST':
|
||||
print("Got form")
|
||||
email = request.form.get('email')
|
||||
|
@ -23,22 +25,32 @@ def register():
|
|||
last_name = request.form.get('last_name')
|
||||
password = request.form.get('password')
|
||||
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:
|
||||
# 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(
|
||||
email=email,
|
||||
password=generate_password_hash(password),
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
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.commit()
|
||||
flask_login.login_user(user)
|
||||
|
@ -48,3 +60,7 @@ def register():
|
|||
flash("Passwords do not match")
|
||||
|
||||
return render_template("register.html", form=form)
|
||||
|
||||
@bp.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
return "OK"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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
|
||||
|
||||
class RegisterForm(FlaskForm):
|
||||
|
@ -10,6 +10,11 @@ class RegisterForm(FlaskForm):
|
|||
password = PasswordField("Password", validators=[DataRequired()])
|
||||
password_confirm = PasswordField("Confirm Password",
|
||||
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()])
|
||||
submit = SubmitField("Register")
|
||||
|
||||
|
|
|
@ -150,8 +150,20 @@ 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;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
$color-bg: #003049;
|
||||
$color-fg: #eee;
|
||||
$color-section-bg: #F5665B;
|
||||
$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"); }
|
||||
|
||||
|
@ -148,9 +148,19 @@ form {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
BIN
goathacks/static/img/favicon.png
Normal file
BIN
goathacks/static/img/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -1,46 +1,10 @@
|
|||
{% include 'header.html' %}
|
||||
<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>
|
||||
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 = "/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() {
|
||||
|
@ -54,8 +18,8 @@
|
|||
<div class="container">
|
||||
<div class="row center justify-content-center" style="margin-top: 10%;">
|
||||
<h1>Hi {{ current_user.first_name }}!</h1>
|
||||
{% if waitlisted %}
|
||||
<h2>You are waitlisted, if space opens up we will let you know...</h2>
|
||||
{% if current_user.waitlisted %}
|
||||
<h2>You are waitlisted, if space opens up we will let you know</h2>
|
||||
{% 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>
|
||||
|
@ -115,7 +79,7 @@
|
|||
</div>
|
||||
<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>
|
||||
|
||||
<script>
|
||||
|
|
13
goathacks/templates/emails/dropped.txt
Normal file
13
goathacks/templates/emails/dropped.txt
Normal 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.
|
19
goathacks/templates/emails/registration.txt
Normal file
19
goathacks/templates/emails/registration.txt
Normal 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.
|
18
goathacks/templates/emails/waitlist_promotion.txt
Normal file
18
goathacks/templates/emails/waitlist_promotion.txt
Normal 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.
|
|
@ -34,9 +34,9 @@
|
|||
<br/>
|
||||
<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/>
|
||||
<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>
|
||||
<br/>
|
||||
<br/>
|
||||
|
@ -98,4 +98,4 @@
|
|||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -29,6 +29,18 @@
|
|||
<div>
|
||||
{{form.last_name}}<br/>{{form.last_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>
|
||||
<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>
|
||||
|
|
|
@ -3,6 +3,7 @@ click==8.1.3
|
|||
Flask==2.2.2
|
||||
Flask-Assets
|
||||
Flask-CORS
|
||||
Flask-Mail
|
||||
Flask-Login==0.6.2
|
||||
Flask-Migrate==4.0.0
|
||||
Flask-SQLAlchemy==3.0.2
|
||||
|
|
Loading…
Add table
Reference in a new issue