separated team and user dashboards

master
James Sigurðarson 2016-07-12 12:03:04 +00:00
parent b99095338c
commit 675e022e4b
5 changed files with 228 additions and 52 deletions

125
app.py
View File

@ -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():

View File

@ -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')

View File

@ -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 %}

105
templates/team.html Normal file
View File

@ -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 %}