diff --git a/estusshots/choices.py b/estusshots/choices.py
index bec7dc0..5fe954b 100644
--- a/estusshots/choices.py
+++ b/estusshots/choices.py
@@ -43,10 +43,11 @@ def enemy_choice_for_season(season_id: int):
"""
db = new_session()
season: Season = db.query(Season).get(season_id)
+ season_enemies = [enemy for enemy in season.enemies if not enemy.is_defeated]
global_enemies = db.query(Enemy).filter(Enemy.season_id == -1).all()
if not season and not global_enemies:
return []
- combined = global_enemies + season.enemies
+ combined = global_enemies + season_enemies
return [(e.id, e.name) for e in combined]
diff --git a/estusshots/forms.py b/estusshots/forms.py
index 3730453..d2bdec8 100644
--- a/estusshots/forms.py
+++ b/estusshots/forms.py
@@ -79,9 +79,7 @@ class EventForm(FlaskForm):
event_id = HiddenField("Event ID")
episode_id = HiddenField("Episode ID")
event_type = SelectField(
- "Type", choices=choices.EventChoiceIterable(), coerce=int,
- validators=[DataRequired()]
- )
+ "Type", choices=choices.EventChoiceIterable(), coerce=int)
time = TimeField("Time", format="%H:%M", validators=[DataRequired()])
player = SelectField("Player", choices=choices.PlayerChoiceIterable(), coerce=int)
enemy = SelectField("Enemy", coerce=int)
diff --git a/estusshots/orm.py b/estusshots/orm.py
index eaa4158..dae29f3 100644
--- a/estusshots/orm.py
+++ b/estusshots/orm.py
@@ -94,6 +94,10 @@ class Enemy(Base):
events: Iterable["Event"] = relationship('Event', back_populates="enemy")
+ @property
+ def is_defeated(self):
+ return any([e for e in self.events if e.type == EventType.Victory])
+
def populate_from_form(self, form: "forms.EnemyForm"):
self.name = str(form.name.data)
self.boss = bool(form.is_boss.data)
diff --git a/estusshots/static/custom.css b/estusshots/static/custom.css
index 33a68be..c61b3c4 100644
--- a/estusshots/static/custom.css
+++ b/estusshots/static/custom.css
@@ -51,8 +51,19 @@ a:hover {
font-family: 'DarkSouls', serif;
}
+.nav-container{
+ margin-bottom: 10px;
+}
+
.btn-toolbar{
- padding: 5px 0;
+ margin: 10px 0;
+}
+.btn-toolbar .btn {
+ margin-right: 10px;
+}
+
+.card{
+ margin-bottom: 25px;
}
.vertical-center {
@@ -64,6 +75,11 @@ a:hover {
}
/* Theme customizations */
+.list-group-item {
+ display: flex;
+ justify-content: space-between;
+}
+
.list-group-item:hover {
color: inherit;
background-color: rgb(34, 34, 34);
@@ -72,3 +88,19 @@ a:hover {
h1 {
font-size: 2.5rem;
}
+html {
+ height: 100%;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+.content{
+ flex: 1 0 auto;
+}
+
+.footer {
+ flex-shrink: 0;
+}
diff --git a/estusshots/templates/base.html b/estusshots/templates/base.html
index 044edae..676d7b6 100644
--- a/estusshots/templates/base.html
+++ b/estusshots/templates/base.html
@@ -14,7 +14,8 @@
{% endblock %}
-
+{% block body %}
+
{% block navbar %}
{% set nav_bar = [
('/season', 'seasons', 'Seasons', 'fas fa-calendar'),
@@ -22,7 +23,7 @@
('/enemy', 'enemies', 'Enemies', 'fas fa-skull-crossbones'),
('/drink', 'drinks', 'Drinks', 'fas fa-beer')
]-%}
-
+
+{% endblock %}
+
+
+
diff --git a/estusshots/templates/enemies.html b/estusshots/templates/enemies.html
index 0aff03e..f01b69c 100644
--- a/estusshots/templates/enemies.html
+++ b/estusshots/templates/enemies.html
@@ -19,6 +19,7 @@
Name |
Season |
Boss Enemy |
+
Defeated |
{% if g.is_editor %}
Editor |
@@ -32,7 +33,12 @@
{{ enemy.season.game }} |
{% if enemy.boss %}
-
+
+ {% endif %}
+ |
+
+ {% if enemy.is_defeated %}
+
{% endif %}
|
diff --git a/estusshots/templates/episode_details.html b/estusshots/templates/episode_details.html
index 9b9b99a..c445c11 100644
--- a/estusshots/templates/episode_details.html
+++ b/estusshots/templates/episode_details.html
@@ -17,7 +17,7 @@
{% endif %}
-
+
@@ -46,7 +46,7 @@
Enemies Defeated:
- {{ 0 }}
+ {{ model.victories|length or 0 }}
Deaths:
@@ -59,8 +59,6 @@
-
-
@@ -72,12 +70,14 @@
| Name |
+ Playtime |
{% for player in model.players %}
| {{ player.name }} |
+ {{ "0m" }} |
{% endfor %}
@@ -89,7 +89,7 @@
-
+
@@ -130,6 +130,45 @@
+
+
+
+
+
+
+ {% if model.victories %}
+
+
+
+
+ | Time |
+ Enemy |
+ Player |
+
+
+
+ {% for entry in model.victories %}
+
+ | {{ entry.time|format_time }} |
+ {{ entry.enemy.name }} |
+ {{ entry.player.name }} |
+
+ {% endfor %}
+
+
+
+ {% else %}
+
+
No Enemies were defeated
+
+ {% endif %}
+
+
+
+
+
{% endblock %}
diff --git a/estusshots/views/episodes.py b/estusshots/views/episodes.py
index 31171e1..9568aef 100644
--- a/estusshots/views/episodes.py
+++ b/estusshots/views/episodes.py
@@ -3,7 +3,7 @@ from flask import render_template, request, redirect, url_for
from estusshots import app
from estusshots import forms, models, orm
from estusshots.util import authorize
-from estusshots.orm import Season, Episode, Player, Event
+from estusshots.orm import Season, Episode, Player
@app.route("/season//episode/")
@@ -12,13 +12,14 @@ def episode_detail(season_id: int, episode_id: int):
db = orm.new_session()
episode: Episode = db.query(Episode).get(episode_id)
deaths = [event for event in episode.events if event.type == orm.EventType.Death]
-
+ victories = [event for event in episode.events if event.type == orm.EventType.Victory]
model = {
"title": f"{episode.season.code}{episode.code}",
"episode": episode,
"season": episode.season,
"players": episode.players,
"deaths": sorted(deaths, key=lambda x: x.time),
+ "victories": sorted(victories, key=lambda x: x.time)
}
return render_template("episode_details.html", model=model)
diff --git a/estusshots/views/events.py b/estusshots/views/events.py
index c7927e4..52fb283 100644
--- a/estusshots/views/events.py
+++ b/estusshots/views/events.py
@@ -3,11 +3,49 @@ from collections import namedtuple
from flask import render_template, request, redirect
from estusshots import app, orm
-from estusshots import forms, models, choices
+from estusshots import forms, choices
from estusshots.util import authorize
from estusshots.orm import new_session, EventType, Event, Episode, Enemy, Penalty
+def death_event(event, db, form):
+ """Add penalties to the event if it is a death event"""
+ if not event.id:
+ for entry in form.penalties:
+ penalty = orm.Penalty()
+ penalty.player_id = entry.player_id.data
+ penalty.drink_id = entry.drink.data
+ db.add(penalty)
+ event.penalties.append(penalty)
+ else:
+ for event in event.penalties:
+ penalty = next((p for p in form.penalties.data if p["penalty_id"] == event.id), None)
+ if not penalty:
+ continue
+ penalty.player_id = form.player_id.data
+ penalty.drink_id = form.drink.data
+
+
+def victory_event(event, db, form):
+ """No need for additional actions yet"""
+ pass
+
+
+def pause_event(event, db, form):
+ """Pause events have neither penalties or enemies"""
+ event.enemy_id = None
+ event.enemy = None
+ event.player_id = None
+ event.player = None
+
+
+event_type_map = {
+ EventType.Death: death_event,
+ EventType.Victory: victory_event,
+ EventType.Pause: pause_event
+}
+
+
@app.route("/season//episode//event/new", methods=["GET"])
@authorize
def event_new(s_id: int, ep_id: int):
@@ -61,21 +99,13 @@ def event_edit(s_id: int, ep_id: int, ev_id: int):
return render_template("event_editor.html", model=model, form=form)
if not event:
event = Event()
- for entry in form.penalties:
- penalty = orm.Penalty()
- penalty.player_id = entry.player_id.data
- penalty.drink_id = entry.drink.data
- db.add(penalty)
- event.penalties.append(penalty)
db.add(event)
- else:
- for event in event.penalties:
- penalty = next((p for p in form.penalties.data if p["penalty_id"] == event.id), None)
- if not penalty:
- continue
- penalty.player_id = form.player_id.data
- penalty.drink_id = form.drink.data
-
event.populate_from_form(form)
+ # Different event types fill different fields
+ func = event_type_map[event.type]
+ func(event, db, form)
db.commit()
return redirect(f"/season/{s_id}/episode/{ep_id}")
+
+
+