Private
Public Access
1
0

adding poll for next week sessions

This commit is contained in:
2025-07-20 16:15:15 +02:00
parent 5b6e87fedd
commit 01d4cf659e
8 changed files with 253 additions and 2 deletions

View File

@@ -37,6 +37,19 @@ formaxlabels = axlabels.copy()
formaxlabels.pop('None') formaxlabels.pop('None')
parchoices = list(sorted(formaxlabels.items(), key=lambda x: x[1])) parchoices = list(sorted(formaxlabels.items(), key=lambda x: x[1]))
class DeepWaterLoginForm(forms.Form):
username = forms.CharField(
max_length=150, label='Username',
widget=forms.TextInput(attrs={'placeholder': 'Username'}))
password = forms.CharField(
max_length=128, label='Password',
widget=forms.PasswordInput(attrs={'placeholder': 'Password'}))
def clean(self):
cleaned_data = super(DeepWaterLoginForm, self).clean()
if not cleaned_data.get('username') or not cleaned_data.get('password'):
raise forms.ValidationError("Please enter both username and password.")
return cleaned_data
class SurveyForm(forms.Form): class SurveyForm(forms.Form):
surveydone = forms.ChoiceField( surveydone = forms.ChoiceField(

View File

@@ -0,0 +1,44 @@
#!/srv/venv/bin/python
import sys
import os
from django.core.management.base import BaseCommand
from rowers.models import Rower
from rowers.tasks import handle_loadnextweek
from rowers.utils import myqueue, dologging
import django_rq
PY3K = sys.version_info >= (3, 0)
queue = django_rq.get_queue('default')
queuelow = django_rq.get_queue('low')
queuehigh = django_rq.get_queue('low')
class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument(
'--testing',
action='store_true',
dest='testing',
default=False,
help="Run in testing mode, don't send emails", )
def handle(self, *args, **options):
if 'testing' in options:
testing = options['testing']
else:
testing = False
rowers = Rower.objects.filter(training_plan_code__isnull=False).exclude(training_plan_code__exact='')
rowers = rowers.filter(training_plan_secret__isnull=False).exclude(training_plan_secret__exact='')
for rower in rowers:
_ = myqueue(queue, handle_loadnextweek,
rower)
self.stdout.write(self.style.SUCCESS(
'Successfully loaded next week plans'
))

View File

@@ -18,6 +18,7 @@ from rowers.models import (
GraphImage, Team, PlannedSession GraphImage, Team, PlannedSession
) )
from rowers.session_utils import is_session_complete from rowers.session_utils import is_session_complete
import math import math
from rowers.courseutils import ( from rowers.courseutils import (
coursetime_paths, coursetime_first, time_in_path, coursetime_paths, coursetime_first, time_in_path,
@@ -333,6 +334,106 @@ def summaryfromsplitdata(splitdata, data, filename, sep='|', workouttype='rower'
return sums, sa, results return sums, sa, results
from rowers.utils import intensitymap
def strcapitalize(s):
if s is None:
return None
if isinstance(s, str):
return s[0].upper() + s[1:]
return s
def correct_intensity(workout):
# reads the steps and if the intensity is an integer, converts it to a string
steps = workout['steps']
for step in steps:
if 'intensity' in step:
if isinstance(step['intensity'], int):
step['intensity'] = intensitymap[step['intensity']]
step['durationType'] = strcapitalize(step['durationType'])
step['targetType'] = strcapitalize(step['targetType'])
step['intensity'] = strcapitalize(step['intensity'])
return workout
@app.task
def handle_loadnextweek(rower, debug=False, **kwargs):
plan = rower.training_plan_code
secret = rower.training_plan_secret
post_data = {
'fitness': rower.actualfit,
'fatigue': rower.actualfatigue,
'plan': plan,
'secret': secret,
}
url = "http://localhost:8898/next-week-plan/"
response = requests.post(url, data=post_data)
if response.status_code in [200, 201]:
data = response.json()
today = timezone.now()
startdate = today - timedelta(days=today.weekday())+timezone.timedelta(days=7)
enddate = startdate + timedelta(days=6)
sps = PlannedSession.objects.filter(
rower__in=[rower],
startdate__gte=startdate,
enddate__lte=enddate,
is_template=False,
)
for ps in sps:
ps.delete()
trainingdays = data['cycles']
# start date is the first day of the following week
ndays = 0
for day in trainingdays:
try:
workouts = day[0][1:]
except IndexError:
workouts =[]
for workout in workouts:
sessionsport = 'water'
try:
sessionsport = mytypes.fitmappinginv[workout['sport'].lower()]
except KeyError:
pass
preferreddate = startdate+timedelta(days=ndays)
sessionmode = 'time'
ps = PlannedSession(
startdate=preferreddate - timedelta(days=preferreddate.weekday()),
enddate=preferreddate + timedelta(days=-preferreddate.weekday()-1, weeks=1),
preferreddate=preferreddate,
sessionsport=sessionsport, # change this
name=workout['workoutName'],
steps=correct_intensity(workout),
manager=rower.user,
sessionmode=sessionmode,
comment=workout['description'],
from_plan=None,
)
ps.save()
ps.rower.add(rower)
ps.save()
if ps.fitfile:
ps.steps = {}
ps.save()
ndays += 1
return 1
return 0
@app.task @app.task
def handle_assignworkouts(workouts, rowers, remove_workout, debug=False, **kwargs): def handle_assignworkouts(workouts, rowers, remove_workout, debug=False, **kwargs):
for workout in workouts: for workout in workouts:

View File

@@ -0,0 +1,32 @@
{% extends "newbase.html" %}
{% load static %}
{% load rowerfilters %}
{% block title %}Login to Deep Water{% endblock %}
{% block main %}
<h1>Login to Deep Water</h1>
<!-- clack_login.html -->
<form method="post"{% if redirect_url %}?redirect={{ redirect_url|urlencode }}{% endif %}>
{% csrf_token %}
{% if error %}
<div class="error">{{ error }}</div>
{% endif %}
<div>
<label for="username">Username:</label>
<input type="text" id="username" name="username" required>
</div>
<div>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required>
</div>
<button type="submit">Login</button>
</form>
{% endblock %}

View File

@@ -1,6 +1,6 @@
from django.contrib.auth.tokens import PasswordResetTokenGenerator from django.contrib.auth.tokens import PasswordResetTokenGenerator
import six import six
import hmac, hashlib, base64, json, time
class AccountActivationTokenGenerator(PasswordResetTokenGenerator): class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp): def _make_hash_value(self, user, timestamp):
@@ -9,5 +9,15 @@ class AccountActivationTokenGenerator(PasswordResetTokenGenerator):
six.text_type(user.is_active) six.text_type(user.is_active)
) )
# Function to create a custom token for user authentication
def create_token(user_id, username=None, secret_key="your-very-secret-key-here-change-this!"):
expires = int(time.time()) + (24 * 3600) # 24 hours
payload = json.dumps({"user_id": user_id, "username": username, "expires": expires})
encoded_payload = base64.b64encode(payload.encode()).decode()
signature = base64.b64encode(
hmac.new(secret_key.encode(), encoded_payload.encode(), hashlib.sha256).digest()
).decode()
return f"{encoded_payload}.{signature}"
account_activation_token = AccountActivationTokenGenerator() account_activation_token = AccountActivationTokenGenerator()

View File

@@ -1115,5 +1115,9 @@ urlpatterns = [
name='nextweekplan_view'), name='nextweekplan_view'),
re_path(r'^currentweekplan/$', views.currentweekplan_view, re_path(r'^currentweekplan/$', views.currentweekplan_view,
name='currentweekplan_view'), name='currentweekplan_view'),
re_path(r'^deepwaterlogin/$', views.deepwatertoken_login,
name='deepwatertoken_login'),
re_path(r'^deepwatertoken/$', views.get_deepwater_token,
name='get_deepwater_token'),
] ]

