Private
Public Access
1
0

excluding strava from analysis

This commit is contained in:
2024-12-04 21:02:58 +01:00
parent fd7e7e35e5
commit 1daed90104
8 changed files with 138 additions and 25 deletions

View File

@@ -217,6 +217,9 @@ def workout_goldmedalstandard(workout, reset=False):
def check_marker(workout): def check_marker(workout):
r = workout.user r = workout.user
if workout.workoutsource == 'strava':
return None
gmstandard, gmseconds = workout_goldmedalstandard(workout) gmstandard, gmseconds = workout_goldmedalstandard(workout)
if gmseconds < 60: if gmseconds < 60:
return None return None

View File

@@ -550,7 +550,7 @@ def goldmedalscorechart(user, startdate=None, enddate=None):
workouts = Workout.objects.filter(user=user.rower, date__gte=startdate, workouts = Workout.objects.filter(user=user.rower, date__gte=startdate,
date__lte=enddate, date__lte=enddate,
workouttype__in=mytypes.rowtypes, workouttype__in=mytypes.rowtypes,
duplicate=False).order_by('date') duplicate=False).order_by('date').exclude(workoutsource='strava')
markerworkouts = workouts.filter(rankingpiece=True) markerworkouts = workouts.filter(rankingpiece=True)
outids = [w.id for w in markerworkouts] outids = [w.id for w in markerworkouts]

View File

@@ -1423,9 +1423,26 @@ parchoicesy1 = list(sorted(favchartlabelsy1.items(), key=lambda x: x[1]))
parchoicesy2 = list(sorted(favchartlabelsy2.items(), key=lambda x: x[1])) parchoicesy2 = list(sorted(favchartlabelsy2.items(), key=lambda x: x[1]))
parchoicesx = list(sorted(favchartlabelsx.items(), key=lambda x: x[1])) parchoicesx = list(sorted(favchartlabelsx.items(), key=lambda x: x[1]))
# special filter for workouts to exclude strava workouts by default
class WorkoutQuerySet(models.QuerySet):
def filter(self, *args, exclude_strava=True, **kwargs):
queryset = super().filter(*args, **kwargs)
if exclude_strava:
queryset = queryset.exclude(workoutsource='strava')
return queryset
def get(self, *args, **kwargs):
queryset = self
return super().get(*args, **kwargs)
class WorkoutManager(models.Manager):
def get_queryset(self):
return WorkoutQuerySet(self.model, using=self._db)
# Saving a chart as a favorite chart # Saving a chart as a favorite chart
class FavoriteChart(models.Model): class FavoriteChart(models.Model):
workouttypechoices = [ workouttypechoices = [
('ote', 'Erg/SkiErg'), ('ote', 'Erg/SkiErg'),
@@ -3704,6 +3721,9 @@ class Workout(models.Model):
default=False, verbose_name='Duplicate Workout') default=False, verbose_name='Duplicate Workout')
impeller = models.BooleanField(default=False, verbose_name='Impeller') impeller = models.BooleanField(default=False, verbose_name='Impeller')
# attach the WorkoutManager
#objects = WorkoutManager()
def url(self): def url(self):
str = '/rowers/workout/{id}/'.format( str = '/rowers/workout/{id}/'.format(
id=encoder.encode_hex(self.id) id=encoder.encode_hex(self.id)

View File

@@ -451,6 +451,11 @@ def is_workout_user(user, workout):
except AttributeError: # pragma: no cover except AttributeError: # pragma: no cover
return False return False
if workout.privacy == 'hidden':
return user == workout.user.user
if workout.workoutsource == 'strava':
return user == workout.user.user
if workout.user == r: if workout.user == r:
return True return True
@@ -469,6 +474,11 @@ def is_workout_team(user, workout):
except AttributeError: # pragma: no cover except AttributeError: # pragma: no cover
return False return False
if workout.privacy == 'hidden':
return user == workout.user.user
if workout.workoutsource == 'strava':
return user == workout.user.user
if workout.user == r: if workout.user == r:
return True return True

View File

@@ -102,13 +102,17 @@ class StravaPrivacy(TestCase):
self.user_workouts = WorkoutFactory.create_batch(5, user=self.r) self.user_workouts = WorkoutFactory.create_batch(5, user=self.r)
for w in self.user_workouts: for w in self.user_workouts:
if w.id <= 3: if w.id <= 2:
w.workoutsource = 'strava' w.workoutsource = 'strava'
w.privacy = 'hidden' w.privacy = 'hidden'
elif w.id == 3: # user can change privacy but cannot change workoutsource
w.workoutsource = 'strava'
w.privacy = 'visible'
else: else:
w.workoutsource = 'concept2' w.workoutsource = 'concept2'
w.privacy = 'visible' w.privacy = 'visible'
w.team.add(self.team) w.team.add(self.team)
w.csvfilename = get_random_file(filename='rowers/tests/testdata/thyro.csv')['filename']
w.save() w.save()
# r2 coaches r # r2 coaches r
@@ -116,6 +120,13 @@ class StravaPrivacy(TestCase):
self.factory = APIRequestFactory() self.factory = APIRequestFactory()
def tearDown(self):
for workout in self.user_workouts:
try:
os.remove(workout.csvfilename)
except (OSError, FileNotFoundError, IOError):
pass
# Test if workout with workoutsource strava and privacy hidden can be seen by coach # Test if workout with workoutsource strava and privacy hidden can be seen by coach
def test_privacy_coach(self): def test_privacy_coach(self):
login = self.c.login(username=self.u2.username, password=self.password2) login = self.c.login(username=self.u2.username, password=self.password2)
@@ -126,6 +137,16 @@ class StravaPrivacy(TestCase):
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,403) self.assertEqual(response.status_code,403)
# Same test as above but for 'workout_edit_view'
def test_privacy_coach_edit(self):
login = self.c.login(username=self.u2.username, password=self.password2)
self.assertTrue(login)
w = self.user_workouts[0]
url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,403)
# Test if workout with workoutsource strava and privacy hidden can be seen by team member # Test if workout with workoutsource strava and privacy hidden can be seen by team member
def test_privacy_member(self): def test_privacy_member(self):
login = self.c.login(username=self.u3.username, password=self.password3) login = self.c.login(username=self.u3.username, password=self.password3)
@@ -136,6 +157,16 @@ class StravaPrivacy(TestCase):
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,403) self.assertEqual(response.status_code,403)
# Same test as above but for 'workout_edit_view'
def test_privacy_member_edit(self):
login = self.c.login(username=self.u3.username, password=self.password3)
self.assertTrue(login)
w = self.user_workouts[0]
url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,403)
# same test as above but with user r and the response code should be 200 # same test as above but with user r and the response code should be 200
def test_privacy_owner(self): def test_privacy_owner(self):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
@@ -146,6 +177,16 @@ class StravaPrivacy(TestCase):
response = self.c.get(url) response = self.c.get(url)
self.assertEqual(response.status_code,200) self.assertEqual(response.status_code,200)
# same test as above but for 'workout_edit_view'
def test_privacy_owner_edit(self):
login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login)
w = self.user_workouts[0]
url = reverse('workout_edit_view',kwargs={'id':encoder.encode_hex(w.id)})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
# test if list_workouts returns all workouts for user r # test if list_workouts returns all workouts for user r
@@ -168,6 +209,7 @@ class StravaPrivacy(TestCase):
'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']]) 'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']])
self.assertEqual(len(workouts),5) self.assertEqual(len(workouts),5)
# same test as above but list_workouts with team id = self.team.id # same test as above but list_workouts with team id = self.team.id
def test_list_workouts_team(self): def test_list_workouts_team(self):
@@ -211,6 +253,27 @@ class StravaPrivacy(TestCase):
self.assertEqual(len(workouts),2) self.assertEqual(len(workouts),2)
# same test as above but with without the teamid kwarg but with a rowerid=self.r.id
def test_list_workouts_team_coach2(self):
login = self.c.login(username=self.u2.username, password=self.password2)
self.assertTrue(login)
url = reverse('workouts_view',kwargs={'rowerid':self.r.id})
response = self.c.get(url)
self.assertEqual(response.status_code,200)
# the response.content is html, so we need to parse it
soup = BeautifulSoup(response.content, 'html.parser')
# the workouts look like <a href="/rowers/workout/{id}/...">...</a> and there should be 5 unique ids
# the id is a hex string
workouts = set([a['href'].split('/')[3] for a in soup.find_all('a') if a['href'].startswith('/rowers/workout/')])
# throw out "c2import", "nkimport", "stravaimport", "concept2import", "sporttracksimport" from the set
workouts = set([w for w in workouts if w not in [
'upload', 'addmanual', 'c2import', 'polarimport', 'rp3import', 'nkimport', 'stravaimport', 'concept2import', 'sporttracksimport']])
self.assertEqual(len(workouts),2)
# same test as the previous one but with self.r3 and the number of workouts found should 0 # same test as the previous one but with self.r3 and the number of workouts found should 0
def test_list_workouts_team_member(self): def test_list_workouts_team_member(self):
login = self.c.login(username=self.u3.username, password=self.password3) login = self.c.login(username=self.u3.username, password=self.password3)
@@ -307,8 +370,9 @@ class StravaPrivacy(TestCase):
# test some analysis, should only use the workouts with workoutsource != strava # test some analysis, should only use the workouts with workoutsource != strava
@patch('rowers.dataprep.read_data', side_effect=mocked_read_data) #@patch('rowers.dataprep.read_data', side_effect=mocked_read_data)
def test_workouts_analysis(self, mocked_read_data): #def test_workouts_analysis(self, mocked_read_data):
def test_workouts_analysis(self):
login = self.c.login(username=self.u.username, password=self.password) login = self.c.login(username=self.u.username, password=self.password)
self.assertTrue(login) self.assertTrue(login)
@@ -388,7 +452,7 @@ class StravaPrivacy(TestCase):
self.assertTrue(data.startswith('data = [')) self.assertTrue(data.startswith('data = ['))
self.assertTrue(data.endswith(']')) self.assertTrue(data.endswith(']'))
# count the number of commas between the brackets # count the number of commas between the brackets
self.assertEqual(data.count(','),1377) self.assertEqual(data.count(','),2062)

