First commit

This commit is contained in:
Gregory Trullemans 2023-04-25 17:06:14 +02:00
commit a1ae3e9134
382 changed files with 192146 additions and 0 deletions

26
.drone.yml Normal file
View File

@ -0,0 +1,26 @@
kind: pipeline
type: docker
name: default
steps:
- name: build
image: python:3.9-slim
commands:
- pip install -r requirements/ci.txt
- pylint ultron --errors-only
- name: discord notification
image: appleboy/drone-discord
settings:
webhook_id:
from_secret: discord_webhook_id
webhook_token:
from_secret: discord_webhook_token
message: >
{{#success build.status}}
Ultron build {{build.number}} succeeded. Good job.
{{else}}
Ultron build {{build.number}} failed. Fix me please. More info : https://drone.grimbox.be/Sulley/Ultron/{{build.number}}
{{/success}}
when:
status:
- failure
- success

43
.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
# Django #
*.log
*.pot
*.pyc
__pycache__
db.sqlite3
*.sql
media
*.yaml
*.json
# Python #
*.py[cod]
*$py.class
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
data/
# Visual Studio Code #
.vscode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history
# Mac OS
.DS_Store
# Project Specific
static/js/plugins/fullcalendar/locales/*
static/js/plugins/fullcalendar/locales-all.js
static/js/plugins/fullcalendar/locales-all.min.js
*.db

575
.pylintrc Normal file
View File

@ -0,0 +1,575 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code.
extension-pkg-allow-list=
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
# for backward compatibility.)
extension-pkg-whitelist=
# Return non-zero exit code if any of these messages/categories are detected,
# even if score is above --fail-under value. Syntax same as enable. Messages
# specified are enabled, while categories only check already-enabled messages.
fail-on=
# Specify a score threshold to be exceeded before program exits with error.
fail-under=10.0
# Files or directories to be skipped. They should be base names, not paths.
ignore=CVS
# Add files or directories matching the regex patterns to the ignore-list. The
# regex matches against paths and can be in Posix or Windows format.
ignore-paths=
# Files or directories matching the regex patterns are skipped. The regex
# matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
init-hook=sys.path.append(".")
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
# number of processors available to use.
jobs=1
# Control the amount of potential inferred values when inferring a single
# object. This can help the performance when dealing with large functions or
# complex, nested conditions.
limit-inference-results=100
# List of plugins (as comma separated values of python module names) to load,
# usually to register additional checkers.
load-plugins=pylint_django
django-settings-module=config.settings
# Pickle collected data for later comparisons.
persistent=yes
# Minimum Python version to use for version dependent checks. Will default to
# the version used to run pylint.
py-version=3.9
# When enabled, pylint would attempt to guess common misconfiguration and emit
# user-friendly hints instead of false-positive error messages.
suggestion-mode=yes
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once). You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=raw-checker-failed,
bad-inline-option,
locally-disabled,
file-ignored,
suppressed-message,
useless-suppression,
deprecated-pragma,
use-symbolic-message-instead,
empty-docstring,
missing-class-docstring,
missing-module-docstring,
missing-function-docstring,
consider-using-f-string,
duplicate-code,
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
[REPORTS]
# Python expression which should return a score less than or equal to 10. You
# have access to the variables 'error', 'warning', 'refactor', and 'convention'
# which contain the number of messages in each category, as well as 'statement'
# which is the total number of statements analyzed. This score is used by the
# global evaluation report (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details.
#msg-template=
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio). You can also give a reporter class, e.g.
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages.
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
# Complete name of functions that never returns. When checking for
# inconsistent-return-statements if a never returning function is called then
# it will be considered as an explicit return statement and no message will be
# printed.
never-returning-functions=sys.exit,argparse.parse_error
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid defining new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of names allowed to shadow builtins
allowed-redefined-builtins=
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,
_cb
# A regular expression matching the name of dummy variables (i.e. expected to
# not be used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore.
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
[SIMILARITIES]
# Comments are removed from the similarity computation
ignore-comments=yes
# Docstrings are removed from the similarity computation
ignore-docstrings=yes
# Imports are removed from the similarity computation
ignore-imports=no
# Signatures are removed from the similarity computation
ignore-signatures=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[BASIC]
# Naming style matching correct argument names.
argument-naming-style=snake_case
# Regular expression matching correct argument names. Overrides argument-
# naming-style.
#argument-rgx=
# Naming style matching correct attribute names.
attr-naming-style=snake_case
# Regular expression matching correct attribute names. Overrides attr-naming-
# style.
#attr-rgx=
# Bad variable names which should always be refused, separated by a comma.
bad-names=foo,
bar,
baz,
toto,
tutu,
tata
# Bad variable names regexes, separated by a comma. If names match any regex,
# they will always be refused
bad-names-rgxs=
# Naming style matching correct class attribute names.
class-attribute-naming-style=any
# Regular expression matching correct class attribute names. Overrides class-
# attribute-naming-style.
#class-attribute-rgx=
# Naming style matching correct class constant names.
class-const-naming-style=UPPER_CASE
# Regular expression matching correct class constant names. Overrides class-
# const-naming-style.
#class-const-rgx=
# Naming style matching correct class names.
class-naming-style=PascalCase
# Regular expression matching correct class names. Overrides class-naming-
# style.
#class-rgx=
# Naming style matching correct constant names.
const-naming-style=UPPER_CASE
# Regular expression matching correct constant names. Overrides const-naming-
# style.
#const-rgx=
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming style matching correct function names.
function-naming-style=snake_case
# Regular expression matching correct function names. Overrides function-
# naming-style.
#function-rgx=
# Good variable names which should always be accepted, separated by a comma.
good-names=i,
j,
k,
ex,
Run,
_
# Good variable names regexes, separated by a comma. If names match any regex,
# they will always be accepted
good-names-rgxs=
# Include a hint for the correct naming format with invalid-name.
include-naming-hint=no
# Naming style matching correct inline iteration names.
inlinevar-naming-style=any
# Regular expression matching correct inline iteration names. Overrides
# inlinevar-naming-style.
#inlinevar-rgx=
# Naming style matching correct method names.
method-naming-style=snake_case
# Regular expression matching correct method names. Overrides method-naming-
# style.
#method-rgx=
# Naming style matching correct module names.
module-naming-style=snake_case
# Regular expression matching correct module names. Overrides module-naming-
# style.
#module-rgx=
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
# These decorators are taken in consideration only for invalid-name.
property-classes=abc.abstractproperty
# Naming style matching correct variable names.
variable-naming-style=snake_case
# Regular expression matching correct variable names. Overrides variable-
# naming-style.
#variable-rgx=
[SPELLING]
# Limits count of emitted suggestions for spelling mistakes.
max-spelling-suggestions=4
# Spelling dictionary name. Available dictionaries: none. To make it work,
# install the 'python-enchant' package.
spelling-dict=
# List of comma separated words that should be considered directives if they
# appear and the beginning of a comment and should not be checked.
spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains the private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to the private dictionary (see the
# --spelling-private-dict-file option) instead of raising a message.
spelling-store-unknown-words=no
[LOGGING]
# The type of string formatting that logging methods do. `old` means using %
# formatting, `new` is for `{}` formatting.
logging-format-style=old
# Logging modules to check that the string format arguments are in logging
# function parameter format.
logging-modules=logging
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# class is considered mixin if its name matches the mixin-class-rgx option.
ignore-mixin-members=yes
# Tells whether to warn about missing members when the owner of the attribute
# is inferred to be None.
ignore-none=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
# Regex pattern to define which classes are considered mixins ignore-mixin-
# members is set to 'yes'
mixin-class-rgx=.*[Mm]ixin
# List of decorators that change the signature of a decorated function.
signature-mutators=
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
# Regular expression of note tags to take in consideration.
#notes-rgx=
[STRING]
# This flag controls whether inconsistent-quotes generates a warning when the
# character used as a quote delimiter is used inconsistently within a module.
check-quote-consistency=no
# This flag controls whether the implicit-str-concat should generate a warning
# on implicit string concatenation in sequences defined over several lines.
check-str-concat-over-line-jumps=no
[DESIGN]
# List of regular expressions of class ancestor names to ignore when counting
# public methods (see R0903)
exclude-too-few-public-methods=
# List of qualified class names to ignore when counting class parents (see
# R0901)
ignored-parents=
# Maximum number of arguments for function / method.
max-args=5
# Maximum number of attributes for a class (see R0902).
max-attributes=7
# Maximum number of boolean expressions in an if statement (see R0916).
max-bool-expr=5
# Maximum number of branch for function / method body.
max-branches=12
# Maximum number of locals for function / method body.
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body.
max-returns=6
# Maximum number of statements in function / method body.
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=2
[CLASSES]
# Warn about protected attribute access inside special methods
check-protected-access-in-special-methods=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
setUp,
__post_init__
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,
_fields,
_replace,
_source,
_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=cls
[IMPORTS]
# List of modules that can be imported at any level, not just the top level
# one.
allow-any-import-level=
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma.
deprecated-modules=
# Output a graph (.gv or any supported image format) of external dependencies
# to the given file (report RP0402 must not be disabled).
ext-import-graph=
# Output a graph (.gv or any supported image format) of all (i.e. internal and
# external) dependencies to the given file (report RP0402 must not be
# disabled).
import-graph=
# Output a graph (.gv or any supported image format) of internal dependencies
# to the given file (report RP0402 must not be disabled).
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
# Couples of modules and preferred modules, separated by a comma.
preferred-modules=
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception

2
Procfile Normal file
View File

@ -0,0 +1,2 @@
release: python manage.py migrate
web: gunicorn config.wsgi

121
README.md Normal file
View File

@ -0,0 +1,121 @@
# Ultron
[![Build Status](https://drone.grimbox.be/api/badges/Sulley/Ultron/status.svg)](https://drone.grimbox.be/Sulley/Ultron)
## Déploiement en local (Docker)
- installer Docker (+ drivers PGSQL)
- créer un fichier `docker-compose.yml` :
```
version: "3"
services:
db:
image: "postgres:14"
restart: always
container_name: "jarvis_container"
environment:
POSTGRES_DB: "jarvis_db"
POSTGRES_USER: "Iamironman"
POSTGRES_PASSWORD: "J4rV1s"
ports:
- "5432:5432"
volumes:
- ./data:/var/lib/postgresql/data
```
- tapez la commande `docker-compose up -d`
- pip install -r requirements.txt
### Installation de weasyprint
En plus du `pip install weasyprint` et `pip install django-weasyprint`, il faut installer weasyprint (via homebrew, …)
puis, **pour les mac M1** exécuter les commandes :
```
sudo ln -s /opt/homebrew/opt/glib/lib/libgobject-2.0.0.dylib /usr/local/lib/gobject-2.0
sudo ln -s /opt/homebrew/opt/pango/lib/libpango-1.0.dylib /usr/local/lib/pango-1.0
sudo ln -s /opt/homebrew/opt/harfbuzz/lib/libharfbuzz.dylib /usr/local/lib/harfbuzz
sudo ln -s /opt/homebrew/opt/fontconfig/lib/libfontconfig.1.dylib /usr/local/lib/fontconfig-1
sudo ln -s /opt/homebrew/opt/pango/lib/libpangoft2-1.0.dylib /usr/local/lib/pangoft2-1.0
````
## Déploiement sur Heroku
- Créer l'application sur Heroku
- Créer les variables d'environnement :
- ALLOWED_HOSTS : avengers-jarvis.herokuapp.com
- DATABASE_NAME : ultron
- DISABLE_COLLECTSTATIC : 1
- SECRET_KEY : django-insecure-g_eoy6z%xshku4o5#k%o%i_%nb%_pz80config_#+t%f
- DATABASE_URL : créé automatiquement lorsqu'on ajoute l' `element` PostgreSQL
Exemple :
```
heroku config:set DISABLE_COLLECTSTATIC="1"
heroku config:set ALLOWED_HOSTS="avengers-jarvis.herokuapp.com"
heroku config:set SECRET_KEY="django-insecure-g_eoy6z%xshku4o5#k%o%i_%nb%_pz80config_#+t%f"
heroku config:set DATABASE_NAME="ultron"
```
- Push de l'application : `git push heroku master`
- Se connecter à Héroku (via l'invite de commande) : `heroku login`
- Création du super user : `heroku run python manage.py createsuperuser`
## Récupération des données :
Pour transferer des données d'un site à un autre, le plus simple est d'utiliser la commande
`./manage.py dumpdata > db.json`.
Pour ne pas récupérer les user, les authorisation et les content-type, utilisez la commande :
```
python manage.py dumpdata --natural-foreign --exclude contenttypes --exclude auth.permission --exclude admin.logentry --exclude sessions.session --indent 4 > save.json
```
Pour charger les données, tapez ensuite :
```
python manage.py loaddata save.json
```
## Applications
### Right
Il y a 3 types pe de droits :
- Administrateur
- Entraineur (groupe `Trainer`)
- Gymnaste (groupe `Gymnast`)
##### Administrateur
Il peut tout faire.
##### Entraîneur
Peut tout faire sauf :
- Gérer les skill
- Gérer les Pays
- Gérer les lieux
##### Gymnaste
Un gymnaste peut tout faire pour *lui-même* :
- Ajouter un chrono
- Ajouter un score
- Ajouter un height/weight
- …
### Skill
### Learned Skill
Il y a quatre niveau de connaissance d'un skill :
- non connu
- connu avec aide (tapis, élastiques, fosse, …)
- connu sans aucune aide
- connu et enchainé

0
config/__init__.py Normal file
View File

16
config/asgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
ASGI config for Ultron project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
application = get_asgi_application()

192
config/settings.py Normal file
View File

@ -0,0 +1,192 @@
"""
Django settings for Ultron project.
Generated by 'django-admin startproject' using Django 3.2.8.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
import environ
from pathlib import Path
# Initialise environment variables
env = environ.Env()
environ.Env.read_env()
# Sentry
SENTRY_DSN = env("SENTRY_DSN", default=None)
if SENTRY_DSN is not None:
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
sentry_sdk.init(
dsn=SENTRY_DSN,
integrations=[DjangoIntegration()],
traces_sample_rate=env("SENTRY_TRACES_SAMPLE_RATE", default=1.0),
send_default_pii=True,
debug=env("SENTRY_DEBUG", default=True),
)
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY", default="Super Little Poney 2000")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = env("ALLOWED_HOSTS", default="localhost").split()
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django_admin_listfilter_dropdown",
"django_extensions",
"jarvis.core",
"jarvis.location",
"jarvis.people",
"jarvis.followup",
"jarvis.tools",
"jarvis.profiles",
"jarvis.planning",
"jarvis.objective",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"whitenoise.middleware.WhiteNoiseMiddleware",
]
ROOT_URLCONF = "config.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
os.path.join(BASE_DIR, "templates"),
],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"jarvis.core.context_processors.git_describe",
],
},
},
]
WSGI_APPLICATION = "config.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {"default": env.db_url("DATABASE_URL", default="sqlite:///jarvis.db")}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOGIN_URL = "/login/"
LOGOUT_URL = "/logout/"
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = "/static/"
STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)
STATIC_ROOT = BASE_DIR / "staticfiles"
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# Simplified static file serving.
# https://warehouse.python.org/project/whitenoise/
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
# CLUB Settings
ZIP = env("ZIP", default=None)
CITY = env("CITY", default=None)
ADDRESS = env("ADDRESS", default=None)
CLUB_NAME = env("CLUB_NAME", default=None)
HEAD_COACH = env("HEAD_COACH", default=None)
SITE_TITLE = env("SITE_TITLE", default=None)
MOBILE_PHONE = env("MOBILE_PHONE", default=None)
HEAD_COACH_EMAIL = env("HEAD_COACH_EMAIL", default=None)
# EMAIL Settings
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
# EMAIL_HOST = env("EMAIL_HOST", default=None)
# EMAIL_PORT = env("EMAIL_PORT", default=None)
# EMAIL_SOURCE = env("EMAIL_SOURCE", default=None)
# EMAIL_USE_TLS = env("EMAIL_USE_TLS", default=None)
# EMAIL_HOST_USER = env("EMAIL_HOST_USER", default=None)
# EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", default=None)
# SENDGRID_WebAPI_KEY = env("SENDGRID_WebAPI_KEY", default=None)
EMAIL_HOST = env("EMAIL_HOST", default=None)
EMAIL_PORT = env("EMAIL_PORT", default=None)
EMAIL_USE_TLS = env("EMAIL_USE_TLS", default=None)
EMAIL_HOST_USER = env("EMAIL_HOST_USER", default=None)
EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", default=None)

42
config/urls.py Normal file
View File

@ -0,0 +1,42 @@
"""Ultron URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
import jarvis.followup.urls
import jarvis.location.urls
import jarvis.people.urls
import jarvis.profiles.urls
import jarvis.planning.urls
import jarvis.objective.urls
urlpatterns = [
# Profile list
path(r"profile/", include(jarvis.profiles.urls.profile_urlpatterns)),
# Gymnast management
path(r"gymnast/", include(jarvis.people.urls.gymnast_urlpatterns)),
# Location management
path(r"location/", include(jarvis.location.urls.urlpatterns)),
# Follow-up management
path(r"follow-up/", include(jarvis.followup.urls.urlpatterns)),
# Objective management
path(r"objective/", include(jarvis.objective.urls.urlpatterns)),
# Planning management
path(r"event/", include(jarvis.planning.urls.event_urlpatterns)),
path("", include("jarvis.core.urls")),
# Administration
path("admin/", admin.site.urls),
]

16
config/wsgi.py Normal file
View File

@ -0,0 +1,16 @@
"""
WSGI config for Ultron project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
application = get_wsgi_application()

14
docker-compose.yml Normal file
View File

@ -0,0 +1,14 @@
version: "3"
services:
db:
image: "postgres:14"
restart: always
container_name: "jarvis_container"
environment:
POSTGRES_DB: "jarvis_db"
POSTGRES_USER: "Iamironman"
POSTGRES_PASSWORD: "J4rV1s"
ports:
- "5432:5432"
volumes:
- ./data:/var/lib/postgresql/data

0
jarvis/core/__init__.py Normal file
View File

18
jarvis/core/admin.py Normal file
View File

@ -0,0 +1,18 @@
from django.contrib import admin
from django_admin_listfilter_dropdown.filters import (
DropdownFilter,
ChoiceDropdownFilter,
RelatedDropdownFilter,
)
from .models import Citation
class CitationAdmin(admin.ModelAdmin):
model = Citation
list_display = ("author", "quote")
admin.site.register(Citation, CitationAdmin)

6
jarvis/core/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class CoreConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "jarvis.core"

View File

@ -0,0 +1,24 @@
import subprocess
def git_describe(request) -> str:
try:
git_describe = subprocess.check_output(
["git", "describe", "--always"], stderr=subprocess.STDOUT
).decode()
except subprocess.CalledProcessError as e:
print("Exception on process, rc=", e.returncode, "output=", e.output)
git_describe = ""
try:
git_date = subprocess.check_output(
["git", "show", "-s", r"--format=%cd", r"--date=format:%d-%m-%Y"], stderr=subprocess.STDOUT
).decode()
except subprocess.CalledProcessError as e:
print("Exception on process, rc=", e.returncode, "output=", e.output)
git_date = ""
return {
"git_describe": git_describe,
"git_date": git_date
}

View File

@ -0,0 +1,29 @@
# Generated by Django 4.1.1 on 2023-01-29 11:38
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="Citation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("quote", models.TextField()),
("author", models.CharField(blank=True, max_length=50, null=True)),
],
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.1 on 2023-01-29 13:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("core", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="citation",
name="quote",
field=models.TextField(help_text="Only MarkDown is authorized"),
),
]

View File

21
jarvis/core/models.py Normal file
View File

@ -0,0 +1,21 @@
from django.db import models
import markdown
class Citation(models.Model):
"""
Représente les citations.
"""
quote = models.TextField(
help_text="Only MarkDown is authorized",
)
author = models.CharField(max_length=50, null=True, blank=True)
def __str__(self):
return f"{self.quote} - {self.author}"
def to_markdown(self):
"""Convertit le champ `informations` en (Github-flavored) Markdown."""
return markdown.markdown(self.quote)

View File

@ -0,0 +1,349 @@
{% load static %}
{% load menuitems %}
{% load has_group %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="author" content="Gregory Trullemans">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="apple-touch-icon" sizes="76x76" href="{% static "img/apple-icon.png" %}">
<link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
<title>• {% block page_title %}Jarvis{% endblock %} •</title>
<!-- Fonts and icons -->
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet" />
<!-- Font Awesome Pro -->
<link href="{% static "css/font_awesome_all_5.15.3.css" %}" rel="stylesheet" />
<!-- Full Calendar Plugin, full documentation here: https://github.com/fullcalendar/fullcalendar -->
<link href="{% static "js/plugins/fullcalendar/main.min.css" %}" rel="stylesheet" />
<!-- JQuery UI CSS -->
<link href="{% static "js/plugins/jqueryui/jquery-ui.theme.min.css" %}" rel="stylesheet" />
<link href="{% static "js/plugins/jqueryui/jquery-ui.min.css" %}" rel="stylesheet" />
<!-- Nucleo Icons -->
<link href="{% static "css/nucleo-icons.css" %}" rel="stylesheet" />
<!-- CSS Files -->
<link href="{% static "css/black-dashboard.css" %}" rel="stylesheet" />
<!-- Maps by mapbox -->
<script src='https://api.mapbox.com/mapbox.js/v3.2.0/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v3.2.0/mapbox.css' rel='stylesheet' />
<!-- Core JS Files -->
<script src="{% static "js/core/jquery-3.6.0.min.js" %}"></script>
<script src="{% static "js/core/popper.min.js" %}"></script>
<script src="{% static "js/core/bootstrap.min.js" %}"></script>
<!-- Chart JS -->
<script src="{% static "js/plugins/momentjs/moment_2.29.1.min.js" %}"></script>
<script src="{% static "js/plugins/momentjs/moment_locale_en-gb.js" %}"></script>
<script src="{% static "js/plugins/chartjs/chartjs_3.9.1.min.js" %}"></script>
<script src="{% static "js/plugins/chartjs/chartjs-adapter-moment_1.0.0.js" %}"></script>
<!-- FullCalendar CSS -->
<!-- <link href="{% static "js/plugins/fullcalendar/main.css" %}" rel="stylesheet" /> -->
{% block header %}{% endblock %}
</head>
<body class="sidebar-mini {% if request.session.template == 1 %}white-content{% endif %}">
<div class="wrapper">
<div class="navbar-minimize-fixed">
<button class="minimize-sidebar btn btn-link btn-just-icon">
<i class="tim-icons icon-align-center visible-on-sidebar-regular text-muted"></i>
<i class="tim-icons icon-bullet-list-67 visible-on-sidebar-mini text-muted"></i>
</button>
</div>
<div class="sidebar">
<div class="sidebar-wrapper">
<div class="logo">
<a href="javascript:void(0)" class="simple-text logo-mini">TRA</a>
<a href="javascript:void(0)" class="simple-text logo-normal">Trampoline</a>
</div>
<ul class="nav">
{% menuitem 'home' 'fal fa-chart-pie' 'Dashboard' %}
{% menuitem 'gymnast_list' 'tim-icons icon-badge' 'Gymnasts' %}
{% menuitem 'skill_list' 'tim-icons icon-molecule-40' 'Skills' %}
{% menuitem 'routine_list' 'tim-icons icon-components' 'Routines' %}
{% menuitem 'event_list' 'fal fa-calendar-alt' 'Events' %}
{% if request.user|has_group:"trainer" %}
{% menuitem 'accident_list' 'fal fa-comment-alt-medical' 'Accidents' %}
{% endif %}
{% menuitem 'place_list' 'fal fa-map-marked-alt' 'Places' %}
{% if request.user|has_group:"trainer" %}
{% menuitem 'chrono_list' 'fal fa-stopwatch' 'Chronos' %}
{% endif %}
{% if request.user.is_staff %}
<li>
<a href="/admin/" target="_blank">
<!-- <i class="tim-icons icon-settings"></i> -->
<i class="fal fa-tools"></i>
<p>Administration</p>
</a>
</li>
{% endif %}
</ul>
</div>
</div>
<div class="main-panel">
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-absolute navbar-transparent">
<div class="container-fluid">
<div class="navbar-wrapper">
<div class="navbar-minimize d-inline">
<button class="minimize-sidebar btn btn-link btn-just-icon" rel="tooltip" data-original-title="Sidebar toggle" data-placement="right">
<i class="tim-icons icon-align-center visible-on-sidebar-regular"></i>
<i class="tim-icons icon-bullet-list-67 visible-on-sidebar-mini"></i>
</button>
</div>
<div class="navbar-toggle d-inline">
<button type="button" class="navbar-toggler">
<span class="navbar-toggler-bar bar1"></span>
<span class="navbar-toggler-bar bar2"></span>
<span class="navbar-toggler-bar bar3"></span>
</button>
</div>
<a class="navbar-brand" href="/">Jarvis</a>
</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navigation" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
<span class="navbar-toggler-bar navbar-kebab"></span>
</button>
<div class="collapse navbar-collapse" id="navigation">
<ul class="navbar-nav ml-auto">
<li class="search-bar input-group">
<button class="btn btn-link" id="search-button" data-toggle="modal" data-target="#searchModal">
<i class="tim-icons icon-zoom-split"></i>
<span class="d-lg-none d-md-block">Search</span>
</button>
</li>
<li class="dropdown nav-item">
<a href="#" class="dropdown-toggle nav-link" data-toggle="dropdown">
<div class="photo">
<img src="{% static '/img/default-avatar.png' %}" alt="Profile Photo">
</div>
<b class="caret d-none d-lg-block d-xl-block"></b>
<p class="d-lg-none">Log out</p>
</a>
<ul class="dropdown-menu dropdown-navbar">
<li class="nav-link">
<a href="{% url 'profile_update' %}" class="nav-item dropdown-item"><i class="fal fa-id-card-alt"></i>&nbsp;{{ request.user }}</a>
</li>
{% if request.user|has_group:"trainer" %}
<li class="nav-link">
<a href="{% url 'notification_update' %}" class="nav-item dropdown-item"><i class="fal fa-envelope"></i>&nbsp;Notifications</a>
</li>
{% endif %}
<li class="dropdown-divider"></li>
<li class="nav-link">
<a href="{% url 'logout' %}" class="nav-item dropdown-item"><i class="fal fa-sign-out-alt"></i>&nbsp;Log out</a>
</li>
</ul>
</li>
<li class="separator d-lg-none"></li>
</ul>
</div>
</div>
</nav>
<div class="modal modal-search" id="searchModal" tabindex="-1" role="dialog" aria-labelledby="searchModal" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<form action="{% url 'global_search' %}" method="GET" class="sidebar-form">
<input type="text" class="form-control" name="pattern" id="inlineFormInputGroup" placeholder="Global Search..." autofocus>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<i class="tim-icons icon-simple-remove"></i>
</button>
</form>
</div>
</div>
</div>
</div>
<!-- End Navbar -->
<div class="content">
{% block content %}{% endblock %}
</div>
</div>
</div>
<script src="{% static "js/plugins/perfect-scrollbar.jquery_1.4.0.min.js" %}"></script>
<!-- Plugin for Switches, full documentation here: http://www.jque.re/plugins/version3/bootstrap.switch/ -->
<script src="{% static "js/plugins/bootstrap-switch_3.3.4.js" %}"></script>
<!-- Plugin for Sweet Alert -->
<script src="{% static "js/plugins/sweetalert2.min.js" %}"></script>
<!-- Plugin for Sorting Tables -->
<script src="{% static "js/plugins/jquery.tablesorter_2.0.5b.js" %}"></script>
<!-- Forms Validations Plugin -->
<script src="{% static "js/plugins/jquery.validate_1.17.0.min.js" %}"></script>
<!-- Plugin for the Wizard, full documentation here: https://github.com/VinceG/twitter-bootstrap-wizard -->
<script src="{% static "js/plugins/jquery.bootstrap-wizard_1.4.2.js" %}"></script>
<!-- Plugin for Select, full documentation here: http://silviomoreto.github.io/bootstrap-select -->
<script src="{% static "js/plugins/bootstrap-selectpicker_1.12.4.js" %}"></script>
<!-- Plugin for the DateTimePicker, full documentation here: https://eonasdan.github.io/bootstrap-datetimepicker/ -->
<script src="{% static "js/plugins/bootstrap-datetimepicker_4.17.47.js" %}"></script>
<!-- DataTables.net Plugin, full documentation here: https://datatables.net/ -->
<script src="{% static "js/plugins/datatables/datatables_1.12.1.min.js" %}"></script>
<!-- Plugin for Tags, full documentation here: https://github.com/bootstrap-tagsinput/bootstrap-tagsinputs -->
<script src="{% static "js/plugins/bootstrap-tagsinput_0.8.0.js" %}"></script>
<!-- Plugin for Fileupload, full documentation here: http://www.jasny.net/bootstrap/javascript/#fileinput -->
<script src="{% static "js/plugins/jasny-bootstrap_3.1.3.min.js" %}"></script>
<!-- Full Calendar Plugin, full documentation here: https://github.com/fullcalendar/fullcalendar -->
<script src="{% static "js/plugins/fullcalendar/main.js" %}"></script>
<!-- Vector Map plugin, full documentation here: http://jvectormap.com/documentation/ -->
<script src="{% static "js/plugins/jquery-jvectormap_2.0.4.js" %}"></script>
<!-- Plugin for the Sliders, full documentation here: http://refreshless.com/nouislider/ -->
<script src="{% static "js/plugins/nouislider_11.1.0.min.js" %}"></script>
<!-- Notifications Plugin -->
<script src="{% static "js/plugins/bootstrap-notify_3.1.5.js" %}"></script>
<!-- Control Center for Black Dashboard: parallax effects, scripts for the example pages etc -->
<script src="{% static "js/black-dashboard.js" %}"></script>
<!-- Jquery UI for autocomplete, etc. -->
<script src="{% static "js/plugins/jqueryui/jquery-ui.min.js" %}"></script>
<script type="text/javascript">
$(document).ready(function() {
$().ready(function() {
$sidebar = $('.sidebar');
$navbar = $('.navbar');
$main_panel = $('.main-panel');
$full_page = $('.full-page');
$sidebar_responsive = $('body > .navbar-collapse');
{% if request.session.is_sidebar_minified %}sidebar_mini_active = true;
{% else %}sidebar_mini_active = false;
{% endif %}
{% if request.session.template == 0 %}white_color = false;
{% else %}white_color = true;
{% endif %}
window_width = $(window).width();
fixed_plugin_open = $('.sidebar .sidebar-wrapper .nav li.active a p').html();
{% if request.session.sidebar == 1 %}color = 'blue';
{% elif request.session.sidebar == 2 %}color = 'green';
{% elif request.session.sidebar == 3 %}color = 'orange';
{% elif request.session.sidebar == 4 %}color = 'red';
{% else %}color = 'purple'
{% endif %}
$sidebar.attr('data', color);
$main_panel.attr('data', color);
$full_page.attr('filter-color', color);
$sidebar_responsive.attr('data', color);
// $('.fixed-plugin a').click(function(event) {
// if ($(this).hasClass('switch-trigger')) {
// if (event.stopPropagation) {
// event.stopPropagation();
// } else if (window.event) {
// window.event.cancelBubble = true;
// }
// }
// });
// $('.fixed-plugin .background-color span').click(function() {
// $(this).siblings().removeClass('active');
// $(this).addClass('active');
// var new_color = $(this).data('color');
// if ($sidebar.length != 0)
// $sidebar.attr('data', new_color);
// if ($main_panel.length != 0)
// $main_panel.attr('data', new_color);
// if ($full_page.length != 0)
// $full_page.attr('filter-color', new_color);
// if ($sidebar_responsive.length != 0)
// $sidebar_responsive.attr('data', new_color);
// });
// $('.switch-sidebar-mini input').on("switchChange.bootstrapSwitch", function() {
// var $btn = $(this);
// if (sidebar_mini_active == true) {
// $('body').removeClass('sidebar-mini');
// sidebar_mini_active = false;
// blackDashboard.showSidebarMessage('Sidebar mini deactivated...');
// } else {
// $('body').addClass('sidebar-mini');
// sidebar_mini_active = true;
// blackDashboard.showSidebarMessage('Sidebar mini activated...');
// }
// // we simulate the window Resize so the charts will get updated in realtime.
// var simulateWindowResize = setInterval(function() {
// window.dispatchEvent(new Event('resize'));
// }, 180);
// // we stop the simulation of Window Resize after the animations are completed
// setTimeout(function() {
// clearInterval(simulateWindowResize);
// }, 1000);
// });
// $('.switch-change-color input').on("switchChange.bootstrapSwitch", function() {
// var $btn = $(this);
// if (white_color == true) {
// $('body').addClass('change-background');
// setTimeout(function() {
// $('body').removeClass('change-background');
// $('body').removeClass('white-content');
// }, 900);
// white_color = true; // false
// } else {
// $('body').addClass('change-background');
// setTimeout(function() {
// $('body').removeClass('change-background');
// $('body').addClass('white-content');
// }, 900);
// white_color = true;
// }
// });
$('.light-badge').click(function() {
$('body').addClass('white-content');
});
$('.dark-badge').click(function() {
$('body').removeClass('white-content');
});
// $('#searchModal').on('shown.bs.modal', function() {
// $('#inlineFormInputGroup').trigger('focus')
// });
});
});
</script>
{% block footerscript %}{% endblock %}
<!-- CSS Files -->
<link href="{% static "css/jarvis.css" %}" rel="stylesheet" />
</body>
</html>

View File

@ -0,0 +1,180 @@
{% extends "base.html" %}
{% load static %}
{% block page_title %}Dashboard{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-quote-left text-danger"></i> Quote</h4>
</div>
<div class="card-body text-justify">
{{ quote.to_markdown | safe }}
</div>
{% if quote.author %}
<div class="card-footer text-right text-muted">
<i>{{ quote.author }}</i>
</div>
{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">
<h4 class=""><i class="text-primary fal fa-laugh-wink"></i> Hi {{ user.username }} !</h4>
</div>
<div class="card-body text-justify">
<p>Welcome to Jarvi v0.80 <span class="text-muted">(last update : 29-01-2023)</span></p>
<p>This application is here to help coaches to manage the gymnasts (evolution, evaluation, routines, scores, …). This tool is not perfect so feel free to make improvement proposals, bug reports, … by sending me an <a href="mailto:gregory@flyingacrobaticstrampoline.be">email</a>.</p>
<p>You can find the user manuel <a href="{% static "files/Manuel_Utilisateur.pdf" %}" download>here (in french)</a>.</p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-chart-area text-warning"></i> Statistics</h4>
</div>
<div class="card-body">
<div class="w-lg m-x-auto">
<div class="progress-container progress-primary">
<div class="progress">
<div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="60"
aria-valuemin="0" aria-valuemax="100" style="width: {{ percentage_week }}%;"></div>
</div>
</div>
{% if nb_active_gymnast or nb_event or nb_skill or nb_routine or nb_score or nb_club %}
<div class="row">
<div class="col-md-7">
<ul class="list-unstyled mb-0">
{% if nb_active_gymnast %}<li>{{ nb_active_gymnast }} active gymnasts</li>{% endif %}
{% if nb_event %}<li>{{ nb_event }} events</li>{% endif %}
{% if nb_score %}<li>{{ nb_score }} scores</li>{% endif %}
</ul>
</div>
<div class="col-md-5">
<ul class="list-unstyled mb-0">
{% if nb_skill %}<li>{{ nb_skill }} skills</li>{% endif %}
{% if nb_routine %}<li>{{ nb_routine }} routines</li>{% endif %}
{% if nb_club %}<li>{{ nb_club }} clubs</li>{% endif %}
</ul>
</div>
</div>
{% else %}
<br />
No statistics to display.
{% endif %}
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-calendar-alt text-warning"></i> Next Events</h4>
</div>
<div class="card-body">
{% if event_list %}
<table class="table tablesorter table-striped table-condensed" data-sort="table" id="event_table">
{% for event in event_list %}
<tr>
<td class="text-left"><a href="{% url 'event_details' event.id %}">{{ event.name }}</a></td>
<td>
{% if event.number_of_week_from_today < 0 %}
{{event.number_of_week_from_today}}
{% else %}
<span class="text-{% if event.number_of_week_from_today > 12 %}success{% elif event.number_of_week_from_today > 9 %}info{% elif event.number_of_week_from_today > 6 %}warning{% else %}danger{% endif %}">
<b>{{event.number_of_week_from_today}}</b></span>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% else %}
No future event defined
{% endif %}
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-exclamation-triangle text-danger"></i> Updated needed</h4>
</div>
<div class="card-body">
{% if waiting_update_gymnast %}
<table class="table tablesorter table-striped table-condensed" data-sort="table" id="gymnast_table">
{% for gymnast in waiting_update_gymnast %}
<tr>
<td class="text-left"><a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast }}</a></td>
<td class="text-right">{{ gymnast.club.acronym }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No update needed.
{% endif %}
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-highlighter text-success"></i> Last updated gymnasts</h4>
</div>
<div class="card-body">
{% if last_updated_gymnast %}
<table class="table tablesorter table-striped table-condensed" data-sort="table" id="gymnast_table">
{% for gymnast in last_updated_gymnast %}
<tr>
<td class="text-left"><a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast }}</a></td>
<td class="text-right">{{ gymnast.club.acronym }}</td>
</tr>
{% endfor %}
</table>
{% else %}
No update since your last visit
{% endif %}
</div>
</div>
</div>
<div class="col-md-3">
<div class="card">
<div class="card-header">
<h4><i class="fal fa-birthday-cake text-info"></i> Next birthday</h4>
</div>
<div class="card-body">
{% if birthday_list %}
<table class="table tablesorter table-striped table-condensed" data-sort="table" id="gymnast_table">
{% for gymnast in birthday_list %}
<tr>
<td class="text-left"><a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast.first_name }}</a></td>
<td class="">{{ gymnast.birthdate | date:"j M"}}</td>
<td class="text-right">{{ gymnast.next_age }} years</td>
</tr>
{% endfor %}
</table>
{% else %}
No next birtday (it's a bug).
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
{% endblock%}

View File

@ -0,0 +1,41 @@
{% extends "base.html" %}
{% block content %}
<!-- <div class="flextable table-actions">
<div class="flextable-item flextable-primary">
<form action="/{% block searchurl %}{% endblock %}/search/" method="GET">
<div class="btn-toolbar-item input-with-icon">
<input type="text" class="form-control input-block" name="pattern" placeholder="Search {% block search %}{% endblock %}">
<span class="icon icon-magnifying-glass"></span>
</div>
</form>
</div>
<div class="flextable-item">
<div class="btn-group">
<a href="/{% block addurl %}{% endblock %}/add">
<button type="button" class="btn btn-primary-outline">
<span class="icon icon-plus"></span>
</button>
</a>
</div>
</div>
</div> -->
<!-- <div class="table-full">
<div class="table-responsive">
{% block upper %}{% endblock %}
</div>
</div> -->
<div class="row">
<div class="col-12">
{% block datacontent %}{% endblock %}
<!-- <div class="flextable table-actions">
{% block under %}{% endblock %}
</div> -->
</div>
</div>
</div>
<!-- {% block pagination %}{% endblock %} -->
{% endblock %}

View File

@ -0,0 +1,132 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="author" content="Gregory Trullemans">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="apple-touch-icon" sizes="76x76" href="{% static "img/apple-icon.png" %}">
<link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
<!-- <title>{% block page_title %}{% endblock %}</title> -->
<title>• JARVIS •</title>
<!-- Fonts and icons -->
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet" />
<link href="https://use.fontawesome.com/releases/v5.0.6/css/all.css" rel="stylesheet" />
<!-- Nucleo Icons -->
<link href="{% static "css/nucleo-icons.css" %}" rel="stylesheet" />
<!-- CSS Files -->
<link href="{% static "css/black-dashboard.css" %}" rel="stylesheet" />
<!-- CSS Just for demo purpose, don't include it in your project -->
<!-- <link href="../assets/demo/demo.css" rel="stylesheet" /> -->
<!-- <link rel="stylesheet" href="{% static "css/application.css" %}"> -->
</head>
<body>
<div class="wrapper wrapper-full-page ">
<div class="full-page login-page " data-color="red">
<!-- you can change the color of the filter page using: data-color="blue | purple | green | orange | red | rose " -->
<div class="content">
<div class="container">
<div class="col-lg-4 col-md-6 ml-auto mr-auto">
<form class="form" action="/login/" method="post" if="formulaire">
{% csrf_token %}
<div class="card card-login card-white">
<div class="card-header">
<img src="{% static "img/card-danger.png" %}" alt="">
<h1 class="card-title">&nbsp;Log in</h1>
</div>
<div class="card-body">
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="tim-icons icon-single-02"></i>
</div>
</div>
<input type="login" name="login" class="form-control" id="login" placeholder="Login">
</div>
<div class="input-group">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="tim-icons icon-lock-circle"></i>
</div>
</div>
<input type="password" name="password" class="form-control" id="password" placeholder="Password">
</div>
{% if message %}
<p class="text-danger"><b>{{message}}</b></p>
{% endif %}
</div>
<div class="card-footer">
<button type="submit" class="btn btn-danger btn-lg btn-block mb-3">Log me in</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<footer>
<div class="footer-content"></div>
</footer>
<script src="{% static "js/core/jquery.min.js" %}"></script>
<script src="{% static "js/core/popper.min.js" %}"></script>
<script src="{% static "js/core/bootstrap.min.js" %}"></script>
<script src="{% static "js/plugins/perfect-scrollbar.jquery.min.js" %}"></script>
<script src="{% static "js/plugins/moment.min.js" %}"></script>
<!-- Plugin for Switches, full documentation here: http://www.jque.re/plugins/version3/bootstrap.switch/ -->
<script src="{% static "js/plugins/bootstrap-switch.js" %}"></script>
<!-- Plugin for Sweet Alert -->
<script src="{% static "js/plugins/sweetalert2.min.js" %}"></script>
<!-- Plugin for Sorting Tables -->
<script src="{% static "js/plugins/jquery.tablesorter.js" %}"></script>
<!-- Forms Validations Plugin -->
<script src="{% static "js/plugins/jquery.validate.min.js" %}"></script>
<!-- Plugin for the Wizard, full documentation here: https://github.com/VinceG/twitter-bootstrap-wizard -->
<script src="{% static "js/plugins/jquery.bootstrap-wizard.js" %}"></script>
<!-- Plugin for Select, full documentation here: http://silviomoreto.github.io/bootstrap-select -->
<script src="{% static "js/plugins/bootstrap-selectpicker.js" %}"></script>
<!-- Plugin for the DateTimePicker, full documentation here: https://eonasdan.github.io/bootstrap-datetimepicker/ -->
<script src="{% static "js/plugins/bootstrap-datetimepicker.js" %}"></script>
<!-- DataTables.net Plugin, full documentation here: https://datatables.net/ -->
<script src="{% static "js/plugins/jquery.dataTables.min.js" %}"></script>
<!-- Plugin for Tags, full documentation here: https://github.com/bootstrap-tagsinput/bootstrap-tagsinputs -->
<script src="{% static "js/plugins/bootstrap-tagsinput.js" %}"></script>
<!-- Plugin for Fileupload, full documentation here: http://www.jasny.net/bootstrap/javascript/#fileinput -->
<script src="{% static "js/plugins/jasny-bootstrap.min.js" %}"></script>
<!-- Full Calendar Plugin, full documentation here: https://github.com/fullcalendar/fullcalendar -->
<script src="{% static "js/plugins/fullcalendar.min.js" %}"></script>
<!-- Vector Map plugin, full documentation here: http://jvectormap.com/documentation/ -->
<script src="{% static "js/plugins/jquery-jvectormap.js" %}"></script>
<!-- Plugin for the Sliders, full documentation here: http://refreshless.com/nouislider/ -->
<script src="{% static "js/plugins/nouislider.min.js" %}"></script>
<!-- Google Maps Plugin -->
<!-- Place this tag in your head or just before your close body tag. -->
<!-- <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY_HERE"></script> -->
<!-- Chart JS -->
<script src="{% static "js/plugins/chartjs.min.js" %}"></script>
<!-- Notifications Plugin -->
<script src="{% static "js/plugins/bootstrap-notify.js" %}"></script>
<!-- Control Center for Black Dashboard: parallax effects, scripts for the example pages etc -->
<script src="{% static "js/black-dashboard.min.js" %}"></script>
<script type="text/javascript" >
$(function(){
$('#login').focus();
});
</script>
</body>
</html>

View File

@ -0,0 +1,70 @@
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="keywords" content="">
<meta name="description" content="">
<meta name="author" content="Gregory Trullemans">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="apple-touch-icon" sizes="76x76" href="{% static "img/apple-icon.png" %}">
<link rel="icon" type="image/png" href="{% static "img/favicon.png" %}">
<title>List of records</title>
<!-- Fonts and icons -->
<link href="https://fonts.googleapis.com/css?family=Poppins:200,300,400,600,700,800" rel="stylesheet" />
<!-- Font Awesome Pro -->
<link href="{% static "css/gymnast_report.css" %}" rel="stylesheet" />
<link href="{% static "css/font_awesome_all_5.15.3.css" %}" rel="stylesheet" />
<link href="{% static "css/black-dashboard_report.css" %}" rel="stylesheet" />
</head>
<header class="white-content">
<div class="row">
<div id="header-left" class="col-7 text-12">
{{ SITE_TITLE }} - {{ CLUB_NAME }}<br />
{{ ADDRESS }} - {{ ZIP }} {{ CITY }}<br />
Season {{ season }} - week {{ week_number }}
</div>
<div id="header-right" class="col-5 text-right text-12">
Head Coach : {{ HEAD_COACH }}<br />
{{ HEAD_COACH_EMAIL }}<br />
{{ today | date:"j F Y" }}
</div>
</div>
<br />
<br />
</header>
<body class="white-content">
<div class="row">
<div class="col-12 text-center">
<h1>Top 10| chrono scores</h1>
<br />
<br />
</div>
<div class="col-6 pl-0">
{% for record in records_list %}
<h3 class="ml-3">{{ record.score }} - {{ record.gymnast__first_name }} {{ record.gymnast__last_name }}</h3>
{% endfor %}
</div>
<div class="col-6 pl-0">
<!-- (seconde partie de la liste) -->
</div>
</div>
<!-- <div class="row">
<div class="col-12">
Confirmé par
</div>
<div class="col-12 text-center">
<img src="{% static '/img/chatons_hebdo.png' %}" alt="Chaton hebdo" width="350">
</div>
</div> -->
</body>
</html>

View File

@ -0,0 +1,264 @@
{% extends "base.html" %}
<!-- {% block page_title %}.: Search results :.{% endblock %} -->
{% block title %}Search results{% endblock %}
{% block content %}
<div class="card mb-0">
{% if gymnast_list or skill_list or event_list or place_list or club_list %}
{% if gymnast_list %}
<div class="card-header">
<h4 class="mb-0"> Gymnasts results</h4>
</div>
<div class="card-body pt-0 pb-1">
<div class="table-responsive pb-0">
<table class="table tablesorter table-striped" data-sort="table" id="gymnast_table">
<thead>
<tr>
<th class="header text-left" style="width: 20%">Firstname</th>
<th class="header text-left" style="width: 20%">Lastname</th>
<th class="header text-left" style="width: 15%">Gender</th>
<th class="header text-left" style="width: 15%">Age</th>
</tr>
</thead>
<tbody>
{% for gymnast in gymnast_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td class="text-left">
<a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast.first_name }}</a>
</td>
<td class="text-left">
<a href="{% url 'gymnast_details' gymnast.id %}">{{ gymnast.last_name }}</a>
</td>
<td class="text-left">{{ gymnast.get_gender_display }}</td>
<td class="text-left">{{ gymnast.age }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if skill_list %}
<div class="card-header">
<h4 class="mb-0"> Skills results</h4>
</div>
<div class="card-body pt-0 pb-1">
<div class="table-responsive pb-0">
<table class="table tablesorter table-striped" data-sort="table" id="skill_table">
<thead class="text-primary">
<tr>
<th class="text-left">&nbsp;Long Label</th>
<th class="text-left">&nbsp;Short Label</th>
<th class="text-center">&nbsp;Age Girl</th>
<th class="text-center">&nbsp;Age Boy</th>
<th class="text-center">Notation</th>
<th class="header text-center">Diff.</th>
<th class="header text-center">Level</th>
<th class="header text-center">Rank</th>
</tr>
</thead>
<tbody>
{% for skill in skill_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td class="text-left">&nbsp;<a href="{% url 'skill_details' skill.id %}">{{ skill.long_label }}</a></td>
<td class="text-left"><a href="{% url 'skill_details' skill.id %}">{{ skill.short_label }}</a></td>
<td class="text-right">{{ skill.age_girl_masterised }}</td>
<td class="text-right">{{ skill.age_boy_masterised }}</td>
<td class="text-center">{{ skill.notation }}</td>
<td class="text-center">{{ skill.difficulty }}</td>
<td class="text-center">{{ skill.level }}</td>
<td class="text-center">{{ skill.rank }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if event_list %}
<div class="card-header">
<h4 class="mb-0"> Events results</h4>
</div>
<div class="card-body pt-0 pb-1">
<div class="table-responsive pb-0">
<table class="table tablesorter table-striped" data-sort="table" id="event_table">
<thead>
<tr>
<th class="text-left">Event</th>
<th class="text-left">Type</th>
<th class="text-center">Date</th>
<th class="text-center"># week</th>
<th class="text-left">Place</th>
</tr>
</thead>
<tbody>
{% for event in event_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td class="text-left"><a href="{% url 'event_details' event.id %}">{{ event.name }}</a></td>
<td class="text-left">{{ event.event_type.name }}</td>
<td class="text-center">{{ event.date_begin | date:"d-m-Y"}}</td>
<td class="text-center">{% if event.number_of_week_from_today < 0 %}{{event.number_of_week_from_today}}{% else %}<span class="text-{% if event.number_of_week_from_today > 12 %}success{% elif event.number_of_week_from_today > 9 %}info{% elif event.number_of_week_from_today > 6 %}warning{% else %}danger{% endif %}"><b>{{event.number_of_week_from_today}}</b></span>{% endif %}</td>
<td class="text-left">{{ event.place }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if place_list %}
<div class="card-header">
<h4 class="mb-0"> Places results</h4>
</div>
<div class="card-body pt-0 pb-1">
<div class="table-responsive pb-0">
<table class="table tablesorter table-striped" data-sort="table" id="place_table">
<thead>
<tr>
<th class="header text-left" style="width: 27%">Name</th>
<th class="header text-left" style="width: 35%">Address</th>
<th class="header text-center" style="width: 10%">Zip</th>
<th class="header text-left" style="width: 15%">City</th>
<!-- <th class="header text-left" style="width: 4%">#km</th>
<th class="header text-left" style="width: 4%">#min</th> -->
<!-- <th class="header text-center">Active ?</th> -->
</tr>
</thead>
<tbody>
{% for place in place_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td class="text-left"><a href="{% url 'place_details' place.id %}">{{ place.name }}</a></td>
<td class="text-left">{{ place.address }}</td>
<td class="text-left">{{ place.postal}}</td>
<td class="text-left">{{ place.city }}</td>
<!-- <td class="text-center">{% if place.nbkm %}{{ place.nbkm }}{% else %}0{% endif %}</td>
<td class="text-center">{% if place.timing %}{{ place.timing }}{% else %}0{% endif %}</td> -->
<!-- <td class="text-center">{{ place.active }}</td> -->
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if club_list %}
<div class="card-header">
<h4 class="mb-0"> Clubs results</h4>
</div>
<div class="card-body pt-0 pb-1">
<div class="table-responsive pb-0">
<table class="table tablesorter table-striped" data-sort="table" id="club_table">
<thead>
<tr>
<th class="header text-left" style="width: 35%">Name</th>
<th class="header text-left" style="width: 10%">Acronym</th>
<th class="header text-left" style="width: 35%">City</th>
</tr>
</thead>
<tbody>
{% for club in club_list %}
<tr>
<td class="text-left"> {{ club.name }}</td>
<td class="text-left">{{ club.acronym }}</td>
<td class="text-left">{{ club.place.city }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% else %}
<div class="card-header">
<h4 class="mb-0"> Search results</h4>
</div>
<div class="card-body">
<p>There are no items corresponding to your criterias : "{{ pattern }}"</p>
</div>
{% endif %}
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function() {
$('#gymnast_table').tablesorter({
headers: {
0: { sorter: false }, // disable first column
},
dateFormat: "uk",
sortList: [[1,0], [2,0]]
})
$('#gymnast_table').DataTable({
paging: false,
searching: false,
ordering: false,
"bInfo" : false,
});
$('#skill_table').tablesorter({
headers: {
4: { sorter: false },
},
sortList: [[0,0], [1,0]]
})
$('#skill_table').DataTable({
paging: false,
searching: false,
ordering: false,
"bInfo" : false,
});
$('#event_table').tablesorter({
headers: {
0: { sorter: false }, // disable first column
},
dateFormat: "uk",
sortList: [[3,1], [1,0]]
});
$('#event_table').DataTable({
paging: false,
searching: false,
ordering: false,
"bInfo" : false,
});
$('#place_table').tablesorter({
headers: {
0: { sorter: false }, // disable first column
},
dateFormat: "uk",
sortList: [[3,0], [1,0]]
});
$('#place_table').DataTable({
paging: false,
searching: false,
ordering: false,
"bInfo" : false,
});
$('#club_table').tablesorter({
sortList: [[0,0], [2,0]]
});
$('#club_table').DataTable({
paging: false,
searching: false,
ordering: false,
"bInfo" : false,
});
});
</script>
{% endblock %}

29
jarvis/core/tests.py Normal file
View File

@ -0,0 +1,29 @@
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
USER = get_user_model()
class HomeTests(TestCase):
def setUp(self):
self.user = USER.objects.create(
username="jbond", email="james@hms.co.uk", password="007"
)
def test_home_view_anonymous_redirected_statuts_code(self):
url = reverse("home")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
def test_home_view_status_code_with_user_connected(self):
"""Note: This test will switch to green once we will get rid of whitenoise
See https://stackoverflow.com/questions/50658241/django-doesnt-load-static-files-valueerrormissing-staticfiles-manifest-entry # pylint: disable=line-too-long
"""
self.client.login(username="jbond", password="007")
url = reverse("home")
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)

16
jarvis/core/urls.py Normal file
View File

@ -0,0 +1,16 @@
from django.urls import path
from .views import login, logout, home, search, generate_best_straightjump_listing
urlpatterns = [
path(
r"core/record_straightjump/",
generate_best_straightjump_listing,
name="generate_best_straightjump_listing",
),
path(r"search/", search, name="global_search"),
path(r"login/", login, name="login"),
path(r"logout/", logout, name="logout"),
path(r"", home, name="home"),
]

View File

@ -0,0 +1,6 @@
def user_has_group_trainer(user):
return user.groups.filter(name='Trainer').exists()
def user_has_group_gymnast(user):
return user.groups.filter(name='Gymnast').exists()

252
jarvis/core/views.py Normal file
View File

@ -0,0 +1,252 @@
from datetime import timedelta
from django.db.models import Q
from django.shortcuts import render
from django.utils import timezone
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_http_methods
from django.template.loader import render_to_string
from django.urls import reverse
from jarvis.objective.models import Routine
from jarvis.profiles.models import Profile
from jarvis.followup.models import Skill, Point, Chrono
from jarvis.location.models import Place, Club
from jarvis.people.models import Gymnast
from jarvis.people.views import gymnast_details
from jarvis.planning.models import Event
from django.db.models import Max
from django.conf import settings
from .models import Citation
from jarvis.tools.models import from_date_to_week_number
from weasyprint import HTML, CSS
import pendulum
def login(request):
"""
Formulaire d'authentifictation.
"""
if request.method == "POST":
username = request.POST["login"]
password = request.POST["password"]
user = authenticate(username=username, password=password)
if user is not None: # Pq pas "if user:" ??
if user.is_active:
auth_login(request, user)
try:
profile = Profile.objects.get(user=user)
request.session["profileid"] = profile.id
request.session["template"] = profile.template_color
request.session["sidebar"] = profile.sidebar_color
request.session["is_sidebar_minified"] = profile.is_sidebar_minified
except Exception:
pass
# request.session["clubid"] = request.POST.get("clubid", None)
return HttpResponseRedirect(reverse("home"))
context = {"message": "Account disabled."}
else:
context = {"message": "Wrong login/password."}
else:
context = {}
return render(request, "login.html", context)
@login_required
@require_http_methods(["GET"])
def logout(request):
"""
Fonction de déconnexion
"""
auth_logout(request)
return HttpResponseRedirect(reverse("login"))
def next_birthdays(request, number_of_birthday):
"""
Renvoie la liste des `number_of_birthday` prochains anniversaires.
"""
birthday_list = sorted(
Gymnast.objects.all(), key=lambda t: t.next_birthday_in_days
)[:number_of_birthday]
return birthday_list
# @lru_cache()
# def get_last_updated_gymnasts(expiration_date):
# ...
@login_required
@require_http_methods(["GET"])
def home(request):
"""
Génère la page d'accueil du site basée sur la saison (si celle-ci est connue)
"""
event_list = Event.objects.filter(date_begin__gte=timezone.now()).order_by(
"date_begin"
)[:10]
quote = Citation.objects.order_by("?").first()
# print(quote)
# mettre tout ca en cache.
last_updated_gymnast = Gymnast.objects.filter(
Q(mindstate__created_at__gt=request.user.last_login)
| Q(points__created_at__gt=request.user.last_login)
| Q(chronos__created_at__gt=request.user.last_login)
| Q(accident__created_at__gt=request.user.last_login)
| Q(known_skills__created_at__gt=request.user.last_login)
).distinct()
limit_date = timezone.now() - timedelta(days=14)
waiting_update_gymnast = Gymnast.objects.exclude(
Q(is_active=False)
| Q(mindstate__created_at__gte=limit_date)
| Q(points__created_at__gte=limit_date)
| Q(chronos__created_at__gte=limit_date)
| Q(accident__created_at__gte=limit_date)
| Q(known_skills__created_at__gte=limit_date)
).distinct()
nb_active_gymnast = Gymnast.objects.filter(is_active=True).count()
nb_event = Event.objects.all().count()
nb_skill = Skill.objects.all().count()
nb_routine = Routine.objects.all().count()
nb_score = Point.objects.all().count()
nb_club = Club.objects.all().count()
# percentage_week = int(
# (get_number_of_weeks_between(datetime(2021, 9, 1), datetime.now()) / 52) * 100
# )
date_begin = pendulum.now().date()
season, week_number = from_date_to_week_number(date_begin)
percentage_week = (week_number / 52) * 100
birthday_list = next_birthdays(request, 10)
# check if gymnast have point
# ---------------------------
# 1. récupérer tous les évènements passés
# 2. pour chaque event, vérifier que tous les gymnastes renseignés
# dans les participants ont des points associés.
# S'il n'y a pas de point, faire une alerte à l'utilisateur qui se connecte.
# Check if gymnast have update
# -----------------------------
# lister tous les gymnastes qui n'ont pas eu d'update depuis... 2 semaines ?
# peut-être le paramètre (en jour) devrait être stocké en DB.
# S'il n'y a pas d'update, faire une alerte à l'utilisateur qui se connecte.
context = {
"quote": quote,
"event_list": event_list,
"last_updated_gymnast": last_updated_gymnast,
"waiting_update_gymnast": waiting_update_gymnast,
"nb_active_gymnast": nb_active_gymnast,
"nb_event": nb_event,
"nb_skill": nb_skill,
"nb_routine": nb_routine,
"nb_score": nb_score,
"nb_club": nb_club,
"percentage_week": percentage_week,
"birthday_list": birthday_list,
}
return render(request, "dashboard/dashboard.html", context)
@login_required
@require_http_methods(["GET"])
def search(request):
"""
Recherche globale au travers de toutes les applications.
"""
pattern = request.GET.get("pattern", None)
if pattern:
gymnast_list = Gymnast.objects.filter(
Q(last_name__icontains=pattern) | Q(first_name__icontains=pattern)
)
if gymnast_list.count() == 1:
gymnast = gymnast_list.first()
return gymnast_details(request, gymnast.id)
else:
skill_list = Skill.objects.filter(
Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern)
)
event_list = Event.objects.filter(
Q(name__icontains=pattern) | Q(place__name__icontains=pattern)
)
place_list = Place.objects.filter(
Q(name__icontains=pattern) | Q(city__icontains=pattern)
)
club_list = Club.objects.filter(
Q(name__icontains=pattern)
| Q(place__name__icontains=pattern)
| Q(place__city__icontains=pattern)
)
context = {
"gymnast_list": gymnast_list,
"skill_list": skill_list,
"event_list": event_list,
"place_list": place_list,
"club_list": club_list,
"pattern": pattern,
}
else:
context = {}
return render(request, "search/results.html", context)
def generate_best_straightjump_listing(request):
""" """
date_begin = pendulum.now().date()
season, week_number = from_date_to_week_number(date_begin)
records_list = (
Chrono.objects.values("gymnast__last_name", "gymnast__first_name")
.annotate(score=Max("score"))
.order_by("-score") # [:16]
)
context = {
"SITE_TITLE": settings.SITE_TITLE,
"CLUB_NAME": settings.CLUB_NAME,
"ADDRESS": settings.ADDRESS,
"CITY": settings.CITY,
"ZIP": settings.ZIP,
"HEAD_COACH": settings.HEAD_COACH,
"MOBILE_PHONE": settings.MOBILE_PHONE,
"HEAD_COACH_EMAIL": settings.HEAD_COACH_EMAIL,
"week_number": week_number,
"today": date_begin,
"records_list": records_list,
}
# return render(request, "core/records_10.html", context)
response = HttpResponse(content_type="application/pdf")
response["Content-Disposition"] = "attachment; filename=report-top_straightjump.pdf"
html = render_to_string("jarvis/core/records_10.html", context)
# font_config = FontConfiguration()
HTML(string=html, base_url=request.build_absolute_uri()).write_pdf(
response,
stylesheets=[
CSS(settings.STATICFILES_DIRS[0] + "/css/gymnast_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/black-dashboard_report.css"),
CSS(settings.STATICFILES_DIRS[0] + "/css/font_awesome_all_5.15.3.css"),
],
) # , font_config=font_config)
return response

View File

269
jarvis/followup/admin.py Normal file
View File

@ -0,0 +1,269 @@
from django.contrib import admin
from django_admin_listfilter_dropdown.filters import (
DropdownFilter,
ChoiceDropdownFilter,
RelatedDropdownFilter,
)
from .models import (
Plan,
Note,
Point,
Chrono,
Accident,
MindState,
Intensity,
HeightWeight,
LearnedSkill,
ChronoDetails,
GymnastHasRoutine,
SeasonInformation,
NumberOfRoutineDone,
CompetitivePointsStats,
)
class ChronoAdmin(admin.ModelAdmin):
model = Chrono
list_display = ("date", "gymnast", "tof", "chrono_type")
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_filter = ("chrono_type", ("gymnast", RelatedDropdownFilter))
# list_filter = (("gymnast", RelatedDropdownFilter),)
autocomplete_fields = ("gymnast",)
date_hierarchy = "date"
related_search_fields = {"gymnast": ("last_name", "first_name")}
class ChronoDetailsAdmin(admin.ModelAdmin):
model = ChronoDetails
list_display = ("chrono", "order", "value") # "chrono__gymnast",
list_filter = (
("chrono", RelatedDropdownFilter),
# ('chrono__gymnast', RelatedDropdownFilter),
)
related_search_fields = {
"chrono": ("date", "chrono__gymnast__last_name", "chrono__gymnast__first_name")
}
class LearnedSkillAdmin(admin.ModelAdmin):
model = LearnedSkill
list_display = ("gymnast", "skill", "learning_step", "date")
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_filter = (
("gymnast", RelatedDropdownFilter),
("skill", RelatedDropdownFilter),
"learning_step",
)
search_fields = ("gymnast", "skill")
autocomplete_fields = ("gymnast", "skill")
date_hierarchy = "date"
class PointAdmin(admin.ModelAdmin):
model = Point
list_display = (
"gymnast",
"point_execution",
"point_difficulty",
"point_time_of_flight",
"total",
)
readonly_fields = ("created_at", "updated_at")
ordering = ("gymnast",)
list_filter = (
("gymnast", RelatedDropdownFilter),
("event", RelatedDropdownFilter),
("routine_type", DropdownFilter),
)
search_fields = (
"gymnast__first_name",
"gymnast__last_name",
"event__place_name",
"event__place_city",
"event__place__country_name",
)
autocomplete_fields = ("gymnast", "event")
class AccidentAdmin(admin.ModelAdmin):
model = Accident
fields = ("date", "gymnast", "skill", "informations") # educative
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_display = ("date", "gymnast", "skill") # educative
list_filter = ("date",)
date_hierarchy = "date"
search_fields = ("date", "gymnast") # educative
autocomplete_fields = ("gymnast", "skill")
class MindStateAdmin(admin.ModelAdmin):
model = MindState
fields = ("gymnast", "date", "score", "informations")
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_display = ("date", "gymnast", "score")
list_filter = (
"date",
("gymnast", RelatedDropdownFilter),
)
autocomplete_fields = ("gymnast",)
date_hierarchy = "date"
class GymnastHasRoutineAdmin(admin.ModelAdmin):
model = GymnastHasRoutine
list_display = ("gymnast", "routine", "routine_type", "date_begin", "date_end")
list_filter = (
("gymnast", RelatedDropdownFilter),
("routine_type", DropdownFilter), # A supprimer ?
)
search_fields = ("gymnast", "routine")
autocomplete_fields = ("gymnast", "routine")
class NumberOfRoutineDoneAdmin(admin.ModelAdmin):
model = NumberOfRoutineDone
list_display = (
"gymnast",
"routine_type",
"date",
"number_of_successes",
"number_of_try",
)
list_filter = (
("gymnast", RelatedDropdownFilter),
("routine_type", DropdownFilter), # A supprimer ?
)
autocomplete_fields = ("gymnast", "routine")
date_hierarchy = "date"
class HeightWeightAdmin(admin.ModelAdmin):
model = HeightWeight
list_display = ("gymnast", "height", "hips_height", "weight", "date")
readonly_fields = ("season", "week_number")
list_filter = (("gymnast", RelatedDropdownFilter),)
date_hierarchy = "date"
autocomplete_fields = ("gymnast",)
class PlanAdmin(admin.ModelAdmin):
model = Plan
list_display = ("gymnast", "date", "educative")
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_filter = (
("gymnast", RelatedDropdownFilter),
("educative", RelatedDropdownFilter),
)
search_fields = (
"gymnast__firstname",
"gymnast__lastname",
"educative__long_label",
"educative__short_label",
)
date_hierarchy = "date"
autocomplete_fields = ("gymnast",)
class NoteAdmin(admin.ModelAdmin):
model = Note
list_display = ("gymnast", "coach", "date", "created_at")
readonly_fields = ("season", "week_number", "created_at", "updated_at")
list_filter = (
("gymnast", RelatedDropdownFilter),
("coach", RelatedDropdownFilter),
"status",
)
search_fields = (
"gymnast__firstname",
"gymnast__lastname",
"coach__last_name",
"coach__first_name",
)
date_hierarchy = "created_at"
autocomplete_fields = ("gymnast",)
class IntensityAdmin(admin.ModelAdmin):
model = Intensity
list_display = (
"gymnast",
"time",
"difficulty",
"quantity_of_skill",
"number_of_passes",
)
readonly_fields = ("season", "week_number")
list_filter = (("gymnast", RelatedDropdownFilter),)
search_fields = (
"gymnast__firstname",
"gymnast__lastname",
)
autocomplete_fields = ("gymnast",)
class SeasonInformationAdmin(admin.ModelAdmin):
model = SeasonInformation
list_display = (
"gymnast",
"season",
"category",
"number_of_training_sessions_per_week",
"number_of_hours_per_week",
"number_of_s_and_c_sessions_per_week",
"number_of_s_and_c_hours_per_week",
# "club",
)
list_filter = (("gymnast", RelatedDropdownFilter),)
search_fields = (
"gymnast__firstname",
"gymnast__lastname",
)
autocomplete_fields = ("gymnast",)
class CompetitivePointsStatsAdmin(admin.ModelAdmin):
model = CompetitivePointsStats
list_display = (
"label",
"age_category",
"gender",
"statistic_type",
"total",
"routine_type",
)
list_filter = (
("statistic_type", ChoiceDropdownFilter),
("routine_type", ChoiceDropdownFilter), # A supprimer ?
)
admin.site.register(Plan, PlanAdmin)
admin.site.register(Note, NoteAdmin)
admin.site.register(Point, PointAdmin)
admin.site.register(Chrono, ChronoAdmin)
admin.site.register(Accident, AccidentAdmin)
admin.site.register(MindState, MindStateAdmin)
admin.site.register(Intensity, IntensityAdmin)
admin.site.register(LearnedSkill, LearnedSkillAdmin)
admin.site.register(HeightWeight, HeightWeightAdmin)
admin.site.register(ChronoDetails, ChronoDetailsAdmin)
admin.site.register(SeasonInformation, SeasonInformationAdmin)
admin.site.register(GymnastHasRoutine, GymnastHasRoutineAdmin)
admin.site.register(NumberOfRoutineDone, NumberOfRoutineDoneAdmin)
admin.site.register(CompetitivePointsStats, CompetitivePointsStatsAdmin)

6
jarvis/followup/apps.py Normal file
View File

@ -0,0 +1,6 @@
from django.apps import AppConfig
class FollowupConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "jarvis.followup"

657
jarvis/followup/forms.py Normal file
View File

@ -0,0 +1,657 @@
from datetime import date
from django import forms
from .models import (
Plan,
Note,
Point,
Chrono,
Accident,
MindState,
Intensity,
HeightWeight,
LearnedSkill,
GymnastHasRoutine,
SeasonInformation,
NumberOfRoutineDone,
)
class ChronoForm(forms.ModelForm):
class Meta:
model = Chrono
fields = ("gymnast", "date", "chrono_type", "score_type", "score", "tof")
widgets = {
"gymnast": forms.HiddenInput(),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"chrono_type": forms.Select(attrs={"class": "form-control selectpicker"}),
"score_type": forms.Select(attrs={"class": "form-control selectpicker"}),
"score": forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "xx,xxx",
"min": "0.01",
"step": "0.01",
}
),
"tof": forms.HiddenInput(),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
class LearnedSkillForm(forms.ModelForm):
class Meta:
model = LearnedSkill
fields = ("gymnast", "skill", "learning_step", "date")
widgets = {
"gymnast": forms.HiddenInput(),
"skill": forms.HiddenInput(),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"learning_step": forms.Select(attrs={"class": "form-control selectpicker"}),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
skill_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching skill…",
"data-ref": "#id_skill",
}
),
)
class ScoreForm(forms.ModelForm):
class Meta:
ROUTINETYPE_CHOICE = (
(0, "Routine 1"),
(1, "Routine 2"),
(2, "Final's routine"),
)
model = Point
fields = (
"gymnast",
"event",
"routine_type",
"point_difficulty",
"point_time_of_flight",
"point_execution",
"point_horizontal_displacement",
"penality",
"total",
)
widgets = {
"gymnast": forms.HiddenInput(),
"event": forms.HiddenInput(),
"routine_type": forms.Select(attrs={"class": "form-control selectpicker"}),
"point_execution": forms.NumberInput(
attrs={"class": "form-control", "placeholder": "xx,xx", "min": "0"}
),
"point_difficulty": forms.NumberInput(
attrs={"class": "form-control", "placeholder": "xx,xx", "min": "0"}
),
"point_time_of_flight": forms.NumberInput(
attrs={"class": "form-control", "placeholder": "xx,xx", "min": "0"}
),
"point_horizontal_displacement": forms.NumberInput(
attrs={"class": "form-control", "placeholder": "x,xx", "min": "0"}
),
"penality": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xx,xx",
"value": "0",
"min": "0",
}
),
"total": forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "000,000",
"readonly": "readonly",
"maxlength": "6",
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching…",
"data-ref": "#id_gymnast",
}
),
)
event_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching…",
"data-ref": "#id_event",
}
),
)
add_to_chrono = forms.NullBooleanField(
required=False,
widget=forms.CheckboxInput(
attrs={"class": "form-control form-check-input ml-0 mt-0"}
),
)
class AccidentForm(forms.ModelForm):
class Meta:
model = Accident
fields = ("gymnast", "date", "nb_week_off", "informations")
widgets = {
"date": forms.DateInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"gymnast": forms.HiddenInput(),
"skill": forms.HiddenInput(),
"nb_week_off": forms.NumberInput(
attrs={"class": "form-control", "placeholder": "xx"}
),
"informations": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about accident: context (why, where, …), consequencies, re-education exercices, …", # pylint: disable=line-too-long
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
skill_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching skill…",
"data-ref": "#id_skill",
}
),
)
class MindStateForm(forms.ModelForm):
class Meta:
model = MindState
fields = ("gymnast", "date", "score", "informations")
widgets = {
"gymnast": forms.HiddenInput(),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"event": forms.HiddenInput(),
"score": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "x",
"min": "0",
"max": "10",
}
),
"informations": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about the psychological state of mind : context (why, where, …), possible consequencies, …", # pylint: disable=line-too-long
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
event_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching event…",
"data-ref": "#id_event",
}
),
)
class GymnastHasRoutineForm(forms.ModelForm):
class Meta:
model = GymnastHasRoutine
fields = ("gymnast", "routine", "routine_type", "date_begin", "date_end")
widgets = {
"gymnast": forms.HiddenInput(),
"routine": forms.HiddenInput(),
"routine_type": forms.Select(attrs={"class": "form-control selectpicker"}),
"date_begin": forms.DateInput(
attrs={
"class": "form-control datepicker",
}
),
"date_end": forms.DateInput(
attrs={
"class": "form-control datepicker",
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
routine_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching routine…",
"data-ref": "#id_routine",
}
),
)
class HeightWeightForm(forms.ModelForm):
"""
Formulaire d'enregistrement d'un couple taille/poids
"""
class Meta:
model = HeightWeight
fields = ("gymnast", "date", "height", "hips_height", "weight")
widgets = {
"gymnast": forms.HiddenInput(),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"height": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx,x",
"min": "100",
"max": "220",
}
),
"hips_height": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx,x",
"min": "50",
"max": "110",
}
),
"weight": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx,x",
"min": "20",
"max": "110",
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
class NumberOfRoutineDoneForm(forms.ModelForm):
class Meta:
model = NumberOfRoutineDone
fields = (
"gymnast",
"routine",
"routine_type",
"date",
"number_of_try",
"number_of_successes",
)
widgets = {
"gymnast": forms.HiddenInput(),
"routine": forms.HiddenInput(),
"routine_type": forms.Select(attrs={"class": "form-control selectpicker"}),
"date": forms.DateInput(
attrs={
"class": "form-control datepicker",
}
),
"number_of_try": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "x",
"min": "0",
"max": "50",
}
),
"number_of_successes": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "x",
"min": "0",
"max": "50",
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
routine_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching routine…",
"data-ref": "#id_routine",
}
),
)
class PlanForm(forms.ModelForm):
"""
Formulaire d'enregistrement d'un plan (gymnast qui doit faire un eductative pour une date X)
"""
class Meta:
model = Plan
fields = (
"date",
"gymnast",
"educative",
"learning_step",
"is_done",
"informations",
)
widgets = {
"gymnast": forms.HiddenInput(),
"educative": forms.HiddenInput(),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"learning_step": forms.Select(attrs={"class": "form-control selectpicker"}),
"is_done": forms.CheckboxInput(
attrs={"class": "form-control form-check-input ml-0 mt-0"}
),
"informations": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about gymnast for this particular skill: usual mistake, fear, …", # pylint: disable=line-too-long
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
educative_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching skill…",
"data-ref": "#id_skill",
}
),
)
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ("gymnast", "coach", "status", "informations", "date")
widgets = {
"gymnast": forms.HiddenInput(),
"coach": forms.HiddenInput(),
"status": forms.Select(attrs={"class": "form-control selectpicker"}),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"informations": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about gymnast: fear, lost skill syndrom, …", # pylint: disable=line-too-long
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
class IntensityForm(forms.ModelForm):
class Meta:
model = Intensity
fields = (
"gymnast",
"time",
"difficulty",
"quantity_of_skill",
"number_of_passes",
"informations",
"date",
)
widgets = {
"gymnast": forms.HiddenInput(),
"time": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx",
}
),
"difficulty": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx",
}
),
"quantity_of_skill": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx",
}
),
"number_of_passes": forms.NumberInput(
attrs={
"class": "form-control",
"placeholder": "xxx",
}
),
"date": forms.TextInput(
attrs={
"class": "form-control datepicker",
"placeholder": date.today().strftime("%Y-%m-%d"),
"value": date.today().strftime("%Y-%m-%d"),
}
),
"informations": forms.Textarea(
attrs={
"class": "form-control",
"placeholder": "Informations about intensity: did you do your full program, did you stop before the end, why did you stop before the end, …", # pylint: disable=line-too-long
}
),
}
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)
class SeasonInformationForm(forms.ModelForm):
class Meta:
model = SeasonInformation
fields = (
"gymnast",
"season",
"number_of_training_sessions_per_week",
"number_of_hours_per_week",
"number_of_s_and_c_sessions_per_week",
"number_of_s_and_c_hours_per_week",
"category",
"club",
)
widgets = {
"gymnast": forms.HiddenInput(),
"season": forms.TextInput(
attrs={"class": "form-control", "placeholder": "202x-202y"}
),
"number_of_training_sessions_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "5"}
),
"number_of_hours_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "11.5"}
),
"number_of_s_and_c_sessions_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "5"}
),
"number_of_s_and_c_hours_per_week": forms.TextInput(
attrs={"class": "form-control", "placeholder": "11.5"}
),
"category": forms.Select(attrs={"class": "form-control selectpicker"}),
"club": forms.HiddenInput(),
}
club_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching club…",
"data-ref": "#id_club",
}
),
)
gymnast_related = forms.CharField(
required=False,
widget=forms.TextInput(
attrs={
"class": "form-control",
"placeholder": "Searching gymnast…",
"data-ref": "#id_gymnast",
}
),
)

View File

View File

@ -0,0 +1,369 @@
# Generated by Django 3.2.8 on 2021-12-02 06:43
import datetime
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
("planning", "0001_initial"),
("people", "0001_initial"),
("objective", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="Point",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"routine_type",
models.PositiveSmallIntegerField(
choices=[(0, "Routine 1"), (1, "Routine 2"), (2, "Final")]
),
),
(
"point_execution",
models.DecimalField(decimal_places=3, max_digits=5),
),
(
"point_difficulty",
models.DecimalField(decimal_places=1, max_digits=3),
),
(
"point_time_of_flight",
models.DecimalField(decimal_places=3, max_digits=5),
),
(
"point_horizontal_displacement",
models.DecimalField(decimal_places=3, max_digits=4),
),
("penality", models.DecimalField(decimal_places=1, max_digits=3)),
("total", models.DecimalField(decimal_places=3, max_digits=6)),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"event",
models.ForeignKey(
default=None,
on_delete=django.db.models.deletion.CASCADE,
to="planning.event",
),
),
(
"gymnast",
models.ForeignKey(
default=None,
on_delete=django.db.models.deletion.CASCADE,
related_name="points",
to="people.gymnast",
),
),
],
),
migrations.CreateModel(
name="MindState",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"informations",
models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
(
"date",
models.DateField(default=datetime.date.today, verbose_name="Date"),
),
("score", models.PositiveSmallIntegerField(verbose_name="Score")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"event",
models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="mindstate",
to="planning.event",
),
),
(
"gymnast",
models.ForeignKey(
default=None,
on_delete=django.db.models.deletion.CASCADE,
related_name="mindstate",
to="people.gymnast",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="GymnastHasRoutine",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"routine_type",
models.PositiveSmallIntegerField(
choices=[
(1, "L1"),
(2, "L2"),
(3, "L3"),
(4, "L4"),
(5, "L1S"),
(6, "L2S"),
(7, "L3S"),
(8, "L4S"),
],
default="1",
verbose_name="Type",
),
),
(
"datebegin",
models.DateField(
default=datetime.date.today, verbose_name="Date begin"
),
),
(
"dateend",
models.DateField(
blank=True,
default=datetime.date.today,
null=True,
verbose_name="Date end",
),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="routines",
to="people.gymnast",
verbose_name="Gymnast",
),
),
(
"routine",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="done_by",
to="objective.routine",
verbose_name="Routine",
),
),
],
options={
"verbose_name": "Gymnast Has Routine",
"verbose_name_plural": "Gymnast Has Routines",
},
),
migrations.CreateModel(
name="Chrono",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"type",
models.PositiveSmallIntegerField(
choices=[
(0, "10 |"),
(1, "L1"),
(2, "L2"),
(3, "L3"),
(4, "L4"),
(99, "Other"),
],
verbose_name="Routine type",
),
),
(
"score_type",
models.PositiveSmallIntegerField(
choices=[(0, "Chrono"), (1, "ToF")], verbose_name="Score type"
),
),
("score", models.DecimalField(decimal_places=3, max_digits=5)),
(
"tof",
models.DecimalField(
blank=True, decimal_places=3, max_digits=5, null=True
),
),
(
"date",
models.DateField(default=datetime.date.today, verbose_name="Date"),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="chronos",
to="people.gymnast",
verbose_name="gymnast",
),
),
],
options={
"verbose_name": "Chrono",
"verbose_name_plural": "Chronos",
"ordering": ["date", "gymnast"],
},
),
migrations.CreateModel(
name="Accident",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"informations",
models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
("date", models.DateField(verbose_name="Date")),
(
"nb_week_off",
models.SmallIntegerField(
blank=True, null=True, verbose_name="# week off"
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="accident",
to="people.gymnast",
verbose_name="Gymnast",
),
),
(
"skill",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="accident",
to="objective.skill",
verbose_name="Skill",
),
),
],
options={
"verbose_name": "Accident",
"verbose_name_plural": "Accidents",
},
),
migrations.CreateModel(
name="LearnedSkill",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"cando",
models.PositiveSmallIntegerField(
choices=[
(0, "No"),
(1, "With help"),
(2, "Without help"),
(3, "Chained"),
],
verbose_name="Can do type",
),
),
(
"date",
models.DateField(default=datetime.date.today, verbose_name="Date"),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="can_do_skill",
to="people.gymnast",
verbose_name="gymnast",
),
),
(
"skill",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="done_by_gymnasts",
to="objective.skill",
verbose_name="Skill",
),
),
],
options={
"verbose_name": "Learned Skill",
"verbose_name_plural": "Learned Skills",
"unique_together": {("gymnast", "skill", "date")},
},
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.8 on 2021-12-02 17:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("people", "0001_initial"),
("followup", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="learnedskill",
name="gymnast",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="known_skills",
to="people.gymnast",
verbose_name="gymnast",
),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.8 on 2021-12-02 17:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0001_initial"),
("followup", "0002_alter_learnedskill_gymnast"),
]
operations = [
migrations.AlterField(
model_name="learnedskill",
name="skill",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="known_by_gymnasts",
to="objective.skill",
verbose_name="Skill",
),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.8 on 2021-12-04 16:53
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0001_initial"),
("followup", "0003_alter_learnedskill_skill"),
]
operations = [
migrations.AlterField(
model_name="learnedskill",
name="skill",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="known_by",
to="objective.skill",
verbose_name="Skill",
),
),
]

View File

@ -0,0 +1,55 @@
# Generated by Django 3.2.8 on 2021-12-05 14:12
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0003_delete_gymnasthasroutine"),
("people", "0001_initial"),
("followup", "0004_alter_learnedskill_skill"),
]
operations = [
migrations.AlterField(
model_name="gymnasthasroutine",
name="gymnast",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="has_routine",
to="people.gymnast",
verbose_name="Gymnast",
),
),
migrations.AlterField(
model_name="gymnasthasroutine",
name="routine",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="done_by_gymnast",
to="objective.routine",
verbose_name="Routine",
),
),
migrations.AlterField(
model_name="gymnasthasroutine",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "L1"),
(2, "L2"),
(3, "L3"),
(4, "L4"),
(5, "L1S"),
(6, "L2S"),
(7, "L3S"),
(8, "L4S"),
],
default="1",
verbose_name="Type",
),
),
]

View File

@ -0,0 +1,130 @@
# Generated by Django 3.2.8 on 2021-12-13 07:46
import datetime
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("people", "0001_initial"),
("objective", "0003_delete_gymnasthasroutine"),
("followup", "0005_auto_20211205_1412"),
]
operations = [
migrations.CreateModel(
name="NumberOfRoutineDone",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"routine_type",
models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "L1"),
(2, "L2"),
(3, "L3"),
(4, "L4"),
(5, "L1S"),
(6, "L2S"),
(7, "L3S"),
(8, "L4S"),
],
default="1",
verbose_name="Type",
),
),
(
"number_of_try",
models.PositiveSmallIntegerField(verbose_name="Number of try"),
),
(
"number_of_successes",
models.PositiveSmallIntegerField(
verbose_name="number of successes"
),
),
(
"date",
models.DateField(default=datetime.date.today, verbose_name="Date"),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="number_of_routine_done",
to="people.gymnast",
verbose_name="Gymnast",
),
),
(
"routine",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="number_of_try",
to="objective.routine",
verbose_name="Routine",
),
),
],
options={
"verbose_name": "Number of routine done",
"verbose_name_plural": "Number of routines done",
},
),
migrations.CreateModel(
name="HeightWeight",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"height",
models.DecimalField(
decimal_places=1, max_digits=4, verbose_name="Height"
),
),
(
"weight",
models.DecimalField(
decimal_places=1, max_digits=4, verbose_name="Weight"
),
),
(
"date",
models.DateField(default=datetime.date.today, verbose_name="Date"),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="height_weight",
to="people.gymnast",
verbose_name="Gymnast",
),
),
],
options={
"verbose_name": "Height & weight",
"verbose_name_plural": "Heights & weights",
},
),
]

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.8 on 2021-12-13 14:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("objective", "0003_delete_gymnasthasroutine"),
("planning", "0001_initial"),
("followup", "0006_heightweight_numberofroutinedone"),
]
operations = [
migrations.AlterField(
model_name="accident",
name="skill",
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="accident",
to="objective.skill",
verbose_name="Skill",
),
),
migrations.AlterField(
model_name="mindstate",
name="event",
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="mindstate",
to="planning.event",
),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.8 on 2021-12-17 08:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("people", "0001_initial"),
("objective", "0003_delete_gymnasthasroutine"),
("followup", "0007_auto_20211213_1445"),
]
operations = [
migrations.AlterUniqueTogether(
name="learnedskill",
unique_together={("gymnast", "skill", "date", "cando")},
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 3.2.8 on 2021-12-22 13:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('people', '0001_initial'),
('followup', '0008_alter_learnedskill_unique_together'),
]
operations = [
migrations.AddField(
model_name='heightweight',
name='hips_height',
field=models.DecimalField(decimal_places=1, default=100, max_digits=4, verbose_name='Hips height'),
preserve_default=False,
),
migrations.AlterUniqueTogether(
name='heightweight',
unique_together={('gymnast', 'date')},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.8 on 2021-12-24 06:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0009_auto_20211222_1318'),
]
operations = [
migrations.RenameField(
model_name='chrono',
old_name='type',
new_name='chrono_type',
),
migrations.AlterField(
model_name='gymnasthasroutine',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'L1'), (2, 'L2'), (3, 'L3'), (4, 'L4'), (5, 'L1S'), (6, 'L2S'), (7, 'L3S'), (8, 'L4S'), (9, 'Other')], default='1', verbose_name='Type'),
),
migrations.AlterField(
model_name='numberofroutinedone',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'L1'), (2, 'L2'), (3, 'L3'), (4, 'L4'), (5, 'L1S'), (6, 'L2S'), (7, 'L3S'), (8, 'L4S'), (9, 'Other')], default='1', verbose_name='Type'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.8 on 2022-01-05 10:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0010_auto_20211224_0627'),
]
operations = [
migrations.AlterField(
model_name='learnedskill',
name='cando',
field=models.PositiveSmallIntegerField(choices=[(0, 'No'), (1, 'With help'), (2, 'Without help'), (3, 'Chained'), (4, 'Masterised')], verbose_name='Can do type'),
),
]

View File

@ -0,0 +1,36 @@
# Generated by Django 3.2.8 on 2022-01-05 15:31
import datetime
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('objective', '0006_delete_plan'),
('people', '0001_initial'),
('followup', '0011_alter_learnedskill_cando'),
]
operations = [
migrations.CreateModel(
name='Plan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(default=datetime.date.today, verbose_name='Date')),
('cando', models.PositiveSmallIntegerField(choices=[(0, 'No'), (1, 'With help'), (2, 'Without help'), (3, 'Chained'), (4, 'Masterised')], default=3, verbose_name='Can do type')),
('is_done', models.BooleanField(default=0)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('educative', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plan', to='objective.educative', verbose_name='Educative')),
('gymnast', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='todo', to='people.gymnast', verbose_name='Gymnast')),
],
options={
'verbose_name': 'Plan',
'verbose_name_plural': 'Plans',
'ordering': ['date', 'educative', 'gymnast'],
'unique_together': {('gymnast', 'educative')},
},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.8 on 2022-01-20 17:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('followup', '0012_plan'),
]
operations = [
migrations.CreateModel(
name='ChronoDetails',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('order', models.SmallIntegerField()),
('value', models.DecimalField(decimal_places=3, max_digits=5)),
('chrono', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='followup.chrono')),
],
options={
'verbose_name': 'Chronos Details',
'verbose_name_plural': 'Chronos Details',
'ordering': ['chrono', 'order'],
},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2022-01-25 17:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('followup', '0013_chronodetails'),
]
operations = [
migrations.AlterField(
model_name='chronodetails',
name='chrono',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='details', to='followup.chrono'),
),
migrations.AlterUniqueTogether(
name='chronodetails',
unique_together={('chrono', 'order')},
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.8 on 2022-02-01 16:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0014_auto_20220125_1751'),
]
operations = [
migrations.AlterField(
model_name='chrono',
name='chrono_type',
field=models.PositiveSmallIntegerField(choices=[(0, '10 |'), (1, 'Routine 1'), (2, 'Routine 2'), (3, 'Routine 3'), (4, 'Routine 4'), (9, 'Other')], verbose_name='Routine type'),
),
migrations.AlterField(
model_name='gymnasthasroutine',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'R1'), (2, 'R2'), (3, 'R3'), (4, 'R4'), (5, 'R1S'), (6, 'R2S'), (7, 'R3S'), (8, 'R4S'), (9, 'Other')], default='1', verbose_name='Type'),
),
migrations.AlterField(
model_name='numberofroutinedone',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'R1'), (2, 'R2'), (3, 'R3'), (4, 'R4'), (5, 'R1S'), (6, 'R2S'), (7, 'R3S'), (8, 'R4S'), (9, 'Other')], default='1', verbose_name='Type'),
),
migrations.AlterField(
model_name='point',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(1, 'Routine 1'), (2, 'Routine 2'), (3, 'Final')]),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 3.2.8 on 2022-02-03 10:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0015_auto_20220201_1633'),
]
operations = [
migrations.AlterField(
model_name='point',
name='point_difficulty',
field=models.DecimalField(decimal_places=1, max_digits=3, verbose_name='Difficulty'),
),
migrations.AlterField(
model_name='point',
name='point_execution',
field=models.DecimalField(decimal_places=3, max_digits=5, verbose_name='Execution'),
),
migrations.AlterField(
model_name='point',
name='point_horizontal_displacement',
field=models.DecimalField(decimal_places=3, max_digits=4, verbose_name='HD'),
),
migrations.AlterField(
model_name='point',
name='point_time_of_flight',
field=models.DecimalField(decimal_places=3, max_digits=5, verbose_name='ToF'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2022-02-03 10:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0016_auto_20220203_1035'),
]
operations = [
migrations.AlterField(
model_name='point',
name='created_at',
field=models.DateTimeField(auto_now_add=True, verbose_name='Created'),
),
migrations.AlterField(
model_name='point',
name='updated_at',
field=models.DateTimeField(auto_now=True, verbose_name='Updated'),
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.8 on 2022-02-08 16:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0017_auto_20220203_1037'),
]
operations = [
migrations.AlterField(
model_name='gymnasthasroutine',
name='dateend',
field=models.DateField(blank=True, null=True, verbose_name='Date end'),
),
migrations.AlterField(
model_name='numberofroutinedone',
name='number_of_successes',
field=models.PositiveSmallIntegerField(default=0, verbose_name='number of successes'),
),
migrations.AlterField(
model_name='numberofroutinedone',
name='number_of_try',
field=models.PositiveSmallIntegerField(default=0, verbose_name='Number of try'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.8 on 2022-02-13 14:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('followup', '0018_auto_20220208_1648'),
]
operations = [
migrations.AlterField(
model_name='gymnasthasroutine',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'R1'), (2, 'R2'), (3, 'R3'), (4, 'R4'), (5, 'R1S'), (6, 'R2S'), (7, 'R3S'), (8, 'R4S')], default='1', verbose_name='Type'),
),
migrations.AlterField(
model_name='numberofroutinedone',
name='routine_type',
field=models.PositiveSmallIntegerField(choices=[(0, 'Other'), (1, 'R1'), (2, 'R2'), (3, 'R3'), (4, 'R4'), (5, 'R1S'), (6, 'R2S'), (7, 'R3S'), (8, 'R4S')], default='1', verbose_name='Type'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.1 on 2022-09-23 07:42
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("followup", "0019_auto_20220213_1441"),
]
operations = [
migrations.RenameField(
model_name="gymnasthasroutine",
old_name="datebegin",
new_name="date_begin",
),
migrations.RenameField(
model_name="gymnasthasroutine",
old_name="dateend",
new_name="date_end",
),
]

View File

@ -0,0 +1,63 @@
# Generated by Django 4.1.1 on 2022-09-26 10:18
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("people", "0004_gymnast_email_trainer"),
("followup", "0020_rename_datebegin_gymnasthasroutine_date_begin_and_more"),
]
operations = [
migrations.CreateModel(
name="Notes",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"informations",
models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"coach",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="notes",
to=settings.AUTH_USER_MODEL,
),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="remarks",
to="people.gymnast",
),
),
],
options={
"abstract": False,
},
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 4.1.1 on 2022-09-26 10:24
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("people", "0004_gymnast_email_trainer"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("followup", "0021_notes"),
]
operations = [
migrations.RenameModel(
old_name="Notes",
new_name="Note",
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.1.1 on 2022-10-06 12:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0022_rename_notes_note"),
]
operations = [
migrations.AddField(
model_name="note",
name="note_type",
field=models.PositiveSmallIntegerField(
choices=[(0, "Draft"), (1, "Published")],
default=0,
verbose_name="Routine type",
),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.1.1 on 2022-10-06 12:36
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("followup", "0023_note_note_type"),
]
operations = [
migrations.RenameField(
model_name="note",
old_name="note_type",
new_name="status",
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 4.1.1 on 2022-10-12 08:37
from django.db import migrations, models
import jarvis.tools.models
class Migration(migrations.Migration):
dependencies = [
("followup", "0024_rename_note_type_note_status"),
]
operations = [
migrations.AddField(
model_name="note",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AddField(
model_name="note",
name="season",
field=models.CharField(default="2022-2023", max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="note",
name="week_number",
field=models.PositiveSmallIntegerField(default=1),
preserve_default=False,
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.1 on 2022-10-12 08:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0025_note_date_note_season_note_week_number"),
]
operations = [
migrations.AlterField(
model_name="note",
name="season",
field=models.CharField(editable=False, max_length=9),
),
migrations.AlterField(
model_name="note",
name="week_number",
field=models.PositiveSmallIntegerField(editable=False),
),
]

View File

@ -0,0 +1,147 @@
# Generated by Django 4.1.1 on 2022-10-12 09:03
from django.db import migrations, models
import jarvis.tools.models
class Migration(migrations.Migration):
dependencies = [
("followup", "0026_alter_note_season_alter_note_week_number"),
]
operations = [
migrations.AddField(
model_name="accident",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="accident",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="chrono",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="chrono",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="heightweight",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="heightweight",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="learnedskill",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="learnedskill",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="mindstate",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="mindstate",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="numberofroutinedone",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="numberofroutinedone",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AddField(
model_name="plan",
name="season",
field=models.CharField(default="2022-2023", editable=False, max_length=9),
preserve_default=False,
),
migrations.AddField(
model_name="plan",
name="week_number",
field=models.PositiveSmallIntegerField(default=1, editable=False),
preserve_default=False,
),
migrations.AlterField(
model_name="accident",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="chrono",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="heightweight",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="learnedskill",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="mindstate",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="numberofroutinedone",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
migrations.AlterField(
model_name="plan",
name="date",
field=models.DateField(
default=jarvis.tools.models.get_default_date, verbose_name="Date"
),
),
]

View File

@ -0,0 +1,32 @@
# Generated by Django 4.1.1 on 2022-10-12 15:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("objective", "0013_alter_skill_position"),
("people", "0004_gymnast_email_trainer"),
(
"followup",
"0027_accident_season_accident_week_number_chrono_season_and_more",
),
]
operations = [
migrations.RenameField(
model_name="learnedskill",
old_name="cando",
new_name="learning_step",
),
migrations.RenameField(
model_name="plan",
old_name="cando",
new_name="learning_step",
),
migrations.AlterUniqueTogether(
name="learnedskill",
unique_together={("gymnast", "skill", "date", "learning_step")},
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.1 on 2022-10-16 06:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0028_rename_cando_learnedskill_learning_step_and_more"),
]
operations = [
migrations.AddField(
model_name="plan",
name="informations",
field=models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 4.1.1 on 2022-10-18 03:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0029_plan_informations"),
]
operations = [
migrations.AlterField(
model_name="heightweight",
name="hips_height",
field=models.DecimalField(
blank=True,
decimal_places=1,
max_digits=4,
null=True,
verbose_name="Hips height",
),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.1.1 on 2022-10-21 13:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0030_alter_heightweight_hips_height"),
]
operations = [
migrations.AlterField(
model_name="note",
name="status",
field=models.PositiveSmallIntegerField(
choices=[(0, "Draft"), (1, "Published")],
default=0,
verbose_name="Status",
),
),
]

View File

@ -0,0 +1,66 @@
# Generated by Django 4.1.1 on 2022-10-25 14:21
from django.db import migrations, models
import django.db.models.deletion
import jarvis.tools.models
class Migration(migrations.Migration):
dependencies = [
("people", "0004_gymnast_email_trainer"),
("followup", "0031_alter_note_status"),
]
operations = [
migrations.CreateModel(
name="Intensity",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"date",
models.DateField(
default=jarvis.tools.models.get_default_date,
verbose_name="Date",
),
),
("season", models.CharField(editable=False, max_length=9)),
("week_number", models.PositiveSmallIntegerField(editable=False)),
(
"informations",
models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
(
"time",
models.PositiveSmallIntegerField(verbose_name="Time (in minutes)"),
),
("difficulty", models.PositiveSmallIntegerField()),
("quantity_of_skill", models.PositiveSmallIntegerField()),
("number_of_passes", models.PositiveSmallIntegerField()),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="intensities",
to="people.gymnast",
),
),
],
options={
"abstract": False,
},
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 4.1.1 on 2022-10-25 15:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("people", "0004_gymnast_email_trainer"),
("followup", "0032_intensity"),
]
operations = [
migrations.AlterModelOptions(
name="intensity",
options={"verbose_name": "Intensity", "verbose_name_plural": "Intensities"},
),
migrations.AlterField(
model_name="intensity",
name="difficulty",
field=models.PositiveSmallIntegerField(
verbose_name="Difficulty (in tenths)"
),
),
migrations.AlterUniqueTogether(
name="intensity",
unique_together={("gymnast", "date")},
),
]

View File

@ -0,0 +1,85 @@
# Generated by Django 4.1.1 on 2022-11-01 14:31
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("location", "0003_auto_20220109_1001"),
("people", "0006_gymnast_created_at"),
(
"followup",
"0033_alter_intensity_options_alter_intensity_difficulty_and_more",
),
]
operations = [
migrations.CreateModel(
name="SeasonInformation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("season", models.CharField(editable=False, max_length=9)),
(
"trainings_by_week",
models.PositiveSmallIntegerField(verbose_name="# Training by week"),
),
(
"hours_by_week",
models.PositiveSmallIntegerField(verbose_name="# Hours by week"),
),
(
"category",
models.PositiveSmallIntegerField(
choices=[
(9, "I9"),
(10, "I10"),
(11, "A11"),
(12, "A12"),
(13, "A13-14"),
(15, "A Junior"),
(18, "A Senior"),
(21, "B11"),
(22, "B12"),
(23, "B13-14"),
(24, "B Junior"),
(25, "B Senior"),
],
verbose_name="Category",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
(
"club",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="season_informations",
to="location.club",
),
),
(
"gymnast",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="season_informations",
to="people.gymnast",
),
),
],
options={
"verbose_name": "Season Information",
"verbose_name_plural": "Season Informations",
"unique_together": {("gymnast", "season")},
},
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 4.1.1 on 2022-11-02 12:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0034_seasoninformation"),
]
operations = [
migrations.RenameField(
model_name="seasoninformation",
old_name="hours_by_week",
new_name="number_of_hours_per_week",
),
migrations.RenameField(
model_name="seasoninformation",
old_name="trainings_by_week",
new_name="number_of_training_sessions_per_week",
),
migrations.AlterField(
model_name="seasoninformation",
name="season",
field=models.CharField(max_length=9),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 4.1.1 on 2022-11-04 09:56
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("people", "0008_alter_gymnast_orientation"),
(
"followup",
"0035_rename_hours_by_week_seasoninformation_number_of_hours_per_week_and_more",
),
]
operations = [
migrations.AlterUniqueTogether(
name="numberofroutinedone",
unique_together={("gymnast", "date", "routine_type")},
),
]

View File

@ -0,0 +1,90 @@
# Generated by Django 4.1.1 on 2022-11-07 21:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0036_alter_numberofroutinedone_unique_together"),
]
operations = [
migrations.AlterField(
model_name="chrono",
name="chrono_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "10 |"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(9, "Other"),
],
verbose_name="Routine type",
),
),
migrations.AlterField(
model_name="gymnasthasroutine",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
],
default="1",
verbose_name="Type",
),
),
migrations.AlterField(
model_name="numberofroutinedone",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
],
default="1",
verbose_name="Type",
),
),
migrations.AlterField(
model_name="point",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
]
),
),
]

View File

@ -0,0 +1,93 @@
# Generated by Django 4.1.1 on 2022-12-07 11:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0037_alter_chrono_chrono_type_and_more"),
]
operations = [
migrations.AlterField(
model_name="chrono",
name="chrono_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "10 |"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(99, "Other"),
],
verbose_name="Routine type",
),
),
migrations.AlterField(
model_name="gymnasthasroutine",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
(99, "Other"),
],
default="1",
verbose_name="Type",
),
),
migrations.AlterField(
model_name="numberofroutinedone",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
(99, "Other"),
],
default="1",
verbose_name="Type",
),
),
migrations.AlterField(
model_name="point",
name="routine_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
(99, "Other"),
]
),
),
]

View File

@ -0,0 +1,112 @@
# Generated by Django 4.1.1 on 2022-12-28 13:28
from django.db import migrations, models
import jarvis.tools.models
class Migration(migrations.Migration):
dependencies = [
("followup", "0038_alter_chrono_chrono_type_and_more"),
]
operations = [
migrations.CreateModel(
name="CompetitivePointsStats",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"date",
models.DateField(
default=jarvis.tools.models.get_default_date,
verbose_name="Date",
),
),
("season", models.CharField(editable=False, max_length=9)),
("week_number", models.PositiveSmallIntegerField(editable=False)),
(
"informations",
models.TextField(
blank=True,
help_text="Only MarkDown is authorized",
null=True,
verbose_name="Comments",
),
),
(
"gender",
models.PositiveSmallIntegerField(
choices=[(0, "Male"), (1, "Female")], verbose_name="Gender"
),
),
(
"age_category",
models.PositiveSmallIntegerField(
choices=[
(11, "11-12"),
(13, "13-14"),
(15, "15-16"),
(17, "17-21"),
(22, "Senior"),
],
verbose_name="Age category",
),
),
(
"point_execution",
models.DecimalField(
decimal_places=3, max_digits=5, verbose_name="Execution"
),
),
(
"point_difficulty",
models.DecimalField(
decimal_places=1, max_digits=3, verbose_name="Difficulty"
),
),
(
"point_time_of_flight",
models.DecimalField(
decimal_places=3, max_digits=5, verbose_name="ToF"
),
),
(
"point_horizontal_displacement",
models.DecimalField(
decimal_places=3, max_digits=4, verbose_name="HD"
),
),
("total", models.DecimalField(decimal_places=3, max_digits=6)),
(
"routine_type",
models.PositiveSmallIntegerField(
choices=[
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
(99, "Other"),
]
),
),
],
options={
"abstract": False,
},
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.1 on 2022-12-28 13:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0039_competitivepointsstats"),
]
operations = [
migrations.AddField(
model_name="competitivepointsstats",
name="place",
field=models.PositiveSmallIntegerField(default=1, verbose_name="place"),
preserve_default=False,
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.1.1 on 2022-12-28 17:22
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0040_competitivepointsstats_place"),
]
operations = [
migrations.AddField(
model_name="competitivepointsstats",
name="label",
field=models.CharField(default="blah", max_length=40),
preserve_default=False,
),
]

View File

@ -0,0 +1,63 @@
# Generated by Django 4.1.1 on 2023-02-01 07:25
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("planning", "0009_alter_event_date_begin_alter_event_date_end"),
("followup", "0041_competitivepointsstats_label"),
]
operations = [
migrations.AddField(
model_name="competitivepointsstats",
name="event",
field=models.ForeignKey(
blank=True,
default=None,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="planning.event",
),
),
migrations.AddField(
model_name="competitivepointsstats",
name="statistic_type",
field=models.PositiveSmallIntegerField(
choices=[
(0, "precise"),
(1, "mean + 4 * standard deviation"),
(2, "mean + 2 * standard deviation"),
(3, "mean + standard deviation"),
(4, "mean + ½ standard deviation"),
(5, "mean + ¼ standard deviation"),
(6, "mean"),
(7, "mean - ¼ standard deviation"),
(8, "mean - ½ standard deviation"),
(9, "mean - standard deviation"),
(10, "mean - 2 * standard deviation"),
(11, "mean - 4 * standard deviation"),
],
default=1,
verbose_name="Type of statistic",
),
preserve_default=False,
),
migrations.AlterField(
model_name="competitivepointsstats",
name="informations",
field=models.TextField(
blank=True,
help_text="Information about even or statistics (mean, standard deviation, …).",
null=True,
),
),
migrations.AlterField(
model_name="competitivepointsstats",
name="place",
field=models.PositiveSmallIntegerField(verbose_name="Place"),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 4.1.1 on 2023-02-09 12:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("followup", "0042_competitivepointsstats_event_and_more"),
]
operations = [
migrations.AddField(
model_name="seasoninformation",
name="number_of_s_and_c_hours_per_week",
field=models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="# S&C hours by week"
),
),
migrations.AddField(
model_name="seasoninformation",
name="number_of_s_and_c_sessions_per_week",
field=models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="# S&C training by week"
),
),
]

View File

@ -0,0 +1,40 @@
# Generated by Django 4.2 on 2023-04-23 06:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"followup",
"0043_seasoninformation_number_of_s_and_c_hours_per_week_and_more",
),
]
operations = [
migrations.AlterField(
model_name="seasoninformation",
name="number_of_hours_per_week",
field=models.PositiveSmallIntegerField(verbose_name="# Hours/w"),
),
migrations.AlterField(
model_name="seasoninformation",
name="number_of_s_and_c_hours_per_week",
field=models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="# S&C hours/w"
),
),
migrations.AlterField(
model_name="seasoninformation",
name="number_of_s_and_c_sessions_per_week",
field=models.PositiveSmallIntegerField(
blank=True, null=True, verbose_name="# S&C training/w"
),
),
migrations.AlterField(
model_name="seasoninformation",
name="number_of_training_sessions_per_week",
field=models.PositiveSmallIntegerField(verbose_name="# Training/w"),
),
]

View File

612
jarvis/followup/models.py Normal file
View File

@ -0,0 +1,612 @@
from django.db import models
from django.contrib.auth import get_user_model
from datetime import date
from jarvis.tools.models import Markdownizable, Seasonisable
from jarvis.people.models import Gymnast, GENDER_CHOICES
from jarvis.planning.models import Event
from jarvis.objective.models import Educative, Skill, Routine
from jarvis.location.models import Club
User = get_user_model()
ROUTINE_TYPE_CHOICE = (
(0, "Other"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(6, "Q1R1S"),
(7, "Q1R2S"),
(8, "Q2R1S"),
(9, "SFS"),
(9, "FS"),
(99, "Other"),
)
LEARNING_STEP_CHOICES = (
(0, "No"),
(1, "With help"),
(2, "Without help"),
(3, "Chained"),
(4, "Masterised"),
)
CHRONO_TYPE_CHOICE = (
(0, "10 |"),
(1, "Q1R1"),
(2, "Q1R2"),
(3, "Q2R1"),
(4, "SF"),
(5, "F"),
(99, "Other"),
)
SCORE_TYPE_CHOICE = (
(0, "Chrono"),
(1, "ToF"),
)
CATEGORY_CHOICES = {
9: "I9",
10: "I10",
11: "A11",
12: "A12",
13: "A13-14",
15: "A Junior",
18: "A Senior",
21: "B11",
22: "B12",
23: "B13-14",
24: "B Junior",
25: "B Senior",
}
AGE_CATOGORY_CHOICES = (
(11, "11-12"),
(13, "13-14"),
(15, "15-16"),
(17, "17-21"),
(22, "Senior"),
)
class Chrono(Seasonisable):
"""
Représente les chronos (de chandelles ou de série) enregistrés pour un(e) gymnaste.
"""
class Meta:
verbose_name = "Chrono"
verbose_name_plural = "Chronos"
ordering = ["date", "gymnast"]
gymnast = models.ForeignKey(
Gymnast,
verbose_name="gymnast",
related_name="chronos",
on_delete=models.CASCADE,
)
chrono_type = models.PositiveSmallIntegerField(
choices=CHRONO_TYPE_CHOICE, verbose_name="Routine type"
)
score_type = models.PositiveSmallIntegerField(
choices=SCORE_TYPE_CHOICE, verbose_name="Score type"
)
score = models.DecimalField(max_digits=5, decimal_places=3)
tof = models.DecimalField(max_digits=5, decimal_places=3, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s (%s - %s)" % (
self.gymnast,
self.score,
self.date,
self.chrono_type,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - New personel best {CHRONO_TYPE_CHOICE[self.chrono_type][1]}: {self.score}' ({self.tof}')</li>"
@staticmethod
def compute_tof(value):
tof = round((value * 13) / 15, 3) * 1000
tof = tof - (tof % 5)
return tof / 1000
class ChronoDetails(models.Model):
"""
Class permettant d'enregistrer des détails d'un chrono : le temps de chaque saut.
"""
class Meta:
verbose_name = "Chronos Details"
verbose_name_plural = "Chronos Details"
ordering = ["chrono", "order"]
unique_together = ["chrono", "order"]
chrono = models.ForeignKey(Chrono, on_delete=models.CASCADE, related_name="details")
order = models.SmallIntegerField()
value = models.DecimalField(max_digits=5, decimal_places=3)
class Accident(Markdownizable, Seasonisable):
"""
La classe `Accident` permet d'indiquer qu'un gymnaste a eu un accident, en liaison avec un
skill ou non.
"""
class Meta:
verbose_name = "Accident"
verbose_name_plural = "Accidents"
# unique_together = ("gymnast", "skill", "date")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="accident",
on_delete=models.CASCADE,
)
skill = models.ForeignKey(
"objective.Skill",
verbose_name="Skill",
related_name="accident",
on_delete=models.SET_NULL,
default=None,
blank=True,
null=True,
)
nb_week_off = models.SmallIntegerField(
blank=True, null=True, verbose_name="# week off"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s (%s)" % (
self.gymnast,
self.date,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)</li>"
class LearnedSkill(Seasonisable):
"""
Représente la capacité d'un gymnaste à savori faire un skill de la ligne d'apprentissage.
"""
class Meta:
verbose_name = "Learned Skill"
verbose_name_plural = "Learned Skills"
unique_together = ("gymnast", "skill", "date", "learning_step")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="gymnast",
related_name="known_skills",
on_delete=models.CASCADE,
)
skill = models.ForeignKey(
Skill,
verbose_name="Skill",
related_name="known_by",
on_delete=models.CASCADE,
)
learning_step = models.PositiveSmallIntegerField(
choices=LEARNING_STEP_CHOICES, verbose_name="Can do type"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s - %s - %s" % (
self.gymnast,
self.date,
self.learning_step,
self.skill,
)
def timeline_representation(self):
return f"<li>{self.date:%d %b %Y} - learning of {self.skill.long_label} ({self.skill.short_label}): {LEARNING_STEP_CHOICES[self.learning_step][1]}</li>"
class Plan(Seasonisable, Markdownizable):
"""
Classe représentant les objectifs qu'un gymnaste devra savoir faire pour une date donnée.
"""
class Meta:
verbose_name = "Plan"
verbose_name_plural = "Plans"
ordering = ["date", "educative", "gymnast"]
unique_together = ("gymnast", "educative")
gymnast = models.ForeignKey(
"people.Gymnast",
verbose_name="Gymnast",
related_name="todo",
on_delete=models.CASCADE,
)
educative = models.ForeignKey(
Educative,
verbose_name="Educative",
related_name="plan",
on_delete=models.CASCADE,
)
learning_step = models.PositiveSmallIntegerField(
choices=LEARNING_STEP_CHOICES, verbose_name="Can do type", default=3
)
is_done = models.BooleanField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s - %s" % (
self.gymnast,
self.educative.short_label,
self.date,
)
class Point(models.Model):
"""
Représente les points obtenus lors d'une compétition.
"""
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, default=None, related_name="points"
)
event = models.ForeignKey(Event, on_delete=models.CASCADE, default=None)
routine_type = models.PositiveSmallIntegerField(choices=ROUTINE_TYPE_CHOICE)
point_execution = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="Execution"
)
point_difficulty = models.DecimalField(
max_digits=3, decimal_places=1, verbose_name="Difficulty"
)
point_time_of_flight = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="ToF"
)
point_horizontal_displacement = models.DecimalField(
max_digits=4, decimal_places=3, verbose_name="HD"
)
penality = models.DecimalField(max_digits=3, decimal_places=1)
total = models.DecimalField(max_digits=6, decimal_places=3)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated")
def __str__(self):
return "%s - %s" % (
self.gymnast,
self.total,
)
class MindState(Markdownizable, Seasonisable):
"""
Représente l'état d'esprit psychologique d'un gymnaste
"""
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, default=None, related_name="mindstate"
)
event = models.ForeignKey(
Event,
on_delete=models.SET_NULL,
default=None,
blank=True,
null=True,
related_name="mindstate",
)
score = models.PositiveSmallIntegerField(verbose_name="Score")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s : %s" % (self.gymnast, self.date, self.score)
class GymnastHasRoutine(models.Model):
"""
Classe représentant le lien entre les gymnastes et leurs séries.
TODO: il y a un champ "date_begin" et un champ "date_end" --> peut hérité de Temporizable
"""
class Meta:
verbose_name = "Gymnast Has Routine"
verbose_name_plural = "Gymnast Has Routines"
# unique_together()
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="has_routine",
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
verbose_name="Routine",
related_name="done_by_gymnast",
on_delete=models.CASCADE,
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
date_begin = models.DateField(default=date.today, verbose_name="Date begin")
date_end = models.DateField(verbose_name="Date end", null=True, blank=True)
def __str__(self):
return "%s - %s : %s" % (self.gymnast, self.routine_type, self.routine)
class NumberOfRoutineDone(Seasonisable):
"""
Classe permettant de suivre le nombre de séries faites par le gymnaste.
"""
class Meta:
verbose_name = "Number of routine done"
verbose_name_plural = "Number of routines done"
unique_together = ("gymnast", "date", "routine_type")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="number_of_routine_done",
on_delete=models.CASCADE,
)
routine = models.ForeignKey(
Routine,
verbose_name="Routine",
related_name="number_of_try",
on_delete=models.SET_NULL,
null=True,
blank=True,
)
routine_type = models.PositiveSmallIntegerField(
choices=ROUTINE_TYPE_CHOICE, verbose_name="Type", default="1"
)
number_of_try = models.PositiveSmallIntegerField(
verbose_name="Number of try", default=0
)
number_of_successes = models.PositiveSmallIntegerField(
verbose_name="number of successes", default=0
)
def __str__(self):
return "%s - %s (%s) : %s | %s" % (
self.gymnast,
self.routine_type,
self.routine,
self.number_of_try,
self.number_of_successes,
)
class HeightWeight(Seasonisable):
"""
Classe permettant de suivre le poids et la taille d'un gymnaste
"""
class Meta:
verbose_name = "Height & weight"
verbose_name_plural = "Heights & weights"
unique_together = ("gymnast", "date")
gymnast = models.ForeignKey(
Gymnast,
verbose_name="Gymnast",
related_name="height_weight",
on_delete=models.CASCADE,
)
height = models.DecimalField(max_digits=4, decimal_places=1, verbose_name="Height")
hips_height = models.DecimalField(
max_digits=4,
decimal_places=1,
verbose_name="Hips height",
null=True,
blank=True,
)
weight = models.DecimalField(max_digits=4, decimal_places=1, verbose_name="Weight")
def __str__(self):
return "%s : %s/%s - %s" % (
self.gymnast,
self.height,
self.hips_height,
self.weight,
)
class Note(Markdownizable, Seasonisable):
"""
Notes relatives à un gymnaste
"""
STATUS_CHOICES = (
(0, "Draft"),
(1, "Published"),
)
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="remarks"
)
coach = models.ForeignKey(
User, on_delete=models.SET_NULL, blank=True, null=True, related_name="notes"
)
status = models.PositiveSmallIntegerField(
choices=STATUS_CHOICES, verbose_name="Status", default=0
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return "%s - %s : %s" % (self.gymnast, self.created_at, self.coach)
class Intensity(Markdownizable, Seasonisable):
"""Classe représentant l'intensité d'un entraînement
L'intensité va prendre 4 valeurs :
- la temps (en minute),
- la difficulté (en 10 ème),
- la quantité de figure et
- le nombre de passafe.
Avec ces 4 informations, la classe va en calculer 4 autres :
- la difficulté moyenne par passage
- la difficulté moyenne par figure
- la quantité moyene de figures par passage
- la quantité moyenne de figure par minute
"""
class Meta:
verbose_name = "Intensity"
verbose_name_plural = "Intensities"
unique_together = ("gymnast", "date")
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="intensities"
)
time = models.PositiveSmallIntegerField(verbose_name="Time (in minutes)")
difficulty = models.PositiveSmallIntegerField(verbose_name="Difficulty (in tenths)")
quantity_of_skill = models.PositiveSmallIntegerField()
number_of_passes = models.PositiveSmallIntegerField()
def __str__(self):
return "%s - %s : %s - %s - %s - %s" % (
self.gymnast,
self.date,
self.time,
self.difficulty,
self.quantity_of_skill,
self.number_of_passes,
)
@property
def mean_difficulty_by_passe(self):
return self.difficulty / self.number_of_passes
@property
def mean_quantity_of_skill(self):
return self.quantity_of_skill / self.time
@property
def quantity_of_skill_by_passe(self):
return self.quantity_of_skill / self.number_of_passes
@property
def mean_difficulty_by_skill(self):
return self.difficulty / self.quantity_of_skill
class SeasonInformation(models.Model):
"""Classe représentant l'intensité d'un entraînement"""
class Meta:
verbose_name = "Season Information"
verbose_name_plural = "Season Informations"
unique_together = ("gymnast", "season")
CATEGORY_CHOICES_ARRAY = [(key, value) for key, value in CATEGORY_CHOICES.items()]
gymnast = models.ForeignKey(
Gymnast, on_delete=models.CASCADE, related_name="season_informations"
)
season = models.CharField(max_length=9)
number_of_training_sessions_per_week = models.PositiveSmallIntegerField(
verbose_name="# Training/w"
)
number_of_hours_per_week = models.PositiveSmallIntegerField(
verbose_name="# Hours/w"
)
number_of_s_and_c_sessions_per_week = models.PositiveSmallIntegerField(
verbose_name="# S&C training/w",
blank=True,
null=True,
)
number_of_s_and_c_hours_per_week = models.PositiveSmallIntegerField(
verbose_name="# S&C hours/w",
blank=True,
null=True,
)
category = models.PositiveSmallIntegerField(
choices=CATEGORY_CHOICES_ARRAY,
verbose_name="Category",
)
club = models.ForeignKey(
Club, null=True, on_delete=models.SET_NULL, related_name="season_informations"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "%s - %s : %s - %s - %s - %s" % (
self.gymnast,
self.season,
self.number_of_training_sessions_per_week,
self.number_of_hours_per_week,
self.category,
self.club,
)
class CompetitivePointsStats(Markdownizable, Seasonisable):
"""Class représentant des points de références de compétitions"""
TYPE_OF_STAT = (
(0, "precise"),
(1, "mean + 4 * standard deviation"),
(2, "mean + 2 * standard deviation"),
(3, "mean + standard deviation"),
(4, "mean + ½ standard deviation"),
(5, "mean + ¼ standard deviation"),
(6, "mean"),
(7, "mean - ¼ standard deviation"),
(8, "mean - ½ standard deviation"),
(9, "mean - standard deviation"),
(10, "mean - 2 * standard deviation"),
(11, "mean - 4 * standard deviation"),
)
label = models.CharField(max_length=40, null=False, blank=False)
gender = models.PositiveSmallIntegerField(
choices=GENDER_CHOICES, verbose_name="Gender"
)
age_category = models.PositiveSmallIntegerField(
choices=AGE_CATOGORY_CHOICES, verbose_name="Age category"
)
statistic_type = models.PositiveSmallIntegerField(
choices=TYPE_OF_STAT, verbose_name="Type of statistic"
)
point_execution = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="Execution"
)
point_difficulty = models.DecimalField(
max_digits=3, decimal_places=1, verbose_name="Difficulty"
)
point_time_of_flight = models.DecimalField(
max_digits=5, decimal_places=3, verbose_name="ToF"
)
point_horizontal_displacement = models.DecimalField(
max_digits=4, decimal_places=3, verbose_name="HD"
)
total = models.DecimalField(max_digits=6, decimal_places=3)
place = models.PositiveSmallIntegerField(verbose_name="Place")
event = models.ForeignKey(
Event, on_delete=models.SET_NULL, default=None, blank=True, null=True
)
routine_type = models.PositiveSmallIntegerField(choices=ROUTINE_TYPE_CHOICE)
informations = models.TextField(
blank=True,
null=True,
help_text="Information about even or statistics (mean, standard deviation, …).",
)
def __str__(self):
return f"{self.age_category} - {self.gender} - {self.routine_type} : {self.total} ({self.statistic_type})"

View File

@ -0,0 +1,84 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if accident_id %}Edit{% else %}Add{% endif %} accident</h4>
</div>
<div class="card-body">
<form action="{% if accident_id %}{% url 'accident_update' accident_id %}{% else %}{% url 'accident_create' %}{% endif %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_date" class="col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div class="col-sm-9 col-md-9 col-lg-6 col-lg-4 col-xl-4 {% if form.jumper.errors %}has-danger{% endif %}">
{% if request.user|has_group:"trainer" %}
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
{% else %}
<input type="text" class="form-control" value="{{ request.user.first_name }} {{ request.user.last_name }}" readonly="readonly" />
<input type="hidden" name="gymnast" id="gymnast" value="{{ request.user.gymnast.id }}" />
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Date <span class="text-danger"><b>*</b></span></label>
<div class="col-sm-3 col-md-4 col-lg-4 col-xl-4 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_skill" class="col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Skill</label>
<div class="col-sm-8 col-md-6 col-lg-6 col-xl-6 {% if form.skill.errors %}has-danger{% endif %}">
{{ form.skill }}
{{ form.skill_related }}
{% if form.skill.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.skill.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_nb_week_off" class="col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label"># Week off</label>
<div class="col-sm-3 col-md-4 col-lg-2 {% if form.nb_week_off.errors %}has-danger{% endif %}">
{{ form.nb_week_off }}
{% if form.nb_week_off.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.nb_week_off.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_information" class="col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Informations</label>
<div class="col-sm-9 col-md-9 col-lg-9 col-xl-9 {% if form.id_informations.errors %}has-danger{% endif %}">
{{ form.informations }}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if accident_id %}Save{% else %}Add{% endif %}" class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript" >
$(function(){
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
const skill_lookup = "{% url 'skill_lookup' %}";
</script>
<script src="{% static "js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static "js/template_users/gymnast_autocomplete_black.js" %}"></script>
<script src="{% static "js/template_users/skill_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static "js/template_users/gymnast_autocomplete.js" %}"></script>
<script src="{% static "js/template_users/skill_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,32 @@
{% extends "base.html" %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-8 col-md-6">
<div class="card">
<div class="card-header">
<h4 class="mb-0">Accident : {{ accident.date | date:"d-m-Y" }}</h4>
</div>
<div class="card-body">
<a href="{% url 'gymnast_details' accident.gymnast.id %}">{{ accident.gymnast }}</a><br />
{% if accident.nb_week_off %}
{{ accident.nb_week_off }} week(s) off.<br />
{% endif %}
<br />
{{ accident.to_markdown | safe }}
<div class="card-footer pl-0 pb-0">
<a href="{% url 'accident_list' %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="tim-icons icon-double-left"></i>
</button>
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,90 @@
{% extends "listing.html" %}
{% block datacontent %}
<div class="card mb-0">
<div class="card-header">
<div class="row">
<div class="col-md-4">
<h4 class=""> Accidents Listing</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
<a href="{% url 'accident_create' %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
{% if accident_list %}
<table class="table tablesorter table-striped" data-sort="table" id="accident_table">
<thead class="text-primary">
<tr>
<th style="width: 3%"></th>
<th class="header text-left" style="width: 10%">Date</th>
<th class="header text-left" style="width: 30%">Gymnast</th>
<th style="width: 30%">Skill</th>
<th style="width: 25%"># Week off</th>
</tr>
</thead>
<tbody>
{% for accident in accident_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td>
<a href="{% url 'accident_update' accident.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td class="text-left"><a href="{% url 'accident_details' accident.id %}">{{ accident.date | date:"d-m-Y" }}</a></td>
<td class="text-left"><a href="{% url 'gymnast_details_tab' accident.gymnast.id 'physiological' %}">{{ accident.gymnast }}</a></td>
<td class="text-left">
{% if accident.skill %}
<a href="{% url 'skill_details' accident.skill.id %}">{{ accident.skill }}</a>
{% else %}
-
{% endif %}
</td>
<td class="text-right">
{% if accident.nb_week_off %}
{{ accident.nb_week_off }}
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="muted-text">There are no accident corresponding to your criterias.</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('#accident_table').tablesorter({
headers: {
0: { sorter: false }, // disable first column
},
dateFormat: "uk",
sortList: [[1, 1]]
});
$('#accident_table').DataTable({
scrollY: 500,
paging: false,
searching: false,
ordering: false,
"bInfo": false,
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,123 @@
{% extends "base.html" %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-8 col-xl-6">
<div class="card">
<div class="card-header">
<h4>Add chrono details : {{ chrono.score }} - {{ chrono.routine.short_label }} on the {{ chrono.date | date:"d N Y" }}</h4>
</div>
<div class="card-body">
<ol>
{% for jump in jump_list %}
<li>
<div class="form-group row pb-0 mb-0">
<label class="col-1 col-sm-1 col-md-1 col-lg-1 col-xl-1 col-form-label text-right mb-0 pb-0">&nbsp;</label>
<div class="col-11 col-sm-11 col-md-11 col-lg-11 col-xl-11 pt-2 text-danger pb-0">
{{ jump.value }}
</div>
</div>
</li>
{% endfor %}
<li id="li_add_score">
<div class="form-group row mb-0">
<label class="col-1 col-sm-1 col-md-1 col-lg-1 col-xl-1 col-form-label text-right">&nbsp;</label>
<div class="col-3 col-sm-3 col-md-3 col-lg-3 col-xl-3">
<input type="number" min="1" max="{% if score_type %}2500{% else %}250{% endif %}" step="1" name="jump_score" placeholder="{% if score_type %}xxxx{% else %}xxx{% endif %}" class="form-control" id="jump_score">
</div>
<div class="col-1 col-sm-1 col-md-1 col-lg-1 col-xl-1"><button type="button" class="btn btn-warning btn-sm" id="plus_button">+</button></div>
</div>
</li>
</ol>
</div>
<div class="card-footer pt-0">
<a href="{% url 'jump_chrono_details' chrono.id %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fal fa-chart-line"></i>
</button>
</a>
&nbsp;&nbsp;&nbsp;
<a href="{% url 'chrono_list_for_gymnast' chrono.gymnast.id %}">
<button type="submit" value="add" class="btn btn-icon btn-warning mr-2">
<i class="fal fa-stopwatch"></i>
</button>
</a>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript" >
$(document).ready(function() {
var number_of_jump = {{ number_of_jump }};
var score_type = {{ score_type }};
$('#jump_score').focus();
function send_score_to_database(score)
{
var jump_value = score / 100;
if(score_type && score.length >= 4) // tof
var jump_value = jump_value / 10;
$.ajax({
url: "{% url 'add_jump_chrono_value' %}",
method: "POST",
data: {
chrono_id: {{ chrono.id }},
order: number_of_jump + 1,
value: jump_value,
csrfmiddlewaretoken: '{{ csrf_token }}'
},
}).done(function() {
insert_entered_value(jump_value);
number_of_jump = number_of_jump + 1;
});
}
function check_number_of_jump()
{
if(number_of_jump >= 10)
$('#li_add_score').hide();
else
$('#li_add_score').show();
}
function insert_entered_value(jump_score)
{
$('#minusButton').remove();
$('ol li:last-child').before('<li><div class="form-group row mb-0"><label class="col-1 col-sm-1 col-md-1 col-lg-1 col-xl-1 col-form-label text-right mb-0">&nbsp;</label><div class="col-3 col-sm-3 col-md-3 col-lg-3 col-xl-3 pt-2">' + jump_score + '</div><div class="col-1 col-sm-1 col-md-1 col-lg-1 col-xl-1"><button type="button" class="btn btn-warning btn-sm" id="minus_button">-</button></div></div></li>');
$('#jump_score').val('').focus();
check_number_of_jump();
}
$('#jump_score').keyup(function(){
var score = $('#jump_score').val();
if((score_type && score.length >= 4) || (!score_type && score.length >= 3))
send_score_to_database(score);
});
$('body').on('click', '#minus_button', function(event){
$.ajax({
url: "{% url 'remove_jump_chrono_value' %}",
method: "POST",
data: {
chrono_id: {{ chrono.id }},
order: number_of_jump,
csrfmiddlewaretoken: '{{ csrf_token }}'
},
}).done(function() {
$('ol li:last-child').prev().remove();
number_of_jump = number_of_jump - 1;
});
});
$('#plus_button').click(function(){
send_score_to_database($('#jump_score').val());
});
check_number_of_jump();
});
</script>
{% endblock %}

View File

@ -0,0 +1,90 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if chrono_id %}Edit{% else %}Add{% endif %} chrono</h4>
</div>
<div class="card-body">
<form action="{% if chrono_id %}{% url 'chrono_update' chrono_id %}{% else %}{% url 'chrono_create' %}{% endif %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_gymnast" class="col-4 col-sm-3 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-sm-9 col-md-9 col-lg-6 col-lg-4 col-xl-4 {% if form.jumper.errors %}has-danger{% endif %}">
{% if request.user|has_group:"trainer" %}
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
{% else %}
<input type="text" class="form-control" value="{{ request.user.first_name }} {{ request.user.last_name }}" readonly="readonly" />
<input type="hidden" name="gymnast" id="gymnast" value="{{ request.user.gymnast.id }}" />
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_gymnast" class="col-4 col-sm-3 col-form-label">Date <span class="text-danger"><b>*</b></span></label>
<div class="col-6 col-sm-3 col-md-4 col-lg-4 col-xl-4 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_chrono_type" class="col-4 col-sm-3 col-form-label">Chrono Type <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-4 col-lg-4 col-xl-4 {% if form.chrono_type.errors %}has-danger{% endif %}">
{{ form.chrono_type }}
{% if form.chrono_type.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.chrono_type.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_score_type" class="col-4 col-sm-3 col-form-label">Score Type <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-4 col-lg-4 col-xl-4 {% if form.score_type.errors %}has-danger{% endif %}">
{{ form.score_type }}
{% if form.score_type.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.score_type.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_score" class="col-4 col-sm-3 col-form-label">Score <span class="text-danger"><b>*</b></span></label>
<div class="col-4 col-sm-3 col-md-4 col-lg-3 {% if form.score.errors %}has-danger{% endif %}">
{{ form.score }}
{% if form.score.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.score.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
{{ form.tof }}
<div class="form-group text-center">
<input type="submit" value="{% if chrono_id %}Save{% else %}Add{% endif %}" class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript" >
$(function(){
blackDashboard.initDateTimePicker();
});
// Ne fonctionne pas avec un "forms.NumberInput" dans le form.
$('#id_score').keyup(function(){
if($(this).val().length == 2 && $(this).val().indexOf(',') == -1) {
var res = $(this).val().toString() + '.'
$(this).val(res);
}
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
</script>
<script src="{% static "js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static "js/template_users/gymnast_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static "js/template_users/gymnast_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block content %}
<div class="card mb-0">
<div class="card-header">
<h3 class="mb-0"><a href="{% url 'gymnast_details_tab' chrono.gymnast.id 'scores' %}">{{ chrono.gymnast }}</a></h3>
<h4 class="card-title"> Chrono of {{ chrono.date | date:'d N Y' }}</h4>
</div>
<div class="card-body pb-0 mb-0">
<div class="row mr-1 ml-1 pb-0 mb-0">
<div class="col-md-2 offset-md-1">
<table class="table tablesorter table-striped" data-sort="table" id="chrono_values_table">
{% for detail in chrono.details.all %}
<tr>
<td>{{ detail.order }}</td>
<td class="text-right">{{ detail.value }}</td>
</tr>
{% endfor %}
<tr>
<td></td>
<td class="text-right"><b>{{ chrono.score }}</b></td>
</tr>
</table>
<a href="{% url 'chrono_list_for_gymnast' chrono.gymnast.id %}">
<button type="submit" value="add" class="btn btn-icon btn-warning mr-2">
<i class="fal fa-stopwatch"></i>
</button>
</a>
</div>
<div class="col-md-8 offset-md-1 alert {% if request.session.template == 0 %}skill-info{% else %}alert-secondary{% endif %} mr-0 pb-0 pl-1 pr-1">
<canvas id="chartjs_chrono_details" class="chartjs" width="800" height="400"></canvas>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
var ctx = document.getElementById("chartjs_chrono_details").getContext("2d");
var gradient_stroke_1 = ctx.createLinearGradient(0, 450, 0, 0);
gradient_stroke_1.addColorStop(1, 'rgba(255, 99, 132, 0.4)');
gradient_stroke_1.addColorStop(0.75, 'rgba(255, 99, 132, 0.3)');
gradient_stroke_1.addColorStop(0.5, 'rgba(255, 99, 132, 0.2)');
gradient_stroke_1.addColorStop(0.25, 'rgba(255, 99, 132, 0)');
new Chart(document.getElementById("chartjs_chrono_details"), {
type: 'line',
data: {
labels: [
{% for detail in chrono.details.all %}
{{ detail.order }},
{% endfor %}
],
datasets: [{
fill: true,
borderColor: 'rgb(255, 99, 132)',
cubicInterpolationMode: 'monotone',
backgroundColor: gradient_stroke_1,
pointBackgroundColor: 'rgb(255, 99, 132)',
data: [
{% for detail in chrono.details.all %}
{{ detail.value }},
{% endfor %}
],
}]
},
options: {
scales: {
yAxes: [{
ticks: {
suggestedMin: {{ chart_min_value }},
suggestedMax: {{ chart_max_value }},
}
}]
},
plugins: {
legend: {
display: false,
}
}
},
});
</script>
{% endblock %}

View File

@ -0,0 +1,99 @@
{% extends "listing.html" %}
{% block datacontent %}
<div class="card mb-0">
<div class="card-header">
<div class="row">
<div class="col-8">
<h4 class=""> Chronos listing {% if gymnast %}for <i><a href="{% url 'gymnast_details_tab' gymnast.id 'scores' %}">{{ gymnast }}</a></i>{% endif %}
</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
<a
href="{% if gymnast %}{% url 'chrono_create_for_gymnast' gymnast.id %}{% else %}{% url 'chrono_create' %}{% endif %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
{% if chrono_list %}
<table class="table table-striped tablesorter" id="chrono_table">
<thead>
<tr>
<th style="width: 5%">&nbsp;</th>
<th style="width: 10%" class="header">Date</th>
<th style="width: 25%" class="header text-left">Gymnast</th>
<th style="width: 25%" class="header text-left">Routine</th>
<th style="width: 15%" class="header">Type</th>
<th style="width: 10%" class="header text-center">Score</th>
<th style="width: 10%" class="header text-center">TOF</th>
</tr>
</thead>
<tbody>
{% for chrono in chrono_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td>
<a href="{% url 'chrono_update' chrono.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
&nbsp;
<a href="{% url 'jump_chrono_values_create_or_update' chrono.id %}">
<span class="far fa-search-plus text-warning"></span>
</a>
</td>
<td>{% if chrono.details.all %}<a href="{% url 'jump_chrono_details' chrono.id %}">{% endif %}{{ chrono.date | date:"d-m-Y" }}{% if chrono.details.all %}</a>{% endif %}</td>
<td class="text-left">
<a href="{% url 'gymnast_details_tab' chrono.gymnast.id 'scores' %}">
{{ chrono.gymnast }}
</a>
</td>
<td class="text-left">
{% if chrono.routine %}
{{ chrono.routine.long_label }}
{% else %}
{{ chrono.get_chrono_type_display }}
{% endif %}
</td>
<td>{{ chrono.get_score_type_display }}</td>
<td class="text-center">{{ chrono.score }}</td>
<td class="text-center">{{ chrono.tof }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">There are no chronos corresponding to your criterias.</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('#chrono_table').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[1, 1], [2, 1]]
});
$('#chrono_table').DataTable({
scrollY: 475,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,256 @@
{% extends "base.html" %}
{% load get_value_at_index %}
{% block content %}
<div class="card mb-0">
<div class="card-header">
<h3 class="mb-0"><a href="{% url 'gymnast_details_tab' gymnast.id 'scores' %}">{{ gymnast }}</a></h3>
Chrono for
<select id="routine_type">
{% for type in distinct_routine_type_list %}
<option value="{{ type }}" {% if type == selected_routine_type %}selected{% endif %}>{{ type }}</option>
{% endfor %}
</select>
from {{ current_season }}
{% if date_begin %}
<i>{{ date_begin | date:'j M Y' }}</i> to <i>{{ date_end | date:'j M Y' }}</i>
{% else %}
<select id="select_season">
{% for season in distinct_season_list %}
<option value="{{ season }}" {% if selected_season|stringformat:"s" == season %}selected{% endif %}>{{ season }}</option>
{% endfor %}
</select>
<span id="week_1_management"> week
<select id="select_week_number">
{% for week_number in distinct_week_number_list %}
<option value="{{ week_number }}" {% if selected_week_number == week_number %}selected{% endif %}>{{ week_number }}</option>
{% endfor %}
</select>
</span>
{% endif %}
<br />
{% if chrono_list %}
<input type="checkbox" id="comparison" name="comparison" /> Compare with: chrono for
<select id="routine_type_2">
{% for type in distinct_routine_type_list %}
<option value="{{ type }}" {% if type == selected_routine_type %}selected{% endif %}>{{ type }}</option>
{% endfor %}
</select>
from {{ current_season }}
{% if date_begin %}
<i>{{ date_begin | date:'j M Y' }}</i> to <i>{{ date_end | date:'j M Y' }}</i>
{% else %}
<select id="select_season_2">
{% for season in distinct_season_list %}
<option value="{{ season }}" {% if selected_season|stringformat:"s" == season %}selected{% endif %}>{{ season }}</option>
{% endfor %}
</select>
<span id="week_2_management"> week
<select id="select_week_number_2">
{% for week_number in distinct_week_number_list %}
<option value="{{ week_number }}" {% if selected_week_number == week_number %}selected{% endif %}>{{ week_number }}</option>
{% endfor %}
</select>
</span>
{% endif %}
{% endif %}
</div>
<div class="card-body pb-0 mb-0">
<div class="row pb-0 mb-0">
{% if chrono_list %}
<div class="col-md-5">
<table class="table table-condensed" id="chrono_values_table">
{% for chrono in chrono_list %}
{% if chrono.details.all %}
<tr>
{% for detail in chrono.details.all %}
{% with stat_value=stat_values|get_value_at_index:forloop.counter0 %}
<td class="{% if stat_value.max_score == detail.value %}cell-success{% endif %}{% if stat_value.min_score == detail.value %}cell-danger{% endif %}">{{ detail.value | floatformat:2 }}</td>
{% endwith %}
{% endfor %}
<td class="text-right"><b>{{ chrono.score | floatformat:2 }}</b></td>
</tr>
{% endif %}
{% endfor %}
<tr>
{% for value in stat_values %}
<td><b>{{ value.avg_score | floatformat:2 }}</b></td>
{% endfor %}
<td class="text-right"><b></b></td>
</tr>
<!-- <tr>
{% for value in stat_values %}
<td><b>{{ value.min_score | floatformat:2 }}</b></td>
{% endfor %}
<td class="text-right"><b></b></td>
</tr><tr>
{% for value in stat_values %}
<td><b>{{ value.max_score | floatformat:2 }}</b></td>
{% endfor %}
<td class="text-right"><b></b></td> -->
</tr>
</table>
</div>
<div class="col-md-7 alert {% if request.session.template == 0 %}skill-info{% else %}alert-secondary{% endif %}">
<canvas id="chartjs_chrono" class="chartjs"></canvas>
</div>
{% else %}
<div class="col-md-12">
No chrono details with the selected criteria (routine type, season and week).
<br />
<br />
</div>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
var ctx = document.getElementById("chartjs_chrono").getContext("2d");
var gradient_stroke_1 = ctx.createLinearGradient(0, 450, 0, 0);
var gradient_stroke_2 = ctx.createLinearGradient(0, 230, 0, 50);
gradient_stroke_1.addColorStop(1, 'rgba(255, 99, 132, 0.4)');
gradient_stroke_1.addColorStop(0.75, 'rgba(255, 99, 132, 0.3)');
gradient_stroke_1.addColorStop(0.5, 'rgba(255, 99, 132, 0.2)');
gradient_stroke_1.addColorStop(0.25, 'rgba(255, 99, 132, 0)');
gradient_stroke_2.addColorStop(1, 'rgba(255, 159, 64, 0.4)');
gradient_stroke_2.addColorStop(0.75, 'rgba(255, 159, 64, 0.3)');
gradient_stroke_2.addColorStop(0.5, 'rgba(255, 159, 64, 0.2)');
gradient_stroke_2.addColorStop(0.25, 'rgba(255, 159, 64, 0)');
var chrono_chart = new Chart(document.getElementById("chartjs_chrono"), {
type: 'line',
data: {
labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
datasets: [{
data: [
{% for value in stat_values %}{{ value.avg_score | floatformat:3 }}, {% endfor %}
],
pointBackgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
backgroundColor: gradient_stroke_1,
cubicInterpolationMode: 'monotone',
fill: true,
}]
},
options: {
// min/max value ?
plugins: {
legend: {
display: false,
}
}
},
});
$('#select_season').change(function(){
var tmp_url = "{% url 'get_chrono_detail_distinct_weeknumber_for_season' gymnast.id '_season_label_' %}";
var season_label = $(this).children("option:selected").val();
target_url = tmp_url.replace('_season_label_', season_label);
$.getJSON(target_url, function(data) {
if (!$.trim(data))
$('#week_1_management').hide();
else
$('#week_1_management').show();
$("#select_week_number option").remove(); // Remove all <option> child tags.
$.each(data, function(key, value) { // Iterates through a collection
$("#select_week_number").append( // Append an object to the inside of the select box
$("<option></option>").text(value).val(value)
);
});
});
});
$('#select_week_number').change(function(){
var tmp_url = "{% url 'average_jump_chrono_details_for_gymnast_routinetype_season_and_week' gymnast.id '8888' '_season_label_' '9999' %}";
var routine_type = $('#routine_type').children("option:selected").val();
var season_label = $('#select_season').children("option:selected").val();
var week_number = $(this).children("option:selected").val();
target_url = tmp_url.replace('8888', routine_type).replace('_season_label_', season_label).replace('9999', week_number);
$(location).attr('href', target_url);
});
$('#select_season_2').change(function(){
var tmp_url = "{% url 'get_chrono_detail_distinct_weeknumber_for_season' gymnast.id '_season_label_' %}";
var season_label = $(this).children("option:selected").val();
target_url = tmp_url.replace('_season_label_', season_label);
$.getJSON(target_url, function(data) {
if (!$.trim(data))
$('#week_2_management').hide();
else
$('#week_2_management').show();
$("#select_week_number_2 option").remove();
$.each(data, function(key, value) {
$("#select_week_number_2").append($("<option></option>").text(value).val(value));
});
});
});
function remove_comparison_data() {
if ((Object.keys(chrono_chart.data.datasets).length) > 1) {
chrono_chart.data.datasets.pop();
chrono_chart.update();
}
}
function load_comparison_data() {
var tmp_url = "{% url 'get_average_jump_chrono_details_for_season_and_week' gymnast.id '8888' '_season_label_' '9999' %}";
var routine_type = $('#routine_type_2').children("option:selected").val();
var season_label = $('#select_season_2').children("option:selected").val();
var week_number = $('#select_week_number_2').children("option:selected").val();
target_url = tmp_url.replace('8888', routine_type).replace('_season_label_', season_label).replace('9999', week_number);
$.getJSON(target_url, function(data) {
if (!$.trim(data))
// $('#week_2_management').hide();
alert('No data for comparison.');
else {
var new_values = []
$.each(data, function(key, value) {
item = JSON.stringify(value);
new_values.push(parseFloat(value['avg_score']).toFixed(3));
});
var new_dataset = {
pointBackgroundColor: 'rgb(255, 159, 64)',
borderColor: 'rgb(255, 159, 64)',
backgroundColor: gradient_stroke_2,
cubicInterpolationMode: 'monotone',
fill: true,
data: new_values,
}
chrono_chart.data.datasets.push(new_dataset);
chrono_chart.update();
}
});
}
$('#select_week_number_2').change(function(){
if ($('#comparison').is(':checked')) {
remove_comparison_data();
load_comparison_data();
}
});
$('#comparison').change(function(){
if (this.checked) {
if (($('#select_week_number_2').val() != $('#select_week_number').val()) || ($('#select_season_2').val() != $('#select_season').val()))
load_comparison_data();
} else {
remove_comparison_data();
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,97 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if heightweight_id %}Edit{% else %}Add{% endif %} height/weight couple</h4>
</div>
<div class="card-body">
{% if form.errors %}
<div class="alert alert-danger">
{{ form.errors }}
</div>
{% endif %}
<form
action="{% if heightweight_id %}{% url 'heightweight_update' heightweight_id %}{% else %}{% url 'heightweight_create' %}{% endif %}"
method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_gymnast" class="col-3 col-sm-2 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div
class="col-9 col-sm-9 col-md-9 col-lg-6 col-lg-8 col-xl-8 {% if form.jumper.errors %}has-danger{% endif %}">
{% if request.user|has_group:"trainer" %}
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
{% else %}
<input type="text" class="form-control" value="{{ request.user.first_name }} {{ request.user.last_name }}" readonly="readonly" />
<input type="hidden" name="gymnast" id="gymnast" value="{{ request.user.gymnast.id }}" />
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-3 col-sm-2 col-form-label">Date <span
class="text-danger"><b>*</b></span></label>
<div class="col-6 col-sm-3 col-md-5 col-lg-4 col-xl-3 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_height" class="col-3 col-sm-2 col-form-label">Height <span
class="text-danger"><b>*</b></span></label>
<div class="col-6 col-sm-3 col-md-3 col-lg-3 {% if form.height.errors %}has-danger{% endif %}">
{{ form.height }}
{% if form.height.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.height.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_hips_height" class="col-3 col-sm-2 col-form-label">Hips Height</label>
<div
class="col-6 col-sm-3 col-md-3 col-lg-3 {% if form.hips_height.errors %}has-danger{% endif %}">
{{ form.hips_height }}
{% if form.hips_height.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.hips_height.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_weight" class="col-3 col-sm-2 col-form-label">Weight <span
class="text-danger"><b>*</b></span></label>
<div class="col-6 col-sm-3 col-md-3 col-lg-3 {% if form.weight.errors %}has-danger{% endif %}">
{{ form.weight }}
{% if form.weight.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.weight.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="Save" class="btn btn-fill btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(function () {
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
</script>
<script src="{% static " js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static " js/template_users/gymnast_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static " js/template_users/gymnast_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends "listing.html" %}
{% load has_group %}
{% block datacontent %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">
<div class="card">
<div class="card-header row">
<div class="col-8">
<h4 class="">Height/Weight list {% if gymnast %}for <a href="{% url 'gymnast_details_tab' gymnast.id 'physiological' %}"><i>{{ gymnast }}</i></a>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
{% if request.user|has_group:"trainer" %}
<a href="{% if gymnast %}{% url 'heightweight_create_for_gymnast' gymnast.id %}{% else %}{% url 'heightweight_create' %}{% endif %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
{% if heightweight_list %}
<table class="table tablesorter table-striped mb-0" data-sort="table" id="heightweight_table">
<thead>
<tr>
<th></th>
<th class="header text-left">Date</th>
<th class="header text-left">Gymnast</th>
<th class="header text-left">Height</th>
<th class="header text-left">Hips height</th>
<th class="header text-left">Weight</th>
</tr>
</thead>
<tbody>
{% for heightweight in heightweight_list %}
<tr>
<td>
<a href="{% url 'heightweight_update' heightweight.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td class="text-left">{{ heightweight.date | date:"d-m-Y" }}</td>
<td class="text-left"><a href="{% url 'gymnast_details' heightweight.gymnast.id %}">{{ heightweight.gymnast }}</a></td>
<td>{{ heightweight.height }}</td>
<td>{{ heightweight.hips_height }}</td>
<td>{{ heightweight.weight }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">There are no scores corresponding to your criterias</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('[data-sort="table"]').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[1, 1]]
});
$('#heightweight_table').DataTable({
scrollY: 500,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,137 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if intensity_id %}Edit{% else %}Add{% endif %} intensity</h4>
</div>
<div class="card-body">
<form
action="{% if intensity_id %}{% url 'intensity_update' intensity_id %}{% else %}{% url 'intensity_create' %}{% endif %}"
method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-sm-9 col-md-9 col-lg-6 col-lg-4 col-xl-4 {% if form.jumper.errors %}has-danger{% endif %}">
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Date <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-4 col-lg-4 col-xl-4 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_time" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Time <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-3 col-lg-2 {% if form.time.errors %}has-danger{% endif %}">
{{ form.time }}
{% if form.time.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.time.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_difficulty" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Difficulty <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-3 col-lg-2 {% if form.difficulty.errors %}has-danger{% endif %}">
{{ form.difficulty }}
{% if form.difficulty.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.difficulty.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_quantity_of_skill" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label"># of skill <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-3 col-lg-2 {% if form.quantity_of_skill.errors %}has-danger{% endif %}">
{{ form.quantity_of_skill }}
{% if form.quantity_of_skill.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.quantity_of_skill.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_number_of_passes" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label"># of passes <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-3 col-lg-2 {% if form.number_of_passes.errors %}has-danger{% endif %}">
{{ form.number_of_passes }}
{% if form.number_of_passes.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.number_of_passes.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_information" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Informations</label>
<div class="col-5 col-sm-10 col-md-10 col-lg-10 col-xl-10 {% if form.id_informations.errors %}has-danger{% endif %}">
{{ form.informations }}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if note_id %}Save{% else %}Add{% endif %}"
class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(function () {
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
$('#id_club_related').autocomplete({
source: function(request, response) {
$.ajax({
url: club_lookup,
method: "POST",
data: {
pattern: $('#id_club_related').val(),
csrfmiddlewaretoken: csrf_token
},
dataType: "json",
success: function(data) {
if(data.length != 0) {
response($.map(data, function(item) {
return {
label: item.Name,
value: item.Name,
clubid: item.ID
}
}))
} else {
response([{ label: 'No result found.', value: '' }]);
};
},
error: function (exception) {
console.log(exception);
}
});
},
minLength: 3,
select: function (event, ui) {
$($(this).data('ref')).val(ui.item.clubid);
},
{% if request.session.template == 0 %}
classes: {
"ui-widget-content": "custom_autocomplete_ul",
"ui-autocomplete": "custom_autocomplete_ul",
"ui-menu-item-wrapper": "custom_autocomplete_li",
"ui-menu-item": "custom_autocomplete_li",
},
{% endif %}
});
</script>
<script src="{% static " js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static " js/template_users/gymnast_autocomplete_black.js" %}"></script>
<script src="{% static " js/template_users/skill_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static " js/template_users/gymnast_autocomplete.js" %}"></script>
<script src="{% static " js/template_users/skill_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,96 @@
{% extends "listing.html" %}
{% block datacontent %}
<div class="card mb-0">
<div class="card-header">
<div class="row">
<div class="col-8">
<h4 class=""> Intensity listing {% if gymnast %}for <i><a href="{% url 'gymnast_details_tab' gymnast.id 'routine' %}">{{ gymnast }}</a></i>{% endif %}
</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
<a href="{% if gymnast %}{% url 'intensity_create_for_gymnast' gymnast.id %}{% else %}{% url 'intensity_create' %}{% endif %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
{% if intensity_list %}
<table class="table table-striped tablesorter" id="intensity_table">
<thead>
<tr>
<th style="width: 3%">&nbsp;</th>
<th style="width: 7%" class="header">Date</th>
<th style="width: 25%" class="header text-left">Gymnast</th>
<th style="width: 7%" class="header text-left">Time</th>
<th style="width: 7%" class="header">Diff</th>
<th style="width: 7%" class="header text-center"># skill</th>
<th style="width: 7%" class="header text-center"># passes</th>
<th style="width: 7%" class="header text-left">mean diff/passe</th>
<th style="width: 7%" class="header">mean # skill</th>
<th style="width: 7%" class="header text-center"># skill/passe</th>
<th style="width: 7%" class="header text-center">mean diff/skill</th>
</tr>
</thead>
<tbody>
{% for intensity in intensity_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td>
<a href="{% url 'intensity_update' intensity.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td>{{ intensity.date | date:"d-m-Y" }}</td>
<td class="text-left">
<a href="{% url 'gymnast_details_tab' intensity.gymnast.id 'routine' %}">
{{ intensity.gymnast }}
</a>
</td>
<td class="text-left">{{ intensity.time }}</td>
<td>{{ intensity.difficulty }}</td>
<td class="text-center">{{ intensity.quantity_of_skill }}</td>
<td class="text-center">{{ intensity.number_of_passes }}</td>
<td class="text-left">{{ intensity.mean_difficulty_by_passe | floatformat:2 }}</td>
<td>{{ intensity.mean_quantity_of_skill | floatformat:2 }}</td>
<td class="text-center">{{ intensity.quantity_of_skill_by_passe | floatformat:2 }}</td>
<td class="text-center">{{ intensity.mean_difficulty_by_skill | floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">There are no intensity corresponding to your criterias.</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('#intensity_table').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[1, 1], [2, 1]]
});
$('#intensity_table').DataTable({
scrollY: 475,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,90 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-8 col-xl-8">
<div class="card">
<div class="card-header">
<h4 class="card-title">Add Jumper/Skill link</h4>
</div>
<div class="card-body">
<form action="{% url 'learnedskill_create' %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_date" class="col-4 col-sm-3 col-form-label">Date</label>
<div class="col-sm-6 col-md-5 col-lg-4 col-xl-3 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_gymnast" class="col-4 col-sm-3 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-md-9 col-lg-6 col-lg-4 col-xl-4 {% if form.jumper.errors %}has-danger{% endif %}">
{% if request.user|has_group:"trainer" %}
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
{% else %}
<input type="text" class="form-control" value="{{ request.user.first_name }} {{ request.user.last_name }}" readonly="readonly" />
<input type="hidden" name="gymnast" id="gymnast" value="{{ request.user.gymnast.id }}" />
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_skill" class="col-4 col-sm-3 col-form-label">Skill</label>
<div class="col-8 col-md-9 col-lg-6 {% if form.skill.errors %}has-danger{% endif %}">
{{ form.skill }}
{{ form.skill_related }}
{% if form.skill.errors %}&nbsp;<skilln class="btn btn-sm btn-danger-outline">{% for error in form.skill.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_learning_step" class="col-4 col-sm-3 col-form-label">Learning step</label>
<div class="col-8 col-sm-4 col-md-3 {% if form.learning_step.errors %}has-danger{% endif %}">
{{ form.learning_step }}
{% if form.learning_step.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.learning_step.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
{% if plan_id %}
<div class="form-group row {% if form.is_done.errors %}has-error has-feedback{% endif %}">
<label for="id_is_done" class="col-4 col-sm-2 col-md-3 col-lg-3 col-xl-3 col-form-label">Is Done</label>
<div class="col-8 col-sm-2 col-md-2 col-lg-2 col-xl-2">
{{ form.is_done }}
{% if form.is_done.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.is_done.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
{% endif %}
<div class="form-group text-center">
<input type="submit" value="Save" class="btn btn-fill btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
<script type="text/javascript" >
$(function(){
$('#id_birthdate').datetimepicker({
format: 'YYYY-MM-DD'
});
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
const skill_lookup = "{% url 'skill_lookup' %}";
</script>
{% if request.session.template == 0 %}
<script src="{% static "js/template_users/gymnast_autocomplete_black.js" %}"></script>
<script src="{% static "js/template_users/skill_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static "js/template_users/gymnast_autocomplete.js" %}"></script>
<script src="{% static "js/template_users/skill_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,92 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-8 col-lg-6 col-xl-6">
<div class="card">
<div class="card-header">
<h4 class="">{% if mindstate_id %}Edit{% else %}Add{% endif %} mind state score</h4>
</div>
<div class="card-body">
<form action="{% if mindstate_id %}{% url 'mindstate_update' mindstate_id %}{% else %}{% url 'mindstate_create' %}{% endif %}" method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
<div class="form-group row ">
<label for="id_gymnast" class="col-3 col-sm-2 col-md-3 col-lg-3 col-xl-2 col-form-label">Gymnast <span class="text-danger"><b>*</b></span></label>
<div class="col-8 col-sm-9 col-md-9 col-lg-6 col-lg-8 col-xl-8 {% if form.jumper.errors %}has-danger{% endif %}">
{% if request.user|has_group:"trainer" %}
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}
<label class="text-danger" for="id_gymnast" id="gymnast-error">
{% for error in form.gymnast.errors %}{{ error }}{% endfor %}
</label>
{% endif %}
{% else %}
<input type="text" class="form-control" value="{{ request.user.first_name }} {{ request.user.last_name }}" readonly="readonly" />
<input type="hidden" name="gymnast" id="gymnast" value="{{ request.user.gymnast.id }}" />
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-3 col-sm-2 col-md-3 col-lg-3 col-xl-2 col-form-label">Date <span class="text-danger"><b>*</b></span></label>
<div class="col-5 col-sm-3 col-md-3 col-lg-4 col-xl-3 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_event" class="col-3 col-sm-2 col-md-3 col-lg-3 col-xl-2 col-form-label">Event</label>
<div class="col-8 col-sm-9 col-md-9 col-lg-9 col-xl-10 {% if form.date.errors %}has-danger{% endif %}">
{{ form.event }}
{{ form.event_related }}
{% if form.eventt.errors %}
<label class="text-danger" for="id_eventt" id="event-error">
{% for error in form.event.errors %}{{ error }}{% endfor %}
</label>
{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_score" class="col-3 col-sm-2 col-md-3 col-lg-3 col-xl-2 col-form-label">Score <span class="text-danger"><b>*</b></span></label>
<div class="col-4 col-sm-3 col-md-3 col-lg-3 {% if form.score.errors %}has-danger{% endif %}">
{{ form.score }}
{% if form.score.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.score.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_information" class="col-3 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Informations</label>
<div class="col-8 col-sm-10 col-md-10 col-lg-10 col-xl-10 {% if form.id_information.errors %}has-danger{% endif %}">
{{ form.informations }}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if mindstate_id %}Save{% else %}Add{% endif %}" class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript" >
$(function(){
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
const event_lookup = "{% url 'event_lookup' %}";
</script>
<script src="{% static "js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static "js/template_users/gymnast_autocomplete_black.js" %}"></script>
<script src="{% static "js/template_users/event_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static "js/template_users/gymnast_autocomplete.js" %}"></script>
<script src="{% static "js/template_users/event_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-8 col-md-6">
<div class="card">
<div class="card-header">
<h4 class="card-title mb-0">Mind State {{ mindstate.date | date:"d N Y" }}</h4>
</div>
<div class="card-body">
<a href="{% url 'gymnast_details_tab' mindstate.gymnast.id 'physiological' %}">{{ mindstate.gymnast }}</a> : {{ mindstate.score }}
<br />
<br />
{{ mindstate.to_markdown | safe }}
<div class="card-footer pl-0 pb-0">
<a href="{% url 'mindstate_list' %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="tim-icons icon-double-left"></i>
</button>
</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,80 @@
{% extends "listing.html" %}
{% load has_group %}
{% block datacontent %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-12 col-xl-12">
<div class="card">
<div class="card-header row">
<div class="col-8">
<h4 class="">Mind State list {% if gymnast %}for <a href="{% url 'gymnast_details_tab' gymnast.id 'physiological' %}"><i>{{ gymnast }}</i></a>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
{% if request.user|has_group:"trainer" %}
<a href="{% if gymnast %}{% url 'mindstate_create_for_gymnast' gymnast.id %}{% else %}{% url 'mindstate_create' %}{% endif %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
{% if mindstate_list %}
<table class="table tablesorter table-striped mb-0" data-sort="table" id="mindstate_table">
<thead>
<tr>
<th></th>
<th class="header text-left">Date</th>
<th class="header text-left">Gymnast</th>
<th class="header text-left">Score</th>
</tr>
</thead>
<tbody>
{% for state in mindstate_list %}
<tr>
<td>
<a href="{% url 'mindstate_update' state.id %}">
<span class="tim-icons icon-pencil text-warning"></span>
</a>
</td>
<td class="text-left">{{ state.date | date:"d-m-Y" }}</td>
<td class="text-left"><a href="{% url 'gymnast_details_tab' state.gymnast.id 'physiological' %}">{{ state.gymnast }}</a></td>
<td>{{ state.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">There are no mindstates corresponding to your criterias</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('[data-sort="table"]').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[1, 1]]
});
$('#mindstate_table').DataTable({
scrollY: 500,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,84 @@
{% extends "base.html" %}
{% load static %}
{% load has_group %}
{% block content %}
<div class="row justify-content-center">
<div class="col-12 col-sm-12 col-md-12 col-lg-10 col-xl-8">
<div class="card">
<div class="card-header">
<h4 class="">{% if note_id %}Edit{% else %}Add{% endif %} notes</h4>
</div>
<div class="card-body">
<form
action="{% if note_id %}{% url 'note_update' note_id %}{% else %}{% url 'note_create' %}{% endif %}"
method="post" class="form-horizontal" id="formulaire" name="formulaire">
{% csrf_token %}
{{ form.coach }}
<div class="form-group row ">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Gymnast
<span class="text-danger"><b>*</b></span></label>
<div
class="col-8 col-md-9 col-lg-6 col-lg-4 col-xl-4 {% if form.jumper.errors %}has-danger{% endif %}">
{{ form.gymnast }}
{{ form.gymnast_related }}
{% if form.gymnast.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.gymnast.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_date" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Date <span
class="text-danger"><b>*</b></span></label>
<div
class="col-8 col-sm-6 col-md-4 col-lg-4 col-xl-4 {% if form.date.errors %}has-danger{% endif %}">
{{ form.date }}
{% if form.date.errors %}<span class="btn btn-sm btn-danger-outline">{% for error in form.date.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group row ">
<label for="id_information"
class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Informations <span
class="text-danger"><b>*</b></span></label>
<div
class="col-8 col-sm-10 col-md-10 col-lg-10 col-xl-10 {% if form.id_informations.errors %}has-danger{% endif %}">
{{ form.informations }}
</div>
</div>
<div class="form-group row ">
<label for="id_status" class="col-4 col-sm-2 col-md-2 col-lg-2 col-xl-2 col-form-label">Status
<span class="text-danger"><b>*</b></span></label>
<div
class="col-8 col-sm-5 col-md-4 col-lg-4 col-xl-4 {% if form.status.errors %}has-danger{% endif %}">
{{ form.status }}
{% if form.status.errors %}&nbsp;<span class="btn btn-sm btn-danger-outline">{% for error in form.status.errors %}{{ error }}{% endfor %}</span>{% endif %}
</div>
</div>
<div class="form-group text-center">
<input type="submit" value="{% if note_id %}Save{% else %}Add{% endif %}"
class="btn btn-warning" />
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(function () {
blackDashboard.initDateTimePicker();
});
const csrf_token = "{{ csrf_token|escapejs }}";
const gymnast_lookup = "{% url 'gymnast_lookup' %}";
const skill_lookup = "{% url 'skill_lookup' %}";
</script>
<script src="{% static " js/template_users/datepicker_maxdate_today.js" %}"></script>
{% if request.session.template == 0 %}
<script src="{% static " js/template_users/gymnast_autocomplete_black.js" %}"></script>
<script src="{% static " js/template_users/skill_autocomplete_black.js" %}"></script>
{% else %}
<script src="{% static " js/template_users/gymnast_autocomplete.js" %}"></script>
<script src="{% static " js/template_users/skill_autocomplete.js" %}"></script>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
<div class="card mb-0">
<div class="card-header">
<h3 class="mb-0"><a href="{% url 'gymnast_details_tab' note.gymnast.id 'scores' %}">{{ note.gymnast }}</a></h3>
<h4 class="card-title"> Note of {{ note.created_at | date:'d-m-Y' }}</h4>
</div>
<div class="card-body pb-0 mb-0">
<div class="row mr-1 ml-1 pb-0 mb-0">
<div class="col-md-12">
<p>{{ note.to_markdown | safe }}</p>
</div>
</div>
<a href="{% url 'note_update' note.id %}"><span class="tim-icons icon-pencil text-warning"></span></a><br /><br />
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,77 @@
{% extends "listing.html" %}
{% block datacontent %}
<div class="card mb-0">
<div class="card-header">
<div class="row">
<div class="col-8">
<h4 class=""> Notes listing {% if gymnast %}for <i>{{ gymnast }}</i>{% endif %}</h4>
</div>
<div class="col-1 ml-auto">
<div class="text-right">
<a href="{% if gymnast %}{% url 'note_create_for_gymnast' gymnast.id %}{% else %}{% url 'note_create' %}{% endif %}">
<button type="submit" value="add" class="btn btn-icon btn-warning ">
<i class="fas fa-plus"></i>
</button>
</a>
</div>
</div>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
{% if note_list %}
<table class="table table-striped tablesorter" id="chrono_table">
<thead>
<tr>
<th style="width: 5%">&nbsp;</th>
<th style="width: 10%" class="header">Date</th>
<th style="width: 25%" class="header text-left">Gymnast</th>
<th style="width: 25%" class="header text-left">Coach</th>
</tr>
</thead>
<tbody>
{% for note in note_list %}
<tr role="row" class="{% cycle 'odd' 'even' %}">
<td>
</td>
<td>
<a href="{% url 'note_details' note.id %}">{{ note.created_at | date:"d-m-Y" }}</a>
</td>
<td class="text-left"><a href="{% url 'gymnast_details' note.gymnast.id %}">{{ note.gymnast
}}</a></td>
<td class="text-left">{{ note.coach }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p class="text-muted">There are no chronos corresponding to your criterias.</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% block footerscript %}
<script type="text/javascript">
$(document).ready(function () {
$('#chrono_table').tablesorter({
headers: {
0: { sorter: false },
},
dateFormat: "uk",
sortList: [[1, 1], [2, 1]]
});
$('#chrono_table').DataTable({
scrollY: 475,
scrollCollapse: true,
paging: false,
searching: false,
ordering: false,
// "bInfo" : false,
});
});
</script>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More