separated team and user dashboards
parent
b99095338c
commit
675e022e4b
125
app.py
125
app.py
|
@ -25,6 +25,8 @@ def make_info_available():
|
|||
try:
|
||||
g.user = User.get(User.id == session["user_id"])
|
||||
g.user_restricts = g.user.restricts.split(",")
|
||||
g.team = g.user.team
|
||||
g.team_restricts = g.team.restricts.split(",")
|
||||
except User.DoesNotExist:
|
||||
return render_template("login.html")
|
||||
|
||||
|
@ -34,6 +36,7 @@ def scoreboard_variables():
|
|||
if "user_id" in session:
|
||||
var["logged_in"] = True
|
||||
var["user"] = g.user
|
||||
var["team"] = g.team
|
||||
# TODO should this apply to users or teams?
|
||||
# var["notifications"] = Notification.select().where(Notification.user == g.user)
|
||||
var["notifications"] = []
|
||||
|
@ -86,7 +89,7 @@ def login():
|
|||
if(user.checkPassword(password)):
|
||||
session["user_id"] = user.id
|
||||
flash("Login successful.")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('team_dashboard'))
|
||||
else:
|
||||
flash("Incorrect username or password", "error")
|
||||
return render_template("login.html")
|
||||
|
@ -105,53 +108,83 @@ def register():
|
|||
if request.method == "GET":
|
||||
return render_template("register.html")
|
||||
elif request.method == "POST":
|
||||
error, message = captcha.verify_captcha()
|
||||
if error:
|
||||
flash(message)
|
||||
return render_template("register.html")
|
||||
# error, message = captcha.verify_captcha()
|
||||
# if error:
|
||||
# flash(message)
|
||||
# return render_template("register.html")
|
||||
|
||||
username = request.form["username"].strip()
|
||||
user_email = request.form["email"].strip()
|
||||
password = request.form["password"].strip()
|
||||
confirm_password = request.form["confirm_password"].strip()
|
||||
affiliation = request.form["affiliation"].strip()
|
||||
background = request.form["background"].strip()
|
||||
country = request.form["country"].strip()
|
||||
|
||||
tshirt_size = request.form["tshirt_size"].strip()
|
||||
gender = request.form["gender"].strip()
|
||||
|
||||
join_team = bool(int(request.form["join_team"].strip()))
|
||||
if join_team:
|
||||
team_key = request.form["team_key"].strip()
|
||||
else:
|
||||
team_name = request.form["team_name"].strip()
|
||||
team_affiliation = request.form["team_affiliation"].strip()
|
||||
|
||||
if len(username) > 50 or not username:
|
||||
flash("You must have a username!")
|
||||
return render_template("register.html")
|
||||
|
||||
try:
|
||||
user = User.get(User.username == username)
|
||||
flash("This username is already in use!")
|
||||
return render_template("register.html")
|
||||
except User.DoesNotExist:
|
||||
pass
|
||||
|
||||
if not (user_email and "." in user_email and "@" in user_email):
|
||||
flash("You must have a valid email!")
|
||||
return render_template("register.html")
|
||||
|
||||
if not affiliation or len(affiliation) > 100:
|
||||
affiliation = "No affiliation"
|
||||
|
||||
if not email.is_valid_email(user_email):
|
||||
flash("You're lying")
|
||||
return render_template("register.html")
|
||||
|
||||
confirmation_key = misc.generate_confirmation_key()
|
||||
|
||||
eligible = False
|
||||
if country == "Iceland":
|
||||
eligible = True
|
||||
team=None
|
||||
if join_team:
|
||||
try:
|
||||
team = Team.get(Team.key == team_key)
|
||||
except Team.DoesNotExist:
|
||||
flash("Couldn't find this team, check your team key.")
|
||||
return rener_template("register.html")
|
||||
else:
|
||||
if not team_affiliation or len(team_affiliation) > 100:
|
||||
team_affiliation = "No affiliation"
|
||||
try:
|
||||
team = Team.get(Team.name == team_name)
|
||||
flash("This team name is already in use!")
|
||||
return render_template("register.html")
|
||||
except Team.DoesNotExist:
|
||||
pass
|
||||
team_key = misc.generate_team_key()
|
||||
team = Team.create(name=team_name, affiliation=team_affiliation, key=team_key)
|
||||
|
||||
|
||||
user = User.create(username=username, email=user_email,
|
||||
eligible=eligible, affiliation=affiliation,
|
||||
country=country, tshirt_size=tshirt_size,
|
||||
gender=gender,
|
||||
email_confirmation_key=confirmation_key)
|
||||
background=background, country=country,
|
||||
tshirt_size=tshirt_size, gender=gender,
|
||||
email_confirmation_key=confirmation_key,
|
||||
team=team)
|
||||
user.setPassword(password)
|
||||
|
||||
email.send_confirmation_email(user_email, confirmation_key)
|
||||
print(confirmation_key)
|
||||
|
||||
# email.send_confirmation_email(user_email, confirmation_key)
|
||||
|
||||
session["user_id"] = user.id
|
||||
flash("Registration finished")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('team_dashboard'))
|
||||
|
||||
@app.route('/logout/')
|
||||
def logout():
|
||||
|
@ -170,27 +203,22 @@ def confirm_email():
|
|||
g.user.save()
|
||||
else:
|
||||
flash("Incorrect confirmation key.")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
@app.route('/user/', methods=["GET", "POST"])
|
||||
@decorators.login_required
|
||||
def dashboard():
|
||||
def user_dashboard():
|
||||
if request.method == "GET":
|
||||
# team_solves = ChallengeSolve.select(ChallengeSolve, Challenge).join(Challenge).where(ChallengeSolve.team == g.team)
|
||||
# team_adjustments = ScoreAdjustment.select().where(ScoreAdjustment.team == g.team)
|
||||
# team_score = sum([i.challenge.points for i in team_solves] + [i.value for i in team_adjustments])
|
||||
first_login = False
|
||||
if g.user.first_login:
|
||||
first_login = True
|
||||
g.user.first_login = False
|
||||
g.user.save()
|
||||
# return render_template("dashboard.html", team_solves=team_solves, team_adjustments=team_adjustments, team_score=team_score, first_login=first_login)
|
||||
return render_template("dashboard.html", first_login=first_login)
|
||||
|
||||
return render_template("user.html", first_login=first_login)
|
||||
elif request.method == "POST":
|
||||
if g.redis.get("ul{}".format(session["user_id"])):
|
||||
flash("You're changing your information too fast!")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
team_name = request.form["team_name"].strip()
|
||||
team_email = request.form["team_email"].strip()
|
||||
|
@ -199,11 +227,11 @@ def dashboard():
|
|||
|
||||
if len(team_name) > 50 or not team_name:
|
||||
flash("You must have a team name!")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
if not (team_email and "." in team_email and "@" in team_email):
|
||||
flash("You must have a valid team email!")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
if not affiliation or len(affiliation) > 100:
|
||||
affiliation = "No affiliation"
|
||||
|
@ -221,7 +249,7 @@ def dashboard():
|
|||
if email_changed:
|
||||
if not email.is_valid_email(team_email):
|
||||
flash("You're lying")
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
|
||||
g.team.email_confirmation_key = misc.generate_confirmation_key()
|
||||
g.team.email_confirmed = False
|
||||
|
@ -233,7 +261,42 @@ def dashboard():
|
|||
g.team.save()
|
||||
|
||||
|
||||
return redirect(url_for('dashboard'))
|
||||
return redirect(url_for('user_dashboard'))
|
||||
@app.route('/team/', methods=["GET", "POST"])
|
||||
@decorators.login_required
|
||||
def team_dashboard():
|
||||
if request.method == "GET":
|
||||
team_solves = ChallengeSolve.select(ChallengeSolve, Challenge).join(Challenge).where(ChallengeSolve.team == g.team)
|
||||
team_adjustments = ScoreAdjustment.select().where(ScoreAdjustment.team == g.team)
|
||||
team_score = sum([i.challenge.points for i in team_solves] + [i.value for i in team_adjustments])
|
||||
return render_template("team.html", team_solves=team_solves, team_adjustments=team_adjustments, team_score=team_score)
|
||||
elif request.method == "POST":
|
||||
if g.redis.get("ul{}".format(session["user_id"])):
|
||||
flash("You're changing your information too fast!")
|
||||
return redirect(url_for('team_dashboard'))
|
||||
|
||||
team_name = request.form["team_name"].strip()
|
||||
affiliation = request.form["affiliation"].strip()
|
||||
|
||||
if len(team_name) > 50 or not team_name:
|
||||
flash("You must have a team name!")
|
||||
return redirect(url_for('team_dashboard'))
|
||||
|
||||
if not affiliation or len(affiliation) > 100:
|
||||
affiliation = "No affiliation"
|
||||
|
||||
email_changed = (team_email != g.team.email)
|
||||
|
||||
g.team.name = team_name
|
||||
g.team.affiliation = affiliation
|
||||
|
||||
g.redis.set("ul{}".format(session["user_id"]), str(datetime.now()), 120)
|
||||
|
||||
flash("Changes saved.")
|
||||
g.team.save()
|
||||
|
||||
|
||||
return redirect(url_for('team_dashboard'))
|
||||
|
||||
@app.route('/teamconfirm/', methods=["POST"])
|
||||
def teamconfirm():
|
||||
|
|
42
database.py
42
database.py
|
@ -6,18 +6,36 @@ class BaseModel(Model):
|
|||
class Meta:
|
||||
database = db
|
||||
|
||||
class Team(BaseModel):
|
||||
name = CharField(unique=True)
|
||||
affiliation = CharField()
|
||||
restricts = TextField(default="")
|
||||
key = CharField(unique=True, index=True)
|
||||
|
||||
def solved(self, challenge):
|
||||
return ChallengeSolve.select().where(ChallengeSolve.team == self, ChallengeSolve.challenge == challenge).count()
|
||||
|
||||
def eligible(self):
|
||||
return all([member.eligible() for member in self.members])
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
challenge_points = sum([i.challenge.points for i in self.solves])
|
||||
adjust_points = sum([i.value for i in self.adjustments])
|
||||
return challenge_points + adjust_points
|
||||
|
||||
class User(BaseModel):
|
||||
username = CharField()
|
||||
email = CharField()
|
||||
username = CharField(unique=True, index=True)
|
||||
email = CharField(unique=True, index=True)
|
||||
email_confirmed = BooleanField(default=False)
|
||||
email_confirmation_key = CharField()
|
||||
password = CharField(null = True)
|
||||
country = CharField()
|
||||
eligible = BooleanField()
|
||||
tshirt_size = CharField(null = True)
|
||||
gender = CharField(null = True)
|
||||
first_login = BooleanField(default=True)
|
||||
restricts = TextField(default="")
|
||||
team = ForeignKeyField(Team, related_name="members")
|
||||
|
||||
def setPassword(self, pw):
|
||||
self.password = bcrypt.hashpw(pw.encode("utf-8"), bcrypt.gensalt())
|
||||
|
@ -26,22 +44,8 @@ class User(BaseModel):
|
|||
def checkPassword(self, pw):
|
||||
return bcrypt.checkpw(pw.encode("utf-8"), self.password)
|
||||
|
||||
class Team(BaseModel):
|
||||
name = CharField()
|
||||
affiliation = CharField()
|
||||
eligible = BooleanField()
|
||||
eligibility_locked = BooleanField(default=False)
|
||||
restricts = TextField(default="")
|
||||
key = CharField()
|
||||
|
||||
def solved(self, challenge):
|
||||
return ChallengeSolve.select().where(ChallengeSolve.team == self, ChallengeSolve.challenge == challenge).count()
|
||||
|
||||
@property
|
||||
def score(self):
|
||||
challenge_points = sum([i.challenge.points for i in self.solves])
|
||||
adjust_points = sum([i.value for i in self.adjustments])
|
||||
return challenge_points + adjust_points
|
||||
def eligible(self):
|
||||
return self.country == "Iceland"
|
||||
|
||||
class TeamAccess(BaseModel):
|
||||
team = ForeignKeyField(Team, related_name='accesses')
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<ul class="right hide-on-med-and-down">
|
||||
{% if logged_in %}
|
||||
<li><a href="{{ url_for('logout') }}">Logout</a></li>
|
||||
<li><a href="{{ url_for('dashboard') }}">{{ user.username }}</a></li>
|
||||
<li><a href="{{ url_for('user_dashboard') }}">{{ user.username }}</a></li>
|
||||
{% endif %}
|
||||
{% if not logged_in %}
|
||||
{% if config.registration %}
|
||||
|
@ -79,10 +79,14 @@
|
|||
<div class="side-icon"><i class="material-icons red-text">block</i></div>
|
||||
<div class="side-text">Logout</div>
|
||||
</a></li>
|
||||
<li><a href="{{ url_for('dashboard') }}">
|
||||
<li><a href="{{ url_for('user_dashboard') }}">
|
||||
<div class="side-icon"><i class="material-icons teal-text">account_circle</i></div>
|
||||
<div class="side-text">{{ user.username }}</div>
|
||||
</a></li>
|
||||
<li><a href="{{ url_for('team_dashboard') }}">
|
||||
<div class="side-icon"><i class="material-icons teal-text">account_circle</i></div>
|
||||
<div class="side-text">{{ team.name }}</div>
|
||||
</a></li>
|
||||
{% endif %}
|
||||
{% if not logged_in %}
|
||||
{% if config.registration %}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
{% block head %}
|
||||
<style type="text/css">
|
||||
section:not(first-child) {
|
||||
margin-top: 15px;
|
||||
}
|
||||
section:last-child {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
td:last-child, th:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
form {
|
||||
margin-top: 30px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.bigger {
|
||||
font-size: medium;
|
||||
}
|
||||
h4 {
|
||||
font-weight: 400;
|
||||
}
|
||||
th {
|
||||
font-weight: normal;
|
||||
}
|
||||
td, h5 {
|
||||
font-weight: 300;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2>{{ team.name }}</h2>
|
||||
|
||||
<h4>Team information</h4>
|
||||
<p>Your score is currently {{ team_score }}. <a href="{{ url_for('challenges') }}">Go solve more challenges!</a></p>
|
||||
<p>Your team email is <code>{{ team.email }}</code>, and you are affiliated with
|
||||
{{ team.affiliation }}.</p>
|
||||
<p>Your team is currently marked {{ "eligible" if team.eligible else "ineligible" }}.</p>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Edit information</h4>
|
||||
<form method="POST">
|
||||
<div class="input-field">
|
||||
<label for="team-name">Team Name</label>
|
||||
<input required maxlength="50" id="team-name" name="team_name" type="text" value="{{ team.name }}" />
|
||||
</div>
|
||||
<div class="input-field">
|
||||
<label for="affiliation">Affiliation</label>
|
||||
<input required maxlength="100" id="affiliation" name="affiliation" type="text" value="{{ team.affiliation }}" />
|
||||
</div>
|
||||
<input name="_csrf_token" type="hidden" value="{{ csrf_token() }}" />
|
||||
<br /><br />
|
||||
<button class="btn waves-effect waves-light" type="submit">Update team</button>
|
||||
</form>
|
||||
</section>
|
||||
<section>
|
||||
<h4>Score calculation</h4>
|
||||
{% if team_solves.count() %}
|
||||
<h5>Solved problems</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Category</th><th>Time</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for solve in team_solves %}
|
||||
<tr>
|
||||
<td>{{ solve.challenge.name }}</td>
|
||||
<td>{{ solve.challenge.category }}</td>
|
||||
<td><abbr class="time" title="{{ solve.time }}">{{ solve.time }}</abbr></td>
|
||||
<td>{{ solve.challenge.points }}</td>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No problems have been solved.</p>
|
||||
{% endif %}
|
||||
{% if team_adjustments.count() %}
|
||||
<h5>Score adjustments</h5>
|
||||
<div class="row">
|
||||
<div class="col s10 offset-s1">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Reason</th><th>Value</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for adj in team_adjustments %}
|
||||
<tr>
|
||||
<td>{{ adj.reason }}</td>
|
||||
<td>{{ adj.value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>No score adjustments have been made.</p>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue