Compare commits

..

21 commits
2025 ... master

Author SHA1 Message Date
Cara Salter
7255ccebfd Re-add Discord link to participant dashboard 2025-01-03 19:52:13 -05:00
Cara Salter
02eb9b4220 update static site 2025-01-03 19:35:55 -05:00
Cara Salter
59266eb865 force MAX_BEFORE_WAITLIST to be an int 2024-10-31 14:41:00 -04:00
Cara Salter
fb2dd9032b fix resume uploading 2024-10-31 14:36:07 -04:00
Cara Salter
97f072ddb8 open registration 2024-10-31 13:37:25 -04:00
Cara Salter
cf725a2206 Add MLH logistics checkbox 2024-10-31 13:37:25 -04:00
Cara Salter
0f4ef0b690 use .env for config from now on
can't believe it took us this long
2024-10-31 13:37:25 -04:00
Cara Salter
b0a1e142cd Update migrations and homepage for 2025 2024-10-31 13:37:25 -04:00
ngolp
c73184060c Changed back the SQL query in /mail route 2024-10-31 13:37:25 -04:00
ngolp
29cdcf1899 See Github Issue #10. Added separation of admins and users on admin dashboard.
Refactored a bit of the original admin.home endpoint into a helper function to accompany two routes that use the same template without having to copy+paste code.
2024-10-31 13:37:25 -04:00
warren yun
3c40553b45 update user schema 2024-10-31 13:37:25 -04:00
warren yun
36bb39a8a0 more fields 2024-10-31 13:37:25 -04:00
warren yun
fa55e10e5b countries 2024-10-31 13:37:25 -04:00
warren yun
35c0197a40 schools dropdown 2024-10-31 13:37:25 -04:00
warren yun
1857e15791 countdown (hot) 2024-10-31 13:37:25 -04:00
warren yun
f6b03460d9 modified theme 2024-10-31 13:37:25 -04:00
warren yun
8a94de44c0 playing around with styling and items 2024-10-31 13:37:25 -04:00
Cara Salter
3dff046e84 Make unauth handler redirect to login 2024-10-31 13:37:25 -04:00
Cara Salter
0b3480dd52 Support editing event category in modals 2024-10-31 13:37:25 -04:00
Cara Salter
b7ca654bf1 Enable editing/creating/deleting events
Wholly through modals, yay!
2024-10-31 13:37:25 -04:00
Cara Salter
c47a46c204 Input event modal and create some supporting infra
update form to split date/time as well
2024-10-31 13:37:25 -04:00
10 changed files with 34 additions and 141 deletions

View file

@ -11,7 +11,8 @@ class Config():
SQLALCHEMY_DATABASE_URI = dotenv_values().get("SQLALCHEMY_DATABASE_URI") or "postgresql://localhost/goathacks"
MAX_BEFORE_WAITLIST = dotenv_values().get("MAX_BEFORE_WAITLIST") or 1
MAX_BEFORE_WAITLIST = int(dotenv_values().get("MAX_BEFORE_WAITLIST") or 1)
MCE_API_KEY = dotenv_values().get("MCE_API_KEY")
SECRET_KEY = dotenv_values().get("SECRET_KEY") or "bad-key-change-me"
UPLOAD_FOLDER = dotenv_values().get("UPLOAD_FOLDER") or "./uploads/"

View file

@ -4,8 +4,6 @@ from flask_mail import Message
from goathacks.models import User
from sqlalchemy.exc import IntegrityError
bp = Blueprint("admin", __name__, url_prefix="/admin")
from goathacks import db, mail as app_mail
@ -223,44 +221,6 @@ def hackers():
users = User.query.all()
return User.create_json_output(users)
@bp.route("/updateHacker", methods=["POST"])
@login_required
def updateHacker():
if not current_user.is_admin:
return redirect(url_for("dashboard.home"))
# get params from json
hacker_id = request.json['hacker_id']
change_field = request.json['change_field']
new_val = request.json['new_val']
# find the user in db
user = User.query.filter_by(id=hacker_id).one()
if user is None:
return {"status": "error", "msg": "user not found"}
# update the hacker depending on change_field
match change_field:
case "first_name":
user.first_name = new_val
case "last_name":
user.last_name = new_val
case "school":
user.school = new_val
case "phone":
user.phone = new_val
try:
db.session.commit()
except IntegrityError as err:
db.session.rollback()
flash("Could not update user information for user " + hacker_id)
return {"status": "error"}
return {"status": "success"}
import json
import csv
from io import StringIO

