Additional refactoring
This commit is contained in:
282
app.py
282
app.py
@@ -3,8 +3,10 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import Flask, g, render_template, request, redirect, session
|
from flask import Flask, g, render_template, request, redirect, session
|
||||||
|
from flask_bootstrap import Bootstrap
|
||||||
|
|
||||||
import db
|
import db
|
||||||
|
import forms
|
||||||
import models
|
import models
|
||||||
import const
|
import const
|
||||||
|
|
||||||
@@ -14,7 +16,14 @@ logging.basicConfig(filename=const.LOG_PATH, level=logging.DEBUG)
|
|||||||
logging.info(f'Starting in working dir: {os.getcwd()}')
|
logging.info(f'Starting in working dir: {os.getcwd()}')
|
||||||
logging.info(f'App base path: {const.BASE_PATH}')
|
logging.info(f'App base path: {const.BASE_PATH}')
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
def create_app():
|
||||||
|
app = Flask(__name__)
|
||||||
|
Bootstrap(app)
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
|
|
||||||
app.secret_key = 'THIS IS A TEST KEY'
|
app.secret_key = 'THIS IS A TEST KEY'
|
||||||
|
|
||||||
@@ -80,7 +89,7 @@ def landing():
|
|||||||
|
|
||||||
@app.route('/seasons')
|
@app.route('/seasons')
|
||||||
@authorize
|
@authorize
|
||||||
def seasons():
|
def season_list():
|
||||||
sql, args = db.load_season()
|
sql, args = db.load_season()
|
||||||
results = db. query_db(sql, args, cls=models.Season)
|
results = db. query_db(sql, args, cls=models.Season)
|
||||||
model = {
|
model = {
|
||||||
@@ -96,23 +105,23 @@ def seasons():
|
|||||||
return render_template('seasons.html', model=model)
|
return render_template('seasons.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/newseason', methods=['GET'])
|
@app.route('/seasons/new', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def new_season():
|
def season_new():
|
||||||
return render_template('editseason.html', model={})
|
return render_template('editseason.html', model={})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/seasons/edit/<id>')
|
@app.route('/seasons/<id>/edit')
|
||||||
@authorize
|
@authorize
|
||||||
def edit_season(id: int):
|
def season_edit(id: int):
|
||||||
sql, args = db.load_season(id)
|
sql, args = db.load_season(id)
|
||||||
loaded = db.query_db(sql, args, one=True, cls=models.Season)
|
loaded = db.query_db(sql, args, one=True, cls=models.Season)
|
||||||
return render_template('editseason.html', model=loaded)
|
return render_template('editseason.html', model=loaded)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/saveseason', methods=['POST'])
|
@app.route('/seasons/save', methods=['POST'])
|
||||||
@authorize
|
@authorize
|
||||||
def save_season():
|
def season_save():
|
||||||
try:
|
try:
|
||||||
season = models.Season.from_form(request.form)
|
season = models.Season.from_form(request.form)
|
||||||
except AttributeError as err:
|
except AttributeError as err:
|
||||||
@@ -123,10 +132,10 @@ def save_season():
|
|||||||
return redirect('/seasons')
|
return redirect('/seasons')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/seasons/<id>', methods=['GET'])
|
@app.route('/seasons/<season_id>', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def season_overview(id: int):
|
def season_overview(season_id: int):
|
||||||
sql, args = db.load_season(id)
|
sql, args = db.load_season(season_id)
|
||||||
db_season = db.query_db(sql, args, one=True, cls=models.Season)
|
db_season = db.query_db(sql, args, one=True, cls=models.Season)
|
||||||
infos = {
|
infos = {
|
||||||
'Number': db_season.code,
|
'Number': db_season.code,
|
||||||
@@ -141,54 +150,109 @@ def season_overview(id: int):
|
|||||||
return render_template('seasonoverview.html', model=model)
|
return render_template('seasonoverview.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/newplayer', methods=['GET'])
|
@app.route('/seasons/<season_id>/episodes', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def new_player():
|
def episode_list(season_id: int):
|
||||||
return render_template('editplayer.html', model={})
|
sql, args = db.load_season(season_id)
|
||||||
|
db_season = db.query_db(sql, args, one=True, cls=models.Season)
|
||||||
|
|
||||||
|
model = {
|
||||||
|
'season_id': season_id,
|
||||||
|
'season_code': db_season.code
|
||||||
|
}
|
||||||
|
return render_template('episodelist.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/saveplayer', methods=['POST'])
|
@app.route('/seasons/<season_id>/new', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def save_player():
|
def episode_new(season_id: int):
|
||||||
data = request.form
|
model = models.GenericFormModel(
|
||||||
player = models.Player(
|
page_title='New Episode',
|
||||||
id=data.get('id', None),
|
form_title='Create New Episode',
|
||||||
real_name=data['real_name'],
|
post_url='/episodes/save')
|
||||||
alias=data['alias'],
|
|
||||||
hex_id=data['hex_id'],
|
form = forms.EpisodeForm(request.form)
|
||||||
anon=data.get('anon', False))
|
form.season_id.data = season_id
|
||||||
res = db.save_player(player)
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
return redirect('/players')
|
|
||||||
|
|
||||||
|
@app.route('/episodes/save', methods=['POST'])
|
||||||
|
@authorize
|
||||||
|
def episode_save():
|
||||||
|
form = forms.EpisodeForm(request.form)
|
||||||
|
val = form.validate()
|
||||||
|
return render_template('editepisode.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/players/new')
|
||||||
|
@authorize
|
||||||
|
def player_new():
|
||||||
|
form = forms.PlayerForm()
|
||||||
|
model = models.GenericFormModel(
|
||||||
|
page_title='Players',
|
||||||
|
form_title='Create a new Player',
|
||||||
|
post_url='/players/edit/null'
|
||||||
|
)
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/players/edit/<player_id>', methods=['GET', 'POST'])
|
||||||
|
@authorize
|
||||||
|
def player_edit(player_id: int):
|
||||||
|
model = models.GenericFormModel(
|
||||||
|
page_title='Players',
|
||||||
|
form_title=f'Edit Player',
|
||||||
|
post_url=f'/players/edit/{player_id}'
|
||||||
|
)
|
||||||
|
# Edit Existing Player
|
||||||
|
if request.method == 'GET':
|
||||||
|
sql, args = db.load_players(player_id)
|
||||||
|
player = db.query_db(sql, args, one=True, cls=models.Player)
|
||||||
|
|
||||||
|
form = forms.PlayerForm()
|
||||||
|
form.player_id.data = player.id
|
||||||
|
form.anonymize.data = player.anon
|
||||||
|
form.real_name.data = player.real_name
|
||||||
|
form.alias.data = player.alias
|
||||||
|
form.hex_id.data = player.hex_id
|
||||||
|
|
||||||
|
model.form_title = f'Edit Player "{player.name}"'
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
# Save POSTed data
|
||||||
|
else:
|
||||||
|
form = forms.PlayerForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
player = models.Player.from_form(form)
|
||||||
|
res = db.save_player(player)
|
||||||
|
return redirect('/players')
|
||||||
|
|
||||||
|
model.form_title = 'Incorrect Data'
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/players')
|
@app.route('/players')
|
||||||
@authorize
|
@authorize
|
||||||
def players():
|
def player_list():
|
||||||
loaded = db.load_players()
|
sql, args = db.load_players()
|
||||||
|
players = db.query_db(sql, args, cls=models.Player)
|
||||||
model = {
|
model = {
|
||||||
'player_list': loaded,
|
'player_list': players,
|
||||||
'columns': [('id', 'ID'),
|
'columns': [('id', 'ID'),
|
||||||
('name', 'Player Name'),
|
('name', 'Player Name'),
|
||||||
('alias', 'Alias'),
|
('alias', 'Alias'),
|
||||||
('hex_id', 'Hex ID')],
|
('hex_id', 'Hex ID')]
|
||||||
'controls': [('edit', 'Edit')]
|
|
||||||
}
|
}
|
||||||
return render_template('players.html', model=model)
|
return render_template('players.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/players/<id>', methods=['GET'])
|
|
||||||
@authorize
|
|
||||||
def edit_player(id: int):
|
|
||||||
loaded = db.load_players(id)[0]
|
|
||||||
return render_template('editplayer.html', model=loaded)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/drinks')
|
@app.route('/drinks')
|
||||||
@authorize
|
@authorize
|
||||||
def drinks():
|
def drink_list():
|
||||||
loaded = db.load_drinks()
|
sql, args = db.load_drinks()
|
||||||
|
drinks = db.query_db(sql, args, cls=models.Drink)
|
||||||
model = {
|
model = {
|
||||||
'drinks': loaded,
|
'drinks': drinks,
|
||||||
'columns': [
|
'columns': [
|
||||||
('id', 'ID'),
|
('id', 'ID'),
|
||||||
('name', 'Drink Name'),
|
('name', 'Drink Name'),
|
||||||
@@ -199,71 +263,123 @@ def drinks():
|
|||||||
return render_template('drinks.html', model=model)
|
return render_template('drinks.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/drinks/<id>', methods=['GET'])
|
@app.route('/drinks/<drink_id>/edit', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def show_drink(id: int):
|
def drink_edit(drink_id: int):
|
||||||
loaded = db.load_drinks(id)[0]
|
sql, args = db.load_drinks(drink_id)
|
||||||
return render_template('editdrink.html', model=loaded)
|
drink = db.query_db(sql, args, one=True, cls=models.Drink)
|
||||||
|
|
||||||
|
form = forms.DrinkForm()
|
||||||
|
form.drink_id.data = drink.id
|
||||||
|
form.name.data = drink.name
|
||||||
|
form.vol.data = drink.vol
|
||||||
|
|
||||||
|
model = models.GenericFormModel(
|
||||||
|
page_title='Edit Drink',
|
||||||
|
form_title=f'Edit Drink "{drink.name}"',
|
||||||
|
post_url='/drinks/save'
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/newdrink', methods=['GET'])
|
@app.route('/drinks/new', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def new_drink():
|
def new_drink():
|
||||||
return render_template('editdrink.html', model={})
|
form = forms.DrinkForm()
|
||||||
|
|
||||||
|
model = models.GenericFormModel(
|
||||||
|
page_title='New Drink',
|
||||||
|
form_title=f'Create a new Drink',
|
||||||
|
post_url='/drinks/save'
|
||||||
|
)
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/savedrink', methods=['POST'])
|
@app.route('/drinks/save', methods=['POST'])
|
||||||
@authorize
|
@authorize
|
||||||
def save_drink():
|
def drink_save():
|
||||||
drink = models.Drink.from_form(request.form)
|
form = forms.DrinkForm()
|
||||||
res = db.save_drink(drink)
|
if form.validate_on_submit():
|
||||||
return redirect('/drinks')
|
drink = models.Drink.from_form(form)
|
||||||
|
res = db.save_drink(drink)
|
||||||
|
return redirect('/drinks')
|
||||||
|
|
||||||
|
model = models.GenericFormModel(
|
||||||
|
page_title='Drinks',
|
||||||
|
form_title='Edit Drink',
|
||||||
|
post_url='/drinks/save'
|
||||||
|
)
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/enemies')
|
@app.route('/enemies')
|
||||||
@authorize
|
@authorize
|
||||||
def enemies():
|
def enemy_list():
|
||||||
loaded = db.load_enemies()
|
sql, args = db.load_enemies()
|
||||||
model = {
|
enemies = db.query_db(sql, args, cls=models.Enemy)
|
||||||
'enemies': loaded,
|
model = {'enemies': enemies}
|
||||||
'columns': [
|
|
||||||
('id', 'ID'),
|
|
||||||
('name', 'Enemy Name'),
|
|
||||||
('boss', 'Is Boss')
|
|
||||||
],
|
|
||||||
'controls': [('edit', 'Edit')]
|
|
||||||
}
|
|
||||||
return render_template('enemies.html', model=model)
|
return render_template('enemies.html', model=model)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/newenemy', methods=['GET'])
|
@app.route('/enemies/new', methods=['GET'])
|
||||||
@authorize
|
@authorize
|
||||||
def new_enemy(preselect_season=None):
|
def enemy_new(preselect_season=None):
|
||||||
sql, args = db.load_season()
|
sql, args = db.load_season()
|
||||||
db_seasons = db.query_db(sql, args, cls=models.Season)
|
seasons = db.query_db(sql, args, cls=models.Season)
|
||||||
db_seasons = sorted(db_seasons, key=lambda s: s.code)
|
seasons = sorted(seasons, key=lambda s: s.code)
|
||||||
|
|
||||||
view_seasons = [(s.id, f'{s.code} - {s.game}') for s in db_seasons]
|
form = forms.EnemyForm()
|
||||||
view_seasons.insert(0, (None, 'No Season'))
|
|
||||||
model = {
|
if preselect_season:
|
||||||
'boss': True,
|
form.season_id.default = preselect_season
|
||||||
'seasons': view_seasons,
|
|
||||||
'select_season': preselect_season
|
model = models.GenericFormModel(
|
||||||
}
|
page_title='Enemies',
|
||||||
return render_template('editenemy.html', model=model)
|
form_title='Create a new Enemy',
|
||||||
|
post_url=f'/enemies/edit/null'
|
||||||
|
)
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/saveenemy', methods=['POST'])
|
@app.route('/enemies/edit/<enemy_id>', methods=['GET', 'POST'])
|
||||||
@authorize
|
@authorize
|
||||||
def save_enemy():
|
def enemy_edit(enemy_id: int):
|
||||||
valid_enemy = models.Enemy.from_form(request.form)
|
model = models.GenericFormModel(
|
||||||
sql, args = db.save_enemy(valid_enemy)
|
page_title='Enemies',
|
||||||
error = db.update_db(sql, args)
|
form_title='Edit Enemy',
|
||||||
|
post_url=f'/enemies/edit/{enemy_id}'
|
||||||
|
)
|
||||||
|
|
||||||
if 'continue' not in request.form:
|
if request.method == 'GET':
|
||||||
return redirect('/enemies')
|
sql, args = db.load_season()
|
||||||
last_selection = valid_enemy.season_id
|
seasons = db.query_db(sql, args, cls=models.Season)
|
||||||
return new_enemy(preselect_season=last_selection)
|
|
||||||
|
sql, args = db.load_enemies(enemy_id)
|
||||||
|
enemy = db.query_db(sql, args, one=True, cls=models.Enemy)
|
||||||
|
|
||||||
|
form = forms.EnemyForm()
|
||||||
|
form.season_id.data = enemy.season_id if enemy.season_id else -1
|
||||||
|
form.name.data = enemy.name
|
||||||
|
form.is_boss.data = enemy.boss
|
||||||
|
form.enemy_id.data = enemy_id
|
||||||
|
|
||||||
|
model.form_title = f'Edit Enemy "{enemy.name}"'
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
else:
|
||||||
|
form = forms.EnemyForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
enemy = models.Enemy.from_form(form)
|
||||||
|
sql, args = db.save_enemy(enemy)
|
||||||
|
errors = db.update_db(sql, args)
|
||||||
|
|
||||||
|
if form.submit_continue_button.data:
|
||||||
|
form.name.data = None
|
||||||
|
return enemy_new(preselect_season=enemy.season_id)
|
||||||
|
return redirect('/enemies')
|
||||||
|
|
||||||
|
model.form_title = 'Incorrect Data'
|
||||||
|
return render_template('generic_form.html', model=model, form=form)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
23
choices.py
23
choices.py
@@ -0,0 +1,23 @@
|
|||||||
|
import models
|
||||||
|
import db
|
||||||
|
|
||||||
|
|
||||||
|
def season_choices():
|
||||||
|
""" Query the database for available seasons.
|
||||||
|
This returns a list of tuples with the season ID and a display string.
|
||||||
|
"""
|
||||||
|
sql, args = db.load_season()
|
||||||
|
seasons = db.query_db(sql, args, cls=models.Season)
|
||||||
|
choices = [(s.id, f'{s.code}: {s.game}') for s in seasons]
|
||||||
|
choices.insert(0, (-1, 'No Season'))
|
||||||
|
return choices
|
||||||
|
|
||||||
|
|
||||||
|
class SeasonChoicesIterable:
|
||||||
|
"""
|
||||||
|
This is used to declare choices for WTForms SelectFields at class definition time.
|
||||||
|
Be aware that this uses an undocumented WTForms feature and is not guaranteed to work.
|
||||||
|
"""
|
||||||
|
def __iter__(self):
|
||||||
|
for choice in season_choices():
|
||||||
|
yield choice
|
||||||
|
|||||||
15
db.py
15
db.py
@@ -80,9 +80,7 @@ def load_players(id=None):
|
|||||||
sql += ' where player.id = ?'
|
sql += ' where player.id = ?'
|
||||||
args = (id, )
|
args = (id, )
|
||||||
sql += ' order by player.id'
|
sql += ' order by player.id'
|
||||||
rows = query_db(sql, args)
|
return sql, args
|
||||||
players = [models.Player(**row) for row in rows]
|
|
||||||
return players
|
|
||||||
|
|
||||||
|
|
||||||
def load_drinks(id=None):
|
def load_drinks(id=None):
|
||||||
@@ -92,9 +90,7 @@ def load_drinks(id=None):
|
|||||||
sql += ' where drink.id = ?'
|
sql += ' where drink.id = ?'
|
||||||
args = (id, )
|
args = (id, )
|
||||||
sql += ' order by drink.id'
|
sql += ' order by drink.id'
|
||||||
rows = query_db(sql, args)
|
return sql, args
|
||||||
drinks = [models.Drink(**row) for row in rows]
|
|
||||||
return drinks
|
|
||||||
|
|
||||||
|
|
||||||
def save_drink_query(drink):
|
def save_drink_query(drink):
|
||||||
@@ -121,9 +117,7 @@ def load_enemies(id=None):
|
|||||||
sql += ' where enemy.id = ?'
|
sql += ' where enemy.id = ?'
|
||||||
args = (id, )
|
args = (id, )
|
||||||
sql += ' order by enemy.id'
|
sql += ' order by enemy.id'
|
||||||
rows = query_db(sql, args)
|
return sql, args
|
||||||
enemies = [models.Enemy(**row) for row in rows]
|
|
||||||
return enemies
|
|
||||||
|
|
||||||
|
|
||||||
def save_enemy(enemy: models.Enemy):
|
def save_enemy(enemy: models.Enemy):
|
||||||
@@ -134,7 +128,7 @@ def save_enemy(enemy: models.Enemy):
|
|||||||
sql = 'update enemy ' \
|
sql = 'update enemy ' \
|
||||||
'set name=?, boss=?, season_id=? ' \
|
'set name=?, boss=?, season_id=? ' \
|
||||||
'where id==?'
|
'where id==?'
|
||||||
args = (enemy.name, enemy.boss, enemy.season_id)
|
args = (enemy.name, enemy.boss, enemy.season_id, enemy.id)
|
||||||
return sql, args
|
return sql, args
|
||||||
|
|
||||||
|
|
||||||
@@ -168,4 +162,3 @@ def load_season(id=None):
|
|||||||
args = (id, )
|
args = (id, )
|
||||||
sql += ' order by season.start'
|
sql += ' order by season.start'
|
||||||
return sql, args
|
return sql, args
|
||||||
|
|
||||||
|
|||||||
42
forms.py
42
forms.py
@@ -0,0 +1,42 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import DateField, TimeField, StringField, SubmitField, BooleanField, \
|
||||||
|
DecimalField, SelectField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
import choices
|
||||||
|
|
||||||
|
class EpisodeForm(FlaskForm):
|
||||||
|
season_id = StringField('Season ID', render_kw={'readonly': True})
|
||||||
|
episode_id = StringField('Episode ID', render_kw={'readonly': True})
|
||||||
|
code = StringField('Episode Code', validators=[DataRequired()])
|
||||||
|
title = StringField('Title', validators=[DataRequired()])
|
||||||
|
description = StringField('Description', validators=[])
|
||||||
|
date = DateField('Episode Date', format='%Y-%m-%d', validators=[DataRequired()])
|
||||||
|
start = TimeField('Start Time', format='%H:%M', validators=[DataRequired()])
|
||||||
|
end = TimeField('End Time', format='%H:%M', validators=[DataRequired()])
|
||||||
|
submit_button = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerForm(FlaskForm):
|
||||||
|
player_id = StringField('Player ID', render_kw={'readonly': True})
|
||||||
|
real_name = StringField('Real Name')
|
||||||
|
alias = StringField('Player Alias', validators=[DataRequired()])
|
||||||
|
hex_id = StringField('Hex ID')
|
||||||
|
anonymize = BooleanField('Anonymize (Show only player alias)')
|
||||||
|
submit_button = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class DrinkForm(FlaskForm):
|
||||||
|
drink_id = StringField('Drink ID', render_kw={'readonly': True})
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
vol = DecimalField('Alcohol %', validators=[DataRequired()])
|
||||||
|
submit_button = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class EnemyForm(FlaskForm):
|
||||||
|
enemy_id = StringField('Enemy ID', render_kw={'readonly': True})
|
||||||
|
season_id = SelectField('Season', choices=choices.SeasonChoicesIterable(), coerce=int)
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
is_boss = BooleanField('Is Boss')
|
||||||
|
submit_button = SubmitField('Submit')
|
||||||
|
submit_continue_button = SubmitField('Submit and Continue')
|
||||||
|
|||||||
71
models.py
71
models.py
@@ -1,25 +1,38 @@
|
|||||||
import datetime
|
import datetime
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import forms
|
||||||
|
|
||||||
INVALID_STR = 'Form entry "{}" is invalid'
|
INVALID_STR = 'Form entry "{}" is invalid'
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GenericFormModel:
|
||||||
|
page_title: str
|
||||||
|
form_title: str
|
||||||
|
post_url: str
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Player:
|
class Player:
|
||||||
id: int
|
id: int
|
||||||
real_name: str
|
real_name: str
|
||||||
name: str
|
|
||||||
alias: str
|
alias: str
|
||||||
hex_id: str
|
hex_id: str
|
||||||
anon: bool = False
|
anon: bool = False
|
||||||
|
|
||||||
def __init__(self, real_name, alias, hex_id, anon=False, id=None):
|
@property
|
||||||
self.real_name = str(real_name)
|
def name(self) -> str:
|
||||||
self.name = str(real_name) if not anon else alias
|
return self.real_name if self.real_name and not self.anon else self.alias
|
||||||
self.hex_id = str(hex_id)
|
|
||||||
self.alias = str(alias)
|
@classmethod
|
||||||
self.anon = bool(anon)
|
def from_form(cls, form: forms.PlayerForm):
|
||||||
self.id = id
|
id = int(form.player_id.data) if form.player_id.data else None
|
||||||
|
real_name = str(form.real_name.data) if form.real_name.data else None
|
||||||
|
alias = str(form.alias.data)
|
||||||
|
hex_id = str(form.hex_id.data) if form.hex_id.data else None
|
||||||
|
anon = bool(form.anonymize.data)
|
||||||
|
return cls(id=id, real_name=real_name, alias=alias, hex_id=hex_id, anon=anon)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -29,19 +42,10 @@ class Drink:
|
|||||||
vol: float
|
vol: float
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_form(cls, form):
|
def from_form(cls, form: forms.DrinkForm):
|
||||||
id = form.get('id', None)
|
id = int(form.drink_id.data) if form.drink_id.data else None
|
||||||
id = int(id) if id else None
|
name = str(form.name.data)
|
||||||
|
vol = float(form.vol.data)
|
||||||
name = form.get('name', None)
|
|
||||||
if not name:
|
|
||||||
raise AttributeError('Form data contains no field "name"')
|
|
||||||
name = str(name)
|
|
||||||
|
|
||||||
vol = form.get('vol', None)
|
|
||||||
if not vol:
|
|
||||||
raise AttributeError('Form data contains no field "vol"')
|
|
||||||
vol = float(vol)
|
|
||||||
|
|
||||||
self = cls(id=id, name=name, vol=vol)
|
self = cls(id=id, name=name, vol=vol)
|
||||||
return self
|
return self
|
||||||
@@ -55,26 +59,11 @@ class Enemy:
|
|||||||
season_id: int
|
season_id: int
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_form(cls, form):
|
def from_form(cls, form: forms.EnemyForm):
|
||||||
id = form.get('id', None)
|
id = int(form.enemy_id.data) if form.enemy_id.data else None
|
||||||
id = int(id) if id else None
|
name = str(form.name.data)
|
||||||
|
boss = bool(form.is_boss.data)
|
||||||
name = form.get('name', None)
|
season = int(form.season_id.data)
|
||||||
if not name:
|
|
||||||
raise AttributeError(INVALID_STR.format('name'))
|
|
||||||
name = str(name)
|
|
||||||
|
|
||||||
boss = form.get('boss', False)
|
|
||||||
if boss not in [True, False, 'True', 'False']:
|
|
||||||
raise AttributeError(INVALID_STR.format('boss'))
|
|
||||||
boss = bool(boss)
|
|
||||||
|
|
||||||
season = form.get('season', None)
|
|
||||||
if season not in ['None', None]:
|
|
||||||
try:
|
|
||||||
season = int(season)
|
|
||||||
except ValueError:
|
|
||||||
raise AttributeError(INVALID_STR.format('boss'))
|
|
||||||
|
|
||||||
self = cls(id=id, name=name, boss=boss, season_id=season)
|
self = cls(id=id, name=name, boss=boss, season_id=season)
|
||||||
return self
|
return self
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
wtforms
|
||||||
|
Flask-Bootstrap4
|
||||||
|
dataclasses
|
||||||
|
Flask-WTF
|
||||||
Click==7.0
|
Click==7.0
|
||||||
Flask==1.0.2
|
Flask==1.0.2
|
||||||
Flask-Login==0.4.1
|
Flask-Login==0.4.1
|
||||||
|
|||||||
4603
static/moment.js
4603
static/moment.js
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,20 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "bootstrap/base.html" %}
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
{% block title %}- Estus Shots{% endblock %}
|
||||||
<link rel="stylesheet" href="/static/bootstrap.css">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous">
|
{% block styles %}
|
||||||
<title>{% block title %}{% endblock %} - Estus Shots</title>
|
{{ super() }}
|
||||||
</head>
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.1/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
|
||||||
<body>
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navbar %}
|
||||||
|
{% set nav_bar = [
|
||||||
|
('/seasons', 'seasons', 'Seasons', 'fas fa-calendar'),
|
||||||
|
('/players', 'players', 'Players', 'fas fa-users'),
|
||||||
|
('/enemies', 'enemies', 'Enemies', 'fas fa-skull-crossbones'),
|
||||||
|
('/drinks', 'drinks', 'Drinks', 'fas fa-beer')
|
||||||
|
]-%}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{% set nav_bar = [
|
|
||||||
('/seasons', 'seasons', 'Seasons', 'fas fa-calendar'),
|
|
||||||
('/players', 'players', 'Players', 'fas fa-users'),
|
|
||||||
('/enemies', 'enemies', 'Enemies', 'fas fa-skull-crossbones'),
|
|
||||||
('/drinks', 'drinks', 'Drinks', 'fas fa-beer')
|
|
||||||
]-%}
|
|
||||||
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
|
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
|
||||||
<ul class="nav navbar-nav mr-auto">
|
<ul class="nav navbar-nav mr-auto">
|
||||||
{% for href, id, caption, icon in nav_bar %}
|
{% for href, id, caption, icon in nav_bar %}
|
||||||
@@ -35,14 +31,19 @@
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div>{% block content %}{% endblock %}</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{% block footer %}
|
|
||||||
© A product of D⁵: <a href="#">Durstiger Donnerstag Digital Drinking Divison</a>.
|
|
||||||
{% endblock %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
{% block page %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
<div>
|
||||||
|
{% block footer %}
|
||||||
|
© Sauf Software by D⁵: <a href="#">Durstiger Donnerstag Digital Drinking Divison</a>.
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% set active_page = "drinks" %}
|
{% set active_page = "drinks" %}
|
||||||
{% block title %}Drinks{% endblock %}
|
{% block title %}Drinks {{ super() }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block page %}
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a class="btn btn-primary" href="/newdrink" role="button">New Drink</a>
|
<a class="btn btn-primary" href="{{ url_for('new_drink') }}" role="button">New Drink</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not model.drinks %}
|
{% if not model.drinks %}
|
||||||
@@ -19,9 +19,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for id, caption in model.controls %}
|
<th scope="col" class="col-sm-auto text-center"></th>
|
||||||
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -33,16 +31,11 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for name, caption in model.controls %}
|
<td class="col-sm-auto text-center">
|
||||||
<td class="col-sm-auto text-center">
|
<a class="btn btn-dark" href="{{ url_for('drink_edit', drink_id = drink.id) }}">
|
||||||
<a class="btn btn-dark"
|
<span class="fas fa-pencil-alt"></span>
|
||||||
href="{% if name == 'edit'%}
|
</a>
|
||||||
/drinks/{{ drink.id }}
|
</td>
|
||||||
{% endif %}">
|
|
||||||
{{ caption }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<form class="needs-validation" novalidate action="/saveseason" method="post">
|
<form class="needs-validation" novalidate action="/seasons/save" method="post">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="id">ID</label>
|
<label for="id">ID</label>
|
||||||
<input name="id"
|
<input name="id"
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% set active_page = "enemies" %}
|
{% set active_page = "enemies" %}
|
||||||
{% block title %}Enemies{% endblock %}
|
{% block title %}Enemies {{ super() }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block page %}
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a class="btn btn-primary" href="/newenemy" role="button">New Enemy</a>
|
<a class="btn btn-primary" href="{{ url_for('enemy_new') }}" role="button">
|
||||||
|
<span class="fas fa-plus"></span> Enemy
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not model.enemies %}
|
{% if not model.enemies %}
|
||||||
@@ -14,35 +16,32 @@
|
|||||||
<table class="table table-hover table-striped table-bordered">
|
<table class="table table-hover table-striped table-bordered">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{% for prop, caption in model.columns %}
|
<th scope="col" class="col-sm-auto text-center">ID</th>
|
||||||
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
<th scope="col" class="col-sm-auto text-center">Name</th>
|
||||||
{% endfor %}
|
<th scope="col" class="col-sm-auto text-center">Boss Enemy</th>
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for id, caption in model.controls %}
|
<th scope="col" class="col-sm-auto text-center">Editor</th>
|
||||||
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in model.enemies %}
|
{% for item in model.enemies %}
|
||||||
<tr>
|
<tr>
|
||||||
{% for prop, caption in model.columns %}
|
<td class="col-sm-auto text-center">{{ item.id }}</td>
|
||||||
<td class="col-sm-auto text-center">{{ item[prop] }}</td>
|
<td class="col-sm-auto text-center">{{ item.name }}</td>
|
||||||
{% endfor %}
|
<td class="col-sm-auto text-center">
|
||||||
|
{% if item.boss %}
|
||||||
|
<span class="fas fa-check"></span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for name, caption in model.controls %}
|
<td class="col-sm-auto text-center">
|
||||||
<td class="col-sm-auto text-center">
|
<a class="btn btn-dark" href="{{ url_for('enemy_edit', enemy_id = item.id) }}">
|
||||||
<a class="btn btn-dark"
|
<span class="fas fa-pencil-alt"></span>
|
||||||
href="{% if name == 'edit'%}
|
</a>
|
||||||
/enemies/{{ item.id }}
|
</td>
|
||||||
{% endif %}">
|
|
||||||
{{ caption }}
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,10 +1,46 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="en">
|
{% set active_page = "seasons" %}
|
||||||
<head>
|
{% block title %}{{ model.season_code }} - Episodes{% endblock %}
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
</body>
|
{% block content %}
|
||||||
</html>
|
{% if g.is_editor %}
|
||||||
|
<div class="btn-toolbar" role="toolbar">
|
||||||
|
<a class="btn btn-primary" href="/newepisode" role="button">New Episode</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if not model.player_list %}
|
||||||
|
There are no episodes.
|
||||||
|
{% else %}
|
||||||
|
<table class="table table-hover table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{% for prop, caption in model.columns %}
|
||||||
|
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<th scope="col" class="col-sm-auto text-center"></th>
|
||||||
|
{% if g.is_editor %}
|
||||||
|
<th scope="col" class="col-sm-auto text-center"></th>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for ep in model.episodes %}
|
||||||
|
<tr>
|
||||||
|
{% for prop, _ in model.columns %}
|
||||||
|
<td class="col-sm-auto text-center">{{ ep[prop] }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<a class="btn btn-dark" href="/episodes/{{ ep['id'] }}">Show</a>
|
||||||
|
{% if g.is_editor %}
|
||||||
|
<td class="col-sm-auto text-center">
|
||||||
|
<a class="btn btn-dark" href="/editepisode/{{ ep['id'] }}">Edit</a>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="en">
|
{% import "bootstrap/wtf.html" as wtf %}
|
||||||
<head>
|
{% set active_page = model.active_page %}
|
||||||
<meta charset="UTF-8">
|
{% block title %}{{ model.page_title }} {{ super() }}{% endblock %}
|
||||||
<title>Title</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
</body>
|
{% block page %}
|
||||||
</html>
|
<div class="text-center">
|
||||||
|
<h1>{{ model.form_title }}</h1>
|
||||||
|
<form action="{{ model.post_url }}" method="post">
|
||||||
|
{{ wtf.quick_form(form, button_map={'submit_button': 'primary'}, form_type='horizontal', horizontal_columns=('lg', 2, 10)) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,18 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "bootstrap/base.html" %}
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<!-- Required meta tags -->
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
{% block title %}Estus Shots{% endblock %}
|
||||||
<link rel="stylesheet" href="/static/bootstrap.css">
|
|
||||||
<link rel="stylesheet" href="/static/custom.css">
|
{% block styles %}
|
||||||
|
{{ super() }}
|
||||||
|
<link rel="stylesheet" href="/static/custom.css">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous">
|
|
||||||
<title>Login - Estus Shots</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container vertical-center">
|
<div class="container vertical-center">
|
||||||
<div class="card text-center mx-auto" style="width: 15rem">
|
<div class="card text-center mx-auto" style="width: 15rem">
|
||||||
<div class="card-header">Login</div>
|
<div class="card-header">Login</div>
|
||||||
@@ -27,5 +23,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
{% endblock %}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% set active_page = "players" %}
|
{% set active_page = "players" %}
|
||||||
{% block title %}Players{% endblock %}
|
{% block title %}Players {{ super() }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block page %}
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a class="btn btn-primary" href="/newplayer" role="button">New Player</a>
|
<a class="btn btn-primary" href="{{ url_for('player_new') }}" role="button">New Player</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not model.player_list %}
|
{% if not model.player_list %}
|
||||||
@@ -19,9 +19,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for name, caption in model.controls %}
|
<th scope="col" class="col-sm-auto text-center">Edit</th>
|
||||||
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -33,11 +31,11 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
{% for name, caption in model.controls %}
|
<td class="col-sm-auto text-center">
|
||||||
<td class="col-sm-auto text-center">
|
<a class="btn btn-dark" href="{{ url_for('player_edit', player_id = player.id) }}">
|
||||||
<a class="btn btn-dark" href="/players/{{ player['id'] }}">{{ caption }}</a>
|
<span class="fas fa-pencil-alt"></span>
|
||||||
</td>
|
</a>
|
||||||
{% endfor %}
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% set active_page = "seasons" %}
|
{% set active_page = "seasons" %}
|
||||||
{% block title %}Seasons{% endblock %}
|
{% block title %}Seasons {{ super() }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block page %}
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
<div class="btn-toolbar" role="toolbar">
|
<div class="btn-toolbar" role="toolbar">
|
||||||
<a class="btn btn-primary" href="/newseason" role="button">New Season</a>
|
<a class="btn btn-primary" href="/seasons/new" role="button"><span class="fas fa-plus"></span> Season</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not model.seasons %}
|
{% if not model.seasons %}
|
||||||
@@ -18,9 +18,11 @@
|
|||||||
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
<th scope="col" class="col-sm-auto text-center">{{ caption }}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<th scope="col" class="col-sm-auto text-center"></th>
|
{# Show #}
|
||||||
|
<th scope="col" class="col-sm-auto text-center">View</th>
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
|
{# Edit #}
|
||||||
<th scope="col" class="col-sm-auto text-center">Editor</th>
|
<th scope="col" class="col-sm-auto text-center">Editor</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
@@ -33,12 +35,16 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<td class="col-sm-auto text-center">
|
<td class="col-sm-auto text-center">
|
||||||
<a class="btn btn-dark" href="/seasons/{{ item.id }}">Show</a>
|
<a class="btn btn-dark" href="/seasons/{{ item.id }}"><span class="fas fa-eye"></span></a>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{% if g.is_editor %}
|
{% if g.is_editor %}
|
||||||
<td class="col-sm-auto text-center">
|
<td class="col-sm-auto text-center">
|
||||||
<a class="btn btn-dark" href="/seasons/edit/{{ item.id }}">Edit</a>
|
<a class="btn btn-dark" href="/seasons/{{ item.id }}/edit"><span class="fas fa-pencil-alt"></span></a>
|
||||||
|
|
||||||
|
<a class="btn btn-dark" href="/seasons/{{ item.id }}/new">
|
||||||
|
<span class="fas fa-plus"></span> Episode
|
||||||
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user