View File

@@ -199,14 +199,14 @@ def analysis_new(request,
startdatetime__lte=enddate, startdatetime__lte=enddate,
workouttype__in=modalities, workouttype__in=modalities,
rankingpiece__in=rankingtypes, rankingpiece__in=rankingtypes,
) ).exclude(workoutsource='strava')
elif theteam is not None and theteam.viewing == 'coachonly': # pragma: no cover elif theteam is not None and theteam.viewing == 'coachonly': # pragma: no cover
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, startdatetime__lte=enddate,
workouttype__in=modalities, workouttype__in=modalities,
rankingpiece__in=rankingtypes, rankingpiece__in=rankingtypes,
) ).exclude(workoutsource='strava')
elif thesession is not None: elif thesession is not None:
workouts = get_workouts_session(r, thesession) workouts = get_workouts_session(r, thesession)
else: else:
@@ -363,6 +363,7 @@ def trendflexdata(workouts, options, userid=0):
savedata = options.get('savedata',False) savedata = options.get('savedata',False)
workouts = workouts.exclude(workoutsource='strava')
fieldlist, fielddict = dataprep.getstatsfields() fieldlist, fielddict = dataprep.getstatsfields()
fieldlist = [xparam, yparam, groupby, fieldlist = [xparam, yparam, groupby,
@@ -566,6 +567,8 @@ def flexalldata(workouts, options):
trendline = options['trendline'] trendline = options['trendline']
promember = True promember = True
workouts = workouts.exclude(workoutsource='strava')
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
userid = options['userid'] userid = options['userid']
@@ -612,6 +615,9 @@ def histodata(workouts, options):
workmax = options['workmax'] workmax = options['workmax']
userid = options['userid'] userid = options['userid']
workouts = workouts.exclude(workoutsource='strava')
if userid == 0: # pragma: no cover if userid == 0: # pragma: no cover
extratitle = '' extratitle = ''
else: else:
@@ -645,7 +651,8 @@ def cpdata(workouts, options):
u = User.objects.get(id=userid) u = User.objects.get(id=userid)
r = u.rower r = u.rower
delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new( delta, cpvalue, avgpower, workoutnames, urls = dataprep.fetchcp_new(
r, workouts) r, workouts)
@@ -798,6 +805,8 @@ def cpdata(workouts, options):
def statsdata(workouts, options): def statsdata(workouts, options):
workouts = workouts.exclude(workoutsource='strava')
includereststrokes = options['includereststrokes'] includereststrokes = options['includereststrokes']
ids = options['ids'] ids = options['ids']
@@ -872,12 +881,13 @@ def statsdata(workouts, options):
def comparisondata(workouts, options): def comparisondata(workouts, options):
workouts = workouts.exclude(workoutsource='strava')
includereststrokes = options['includereststrokes'] includereststrokes = options['includereststrokes']
xparam = options['xaxis'] xparam = options['xaxis']
yparam1 = options['yaxis1'] yparam1 = options['yaxis1']
plottype = options['plottype'] plottype = options['plottype']
promember = True promember = True
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
ids = [w.id for w in workouts] ids = [w.id for w in workouts]
@@ -915,6 +925,7 @@ def comparisondata(workouts, options):
def boxplotdata(workouts, options): def boxplotdata(workouts, options):
workouts = workouts.exclude(workoutsource='strava')
includereststrokes = options['includereststrokes'] includereststrokes = options['includereststrokes']
spmmin = options['spmmin'] spmmin = options['spmmin']
@@ -926,7 +937,7 @@ def boxplotdata(workouts, options):
plotfield = options['plotfield'] plotfield = options['plotfield']
workstrokesonly = not includereststrokes workstrokesonly = not includereststrokes
datemapping = { datemapping = {
w.id: w.date for w in workouts w.id: w.date for w in workouts
} }
@@ -1020,11 +1031,14 @@ def analysis_view_data(request, userid=0):
for id in ids: for id in ids:
try: try:
workouts.append(Workout.objects.get(id=id)) w = Workout.objects.get(id=id)
if w.workoutsource != 'strava':
workouts.append(w)
except Workout.DoesNotExist: # pragma: no cover except Workout.DoesNotExist: # pragma: no cover
pass pass
if function == 'boxplot': if function == 'boxplot':
script, div = boxplotdata(workouts, options) script, div = boxplotdata(workouts, options)
elif function == 'trendflex': # pragma: no cover elif function == 'trendflex': # pragma: no cover
@@ -1069,7 +1083,7 @@ def create_marker_workouts_view(request, userid=0,
workouts = Workout.objects.filter(user=theuser.rower, date__gte=startdate, workouts = Workout.objects.filter(user=theuser.rower, date__gte=startdate,
date__lte=enddate, date__lte=enddate,
workouttype__in=mytypes.rowtypes, workouttype__in=mytypes.rowtypes,
duplicate=False).order_by('date') duplicate=False).order_by('date').exclude(workoutsource='strava')
for workout in workouts: for workout in workouts:
_ = dataprep.check_marker(workout) _ = dataprep.check_marker(workout)
@@ -1113,7 +1127,7 @@ def goldmedalscores_view(request, userid=0,
theuser, startdate=startdate, enddate=enddate, theuser, startdate=startdate, enddate=enddate,
) )
bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date').exclude(workoutsource='strava')
breadcrumbs = [ breadcrumbs = [
{ {
@@ -1311,7 +1325,7 @@ def performancemanager_view(request, userid=0, mode='rower',
user = therower, date__gte=startdate-datetime.timedelta(days=90), user = therower, date__gte=startdate-datetime.timedelta(days=90),
date__lte=enddate, date__lte=enddate,
duplicate=False, duplicate=False,
rankingpiece=True, workouttype__in=mytypes.rowtypes).order_by('date') rankingpiece=True, workouttype__in=mytypes.rowtypes).order_by('date').exclude(workoutsource='strava')
ids = [w.id for w in markerworkouts] ids = [w.id for w in markerworkouts]
form = PerformanceManagerForm(initial={ form = PerformanceManagerForm(initial={
@@ -1323,7 +1337,7 @@ def performancemanager_view(request, userid=0, mode='rower',
ids = pd.Series(ids, dtype='int').dropna().values ids = pd.Series(ids, dtype='int').dropna().values
bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date') bestworkouts = Workout.objects.filter(id__in=ids).order_by('-date').exclude(workoutsource='strava')
breadcrumbs = [ breadcrumbs = [
{ {

View File

@@ -28,6 +28,7 @@ from rest_framework.response import Response
from rq.job import Job from rq.job import Job
from rules.contrib.views import permission_required, objectgetter from rules.contrib.views import permission_required, objectgetter
from django.core.cache import cache from django.core.cache import cache
from django.db import models
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from rq.registry import StartedJobRegistry from rq.registry import StartedJobRegistry
from rq.exceptions import NoSuchJobError from rq.exceptions import NoSuchJobError

View File

@@ -2204,25 +2204,25 @@ def workouts_view(request, message='', successmessage='',
team=theteam, team=theteam,
startdatetime__gte=startdate, startdatetime__gte=startdate,
startdatetime__lte=enddate, startdatetime__lte=enddate,
privacy='visible').order_by("-date", "-starttime") privacy='visible').order_by("-date", "-starttime").exclude(workoutsource='strava')
g_workouts = Workout.objects.filter( g_workouts = Workout.objects.filter(
team=theteam, team=theteam,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate, startdatetime__lte=activity_enddate,
duplicate=False, duplicate=False,
privacy='visible').order_by("-date", "-starttime") privacy='visible').order_by("-date", "-starttime").exclude(workoutsource='strava')
elif theteam.viewing == 'coachonly': # pragma: no cover elif theteam.viewing == 'coachonly': # pragma: no cover
workouts = Workout.objects.filter( workouts = Workout.objects.filter(
team=theteam, user=r, team=theteam, user=r,
startdatetime__gte=startdate, startdatetime__gte=startdate,
startdatetime__lte=enddate, startdatetime__lte=enddate,
privacy='visible').order_by("-startdatetime") privacy='visible').order_by("-startdatetime").exclude(workoutsource='strava')
g_workouts = Workout.objects.filter( g_workouts = Workout.objects.filter(
team=theteam, user=r, team=theteam, user=r,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate, startdatetime__lte=activity_enddate,
duplicate=False, duplicate=False,
privacy='visible').order_by("-startdatetime") privacy='visible').order_by("-startdatetime").exclude(workoutsource='strava')
elif request.user != r.user: elif request.user != r.user:
theteam = None theteam = None
@@ -2230,13 +2230,13 @@ def workouts_view(request, message='', successmessage='',
user=r, user=r,
startdatetime__gte=startdate, startdatetime__gte=startdate,
startdatetime__lte=enddate, startdatetime__lte=enddate,
privacy='visible').order_by("-date", "-starttime") privacy='visible').order_by("-date", "-starttime").exclude(workoutsource='strava')
g_workouts = Workout.objects.filter( g_workouts = Workout.objects.filter(
user=r, user=r,
startdatetime__gte=activity_startdate, startdatetime__gte=activity_startdate,
startdatetime__lte=activity_enddate, startdatetime__lte=activity_enddate,
duplicate=False, duplicate=False,
privacy='visible').order_by("-startdatetime") privacy='visible').order_by("-startdatetime").exclude(workoutsource='strava')
else: else:
theteam = None theteam = None
workouts = Workout.objects.filter( workouts = Workout.objects.filter(
@@ -2252,7 +2252,7 @@ def workouts_view(request, message='', successmessage='',
if g_workouts.count() == 0: if g_workouts.count() == 0:
g_workouts = Workout.objects.filter( g_workouts = Workout.objects.filter(
user=r, user=r,
startdatetime__gte=timezone.now()-timedelta(days=15)).order_by("-startdatetime") startdatetime__gte=timezone.now()-timedelta(days=15)).order_by("-startdatetime").exclude(workoutsource='strava')
g_enddate = timezone.now() g_enddate = timezone.now()
g_startdate = (timezone.now()-timedelta(days=15)) g_startdate = (timezone.now()-timedelta(days=15))
@@ -2266,7 +2266,8 @@ def workouts_view(request, message='', successmessage='',
reduce(operator.and_, reduce(operator.and_,
(Q(name__icontains=q) for q in query_list)) | (Q(name__icontains=q) for q in query_list)) |
reduce(operator.and_, reduce(operator.and_,
(Q(notes__icontains=q) for q in query_list)) (Q(notes__icontains=q) for q in query_list)),
exclude_strava=False,
) )
searchform = SearchForm(initial={'q': query}) searchform = SearchForm(initial={'q': query})
else: else: