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 %} +
+ +{% 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 @@ + + +
    +
    + Victories +
    +
    + + {% if model.victories %} + + + + + + + + + + + {% for entry in model.victories %} + + + + + + {% endfor %} + +
    TimeEnemyPlayer
    {{ entry.time|format_time }}{{ entry.enemy.name }}{{ entry.player.name }}
    + + {% 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}") + + +