Merge branch 'release/v3.48'
This commit is contained in:
+1
-1
@@ -411,7 +411,7 @@ def save_workout_database(f2,r,dosmooth=True,workouttype='rower',
|
|||||||
summary='',
|
summary='',
|
||||||
makeprivate=False,
|
makeprivate=False,
|
||||||
oarlength=2.89,inboard=0.88,
|
oarlength=2.89,inboard=0.88,
|
||||||
consistencychecks=True):
|
consistencychecks=False):
|
||||||
message = None
|
message = None
|
||||||
powerperc = 100*np.array([r.pw_ut2,
|
powerperc = 100*np.array([r.pw_ut2,
|
||||||
r.pw_ut1,
|
r.pw_ut1,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from bokeh.palettes import Dark2_8 as palette
|
|||||||
import itertools
|
import itertools
|
||||||
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
from bokeh.plotting import figure, ColumnDataSource, Figure,curdoc
|
||||||
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation
|
from bokeh.models import CustomJS,Slider, TextInput,BoxAnnotation
|
||||||
from bokeh.charts import Histogram,HeatMap,Area,BoxPlot
|
from bokeh.charts import Histogram,HeatMap,Area,BoxPlot,Bar
|
||||||
from bokeh.resources import CDN,INLINE
|
from bokeh.resources import CDN,INLINE
|
||||||
from bokeh.embed import components
|
from bokeh.embed import components
|
||||||
from bokeh.layouts import layout,widgetbox
|
from bokeh.layouts import layout,widgetbox
|
||||||
@@ -219,6 +219,63 @@ def interactive_boxchart(datadf,fieldname,extratitle=''):
|
|||||||
return script,div
|
return script,div
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_activitychart(workouts,startdate,enddate):
|
||||||
|
if len(workouts) == 0:
|
||||||
|
return "",""
|
||||||
|
|
||||||
|
dates = []
|
||||||
|
types = []
|
||||||
|
durations = []
|
||||||
|
|
||||||
|
for w in workouts:
|
||||||
|
if w.privacy == 'visible':
|
||||||
|
dd = w.date.strftime('%m/%d')
|
||||||
|
du = w.duration.hour*60+w.duration.minute
|
||||||
|
dates.append(dd)
|
||||||
|
durations.append(du)
|
||||||
|
types.append(w.workouttype)
|
||||||
|
|
||||||
|
d = startdate
|
||||||
|
|
||||||
|
while d<=enddate:
|
||||||
|
dates.append(d.strftime('%m/%d'))
|
||||||
|
durations.append(0)
|
||||||
|
types.append('rower')
|
||||||
|
d += datetime.timedelta(days=1)
|
||||||
|
|
||||||
|
|
||||||
|
df = pd.DataFrame({
|
||||||
|
'date':dates,
|
||||||
|
'duration':durations,
|
||||||
|
'type':types,
|
||||||
|
})
|
||||||
|
|
||||||
|
p = Bar(df,'date',values='duration',title='Activity',
|
||||||
|
stack='type',
|
||||||
|
plot_width=350,
|
||||||
|
plot_height=250,
|
||||||
|
toolbar_location = None,
|
||||||
|
)
|
||||||
|
|
||||||
|
for legend in p.legend:
|
||||||
|
new_items = []
|
||||||
|
for legend_item in legend.items:
|
||||||
|
it = legend_item.label['value']
|
||||||
|
tot = df[df['type']==it].duration.sum()
|
||||||
|
if tot != 0:
|
||||||
|
new_items.append(legend_item)
|
||||||
|
legend.items = new_items
|
||||||
|
|
||||||
|
|
||||||
|
p.legend.location = "top_left"
|
||||||
|
p.legend.background_fill_alpha = 0.7
|
||||||
|
|
||||||
|
p.yaxis.axis_label = 'Minutes'
|
||||||
|
|
||||||
|
script, div = components(p)
|
||||||
|
|
||||||
|
return script,div
|
||||||
|
|
||||||
def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
def interactive_forcecurve(theworkouts,workstrokesonly=False):
|
||||||
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
TOOLS = 'save,pan,box_zoom,wheel_zoom,reset,tap,hover,resize,crosshair'
|
||||||
|
|
||||||
|
|||||||
@@ -135,6 +135,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="grid_4" id="interactiveplot">
|
||||||
|
<script type="text/javascript" src="/static/js/bokeh-0.12.3.min.js"></script>
|
||||||
|
<script async="true" type="text/javascript">
|
||||||
|
Bokeh.set_log_level("info");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{ interactiveplot |safe }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Set things up to resize the plot on a window resize. You can play with
|
||||||
|
// the arguments of resize_width_height() to change the plot's behavior.
|
||||||
|
var plot_resize_setup = function () {
|
||||||
|
var plotid = Object.keys(Bokeh.index)[0]; // assume we have just one plot
|
||||||
|
var plot = Bokeh.index[plotid];
|
||||||
|
var plotresizer = function() {
|
||||||
|
// arguments: use width, use height, maintain aspect ratio
|
||||||
|
plot.resize_width_height(true, true, true);
|
||||||
|
};
|
||||||
|
window.addEventListener('resize', plotresizer);
|
||||||
|
plotresizer();
|
||||||
|
};
|
||||||
|
window.addEventListener('load', plot_resize_setup);
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
/* Need this to get the page in "desktop mode"; not having an infinite height.*/
|
||||||
|
html, body {height: 100%; margin:5px;}
|
||||||
|
</style>
|
||||||
|
{{ the_div |safe }}
|
||||||
|
</div>
|
||||||
<div class="grid_4" id="announcements">
|
<div class="grid_4" id="announcements">
|
||||||
{% if announcements %}
|
{% if announcements %}
|
||||||
<h3>What's New?</h3>
|
<h3>What's New?</h3>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<p>
|
<p>
|
||||||
Links to the cumulative statistics pages for your team's members
|
Links to the cumulative statistics pages for your team's members
|
||||||
</p>
|
</p>
|
||||||
<table width="70%" class="listtable shortpadded">
|
<table width="90%" class="listtable shortpadded">
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for u in theusers %}
|
{% for u in theusers %}
|
||||||
<tr>
|
<tr>
|
||||||
@@ -21,6 +21,8 @@
|
|||||||
<td><a href="/rowers/histo/u/{{ u.id }}">Power Histogram</a></td>
|
<td><a href="/rowers/histo/u/{{ u.id }}">Power Histogram</a></td>
|
||||||
<td><a href="/rowers/cumstats/u/{{ u.id }}">Stats</a></td>
|
<td><a href="/rowers/cumstats/u/{{ u.id }}">Stats</a></td>
|
||||||
<td><a href="/rowers/user-boxplot-select/user/{{ u.id }}/">Box Chart</a></td>
|
<td><a href="/rowers/user-boxplot-select/user/{{ u.id }}/">Box Chart</a></td>
|
||||||
|
<td><a href="/rowers/{{ u.id }}/otw-bests">OTW Ranking Pieces</a></td>
|
||||||
|
<td><a href="/rowers/user-multiflex-select/user/{{ u.id }}/">Trend Flex</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -4014,6 +4014,9 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
enddate = startdate
|
enddate = startdate
|
||||||
startdate = s
|
startdate = s
|
||||||
|
|
||||||
|
# start date for the small graph
|
||||||
|
activity_startdate = enddate-datetime.timedelta(days=15)
|
||||||
|
|
||||||
if teamid:
|
if teamid:
|
||||||
try:
|
try:
|
||||||
theteam = Team.objects.get(id=teamid)
|
theteam = Team.objects.get(id=teamid)
|
||||||
@@ -4024,10 +4027,16 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
workouts = Workout.objects.filter(team=theteam,
|
workouts = Workout.objects.filter(team=theteam,
|
||||||
startdatetime__gte=startdate,
|
startdatetime__gte=startdate,
|
||||||
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
||||||
|
g_workouts = Workout.objects.filter(team=theteam,
|
||||||
|
startdatetime__gte=activity_startdate,
|
||||||
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
||||||
elif theteam.viewing == 'coachonly':
|
elif theteam.viewing == 'coachonly':
|
||||||
workouts = Workout.objects.filter(team=theteam,user=r,
|
workouts = Workout.objects.filter(team=theteam,user=r,
|
||||||
startdatetime__gte=startdate,
|
startdatetime__gte=startdate,
|
||||||
startdatetime__lte=enddate).order_by("-date","-starttime")
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
||||||
|
g_workouts = Workout.objects.filter(team=theteam,user=r,
|
||||||
|
startdatetime__gte=activity_startdate,
|
||||||
|
enddatetime__lte=enddate).order_by("-date","-starttime")
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -4035,6 +4044,9 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
workouts = Workout.objects.filter(user=r,
|
workouts = Workout.objects.filter(user=r,
|
||||||
startdatetime__gte=startdate,
|
startdatetime__gte=startdate,
|
||||||
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
startdatetime__lte=enddate).order_by("-date", "-starttime")
|
||||||
|
g_workouts = Workout.objects.filter(user=r,
|
||||||
|
startdatetime__gte=activity_startdate,
|
||||||
|
startdatetime__lte=enddate).order_by("-date","-starttime")
|
||||||
|
|
||||||
query = request.GET.get('q')
|
query = request.GET.get('q')
|
||||||
if query:
|
if query:
|
||||||
@@ -4064,6 +4076,10 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
"-id"
|
"-id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
script,div = interactive_activitychart(g_workouts,
|
||||||
|
activity_startdate,
|
||||||
|
enddate)
|
||||||
|
|
||||||
messages.info(request,successmessage)
|
messages.info(request,successmessage)
|
||||||
messages.error(request,message)
|
messages.error(request,message)
|
||||||
|
|
||||||
@@ -4075,6 +4091,8 @@ def workouts_view(request,message='',successmessage='',
|
|||||||
'announcements':announcements[0:4],
|
'announcements':announcements[0:4],
|
||||||
'team':theteam,
|
'team':theteam,
|
||||||
'teams':get_my_teams(request.user),
|
'teams':get_my_teams(request.user),
|
||||||
|
'interactiveplot':script,
|
||||||
|
'the_div':div,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -7421,6 +7439,12 @@ def team_workout_upload_view(request,message="",
|
|||||||
url = reverse(team_workout_upload_view)
|
url = reverse(team_workout_upload_view)
|
||||||
response = HttpResponseRedirect(url)
|
response = HttpResponseRedirect(url)
|
||||||
return response
|
return response
|
||||||
|
elif id == -1:
|
||||||
|
message = 'The zip archive will be processed in the background. The files in the archive will only be uploaded without the extra actions. You will receive email when the workouts are ready.'
|
||||||
|
messages.info(request,message)
|
||||||
|
url = reverse(team_workout_upload_view)
|
||||||
|
response = HttpResponseRedirect(url)
|
||||||
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
successmessage = "The workout was added to the user's account"
|
successmessage = "The workout was added to the user's account"
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ DATABASES = {
|
|||||||
'PASSWORD': CFG['db_password'],
|
'PASSWORD': CFG['db_password'],
|
||||||
'HOST': CFG['db_host'],
|
'HOST': CFG['db_host'],
|
||||||
'PORT': CFG['db_port'],
|
'PORT': CFG['db_port'],
|
||||||
},
|
},
|
||||||
'slave': {
|
'slave': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ DATABASES = {
|
|||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
'HOST': 'localhost'
|
'HOST': 'localhost',
|
||||||
},
|
},
|
||||||
# 'TEST': {
|
# 'TEST': {
|
||||||
# 'CHARSET': 'utf8',
|
# 'CHARSET': 'utf8',
|
||||||
|
|||||||
Reference in New Issue
Block a user