View file

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

View file

@ -6,7 +6,6 @@ from goathacks.registration.forms import LoginForm, PwResetForm, RegisterForm, R
from werkzeug.security import check_password_hash, generate_password_hash
from flask_mail import Message
import ulid
from sqlalchemy.exc import IntegrityError
from goathacks import db, mail as app_mail
from goathacks.models import PwResetRequest, User
@ -61,20 +60,8 @@ def register():
dietary_restrictions=dietary_restrictions,
newsletter=newsletter
)
#try to add the user to the database, checking for duplicate users
try:
db.session.add(user)
db.session.commit()
except IntegrityError as err:
db.session.rollback()
if "duplicate key value violates unique constraint" in str(err):
flash("User with email " + email + " already exists.")
else:
flash("An unknown error occurred.")
return redirect(url_for("registration.login"))
#user successfully registered, so login
db.session.add(user)
db.session.commit()
flask_login.login_user(user)
if waitlisted:
@ -142,10 +129,9 @@ def reset():
user_id=user.id,
expires=datetime.now() + timedelta(minutes=30)
)
db.session.add(r)
db.session.commit()
msg = Message("GoatHacks - Password Reset Request")
msg.add_recipient(user.email)
msg.body = render_template("emails/password_reset.txt", code=r.id)

View file

@ -22,8 +22,8 @@ class RegisterForm(FlaskForm):
gender = SelectField("Gender", choices=[("F", "Female"), ("M", "Male"),
("NB", "Non-binary/Other")],
widget=widgets.Select())
country = SelectField("Country of Origin", choices=[(country.split(",")[0], country.split(",")[0]) for country in countries_list], widget=widgets.Select())
newsletter = BooleanField("'I authorize MLH to send me occasional emails about relevant events, career opportunities, and community announcements.")
country = SelectField("Country", choices=[(country.split(",")[0], country.split(",")[0]) for country in countries_list], widget=widgets.Select())
newsletter = BooleanField("Subscribe to the MLH newsletter?")
agree_coc = BooleanField("I confirm that I have read and agree to the Code of Conduct", validators=[DataRequired()])
logistics = BooleanField("I authorize you to share my application/registration with Major League Hacking for event administration, ranking, and MLH administration in-line with the MLH privacy policy.I further agree to the terms of both the MLH Contest Terms and Conditions and the MLH Privacy Policy.", validators=[DataRequired()])

View file

@ -1,4 +1,4 @@
Worcester Polytechnic Institute
21st Century Cyber Charter School
Aalto University
Aarhus University
@ -2198,6 +2198,7 @@ Woodbridge High School - London
"Woodbridge High School - Woodbridge, NJ"
"Woodbridge High School - Woodbridge, ON"
"Woodbridge High School - Woodbridge, VA"
Worcester Polytechnic Institute
Worcester State University
World Communications Charter School
Wright State University

View file

