diff --git a/estusshots/choices.py b/estusshots/choices.py
index b6ff8f3..bec7dc0 100644
--- a/estusshots/choices.py
+++ b/estusshots/choices.py
@@ -1,16 +1,16 @@
-from estusshots import orm, models, db
-from estusshots.orm import Season
+from estusshots.orm import new_session, EventType, Season, Player, Drink, Enemy
def event_choices():
- return [(member.value, member.name) for member in models.EventType]
+ return [(member.value, member.name) for member in EventType]
def season_choices():
- """ Query the database for available seasons.
+ """
+ Query the database for available seasons.
This returns a list of tuples with the season ID and a display string.
"""
- db = orm.new_session()
+ db = new_session()
seasons = db.query(Season).order_by(Season.code).all()
choices = [(s.id, f"{s.code}: {s.game}") for s in seasons]
choices.insert(0, (-1, "No Season"))
@@ -21,8 +21,8 @@ def player_choice():
"""
Query database for a list of available players to bind them to a select box
"""
- sql, args = db.load_players()
- players = db.query_db(sql, args, cls=models.Player)
+ db = new_session()
+ players = sorted(db.query(Player).all(), key=lambda x: x.name)
return [(p.id, p.name) for p in players]
@@ -30,8 +30,8 @@ def drink_choice():
"""
Query database for a list of all available drinks to select from
"""
- sql, args = db.load_drinks()
- drinks = db.query_db(sql, args, cls=models.Drink)
+ db = new_session()
+ drinks = db.query(Drink).order_by(Drink.name).all()
choices = [(d.id, d.name) for d in drinks]
choices.insert(0, (-1, "None"))
return choices
@@ -41,9 +41,13 @@ def enemy_choice_for_season(season_id: int):
"""
Query database for all available enemies in this season
"""
- sql, args = db.load_enemies_for_season(season_id)
- enemies = db.query_db(sql, args, cls=models.Enemy)
- return [(e.id, e.name) for e in enemies]
+ db = new_session()
+ season: Season = db.query(Season).get(season_id)
+ global_enemies = db.query(Enemy).filter(Enemy.season_id == -1).all()
+ if not season and not global_enemies:
+ return []
+ combined = global_enemies + season.enemies
+ return [(e.id, e.name) for e in combined]
class IterableBase:
diff --git a/estusshots/orm.py b/estusshots/orm.py
index 630053e..eaa4158 100644
--- a/estusshots/orm.py
+++ b/estusshots/orm.py
@@ -1,4 +1,6 @@
import enum
+from typing import Iterable, List
+
import sqlalchemy
from sqlalchemy import create_engine, ForeignKey, Table
from sqlalchemy.ext.declarative import declarative_base
@@ -10,6 +12,13 @@ from estusshots import util, forms
engine = create_engine('sqlite:///../databases/test.db')
Base = declarative_base()
+player_episode = Table(
+ 'player_episode',
+ Base.metadata,
+ Column('player_id', ForeignKey('players.id'), primary_key=True),
+ Column('episode_id', ForeignKey('episodes.id'), primary_key=True)
+)
+
class EventType(enum.Enum):
Pause = 0
@@ -27,6 +36,7 @@ class Player(Base):
anon = Column(Boolean, default=False)
events = relationship("Event", back_populates="player")
+ episodes = relationship("Episode", secondary=player_episode, back_populates="players")
@property
def name(self) -> str:
@@ -61,8 +71,8 @@ class Season(Base):
start = Column(Date)
end = Column(Date)
- episodes = relationship("Episode", back_populates="season")
- enemies = relationship("Enemy", back_populates="season")
+ episodes: Iterable["Episode"] = relationship("Episode", back_populates="season")
+ enemies: Iterable["Enemy"] = relationship("Enemy", back_populates="season")
def populate_from_form(self, form: "forms.SeasonForm"):
self.code = str(form.code.data)
@@ -82,7 +92,7 @@ class Enemy(Base):
season_id = Column(Integer, ForeignKey('seasons.id'))
season = relationship("Season", back_populates="enemies")
- events = relationship('Event', back_populates="enemy")
+ events: Iterable["Event"] = relationship('Event', back_populates="enemy")
def populate_from_form(self, form: "forms.EnemyForm"):
self.name = str(form.name.data)
@@ -103,18 +113,26 @@ class Episode(Base):
season_id = Column(Integer, ForeignKey('seasons.id'))
season = relationship("Season", back_populates="episodes")
- events = relationship('Event', back_populates='episode')
+ events: List["Event"] = relationship('Event', back_populates='episode')
+ players = relationship("Player", secondary=player_episode, back_populates="episodes")
@property
def playtime(self):
- return util.timedelta(self.start, self.end)
+ return util.compute_timedelta(self.start, self.end)
+
+ def populate_from_form(self, form: "forms.EpisodeForm"):
+ self.code = str(form.code.data)
+ self.title = str(form.title.data)
+ self.date = form.date.data
+ self.start = form.start.data
+ self.end = form.end.data
class Event(Base):
__tablename__ = 'events'
id = Column(Integer, primary_key=True)
- type = Column(Enum(EventType))
+ type: EventType = Column(Enum(EventType))
time = Column(Time)
comment = Column(String)
@@ -127,7 +145,15 @@ class Event(Base):
enemy_id = Column(Integer, ForeignKey('enemies.id'))
enemy = relationship('Enemy', back_populates='events')
- penalties = relationship('Penalty', back_populates='event')
+ penalties: List["Penalty"] = relationship('Penalty', back_populates='event')
+
+ def populate_from_form(self, form: "forms.EventForm"):
+ self.episode_id = int(form.episode_id.data)
+ self.type = EventType(form.event_type.data)
+ self.time = form.time.data
+ self.comment = str(form.comment.data) if form.comment.data else None
+ self.player_id = int(form.player.data) if form.player.data else None
+ self.enemy_id = int(form.enemy.data) if form.enemy.data else None
class Penalty(Base):
diff --git a/estusshots/templates/episode_details.html b/estusshots/templates/episode_details.html
index b6c7b38..9b9b99a 100644
--- a/estusshots/templates/episode_details.html
+++ b/estusshots/templates/episode_details.html
@@ -34,23 +34,23 @@
Start:
- {{ model.episode.start|format_time }}
+ {{ model.episode.start|format_time or "Not started yet" }}
End:
- {{ model.episode.end|format_time }}
+ {{ model.episode.end|format_time or "Not ended yet"}}
Play Time:
- {{ model.episode.playtime }}
+ {{ model.episode.playtime or 0 }} Hours
Enemies Defeated:
- {{ model.events.victory_count }}
+ {{ 0 }}
Deaths:
- {{ model.events.defeat_count }}
+ {{ model.deaths|length or 0}}
@@ -88,46 +88,48 @@
+
- {% if model.events %}
+
+
+
+ {% if model.deaths %}
+
- | Time |
- Type |
- Player |
- Enemy |
+ Time |
+ Enemy |
+ Player |
- {% for entry in model.events.entries %}
+ {% for entry in model.deaths %}
- | {{ entry.time }} |
- {{ entry.type.name }} |
- {{ entry.type.player_name }} |
- {{ entry.type.enemy_name }} |
+ {{ entry.time|format_time }} |
+ {{ entry.enemy.name }} |
+ {{ entry.player.name }} |
{% endfor %}
+
+ {% else %}
+
+
Nothing did happen yet
+
+ {% endif %}
- {% else %}
-
-
-
-
Nothing did happen yet
-
-
- {% endif %}
+
+
+
{% endblock %}
diff --git a/estusshots/util.py b/estusshots/util.py
index 6ab2e3f..91ef43f 100644
--- a/estusshots/util.py
+++ b/estusshots/util.py
@@ -37,17 +37,19 @@ def timedelta_to_str(data: timedelta) -> str:
)
-def timedelta(start: time, end: time) -> float:
- startDateTime = datetime.combine(date.today(), start)
+def compute_timedelta(start: time, end: time) -> float:
+ if not start or not end:
+ return 0
+ s = datetime.combine(date.today(), start)
# Check if the the end is still on the same day
if start.hour > end.hour:
base = date.today() + timedelta(days=1)
else:
base = date.today()
- endDateTime = datetime.combine(base, end)
- difference = startDateTime - endDateTime
+ e = datetime.combine(base, end)
+ difference = s - e
difference_hours = difference.total_seconds() / 3600
- return difference_hours
+ return abs(difference_hours)
def combine_datetime(date: datetime.date, time: datetime.time):
diff --git a/estusshots/views/enemies.py b/estusshots/views/enemies.py
index ee0479d..99eafc2 100644
--- a/estusshots/views/enemies.py
+++ b/estusshots/views/enemies.py
@@ -1,4 +1,4 @@
-from flask import render_template, request, redirect
+from flask import render_template, request, redirect, url_for
from estusshots import app
from estusshots import forms, models, orm
@@ -18,11 +18,12 @@ def enemy_list():
@app.route("/enemy/new", methods=["GET"])
@authorize
-def enemy_new(preselect_season=None):
+def enemy_new():
form = forms.EnemyForm()
- if preselect_season:
- form.season_id.default = preselect_season
+ if "preselect" in request.args:
+ form.season_id.process_data(request.args['preselect'])
+ form.is_boss.data = True
model = models.GenericFormModel(
page_title="Enemies",
@@ -64,9 +65,8 @@ def enemy_edit(enemy_id: int):
enemy.populate_from_form(form)
db.commit()
if form.submit_continue_button.data:
- form.name.data = None
- return enemy_new(preselect_season=enemy.season_id)
- return redirect("/enemy")
+ return redirect(url_for("enemy_new", preselect=form.season_id.data))
+ return redirect(url_for("enemy_list"))
model.form_title = "Incorrect Data"
return render_template("generic_form.html", model=model, form=form)
diff --git a/estusshots/views/episodes.py b/estusshots/views/episodes.py
index bb6074b..31171e1 100644
--- a/estusshots/views/episodes.py
+++ b/estusshots/views/episodes.py
@@ -1,45 +1,24 @@
-from typing import List
-
-from flask import render_template, request, redirect
+from flask import render_template, request, redirect, url_for
from estusshots import app
-from estusshots import forms, models, db
+from estusshots import forms, models, orm
from estusshots.util import authorize
+from estusshots.orm import Season, Episode, Player, Event
@app.route("/season//episode/")
@authorize
def episode_detail(season_id: int, episode_id: int):
- sql, args = db.load_season(season_id)
- season = db.query_db(sql, args, one=True, cls=models.Season)
- sql, args = db.load_episode(episode_id)
- episode = db.query_db(sql, args, one=True, cls=models.Episode)
- sql, args = db.load_episode_players(episode_id)
- ep_players = db.query_db(sql, args, cls=models.Player)
- sql, args = db.load_events(episode_id)
- ep_events: List[models.Event] = db.query_db(sql, args, cls=models.Event)
- sql, args = db.load_enemies(season_id)
- enemies = db.query_db(sql, args, cls=models.Enemy)
-
- deaths = [ev for ev in ep_events if ev.type == models.EventType.Death]
- entries = []
- for death in deaths:
- entries.append({
- "time": death.time.time(),
- "type": death.type,
- "player_name": [p.name for p in ep_players if p.id == death.player_id],
- "enemy_name": [e.name for e in enemies if e.id == death.enemy_id]
- })
- events = None
- if ep_events:
- events = {"entries": death, "victory_count": 0, "defeat_count": 0}
+ 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]
model = {
- "title": f"{season.code}{episode.code}",
+ "title": f"{episode.season.code}{episode.code}",
"episode": episode,
- "season": season,
- "players": ep_players,
- "events": events,
+ "season": episode.season,
+ "players": episode.players,
+ "deaths": sorted(deaths, key=lambda x: x.time),
}
return render_template("episode_details.html", model=model)
@@ -48,12 +27,9 @@ def episode_detail(season_id: int, episode_id: int):
@app.route("/season//episode", methods=["GET"])
@authorize
def episode_list(season_id: int):
- sql, args = db.load_season(season_id)
- season = db.query_db(sql, args, one=True, cls=models.Season)
- sql, args = db.load_episodes(season_id)
- episodes = db.query_db(sql, args, cls=models.Episode)
-
- model = {"season_id": season_id, "season_code": season.code}
+ db = orm.new_session()
+ season = db.query(Season).filter(Season.id == season_id).first()
+ model = {"season_id": season.id, "season_code": season.code}
return render_template("episode_list.html", model=model)
@@ -65,7 +41,6 @@ def episode_new(season_id: int):
form_title="Create New Episode",
post_url=f"/season/{season_id}/episode/null/edit",
)
-
form = forms.EpisodeForm(request.form)
form.season_id.data = season_id
return render_template("generic_form.html", model=model, form=form)
@@ -79,15 +54,10 @@ def episode_edit(season_id: int, episode_id: int):
form_title="Edit Episode",
post_url=f"/season/{season_id}/episode/{episode_id}/edit",
)
-
+ form = forms.EpisodeForm()
+ db = orm.new_session()
+ episode: Episode = db.query(Episode).get(episode_id)
if request.method == "GET":
- sql, args = db.load_episode(episode_id)
- episode: models.Episode = db.query_db(sql, args, one=True, cls=models.Episode)
-
- sql, args = db.load_episode_players(episode_id)
- ep_players = db.query_db(sql, args, cls=models.Player)
-
- form = forms.EpisodeForm()
form.season_id.data = episode.season_id
form.episode_id.data = episode.id
form.code.data = episode.code
@@ -95,43 +65,24 @@ def episode_edit(season_id: int, episode_id: int):
form.start.data = episode.start
form.end.data = episode.end
form.title.data = episode.title
- form.players.data = [p.id for p in ep_players]
-
+ form.players.data = [p.id for p in episode.players]
model.form_title = f"Edit Episode '{episode.code}: {episode.title}'"
return render_template("generic_form.html", model=model, form=form)
else:
- form = forms.EpisodeForm()
-
if not form.validate_on_submit():
model.errors = form.errors
return render_template("generic_form.html", model=model, form=form)
-
- errors = False
- episode = models.Episode.from_form(form)
- sql, args = db.save_episode(episode)
-
- last_key = db.update_db(sql, args, return_key=True)
-
- episode_id = episode.id if episode.id else last_key
-
- form_ids = form.players.data
-
- sql, args = db.load_episode_players(episode_id)
- ep_players = db.query_db(sql, args, cls=models.Player)
- pids = [p.id for p in ep_players]
-
- new_ids = [pid for pid in form_ids if pid not in pids]
- removed_ids = [pid for pid in pids if pid not in form_ids]
-
- if removed_ids:
- sql, args = db.remove_episode_player(episode_id, removed_ids)
- errors = db.update_db(sql, args)
-
- if new_ids:
- sql, args = db.save_episode_players(episode_id, new_ids)
- errors = db.update_db(sql, args)
-
+ if not episode:
+ episode = Episode()
+ db.add(episode)
+ season: Season = db.query(Season).get(season_id)
+ episode.populate_from_form(form)
+ episode.season = season
+ player_ids = list(form.players.data)
+ players = db.query(Player).filter(Player.id.in_(player_ids)).all()
+ episode.players = players
+ errors = db.commit()
if errors:
model.errors = {"Error saving episode": [errors]}
return render_template("generic_form.html", model=model, form=form)
- return redirect(url_for("season_overview", season_id=season_id))
+ return redirect(url_for("episode_detail", season_id=season_id, episode_id=episode_id))
diff --git a/estusshots/views/events.py b/estusshots/views/events.py
index e2bac8e..c7927e4 100644
--- a/estusshots/views/events.py
+++ b/estusshots/views/events.py
@@ -2,9 +2,10 @@ from collections import namedtuple
from flask import render_template, request, redirect
-from estusshots import app
-from estusshots import forms, models, db, choices
+from estusshots import app, orm
+from estusshots import forms, models, choices
from estusshots.util import authorize
+from estusshots.orm import new_session, EventType, Event, Episode, Enemy, Penalty
@app.route("/season//episode//event/new", methods=["GET"])
@@ -15,19 +16,16 @@ def event_new(s_id: int, ep_id: int):
"form_title": "Create New Event",
"post_url": f"/season/{s_id}/episode/{ep_id}/event/null/edit",
}
- sql, args = db.load_episode(ep_id)
- episode: models.Episode = db.query_db(sql, args, one=True, cls=models.Episode)
-
- sql, args = db.load_episode_players(ep_id)
- ep_players = db.query_db(sql, args, cls=models.Player)
+ db = new_session()
+ episode: Episode = db.query(Episode).get(ep_id)
form = forms.EventForm()
form.episode_id.data = ep_id
form.enemy.choices = choices.enemy_choice_for_season(s_id)
- form.event_type.data = 1
+ form.event_type.data = EventType.Death.value
Penalty = namedtuple("Penalty", ["penalty_id", "player_id", "player", "drink"])
- for player in ep_players:
+ for player in episode.players:
form.penalties.append_entry(Penalty(None, player.id, player.name, 1))
return render_template("event_editor.html", model=model, form=form)
@@ -41,16 +39,43 @@ def event_edit(s_id: int, ep_id: int, ev_id: int):
"form_title": "Edit Event",
"post_url": f"/season/{s_id}/episode/{ep_id}/event/{ev_id}/edit",
}
+ db = new_session()
+ event: Event = db.query(Event).get(ev_id)
+ form = forms.EventForm()
+ form.enemy.choices = choices.enemy_choice_for_season(s_id)
+
if request.method == "GET":
+ form.episode_id.process_data(event.episode_id)
+ form.event_type.process_data(event.type.value)
+ form.enemy.process_data(event.enemy_id)
+ form.player.process_data(event.player_id)
+ form.time.process_data(event.time)
+ form.comment.process_data(event.comment)
+ Penalty = namedtuple("Penalty", ["penalty_id", "player_id", "player", "drink"])
+ for penalty in event.penalties:
+ form.penalties.append_entry(Penalty(penalty.id, penalty.player_id, penalty.player.name, penalty.drink_id))
return render_template("event_editor.html", model=model)
else:
- form = forms.EventForm()
- form.enemy.choices = choices.enemy_choice_for_season(s_id)
if not form.validate_on_submit():
model["errors"] = form.errors
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 = models.Event.from_form(form)
- sql, args = db.save_event(event)
- errors = db.update_db(sql, args)
+ event.populate_from_form(form)
+ db.commit()
return redirect(f"/season/{s_id}/episode/{ep_id}")