Restructure and add search.
This commit is contained in:
parent
5a2686f6f7
commit
89f8b5d3b3
256
aacstats.py
256
aacstats.py
@ -1,17 +1,154 @@
|
|||||||
from flask import Flask, abort, request, render_template, url_for
|
from flask import Flask, abort, request, render_template, url_for
|
||||||
|
import datetime as dt
|
||||||
import math
|
import math
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
import MySQLdb.cursors
|
import MySQLdb.cursors
|
||||||
import datetime as dt
|
import re
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
PAGE_SIZE=20
|
PAGE_SIZE=20
|
||||||
# TODO: Search
|
MIN_MONTHS_FOR_LISTINGS=3
|
||||||
|
|
||||||
|
|
||||||
|
def read_db(start=0, limit=PAGE_SIZE, listing=None, event=None, person=None, licence=None, search=dict(), year=None):
|
||||||
|
db = MySQLdb.connect(user='aac', passwd='saOAcCWHg4LaoSSA', db='AAC',
|
||||||
|
use_unicode=True, charset="utf8", cursorclass=MySQLdb.cursors.DictCursor)
|
||||||
|
c = db.cursor()
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
select = '*'
|
||||||
|
where = 'WHERE club LIKE "AAC"'
|
||||||
|
group = ''
|
||||||
|
order = 'date DESC, event, position'
|
||||||
|
close = ''
|
||||||
|
if event:
|
||||||
|
if isinstance(event, str):
|
||||||
|
event = db.escape_string(event).decode()
|
||||||
|
where += ' AND event LIKE "{}"'.format(event.lower())
|
||||||
|
if person:
|
||||||
|
if isinstance(person, str):
|
||||||
|
person = db.escape_string(person).decode()
|
||||||
|
where += ' AND CONCAT_WS(" ", name, surname) LIKE "{}"'.format(person.lower())
|
||||||
|
if licence:
|
||||||
|
if isinstance(licence, str):
|
||||||
|
licence = db.escape_string(licence).decode()
|
||||||
|
where += ' AND licence LIKE "{}"'.format(licence.lower())
|
||||||
|
if year:
|
||||||
|
if not isinstance(year, (int, float)):
|
||||||
|
year = int(db.escape_string(year).decode())
|
||||||
|
firstdate = dt.datetime.min
|
||||||
|
lastdate = dt.datetime.max
|
||||||
|
firstdate = firstdate.replace(year=int(year))
|
||||||
|
lastdate = lastdate.replace(year=int(year))
|
||||||
|
where += ' AND date > "{}" AND date < "{}"'.format(firstdate, lastdate)
|
||||||
|
|
||||||
|
for column in search.keys():
|
||||||
|
if isinstance(column, str):
|
||||||
|
column = db.escape_string(column).decode()
|
||||||
|
if isinstance(search[column], str):
|
||||||
|
query = db.escape_string(search[column]).decode()
|
||||||
|
if not query:
|
||||||
|
next
|
||||||
|
if column in ( 'licence', 'event', ):
|
||||||
|
where += ' AND {} LIKE "%{}%"'.format(column, query.lower())
|
||||||
|
elif column in ( 'name', ):
|
||||||
|
where += ' AND ( CONCAT_WS(" ", name, surname) LIKE "%{}%" )'.format(query.lower())
|
||||||
|
elif column in ( 'position', ):
|
||||||
|
where += ' AND {} LIKE "{}"'.format(column, query)
|
||||||
|
elif column == 'after':
|
||||||
|
try:
|
||||||
|
after = dt.datetime.strptime(query, '%Y-%m-%d')
|
||||||
|
date = dt.datetime.min
|
||||||
|
date = date.replace(year=after.year, month=after.month, day=after.day)
|
||||||
|
where += ' AND date > "{}"'.format(date)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif column == 'before':
|
||||||
|
try:
|
||||||
|
before = dt.datetime.strptime(query, '%Y-%m-%d')
|
||||||
|
date = dt.datetime.max
|
||||||
|
date = date.replace(year=before.year, month=before.month, day=before.day)
|
||||||
|
where += ' AND date < "{}"'.format(date)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if listing:
|
||||||
|
if listing == 'races':
|
||||||
|
select = 'event, date'
|
||||||
|
group = 'GROUP BY event'
|
||||||
|
elif listing == 'runners':
|
||||||
|
select = 'CONCAT_WS(" ", name, surname) person, FORMAT(SUM(distance),0) total'
|
||||||
|
where += ' AND CONCAT_WS(" ", name, surname) NOT LIKE "%NO RETURN%"'
|
||||||
|
where += ' AND CONCAT_WS(" ", name, surname) NOT LIKE "%BLANK CARD%"'
|
||||||
|
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
||||||
|
order = 'SUM(distance) DESC, surname'
|
||||||
|
elif listing == 'rankings':
|
||||||
|
select = 'CONCAT_WS(" ", name, surname) person, SUM(position) positions, COUNT(event) races, SUM(position)/COUNT(event) podiums, FORMAT(SUM(position)/COUNT(event),1) score'
|
||||||
|
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
||||||
|
order = 'podiums, races DESC'
|
||||||
|
elif listing == 'licence':
|
||||||
|
select = 'licence, date, CONCAT_WS(" ", name, surname) person'
|
||||||
|
group = 'GROUP BY licence'
|
||||||
|
order = 'surname, date DESC'
|
||||||
|
|
||||||
|
sql = 'SELECT {} FROM `results` {} {} ORDER BY {} LIMIT {},{} {};'.format(select, where, group, order, start, limit, close)
|
||||||
|
app.logger.debug(sql)
|
||||||
|
c.execute(sql)
|
||||||
|
queryresults = c.fetchall()
|
||||||
|
|
||||||
|
select = 'COUNT(*)'
|
||||||
|
if listing:
|
||||||
|
if listing == 'races':
|
||||||
|
select = 'COUNT(*) FROM ( SELECT COUNT(event)'
|
||||||
|
close = ') races'
|
||||||
|
elif listing == 'runners':
|
||||||
|
select = 'COUNT(*) FROM ( SELECT COUNT(name)'
|
||||||
|
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
||||||
|
close = ') runners'
|
||||||
|
elif listing == 'rankings':
|
||||||
|
select = 'COUNT(*) FROM ( SELECT COUNT(name)'
|
||||||
|
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
||||||
|
close = ') rankings'
|
||||||
|
elif listing == 'licence':
|
||||||
|
pass
|
||||||
|
|
||||||
|
sql = 'SELECT {} FROM `results` {} {} {};'.format(select, where, group, close)
|
||||||
|
#app.logger.debug(sql)
|
||||||
|
c.execute(sql)
|
||||||
|
countresult = c.fetchone()
|
||||||
|
for x in countresult.keys():
|
||||||
|
count = countresult[x]
|
||||||
|
#app.logger.debug(count)
|
||||||
|
|
||||||
|
return { 'count': int(count), 'rows': queryresults }
|
||||||
|
|
||||||
def now():
|
def now():
|
||||||
# TODO: If it's January and no races have been run this year, return last year
|
|
||||||
return dt.datetime.now()
|
return dt.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
@app.template_filter('pace')
|
||||||
|
def pace(time):
|
||||||
|
return (dt.datetime(1,1,1) + time).strftime('%M:%S')
|
||||||
|
|
||||||
|
@app.template_filter('year')
|
||||||
|
def year(time):
|
||||||
|
return time.strftime('%Y')
|
||||||
|
|
||||||
|
@app.template_filter('cleandate')
|
||||||
|
def clean_date(time):
|
||||||
|
if time.month == 1 and time.day == 1:
|
||||||
|
return time.strftime('%Y')
|
||||||
|
return time.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
@app.template_filter('ordinal')
|
||||||
|
def ordinal(n):
|
||||||
|
if not isinstance(n, int) or n < 1:
|
||||||
|
return
|
||||||
|
return "%d%s" % (n,"tsnrhtdd"[(math.floor(n/10)%10!=1)*(n%10<4)*n%10::4])
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
@app.route('/list')
|
@app.route('/list')
|
||||||
@app.route('/list/<title>') # title = { races, rankings, runners, licence }
|
@app.route('/list/<title>') # title = { races, rankings, runners, licence }
|
||||||
@ -23,6 +160,18 @@ def list(title=None, year=None):
|
|||||||
title = 'runners'
|
title = 'runners'
|
||||||
if title not in ( 'races', 'rankings', 'runners', 'licence', ):
|
if title not in ( 'races', 'rankings', 'runners', 'licence', ):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
''' In early January, we'll be left with blank pages in listings, since there won't
|
||||||
|
be any results yet, so hack it to return last year's info '''
|
||||||
|
date = dt.datetime.now()
|
||||||
|
if date.year == year and date.month <= MIN_MONTHS_FOR_LISTINGS:
|
||||||
|
results = read_db(year=date.year)
|
||||||
|
app.logger.debug(results['count'])
|
||||||
|
if results['count'] < 1:
|
||||||
|
''' get an approximate time about two months ago (at the end of the previous year) '''
|
||||||
|
lastyear = date - dt.timedelta(days=(MIN_MONTHS_FOR_LISTINGS * 31))
|
||||||
|
year = lastyear.year
|
||||||
|
|
||||||
start = int(request.args.get('start', '0'))
|
start = int(request.args.get('start', '0'))
|
||||||
limit = int(request.args.get('limit', PAGE_SIZE))
|
limit = int(request.args.get('limit', PAGE_SIZE))
|
||||||
results = read_db(start, limit, listing=title, year=year)
|
results = read_db(start, limit, listing=title, year=year)
|
||||||
@ -70,99 +219,14 @@ def licence(year=now().year, title=None):
|
|||||||
results=results, start=start, limit=limit,
|
results=results, start=start, limit=limit,
|
||||||
request=request, now=now(), PAGE_SIZE=PAGE_SIZE)
|
request=request, now=now(), PAGE_SIZE=PAGE_SIZE)
|
||||||
|
|
||||||
|
@app.route('/search')
|
||||||
def read_db(start=0, limit=PAGE_SIZE, listing=None, event=None, person=None, licence=None, year=None):
|
def search():
|
||||||
db = MySQLdb.connect(user = 'aac', passwd = 'saOAcCWHg4LaoSSA', db = 'AAC', cursorclass = MySQLdb.cursors.DictCursor)
|
start = int(request.args.get('start', '0'))
|
||||||
c = db.cursor()
|
limit = int(request.args.get('limit', PAGE_SIZE))
|
||||||
|
results = read_db(start, limit, search=request.args)
|
||||||
count = 0
|
return render_template('search.html', ltype='index', year=None,
|
||||||
|
results=results, start=start, limit=limit,
|
||||||
select = '*'
|
request=request, now=now(), PAGE_SIZE=PAGE_SIZE)
|
||||||
where = 'WHERE club LIKE "AAC"'
|
|
||||||
group = ''
|
|
||||||
order = 'date DESC, event, position'
|
|
||||||
close = ''
|
|
||||||
if event:
|
|
||||||
where += ' AND event LIKE "{}"'.format(event)
|
|
||||||
if person:
|
|
||||||
where += ' AND CONCAT_WS(" ", name, surname) LIKE "{}"'.format(person)
|
|
||||||
if licence:
|
|
||||||
where += ' AND licence LIKE "{}"'.format(licence)
|
|
||||||
if year:
|
|
||||||
firstdate = dt.datetime.min
|
|
||||||
lastdate = dt.datetime.max
|
|
||||||
firstdate = firstdate.replace(year=int(year))
|
|
||||||
lastdate = lastdate.replace(year=int(year))
|
|
||||||
where += ' AND date > "{}" AND date < "{}"'.format(firstdate, lastdate)
|
|
||||||
|
|
||||||
if listing:
|
|
||||||
if listing == 'races':
|
|
||||||
select = 'event, date'
|
|
||||||
group = 'GROUP BY event'
|
|
||||||
elif listing == 'runners':
|
|
||||||
select = 'CONCAT_WS(" ", name, surname) person, FORMAT(SUM(distance),0) total'
|
|
||||||
where += ' AND CONCAT_WS(" ", name, surname) NOT LIKE "%NO RETURN%"'
|
|
||||||
where += ' AND CONCAT_WS(" ", name, surname) NOT LIKE "%BLANK CARD%"'
|
|
||||||
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
|
||||||
order = 'SUM(distance) DESC, surname'
|
|
||||||
elif listing == 'rankings':
|
|
||||||
select = 'CONCAT_WS(" ", name, surname) person, SUM(position) positions, COUNT(event) races, SUM(position)/COUNT(event) podiums, FORMAT(SUM(position)/COUNT(event),1) score'
|
|
||||||
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
|
||||||
order = 'podiums, races DESC'
|
|
||||||
elif listing == 'licence':
|
|
||||||
select = 'licence, date, CONCAT_WS(" ", name, surname) person'
|
|
||||||
group = 'GROUP BY licence'
|
|
||||||
order = 'surname, date DESC'
|
|
||||||
|
|
||||||
sql = 'SELECT {} FROM `results` {} {} ORDER BY {} LIMIT {},{} {};'.format(select, where, group, order, start, limit, close)
|
|
||||||
#app.logger.debug(sql)
|
|
||||||
c.execute(sql)
|
|
||||||
queryresults = c.fetchall()
|
|
||||||
|
|
||||||
select = 'COUNT(*)'
|
|
||||||
if listing:
|
|
||||||
if listing == 'races':
|
|
||||||
select = 'COUNT(*) FROM ( SELECT COUNT(event)'
|
|
||||||
close = ') races'
|
|
||||||
elif listing == 'runners':
|
|
||||||
select = 'COUNT(*) FROM ( SELECT COUNT(name)'
|
|
||||||
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
|
||||||
close = ') runners'
|
|
||||||
elif listing == 'rankings':
|
|
||||||
select = 'COUNT(*) FROM ( SELECT COUNT(name)'
|
|
||||||
group = 'GROUP BY CONCAT_WS(" ", name, surname)'
|
|
||||||
close = ') rankings'
|
|
||||||
elif listing == 'licence':
|
|
||||||
pass
|
|
||||||
|
|
||||||
sql = 'SELECT {} FROM `results` {} {} {};'.format(select, where, group, close)
|
|
||||||
#app.logger.debug(sql)
|
|
||||||
c.execute(sql)
|
|
||||||
countresult = c.fetchone()
|
|
||||||
for x in countresult.keys():
|
|
||||||
count = countresult[x]
|
|
||||||
app.logger.debug(count)
|
|
||||||
|
|
||||||
return { 'count': int(count), 'rows': queryresults }
|
|
||||||
|
|
||||||
|
|
||||||
@app.template_filter('pace')
|
|
||||||
def pace(time):
|
|
||||||
return (dt.datetime(1,1,1) + time).strftime('%M:%S')
|
|
||||||
|
|
||||||
@app.template_filter('year')
|
|
||||||
def year(time):
|
|
||||||
return time.strftime('%Y')
|
|
||||||
|
|
||||||
@app.template_filter('cleandate')
|
|
||||||
def clean_date(time):
|
|
||||||
if time.month == 1 and time.day == 1:
|
|
||||||
return time.strftime('%Y')
|
|
||||||
return time.strftime('%Y-%m-%d')
|
|
||||||
|
|
||||||
@app.template_filter('ordinal')
|
|
||||||
def ordinal(n):
|
|
||||||
return "%d%s" % (n,"tsnrhtdd"[(math.floor(n/10)%10!=1)*(n%10<4)*n%10::4])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -154,7 +154,8 @@ def load_into_db(rows):
|
|||||||
if rows is None or len(rows) < 1:
|
if rows is None or len(rows) < 1:
|
||||||
log.warning("No data found in spreadsheet")
|
log.warning("No data found in spreadsheet")
|
||||||
else:
|
else:
|
||||||
db = MySQLdb.connect(user='aac', passwd='saOAcCWHg4LaoSSA', db='AAC', use_unicode=True, charset="utf8")
|
db = MySQLdb.connect(user='aac', passwd='saOAcCWHg4LaoSSA', db='AAC',
|
||||||
|
use_unicode=True, charset="utf8")
|
||||||
c = db.cursor()
|
c = db.cursor()
|
||||||
|
|
||||||
''' Check for duplicate values by DATE and POSITION and RACE and EVENT '''
|
''' Check for duplicate values by DATE and POSITION and RACE and EVENT '''
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0 auto 15px;
|
margin: 0 auto;
|
||||||
padding: 0 8px;
|
|
||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
font-family: 'Roboto Condensed', sans-serif;
|
font-family: 'Roboto Condensed', sans-serif;
|
||||||
font-size: 11pt;
|
font-size: 11pt;
|
||||||
@ -11,6 +10,9 @@ a {
|
|||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
article {
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
table {
|
table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -38,6 +40,7 @@ nav span {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
nav span {
|
nav span {
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
nav span a {
|
nav span a {
|
||||||
display: block;
|
display: block;
|
||||||
@ -68,6 +71,33 @@ nav.tabs span:last-child {
|
|||||||
}
|
}
|
||||||
nav.tabs span a {
|
nav.tabs span a {
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
font-size: 11pt;
|
font-size: 10pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
div.search {
|
||||||
|
margin: 0 10px 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
div.search > div {
|
||||||
|
flex: 1 1 300px;
|
||||||
|
padding: 5px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
div.search label {
|
||||||
|
flex: 1;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
div.search input {
|
||||||
|
flex: 2;
|
||||||
|
font-family: 'Roboto Condensed', sans-serif; /* Override firefox's occassional monospace weirdness */
|
||||||
|
padding-top: 5px; /* Override Firefox weirdness */
|
||||||
|
padding-bottom: 5px; /* Override Firefox weirdness */
|
||||||
|
}
|
||||||
|
div.search input#searchsubmit {
|
||||||
|
flex: none;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
@ -42,9 +42,9 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<td>{{ row.date | cleandate }}</td>
|
<td>{{ row.date | cleandate }}</td>
|
||||||
<td>
|
<td>
|
||||||
{%- if row.sex and row.sexposition and row.sexposition <= 100 %}{{ row.sexposition | ordinal }} {{ row.sex.lower() }}{% endif -%}
|
{%- if row.sex and row.sexposition and row.sexposition | int <= 100 %}{{ row.sexposition | ordinal }} {{ row.sex.lower() }}{% endif -%}
|
||||||
{%- if row.sexposition and row.catposition %}/{% endif -%}
|
{%- if row.sexposition and row.sexposition | int <= 100 and row.catposition and row.catposition | int <= 100 %} and {% endif -%}
|
||||||
{%- if row.catposition and row.catposition <= 100 %}{{ row.catposition | ordinal }} in category{% endif -%}
|
{%- if row.catposition and row.catposition | int <= 100 %}{{ row.catposition | ordinal }} in category{% endif -%}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
|
80
templates/search.html
Normal file
80
templates/search.html
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{% set ns = namespace() -%}
|
||||||
|
|
||||||
|
{% include 'head.html' with context %}
|
||||||
|
<article>
|
||||||
|
<h1>AAC Statistics {% if title %}: {{ title }}{% endif %}{% if year %} {{ year }}{% endif %}</h1>
|
||||||
|
<form class="search">
|
||||||
|
<div class="search">
|
||||||
|
<div>
|
||||||
|
<label for="searchname">Name</label>
|
||||||
|
<input type="text" id="searchname" name="name" value="{{ request.args.get('name', '') }}" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="searchlicence">Licence</label>
|
||||||
|
<input type="text" id="searchlicence" name="licence" value="{{ request.args.get('licence', '') }}" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="searchafter">Older than</label>
|
||||||
|
<input type="date" id="searchafter" name="after" max="" value="{{ request.args.get('after', '') }}" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="searchbefore">Newer than</label>
|
||||||
|
<input type="date" id="searchbefore" name="before" min="" value="{{ request.args.get('before', '') }}" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="searchevent">Race</label>
|
||||||
|
<input type="text" id="searchevent" name="event" value="{{ request.args.get('event', '') }}" />
|
||||||
|
</div>
|
||||||
|
<!--div>
|
||||||
|
<label for="searchposition">Position</label>
|
||||||
|
<input type="text" id="searchposition" name="position" value="{{ request.args.get('position', '') }}" />
|
||||||
|
</div-->
|
||||||
|
<div><input type="submit" id="searchsubmit" name="submit" /></div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% if results -%}
|
||||||
|
{%- set ns.total = 0 -%}
|
||||||
|
{%- if 'count' in results -%}
|
||||||
|
{%- set ns.total = results['count'] -%}
|
||||||
|
{%- endif -%}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Position</th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Licence</th>
|
||||||
|
<th>Time</th>
|
||||||
|
<th>Average Pace</th>
|
||||||
|
<th>Event</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{%- for row in results['rows'] -%}
|
||||||
|
{%- set person='{} {}'.format(row.name, row.surname) -%}
|
||||||
|
{%- if distance %}{# set total_km += row.distance #}{% endif -%}
|
||||||
|
<tr>
|
||||||
|
<td>{{ row.position }}</td>
|
||||||
|
<td><a href="{{ url_for('person', title=person, start=None) }}">{{ person }}</a></td>
|
||||||
|
<td><a href="{{ url_for('licence', title=row.licence, year=row.date|year, start=None, limit=ns.limit) }}">{{ row.licence }}</a></td>
|
||||||
|
<td>{{ row.time }}</td>
|
||||||
|
<td class="nowrap">{% if row.distance is number %}{{ (row.time / row.distance) | pace }} min/KM{% endif %}</td>
|
||||||
|
<td><a href="{{ url_for('race', title=row.event, year=row.date|year, start=None, limit=ns.limit) }}">{{ row.event }} ({{ row.distance }} KM)</a></td>
|
||||||
|
<td>{{ row.date | cleandate }}</td>
|
||||||
|
<td>
|
||||||
|
{%- if row.sex and row.sexposition and row.sexposition | int <= 100 %}{{ row.sexposition | ordinal }} {{ row.sex.lower() }}{% endif -%}
|
||||||
|
{%- if row.sexposition and row.sexposition | int <= 100 and row.catposition and row.catposition | int <= 100 %} and {% endif -%}
|
||||||
|
{%- if row.catposition and row.catposition | int <= 100 %}{{ row.catposition | ordinal }} in category{% endif -%}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{%- endfor -%}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{%- endif %}
|
||||||
|
</article>
|
||||||
|
<footer>
|
||||||
|
{% include 'prevnext.html' with context %}
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -2,6 +2,7 @@
|
|||||||
<span><a href="{{ url_for('list', title='runners', year=now|year, start=None, limit=ns.limit) }}">Runners</a></span>
|
<span><a href="{{ url_for('list', title='runners', year=now|year, start=None, limit=ns.limit) }}">Runners</a></span>
|
||||||
<span><a href="{{ url_for('list', title='rankings', year=now|year, start=None, limit=ns.limit) }}">Rankings</a></span>
|
<span><a href="{{ url_for('list', title='rankings', year=now|year, start=None, limit=ns.limit) }}">Rankings</a></span>
|
||||||
<span><a href="{{ url_for('list', title='races', year=now|year, start=None, limit=ns.limit) }}">Races</a></span>
|
<span><a href="{{ url_for('list', title='races', year=now|year, start=None, limit=ns.limit) }}">Races</a></span>
|
||||||
<span><a href="{{ url_for('list', title='licences', year=now|year, start=None, limit=ns.limit) }}">Licences</a></span>
|
<span><a href="{{ url_for('list', title='licence', year=now|year, start=None, limit=ns.limit) }}">Licences</a></span>
|
||||||
<span><a href="{{ url_for('index', title=None, year=None, start=None, limit=ns.limit) }}">All Results</a></span>
|
<span><a href="{{ url_for('index', title=None, year=None, start=None, limit=ns.limit) }}">All Results</a></span>
|
||||||
|
<span><a href="{{ url_for('search', title=None, year=None, start=None, limit=ns.limit) }}">Search</a></span>
|
||||||
</nav>
|
</nav>
|
||||||
|
Loading…
Reference in New Issue
Block a user