@ -14,22 +14,8 @@
{% block app_content %}
<div class="card text-center">
<div class="card-body">
<div style="display:flex;flex-wrap:nowrap;align-items:center;">
<div class="dropdown">
<a href="#" class="btn btn-primary dropdown-toggle"
data-bs-toggle="dropdown">Search<span
class="caret"></span></a>
<ul class="dropdown-menu">
<input style="padding:5px;margin-left:10px;margin-right:10px;" type="text" id="searchbox" name="searchbox-text" placeholder="Search By Email" onkeyup="filterHackers()"/>
</ul>
</div>
<!-- TODO: get "Registered Users" properly centered -->
<h1 style="flex-grow:1;justify-content:center;" class="h3 mb-3 fw-normal">Registered Users</h1>
</div>
<table id="hackers" class="table table-striped sortable">
<h1 class="h3 mb-3 fw-normal">Registered Users</h1>
<table id="hackers" class="table table-striped">
<thead>
<tr>
<th>Options</th>
@ -93,71 +79,16 @@
<td>{{ hacker.id }}</td>
<td>{{ hacker.last_login }}</td>
<td>{{ hacker.email }}</td>
<td>
<div style="display: flex; justify-content: flex-start;">
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-namebox-first" placeholder="first_name" value="{{hacker.first_name}}" onchange="updateHacker(this.id, 'first_name', this.value)"/>
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-namebox-last" placeholder="last_name" value="{{hacker.last_name}}" onchange="updateHacker(this.id, 'last_name', this.value)"/>
</div>
</td>
<td>
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:100px;" type="text" id="{{hacker.id}}-phonebox" placeholder="phone" value="{{hacker.phone}}" onchange="updateHacker(this.id, 'phone', this.value)"/>
</td>
<td>{{ hacker.first_name + ' ' + hacker.last_name }}</td>
<td>{{ hacker.phone }}</td>
<td>{{ hacker.shirt_size }}</td>
<td>{{ hacker.accomodations }}</td>
<td>
<input style="padding:5px; margin-left:10px; margin-right:10px;width:fit-content;max-width:75px;" type="text" id="{{hacker.id}}-schoolbox" placeholder="school" value="{{hacker.school}}" onchange="updateHacker(this.id, 'school', this.value)"/>
</td>
<td>{{ hacker.school }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<script type="text/javascript">
function filterHackers() {
//get hacker table and searchbox info
const input = document.getElementById("searchbox").value.toLowerCase();
const hackertable = document.getElementById("hackers");
let rows = hackertable.getElementsByTagName("tr");
//iterate over all rows
for(let i = 1; i < rows.length; i++) {
//get the email
const cells = rows[i].getElementsByTagName("td");
const emailCell = cells[6];
//if there is an email, display or dont display based on searchbox
if(emailCell) {
const emailText = emailCell.textContent.toLowerCase();
if(!emailText.includes(input)) {
rows[i].style.display = "none";
}
else {
rows[i].style.display = "";
}
}
}
}
function updateHacker(id, change_field, new_val) {
//tell backend to update a specific field for a hacker
const headers = [
["Content-Type", "application/json"],
];
let body = {
"hacker_id": id.substr(0, id.indexOf('-')),
"change_field": change_field,
"new_val": new_val,
}
//send the post request, and report the error if there is one
fetch('/admin/updateHacker', {method: 'POST', body: JSON.stringify(body), headers: headers}).catch((err) => {
window.alert("Error updating user - see console for details");
console.log(err);
})
}
</script>
{% endblock %}

View file

@ -15,6 +15,12 @@
<p class="card-text">Let us know if you have any questions by sending
them to <a href="mailto:hack@wpi.edu">hack@wpi.edu</a></p>
{% if not current_user.waitlisted and config['DISCORD_LINK'] %}
<p>Make sure to join our Discord to get the latest updates!</p>
<button type="button" class="btn btn-primary mb-3"><a href="{{ config['DISCORD_LINK']
}}" class="link-light">Discord</a></button>
{% endif %}
<div class="row center justify-content-center">
<form method="post">
{{ form.csrf_token() }}
@ -33,7 +39,7 @@
</div>
<hr/>
<div class="row center justify-content-center">
<form method="post" action={{url_for('dashboard.resume')}}"
<form method="post" action="{{url_for('dashboard.resume')}}"
enctype="multipart/form-data">
{{ resform.csrf_token() }}
<p><b>If you'd like, add your resume to send to

@ -1 +1 @@
Subproject commit 261ffb2bab157162c86f60cdd4b22723565c903e
Subproject commit db2a7a865f9b3865fa2180b1b53b1c2d2640be81

View file

@ -102,7 +102,7 @@ privacy policy. I further agree to the terms of both the <a
</div>
<div class="form-check mb-3">
{{ form.newsletter }}
I authorize MLH to send me occasional emails about relevant events, career opportunities, and community announcements.
Subscribe to the MLH newsletter?
</div>
{{ render_field(form.submit) }}
</form>