View File

@@ -1,5 +1,51 @@
from rowers.views.statements import * from rowers.views.statements import *
from rowers.rower_rules import user_is_not_basic, user_is_coachee from rowers.rower_rules import user_is_not_basic, user_is_coachee
from rowers.tokens import create_token
from rowers.forms import DeepWaterLoginForm
import jwt
def deepwatertoken_login(request):
if request.method == 'POST':
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
payload = {
'user_id': user.id,
'username': user.username,
'email': user.email,
'exp': timezone.now() + datetime.timedelta(days=1), # Token valid for 1 day
'iat': timezone.now(),
}
token = jwt.encode(payload, settings.DEEP_WATER_SECRET_KEY, algorithm='HS256')
# Debug: Print the token
print(f"Generated token: {token}")
print(f"Token length: {len(token)}")
print(f"Token parts: {token.split('.')}")
redirect_url = request.GET.get('redirect', settings.DEEP_WATER_URL)
return HttpResponseRedirect(f"{redirect_url}?token={token}")
else:
messages.error(request, 'Invalid credentials')
return render(request, 'deepwaterlogin.html')
redirect_url = request.GET.get('redirect', settings.DEEP_WATER_URL)
return render(request, "deepwaterlogin.html")
@login_required()
def get_deepwater_token(request):
payload = {
'user_id': request.user.id,
'username': request.user.username,
'email': request.user.email,
'exp': timezone.now() + datetime.timedelta(days=1), # Token valid for 1 day
'iat': timezone.now(),
}
token = jwt.encode(payload, settings.DEEP_WATER_SECRET_KEY, algorithm='HS256')
return JsonResponse({'token': token})
@login_required() @login_required()
def deactivate_user(request): def deactivate_user(request):

View File

@@ -31,7 +31,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = CFG['secret_key'] SECRET_KEY = CFG['secret_key']
DEEP_WATER_SECRET_KEY = CFG['deep_water_secret_key']
DEEP_WATER_URL = CFG['deep_water_url']
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False DEBUG = False