Reviewed bug allowing database to be queried on non-existing columns. Looks like a freak bug and code to prevent it was already in place. Not sure what happened to trigger the bug on the production server. Wait for another occurence, then solve. Added Flex Chart as a button on the left panel. Added Workflow configuration to the User Settings page
217 lines
5.8 KiB
Python
217 lines
5.8 KiB
Python
import math
|
|
import numpy as np
|
|
import pandas as pd
|
|
import colorsys
|
|
|
|
lbstoN = 4.44822
|
|
|
|
landingpages = (
|
|
('workout_edit_view','Edit View'),
|
|
('workout_workflow_view','Workflow View'),
|
|
)
|
|
|
|
|
|
workflowmiddlepanel = (
|
|
('panel_statcharts.html','Static Charts'),
|
|
('flexthumbnails.html','Flex Charts'),
|
|
('panel_summary.html','Summary'),
|
|
('panel_map.html','Map'),
|
|
('panel_comments.html','Basic Info and Links'),
|
|
('panel_middlesocial.html','Social Media Share Buttons'),
|
|
)
|
|
|
|
defaultmiddle = ['panel_middlesocial.html',
|
|
'panel_statcharts.html',
|
|
'flexthumbnails.html',
|
|
'panel_summary.html',
|
|
'panel_map.html']
|
|
|
|
workflowleftpanel = (
|
|
('panel_navigationheader.html','Navigation Header'),
|
|
('panel_editbuttons.html','Edit Workout Button'),
|
|
('panel_delete.html','Delete Workout Button'),
|
|
('panel_export.html','Export Workout Button'),
|
|
('panel_social.html','Social Media Share Buttons'),
|
|
('panel_advancededit.html','Advanced Workout Edit Button'),
|
|
('panel_editintervals.html','Edit Intervals Button'),
|
|
('panel_stats.html','Workout Statistics Button'),
|
|
('panel_flexchart.html','Flex Chart'),
|
|
('panel_staticchart.html','Create Static Charts Buttons'),
|
|
('panel_geekyheader.html','Geeky Header'),
|
|
('panel_editwind.html','Edit Wind Data'),
|
|
('panel_editstream.html','Edit Stream Data'),
|
|
('panel_otwpower.html','Run OTW Power Calculations'),
|
|
('panel_mapview.html','Map'),
|
|
)
|
|
|
|
defaultleft = [
|
|
'panel_navigationheader.html',
|
|
'panel_editbuttons.html',
|
|
'panel_advancededit.html',
|
|
'panel_editintervals.html',
|
|
'panel_stats.html',
|
|
'panel_staticchart.html',
|
|
]
|
|
|
|
|
|
def absolute(request):
|
|
urls = {
|
|
'ABSOLUTE_ROOT': request.build_absolute_uri('/')[:-1].strip("/"),
|
|
'ABSOLUTE_ROOT_URL': request.build_absolute_uri('/').strip("/"),
|
|
'PATH':request.build_absolute_uri(),
|
|
}
|
|
|
|
return urls
|
|
|
|
def trcolors(r1,g1,b1,r2,g2,b2):
|
|
r1 = r1/255.
|
|
r2 = r2/255.
|
|
g1 = g1/255.
|
|
g2 = g2/255.
|
|
b2 = b2/255.
|
|
b1 = b1/255.
|
|
h1,s1,v1 = colorsys.rgb_to_hsv(r1,g1,b1)
|
|
h2,s2,v2 = colorsys.rgb_to_hsv(r2,g2,b2)
|
|
|
|
|
|
return 360*h1,360*(h2-h1),s1,(s2-s1),v1,(v2-v1)
|
|
|
|
palettes = {
|
|
'monochrome_blue':(207,-4,0.06,0.89,1.0,-0.38),
|
|
'gold_sunset':(47,-31,.26,-0.12,0.94,-0.5),
|
|
'blue_red':(207,-200,.85,0,.74,-.24),
|
|
'blue_green':(207,-120,.85,0,.75,.25),
|
|
'cyan_green':(192,-50,.08,.65,.98,-.34),
|
|
'cyan_purple':trcolors(237,248,251,136,65,157),
|
|
'green_blue':trcolors(240,249,232,8,104,172),
|
|
'orange_red':trcolors(254,240,217,179,0,0),
|
|
'cyan_blue':trcolors(241,238,246,4,90,141),
|
|
'cyan_green':trcolors(246,239,247,1,108,89),
|
|
'cyan_magenta':trcolors(241,238,246,152,0,67),
|
|
'beige_magenta':trcolors(254,235,226,122,1,119),
|
|
'yellow_green':trcolors(255,255,204,0,104,55),
|
|
'yellow_blue':trcolors(255,255,205,37,52,148),
|
|
'autumn':trcolors(255,255,212,153,52,4),
|
|
'yellow_red':trcolors(255,255,178,189,0,39)
|
|
}
|
|
|
|
|
|
def range_to_color_hex(groupcols,palette='monochrome_blue'):
|
|
|
|
try:
|
|
plt = palettes[palette]
|
|
except KeyErro:
|
|
plt = palettes['monochrome_blue']
|
|
|
|
rgb = [colorsys.hsv_to_rgb((plt[0]+plt[1]*x)/360.,
|
|
plt[2]+plt[3]*x,
|
|
plt[4]+plt[5]*x) for x in groupcols]
|
|
|
|
RGB = [(int(255.*r),int(255.*g),int(255.*b)) for (r, g, b) in rgb]
|
|
colors = ["#%02x%02x%02x" % (r, g, b) for (r, g, b) in RGB]
|
|
|
|
return colors
|
|
|
|
def str2bool(v):
|
|
return v.lower() in ("yes", "true", "t", "1")
|
|
|
|
def uniqify(seq, idfun=None):
|
|
# order preserving
|
|
if idfun is None:
|
|
def idfun(x): return x
|
|
seen = {}
|
|
result = []
|
|
for item in seq:
|
|
marker = idfun(item)
|
|
# in old Python versions:
|
|
# if seen.has_key(marker)
|
|
# but in new ones:
|
|
if marker in seen: continue
|
|
seen[marker] = 1
|
|
result.append(item)
|
|
return result
|
|
|
|
def serialize_list(value,token=','):
|
|
assert(isinstance(value, list) or isinstance(value, tuple) or isinstance(value,np.ndarray))
|
|
return token.join([unicode(s) for s in value])
|
|
|
|
def deserialize_list(value,token=','):
|
|
if isinstance(value, list):
|
|
return value
|
|
elif isinstance(value, np.ndarray):
|
|
return value
|
|
return value.split(token)
|
|
|
|
def geo_distance(lat1,lon1,lat2,lon2):
|
|
""" Approximate distance and bearing between two points
|
|
defined by lat1,lon1 and lat2,lon2
|
|
This is a slight underestimate but is close enough for our purposes,
|
|
We're never moving more than 10 meters between trackpoints
|
|
|
|
Bearing calculation fails if one of the points is a pole.
|
|
(Hey, from the North pole you can walk South, East, North and end up
|
|
on the same spot!)
|
|
|
|
"""
|
|
|
|
# radius of our earth in km --> should be moved to settings if
|
|
# rowing takes off on other planets
|
|
R = 6373.0
|
|
|
|
# pi
|
|
pi = math.pi
|
|
|
|
lat1 = math.radians(lat1)
|
|
lat2 = math.radians(lat2)
|
|
lon1 = math.radians(lon1)
|
|
lon2 = math.radians(lon2)
|
|
|
|
dlon = lon2 - lon1
|
|
dlat = lat2 - lat1
|
|
|
|
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
|
|
c = 2 * atan2(sqrt(a), sqrt(1 - a))
|
|
|
|
distance = R * c
|
|
|
|
tc1 = atan2(sin(lon2-lon1)*cos(lat2),
|
|
cos(lat1)*sin(lat2)-sin(lat1)*cos(lat2)*cos(lon2-lon1))
|
|
|
|
tc1 = tc1 % (2*pi)
|
|
|
|
bearing = math.degrees(tc1)
|
|
|
|
return [distance,bearing]
|
|
|
|
|
|
def isbreakthrough(delta,cpvalues,p0,p1,p2,p3,ratio):
|
|
pwr = abs(p0)/(1+(delta/abs(p2)))
|
|
pwr += abs(p1)/(1+(delta/abs(p3)))
|
|
|
|
dd = 0.25*(ratio-1)
|
|
pwr2 = pwr*(1+dd)
|
|
|
|
pwr *= ratio
|
|
|
|
|
|
delta = delta.values
|
|
cpvalues = cpvalues.values
|
|
|
|
res = np.sum(cpvalues>pwr)
|
|
res2 = np.sum(cpvalues>pwr2)
|
|
|
|
|
|
btdf = pd.DataFrame(
|
|
{
|
|
'delta':delta[cpvalues>pwr],
|
|
'cpvalues':cpvalues[cpvalues>pwr],
|
|
'pwr':pwr[cpvalues>pwr],
|
|
}
|
|
)
|
|
|
|
|
|
btdf.sort_values('delta',axis=0,inplace=True)
|
|
|
|
|
|
return res>1,btdf,res2>1
|