Advanced event types
This commit is contained in:
@@ -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]
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="content">
|
||||
{% 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')
|
||||
]-%}
|
||||
<div class="container">
|
||||
<div class="container nav-container">
|
||||
<nav class="navbar navbar-expand-lg bg-dark navbar-dark">
|
||||
|
||||
<a class="navbar-brand">
|
||||
@@ -58,11 +59,12 @@
|
||||
{% block page %}
|
||||
|
||||
{% endblock %}
|
||||
<div>
|
||||
{% block footer %}
|
||||
© Sauf Software by D⁵: <a href="#">Durstiger Donnerstag Digital Drinking Divison</a>.
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="container footer">© Sauf Software by D⁵: <a href="#">Durstiger Donnerstag Digital Drinking Divison</a>.</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<th scope="col" class="col-sm-auto text-center">Name</th>
|
||||
<th scope="col" class="col-sm-auto text-center">Season</th>
|
||||
<th scope="col" class="col-sm-auto text-center">Boss Enemy</th>
|
||||
<th scope="col" class="col-sm-auto text-center">Defeated</th>
|
||||
|
||||
{% if g.is_editor %}
|
||||
<th scope="col" class="col-sm-auto text-center">Editor</th>
|
||||
@@ -32,7 +33,12 @@
|
||||
<td class="col-sm-auto text-center">{{ enemy.season.game }}</td>
|
||||
<td class="col-sm-auto text-center">
|
||||
{% if enemy.boss %}
|
||||
<span class="fas fa-check"></span>
|
||||
<i class="fas fa-check"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-sm-auto text-center">
|
||||
{% if enemy.is_defeated %}
|
||||
<i class="fas fa-check"></i>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-4">
|
||||
|
||||
<!--region Info Card-->
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="font-weight-bold">Enemies Defeated:</span>
|
||||
{{ 0 }}
|
||||
{{ model.victories|length or 0 }}
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<span class="font-weight-bold">Deaths:</span>
|
||||
@@ -59,8 +59,6 @@
|
||||
|
||||
<!--endregion-->
|
||||
|
||||
<hr>
|
||||
|
||||
<!--region Player Card-->
|
||||
|
||||
<div class="card">
|
||||
@@ -72,12 +70,14 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-sm-auto text-center">Name</th>
|
||||
<th scope="col" class="col-sm-auto text-center">Playtime</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for player in model.players %}
|
||||
<tr>
|
||||
<td class="col-sm-auto text-center">{{ player.name }}</td>
|
||||
<td class="col-sm-auto text-center">{{ "0m" }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-8">
|
||||
|
||||
<!--region Deaths Card-->
|
||||
|
||||
@@ -130,6 +130,45 @@
|
||||
|
||||
<!--endregion-->
|
||||
|
||||
<!--region Victories Card-->
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header text-center">
|
||||
Victories
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
{% if model.victories %}
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="col-xs-auto text-center">Time</th>
|
||||
<th scope="col" class="col-xs text-center">Enemy</th>
|
||||
<th scope="col" class="col-xs text-center">Player</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in model.victories %}
|
||||
<tr>
|
||||
<td class="col-xs-auto text-center">{{ entry.time|format_time }}</td>
|
||||
<td class="col-xs text-center">{{ entry.enemy.name }}</td>
|
||||
<td class="col-xs text-center">{{ entry.player.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% else %}
|
||||
|
||||
<div class="alert alert-info">No Enemies were defeated</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--endregion-->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -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/<season_id>/episode/<episode_id>")
|
||||
@@ -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)
|
||||
|
||||
@@ -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/<s_id>/episode/<ep_id>/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}")
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user