adding poll for next week sessions
This commit is contained in:
@@ -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(
|
||||||
|
|||||||
44
rowers/management/commands/loadnextweeksessions.py
Normal file
44
rowers/management/commands/loadnextweeksessions.py
Normal 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'
|
||||||
|
))
|
||||||
101
rowers/tasks.py
101
rowers/tasks.py
@@ -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:
|
||||||
|
|||||||
32
rowers/templates/deepwaterlogin.html
Normal file
32
rowers/templates/deepwaterlogin.html
Normal 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 %}
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user