From a1ae3e91343679e34e6980b5cc2fcf1cc58b1fdc Mon Sep 17 00:00:00 2001 From: Gregory Trullemans Date: Tue, 25 Apr 2023 17:06:14 +0200 Subject: [PATCH] First commit --- .drone.yml | 26 + .gitignore | 43 + .pylintrc | 575 + Procfile | 2 + README.md | 121 + config/__init__.py | 0 config/asgi.py | 16 + config/settings.py | 192 + config/urls.py | 42 + config/wsgi.py | 16 + docker-compose.yml | 14 + jarvis/core/__init__.py | 0 jarvis/core/admin.py | 18 + jarvis/core/apps.py | 6 + jarvis/core/context_processors.py | 24 + jarvis/core/migrations/0001_initial.py | 29 + .../migrations/0002_alter_citation_quote.py | 18 + jarvis/core/migrations/__init__.py | 0 jarvis/core/models.py | 21 + jarvis/core/templates/base.html | 349 + .../core/templates/dashboard/dashboard.html | 180 + jarvis/core/templates/listing.html | 41 + jarvis/core/templates/login.html | 132 + jarvis/core/templates/records_10.html | 70 + jarvis/core/templates/search/results.html | 264 + jarvis/core/tests.py | 29 + jarvis/core/urls.py | 16 + jarvis/core/user_group_check.py | 6 + jarvis/core/views.py | 252 + jarvis/followup/__init__.py | 0 jarvis/followup/admin.py | 269 + jarvis/followup/apps.py | 6 + jarvis/followup/forms.py | 657 + jarvis/followup/management/__init__.py | 0 .../followup/management/commands/__init__.py | 0 jarvis/followup/migrations/0001_initial.py | 369 + .../0002_alter_learnedskill_gymnast.py | 25 + .../0003_alter_learnedskill_skill.py | 25 + .../0004_alter_learnedskill_skill.py | 25 + .../migrations/0005_auto_20211205_1412.py | 55 + .../0006_heightweight_numberofroutinedone.py | 130 + .../migrations/0007_auto_20211213_1445.py | 41 + ...0008_alter_learnedskill_unique_together.py | 19 + .../migrations/0009_auto_20211222_1318.py | 24 + .../migrations/0010_auto_20211224_0627.py | 28 + .../0011_alter_learnedskill_cando.py | 18 + jarvis/followup/migrations/0012_plan.py | 36 + .../followup/migrations/0013_chronodetails.py | 28 + .../migrations/0014_auto_20220125_1751.py | 23 + .../migrations/0015_auto_20220201_1633.py | 33 + .../migrations/0016_auto_20220203_1035.py | 33 + .../migrations/0017_auto_20220203_1037.py | 23 + .../migrations/0018_auto_20220208_1648.py | 28 + .../migrations/0019_auto_20220213_1441.py | 23 + ...n_gymnasthasroutine_date_begin_and_more.py | 23 + jarvis/followup/migrations/0021_notes.py | 63 + .../migrations/0022_rename_notes_note.py | 20 + .../migrations/0023_note_note_type.py | 22 + .../0024_rename_note_type_note_status.py | 18 + ..._note_date_note_season_note_week_number.py | 33 + ...lter_note_season_alter_note_week_number.py | 23 + ...dent_week_number_chrono_season_and_more.py | 147 + ...ndo_learnedskill_learning_step_and_more.py | 32 + .../migrations/0029_plan_informations.py | 23 + .../0030_alter_heightweight_hips_height.py | 24 + .../migrations/0031_alter_note_status.py | 22 + jarvis/followup/migrations/0032_intensity.py | 66 + ...ons_alter_intensity_difficulty_and_more.py | 29 + .../migrations/0034_seasoninformation.py | 85 + ...ation_number_of_hours_per_week_and_more.py | 28 + ...ter_numberofroutinedone_unique_together.py | 21 + .../0037_alter_chrono_chrono_type_and_more.py | 90 + .../0038_alter_chrono_chrono_type_and_more.py | 93 + .../migrations/0039_competitivepointsstats.py | 112 + .../0040_competitivepointsstats_place.py | 19 + .../0041_competitivepointsstats_label.py | 19 + ...2_competitivepointsstats_event_and_more.py | 63 + ...mber_of_s_and_c_hours_per_week_and_more.py | 27 + ...ation_number_of_hours_per_week_and_more.py | 40 + jarvis/followup/migrations/__init__.py | 0 jarvis/followup/models.py | 612 + .../followup/templates/accidents/create.html | 84 + .../followup/templates/accidents/details.html | 32 + jarvis/followup/templates/accidents/list.html | 90 + .../templates/chronos/add_details.html | 123 + jarvis/followup/templates/chronos/create.html | 90 + .../followup/templates/chronos/details.html | 88 + jarvis/followup/templates/chronos/list.html | 99 + .../templates/chronos/list_details.html | 256 + .../templates/heightweight/create.html | 97 + .../followup/templates/heightweight/list.html | 84 + .../templates/intensities/create.html | 137 + .../followup/templates/intensities/list.html | 96 + .../templates/learnedskills/create.html | 90 + .../followup/templates/mindstates/create.html | 92 + .../templates/mindstates/details.html | 30 + .../followup/templates/mindstates/list.html | 80 + jarvis/followup/templates/notes/create.html | 84 + jarvis/followup/templates/notes/details.html | 19 + jarvis/followup/templates/notes/list.html | 77 + jarvis/followup/templates/plan/create.html | 129 + .../templates/routinedone/create.html | 129 + .../followup/templates/routinedone/list.html | 91 + jarvis/followup/templates/scores/create.html | 155 + jarvis/followup/templates/scores/list.html | 97 + .../templates/seasoninformations/create.html | 156 + .../templates/seasoninformations/list.html | 90 + jarvis/followup/templatetags/__init__.py | 0 .../templatetags/get_value_at_index.py | 8 + jarvis/followup/tests_urls.py | 146 + jarvis/followup/urls.py | 292 + jarvis/followup/views.py | 1358 + jarvis/location/__init__.py | 0 jarvis/location/admin.py | 35 + jarvis/location/apps.py | 6 + jarvis/location/forms.py | 53 + jarvis/location/migrations/0001_initial.py | 102 + .../migrations/0002_alter_club_acronym.py | 18 + .../migrations/0003_auto_20220109_1001.py | 23 + jarvis/location/migrations/__init__.py | 0 jarvis/location/models.py | 73 + jarvis/location/templates/clubs/create.html | 97 + jarvis/location/templates/clubs/details.html | 62 + jarvis/location/templates/clubs/list.html | 53 + jarvis/location/templates/places/create.html | 82 + jarvis/location/templates/places/details.html | 59 + jarvis/location/templates/places/list.html | 90 + jarvis/location/tests_urls.py | 20 + jarvis/location/urls.py | 16 + jarvis/location/views.py | 139 + jarvis/objective/__init__.py | 0 jarvis/objective/admin.py | 193 + jarvis/objective/apps.py | 6 + jarvis/objective/forms.py | 62 + .../rebuild_closure_table_tree_for_routine.py | 122 + .../rebuild_closure_table_tree_for_skill.py | 123 + .../commands/remove_rank_from_skill.py | 13 + .../remove_space_from_skill_notation.py | 27 + jarvis/objective/migrations/0001_initial.py | 274 + .../migrations/0002_gymnasthasroutine.py | 85 + .../0003_delete_gymnasthasroutine.py | 16 + jarvis/objective/migrations/0004_plan.py | 58 + .../migrations/0005_alter_plan_educative.py | 19 + .../objective/migrations/0006_delete_plan.py | 16 + .../migrations/0007_auto_20220108_0956.py | 61 + .../migrations/0008_auto_20220108_1011.py | 23 + .../migrations/0009_auto_20220108_1956.py | 23 + .../migrations/0010_auto_20220109_1001.py | 22 + .../migrations/0011_auto_20220109_1016.py | 23 + .../migrations/0012_prerequisiteclosure.py | 27 + .../migrations/0013_alter_skill_position.py | 18 + ..._notation_alter_skill_rotation_and_more.py | 33 + .../migrations/0015_alter_skill_position.py | 29 + jarvis/objective/migrations/__init__.py | 0 jarvis/objective/models.py | 442 + .../objective/templates/routines/compose.html | 135 + .../objective/templates/routines/create.html | 61 + .../objective/templates/routines/details.html | 89 + jarvis/objective/templates/routines/list.html | 101 + jarvis/objective/templates/skills/create.html | 28 + .../objective/templates/skills/details.html | 127 + .../templates/skills/learning_line.html | 193 + jarvis/objective/templates/skills/list.html | 87 + jarvis/objective/tests.py | 84 + jarvis/objective/tests_models.py | 111 + jarvis/objective/tests_tools.py | 139 + jarvis/objective/tests_urls.py | 55 + jarvis/objective/tools.py | 162 + jarvis/objective/urls.py | 50 + jarvis/objective/views.py | 322 + jarvis/people/__init__.py | 0 jarvis/people/admin.py | 43 + jarvis/people/apps.py | 6 + jarvis/people/forms.py | 83 + jarvis/people/migrations/0001_initial.py | 70 + jarvis/people/migrations/0002_gymnast_user.py | 21 + .../migrations/0003_alter_gymnast_user.py | 21 + .../migrations/0004_gymnast_email_trainer.py | 20 + ...st_orientation_gymnast_year_of_practice.py | 28 + .../migrations/0006_gymnast_created_at.py | 22 + ...b_remove_gymnast_hours_by_week_and_more.py | 25 + .../0008_alter_gymnast_orientation.py | 23 + jarvis/people/migrations/__init__.py | 0 jarvis/people/models.py | 269 + jarvis/people/templates/gymnasts/create.html | 171 + jarvis/people/templates/gymnasts/details.html | 195 + .../gymnasts/gymnast_level_chart_bar.html | 18 + .../gymnasts/gymnast_level_doughnut.html | 41 + .../templates/gymnasts/link_to_routine.html | 107 + jarvis/people/templates/gymnasts/list.html | 93 + .../templates/gymnasts/list_accident.html | 58 + .../gymnasts/list_events_and_notes.html | 210 + .../templates/gymnasts/list_mindstate.html | 121 + .../templates/gymnasts/report_choices.html | 96 + .../gymnasts/reports/report_evaluation.html | 380 + .../gymnasts/reports/report_timeline.html | 67 + .../gymnasts/reports/report_week.html | 380 + .../gymnasts/tabs/tab_documents.html | 27 + .../gymnasts/tabs/tab_events_and_notes.html | 208 + .../gymnasts/tabs/tab_physiological.html | 266 + .../tabs/tab_routines_and_routine_stats.html | 435 + .../gymnasts/tabs/tab_scores_and_chronos.html | 357 + .../templates/gymnasts/tabs/tab_skill.html | 656 + jarvis/people/templatetags/__init__.py | 0 jarvis/people/templatetags/level_chart_bar.py | 17 + jarvis/people/templatetags/skill_doughnut.py | 34 + jarvis/people/tests.py | 70 + jarvis/people/tests_views.py | 23 + jarvis/people/urls.py | 103 + jarvis/people/views.py | 1178 + jarvis/planning/__init__.py | 0 jarvis/planning/admin.py | 58 + jarvis/planning/apps.py | 6 + jarvis/planning/forms.py | 58 + jarvis/planning/migrations/0001_initial.py | 126 + .../migrations/0002_auto_20220102_1051.py | 25 + .../migrations/0003_alter_event_datebegin.py | 20 + .../migrations/0004_alter_event_datebegin.py | 20 + .../migrations/0005_alter_event_datebegin.py | 19 + ..._event_participation_eventparticipation.py | 18 + .../migrations/0007_eventtype_color.py | 19 + ...ame_datebegin_event_date_begin_and_more.py | 28 + ...r_event_date_begin_alter_event_date_end.py | 28 + jarvis/planning/migrations/__init__.py | 0 jarvis/planning/models.py | 108 + jarvis/planning/templates/events/create.html | 147 + jarvis/planning/templates/events/details.html | 204 + jarvis/planning/templates/events/grid.html | 52 + jarvis/planning/templates/events/list.html | 99 + jarvis/planning/urls.py | 25 + jarvis/planning/views.py | 176 + jarvis/profiles/__init__.py | 0 jarvis/profiles/admin.py | 27 + jarvis/profiles/apps.py | 6 + jarvis/profiles/forms.py | 26 + jarvis/profiles/migrations/0001_initial.py | 61 + .../profiles/migrations/0002_notification.py | 68 + jarvis/profiles/migrations/__init__.py | 0 jarvis/profiles/models.py | 74 + .../templates/notification_update.html | 79 + jarvis/profiles/templates/update.html | 50 + jarvis/profiles/tests_urls.py | 15 + jarvis/profiles/tests_views.py | 40 + jarvis/profiles/urls.py | 17 + jarvis/profiles/views.py | 93 + jarvis/tools/__init__.py | 0 jarvis/tools/date_week_transition.py | 138 + jarvis/tools/models.py | 218 + jarvis/tools/templatetags/__init__.py | 0 jarvis/tools/templatetags/has_group.py | 9 + .../templatetags/is_user_equal_to_gymnast.py | 9 + jarvis/tools/templatetags/menuitems.py | 50 + jarvis/tools/tests_date_week_transition.py | 73 + jarvis/tools/tests_models.py | 55 + manage.py | 22 + requirements.txt | 1 + requirements/base.txt | 12 + requirements/ci.txt | 5 + requirements/dev.txt | 12 + static/css/black-dashboard.css | 29678 +++++++++++++++ static/css/black-dashboard.css.map | 356 + static/css/black-dashboard.min.css | 31 + static/css/black-dashboard_report.css | 29684 ++++++++++++++++ static/css/font_awesome_all_5.15.3.css | 5 + static/css/gymnast_report.css | 8 + static/css/jarvis.css | 141 + static/css/nucleo-icons.css | 543 + static/files/Manuel_Utilisateur.pdf | Bin 0 -> 3403862 bytes static/fonts/nucleo.eot | Bin 0 -> 26524 bytes static/fonts/nucleo.ttf | Bin 0 -> 26364 bytes static/fonts/nucleo.woff | Bin 0 -> 15168 bytes static/fonts/nucleo.woff2 | Bin 0 -> 12616 bytes static/img/apple-icon.png | Bin 0 -> 2446 bytes static/img/asc.gif | Bin 0 -> 60 bytes static/img/bg.gif | Bin 0 -> 70 bytes static/img/card-danger.png | Bin 0 -> 154033 bytes static/img/card-info.png | Bin 0 -> 126089 bytes static/img/card-primary.png | Bin 0 -> 137642 bytes static/img/card-success.png | Bin 0 -> 143579 bytes static/img/card-warning.png | Bin 0 -> 127671 bytes static/img/default-avatar.png | Bin 0 -> 2864 bytes static/img/desc.gif | Bin 0 -> 60 bytes static/img/favicon.png | Bin 0 -> 46716 bytes static/img/image_placeholder.jpg | Bin 0 -> 49086 bytes static/img/placeholder.jpg | Bin 0 -> 29544 bytes static/js/admin/routine.js | 13 + static/js/admin/skill.js | 196 + static/js/black-dashboard.js | 432 + static/js/black-dashboard.js.map | 1 + static/js/black-dashboard.min.js | 17 + static/js/core/bootstrap.min.js | 6 + static/js/core/jquery-3.6.0.min.js | 2 + static/js/core/jquery-3.6.0.min.map | 1 + static/js/core/popper.min.js | 4 + static/js/plugins/D3-dag/d3-dag_0.11.1.min.js | 16 + static/js/plugins/D3-dag/d3_7.6.1.min.js | 2 + .../bootstrap-datetimepicker_4.17.47.js | 2707 ++ .../bootstrap-datetimepicker_4.17.49.js | 2611 ++ static/js/plugins/bootstrap-notify_3.1.5.js | 432 + .../plugins/bootstrap-selectpicker_1.12.4.js | 1914 + static/js/plugins/bootstrap-switch_3.3.4.js | 786 + .../js/plugins/bootstrap-tagsinput_0.8.0.js | 685 + .../chartjs/chartjs-adapter-moment_1.0.0.js | 72 + .../js/plugins/chartjs/chartjs_3.9.1.min.js | 13 + .../datatables/datatables_1.12.1.min.js | 900 + .../jquery.dataTables_1.10.18.min.js | 643 + .../js/plugins/datatables/pdfmake.min.js.map | 1 + static/js/plugins/fullcalendar/main.css | 1467 + static/js/plugins/fullcalendar/main.js | 14959 ++++++++ static/js/plugins/fullcalendar/main.min.css | 1 + static/js/plugins/fullcalendar/main.min.js | 6 + .../js/plugins/jasny-bootstrap_3.1.3.min.js | 6 + static/js/plugins/jquery-jvectormap_2.0.4.js | 4183 +++ .../plugins/jquery.bootstrap-wizard_1.4.2.js | 345 + .../js/plugins/jquery.tablesorter_2.0.5b.js | 1056 + .../js/plugins/jquery.validate_1.17.0.min.js | 4 + .../ui-bg_diagonals-thick_90_eeeeee_40x40.png | Bin 0 -> 312 bytes .../images/ui-bg_glass_100_e4f1fb_1x400.png | Bin 0 -> 350 bytes .../images/ui-bg_glass_50_3baae3_1x400.png | Bin 0 -> 336 bytes .../images/ui-bg_glass_80_d7ebf9_1x400.png | Bin 0 -> 346 bytes .../ui-bg_highlight-hard_100_f2f5f7_1x100.png | Bin 0 -> 332 bytes .../ui-bg_highlight-hard_70_000000_1x100.png | Bin 0 -> 249 bytes .../ui-bg_highlight-soft_100_deedf7_1x100.png | Bin 0 -> 387 bytes .../ui-bg_highlight-soft_25_ffef8f_1x100.png | Bin 0 -> 309 bytes .../images/ui-icons_2694e8_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_3d80b3_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_72a7cf_256x240.png | Bin 0 -> 4549 bytes .../images/ui-icons_ffffff_256x240.png | Bin 0 -> 6299 bytes static/js/plugins/jqueryui/index.html | 559 + static/js/plugins/jqueryui/jquery-ui.css | 1312 + static/js/plugins/jqueryui/jquery-ui.js | 18706 ++++++++++ static/js/plugins/jqueryui/jquery-ui.min.css | 7 + static/js/plugins/jqueryui/jquery-ui.min.js | 13 + .../plugins/jqueryui/jquery-ui.structure.css | 886 + .../jqueryui/jquery-ui.structure.min.css | 5 + .../js/plugins/jqueryui/jquery-ui.theme.css | 443 + .../plugins/jqueryui/jquery-ui.theme.min.css | 5 + .../js/plugins/momentjs/moment_2.29.1.min.js | 7 + .../plugins/momentjs/moment_2.29.1.min.js.map | 1 + .../plugins/momentjs/moment_locale_en-gb.js | 79 + static/js/plugins/nouislider_11.1.0.min.js | 3 + .../perfect-scrollbar.jquery_1.4.0.min.js | 6 + static/js/plugins/sweetalert2.min.js | 1 + .../js/template_users/country_autocomplete.js | 38 + .../country_autocomplete_black.js | 44 + .../datepicker_maxdate_today.js | 6 + .../js/template_users/event_autocomplete.js | 38 + .../event_autocomplete_black.js | 44 + .../js/template_users/gymnast_autocomplete.js | 38 + .../gymnast_autocomplete_black.js | 44 + .../js/template_users/routine_autocomplete.js | 38 + .../routine_autocomplete_black.js | 44 + .../js/template_users/skill_autocomplete.js | 38 + .../skill_autocomplete_black.js | 44 + static/reglement/Sans titre.rtf | 26 + static/reglement/info.rtf | 7 + static/webfonts/fa-brands-400.eot | Bin 0 -> 134346 bytes static/webfonts/fa-brands-400.svg | 3717 ++ static/webfonts/fa-brands-400.ttf | Bin 0 -> 134040 bytes static/webfonts/fa-brands-400.woff | Bin 0 -> 90060 bytes static/webfonts/fa-brands-400.woff2 | Bin 0 -> 76740 bytes static/webfonts/fa-duotone-900.eot | Bin 0 -> 562882 bytes static/webfonts/fa-duotone-900.svg | 15326 ++++++++ static/webfonts/fa-duotone-900.ttf | Bin 0 -> 562584 bytes static/webfonts/fa-duotone-900.woff | Bin 0 -> 258888 bytes static/webfonts/fa-duotone-900.woff2 | Bin 0 -> 181948 bytes static/webfonts/fa-light-300.eot | Bin 0 -> 489242 bytes static/webfonts/fa-light-300.svg | 12420 +++++++ static/webfonts/fa-light-300.ttf | Bin 0 -> 488960 bytes static/webfonts/fa-light-300.woff | Bin 0 -> 245396 bytes static/webfonts/fa-light-300.woff2 | Bin 0 -> 184144 bytes static/webfonts/fa-regular-400.eot | Bin 0 -> 450238 bytes static/webfonts/fa-regular-400.svg | 11323 ++++++ static/webfonts/fa-regular-400.ttf | Bin 0 -> 449944 bytes static/webfonts/fa-regular-400.woff | Bin 0 -> 224592 bytes static/webfonts/fa-regular-400.woff2 | Bin 0 -> 168768 bytes static/webfonts/fa-solid-900.eot | Bin 0 -> 384110 bytes static/webfonts/fa-solid-900.svg | 9653 +++++ static/webfonts/fa-solid-900.ttf | Bin 0 -> 383828 bytes static/webfonts/fa-solid-900.woff | Bin 0 -> 183368 bytes static/webfonts/fa-solid-900.woff2 | Bin 0 -> 136824 bytes 382 files changed, 192146 insertions(+) create mode 100644 .drone.yml create mode 100644 .gitignore create mode 100644 .pylintrc create mode 100644 Procfile create mode 100644 README.md create mode 100644 config/__init__.py create mode 100644 config/asgi.py create mode 100644 config/settings.py create mode 100644 config/urls.py create mode 100644 config/wsgi.py create mode 100644 docker-compose.yml create mode 100644 jarvis/core/__init__.py create mode 100644 jarvis/core/admin.py create mode 100644 jarvis/core/apps.py create mode 100644 jarvis/core/context_processors.py create mode 100644 jarvis/core/migrations/0001_initial.py create mode 100644 jarvis/core/migrations/0002_alter_citation_quote.py create mode 100644 jarvis/core/migrations/__init__.py create mode 100644 jarvis/core/models.py create mode 100644 jarvis/core/templates/base.html create mode 100644 jarvis/core/templates/dashboard/dashboard.html create mode 100644 jarvis/core/templates/listing.html create mode 100644 jarvis/core/templates/login.html create mode 100644 jarvis/core/templates/records_10.html create mode 100644 jarvis/core/templates/search/results.html create mode 100644 jarvis/core/tests.py create mode 100644 jarvis/core/urls.py create mode 100644 jarvis/core/user_group_check.py create mode 100644 jarvis/core/views.py create mode 100644 jarvis/followup/__init__.py create mode 100644 jarvis/followup/admin.py create mode 100644 jarvis/followup/apps.py create mode 100644 jarvis/followup/forms.py create mode 100644 jarvis/followup/management/__init__.py create mode 100644 jarvis/followup/management/commands/__init__.py create mode 100644 jarvis/followup/migrations/0001_initial.py create mode 100644 jarvis/followup/migrations/0002_alter_learnedskill_gymnast.py create mode 100644 jarvis/followup/migrations/0003_alter_learnedskill_skill.py create mode 100644 jarvis/followup/migrations/0004_alter_learnedskill_skill.py create mode 100644 jarvis/followup/migrations/0005_auto_20211205_1412.py create mode 100644 jarvis/followup/migrations/0006_heightweight_numberofroutinedone.py create mode 100644 jarvis/followup/migrations/0007_auto_20211213_1445.py create mode 100644 jarvis/followup/migrations/0008_alter_learnedskill_unique_together.py create mode 100644 jarvis/followup/migrations/0009_auto_20211222_1318.py create mode 100644 jarvis/followup/migrations/0010_auto_20211224_0627.py create mode 100644 jarvis/followup/migrations/0011_alter_learnedskill_cando.py create mode 100644 jarvis/followup/migrations/0012_plan.py create mode 100644 jarvis/followup/migrations/0013_chronodetails.py create mode 100644 jarvis/followup/migrations/0014_auto_20220125_1751.py create mode 100644 jarvis/followup/migrations/0015_auto_20220201_1633.py create mode 100644 jarvis/followup/migrations/0016_auto_20220203_1035.py create mode 100644 jarvis/followup/migrations/0017_auto_20220203_1037.py create mode 100644 jarvis/followup/migrations/0018_auto_20220208_1648.py create mode 100644 jarvis/followup/migrations/0019_auto_20220213_1441.py create mode 100644 jarvis/followup/migrations/0020_rename_datebegin_gymnasthasroutine_date_begin_and_more.py create mode 100644 jarvis/followup/migrations/0021_notes.py create mode 100644 jarvis/followup/migrations/0022_rename_notes_note.py create mode 100644 jarvis/followup/migrations/0023_note_note_type.py create mode 100644 jarvis/followup/migrations/0024_rename_note_type_note_status.py create mode 100644 jarvis/followup/migrations/0025_note_date_note_season_note_week_number.py create mode 100644 jarvis/followup/migrations/0026_alter_note_season_alter_note_week_number.py create mode 100644 jarvis/followup/migrations/0027_accident_season_accident_week_number_chrono_season_and_more.py create mode 100644 jarvis/followup/migrations/0028_rename_cando_learnedskill_learning_step_and_more.py create mode 100644 jarvis/followup/migrations/0029_plan_informations.py create mode 100644 jarvis/followup/migrations/0030_alter_heightweight_hips_height.py create mode 100644 jarvis/followup/migrations/0031_alter_note_status.py create mode 100644 jarvis/followup/migrations/0032_intensity.py create mode 100644 jarvis/followup/migrations/0033_alter_intensity_options_alter_intensity_difficulty_and_more.py create mode 100644 jarvis/followup/migrations/0034_seasoninformation.py create mode 100644 jarvis/followup/migrations/0035_rename_hours_by_week_seasoninformation_number_of_hours_per_week_and_more.py create mode 100644 jarvis/followup/migrations/0036_alter_numberofroutinedone_unique_together.py create mode 100644 jarvis/followup/migrations/0037_alter_chrono_chrono_type_and_more.py create mode 100644 jarvis/followup/migrations/0038_alter_chrono_chrono_type_and_more.py create mode 100644 jarvis/followup/migrations/0039_competitivepointsstats.py create mode 100644 jarvis/followup/migrations/0040_competitivepointsstats_place.py create mode 100644 jarvis/followup/migrations/0041_competitivepointsstats_label.py create mode 100644 jarvis/followup/migrations/0042_competitivepointsstats_event_and_more.py create mode 100644 jarvis/followup/migrations/0043_seasoninformation_number_of_s_and_c_hours_per_week_and_more.py create mode 100644 jarvis/followup/migrations/0044_alter_seasoninformation_number_of_hours_per_week_and_more.py create mode 100644 jarvis/followup/migrations/__init__.py create mode 100644 jarvis/followup/models.py create mode 100644 jarvis/followup/templates/accidents/create.html create mode 100644 jarvis/followup/templates/accidents/details.html create mode 100644 jarvis/followup/templates/accidents/list.html create mode 100644 jarvis/followup/templates/chronos/add_details.html create mode 100644 jarvis/followup/templates/chronos/create.html create mode 100644 jarvis/followup/templates/chronos/details.html create mode 100644 jarvis/followup/templates/chronos/list.html create mode 100644 jarvis/followup/templates/chronos/list_details.html create mode 100644 jarvis/followup/templates/heightweight/create.html create mode 100644 jarvis/followup/templates/heightweight/list.html create mode 100644 jarvis/followup/templates/intensities/create.html create mode 100644 jarvis/followup/templates/intensities/list.html create mode 100644 jarvis/followup/templates/learnedskills/create.html create mode 100644 jarvis/followup/templates/mindstates/create.html create mode 100644 jarvis/followup/templates/mindstates/details.html create mode 100644 jarvis/followup/templates/mindstates/list.html create mode 100644 jarvis/followup/templates/notes/create.html create mode 100644 jarvis/followup/templates/notes/details.html create mode 100644 jarvis/followup/templates/notes/list.html create mode 100644 jarvis/followup/templates/plan/create.html create mode 100644 jarvis/followup/templates/routinedone/create.html create mode 100644 jarvis/followup/templates/routinedone/list.html create mode 100644 jarvis/followup/templates/scores/create.html create mode 100644 jarvis/followup/templates/scores/list.html create mode 100644 jarvis/followup/templates/seasoninformations/create.html create mode 100644 jarvis/followup/templates/seasoninformations/list.html create mode 100644 jarvis/followup/templatetags/__init__.py create mode 100644 jarvis/followup/templatetags/get_value_at_index.py create mode 100644 jarvis/followup/tests_urls.py create mode 100644 jarvis/followup/urls.py create mode 100644 jarvis/followup/views.py create mode 100644 jarvis/location/__init__.py create mode 100644 jarvis/location/admin.py create mode 100644 jarvis/location/apps.py create mode 100644 jarvis/location/forms.py create mode 100644 jarvis/location/migrations/0001_initial.py create mode 100644 jarvis/location/migrations/0002_alter_club_acronym.py create mode 100644 jarvis/location/migrations/0003_auto_20220109_1001.py create mode 100644 jarvis/location/migrations/__init__.py create mode 100644 jarvis/location/models.py create mode 100644 jarvis/location/templates/clubs/create.html create mode 100644 jarvis/location/templates/clubs/details.html create mode 100644 jarvis/location/templates/clubs/list.html create mode 100644 jarvis/location/templates/places/create.html create mode 100644 jarvis/location/templates/places/details.html create mode 100644 jarvis/location/templates/places/list.html create mode 100644 jarvis/location/tests_urls.py create mode 100644 jarvis/location/urls.py create mode 100644 jarvis/location/views.py create mode 100644 jarvis/objective/__init__.py create mode 100644 jarvis/objective/admin.py create mode 100644 jarvis/objective/apps.py create mode 100644 jarvis/objective/forms.py create mode 100644 jarvis/objective/management/commands/rebuild_closure_table_tree_for_routine.py create mode 100644 jarvis/objective/management/commands/rebuild_closure_table_tree_for_skill.py create mode 100644 jarvis/objective/management/commands/remove_rank_from_skill.py create mode 100644 jarvis/objective/management/commands/remove_space_from_skill_notation.py create mode 100644 jarvis/objective/migrations/0001_initial.py create mode 100644 jarvis/objective/migrations/0002_gymnasthasroutine.py create mode 100644 jarvis/objective/migrations/0003_delete_gymnasthasroutine.py create mode 100644 jarvis/objective/migrations/0004_plan.py create mode 100644 jarvis/objective/migrations/0005_alter_plan_educative.py create mode 100644 jarvis/objective/migrations/0006_delete_plan.py create mode 100644 jarvis/objective/migrations/0007_auto_20220108_0956.py create mode 100644 jarvis/objective/migrations/0008_auto_20220108_1011.py create mode 100644 jarvis/objective/migrations/0009_auto_20220108_1956.py create mode 100644 jarvis/objective/migrations/0010_auto_20220109_1001.py create mode 100644 jarvis/objective/migrations/0011_auto_20220109_1016.py create mode 100644 jarvis/objective/migrations/0012_prerequisiteclosure.py create mode 100644 jarvis/objective/migrations/0013_alter_skill_position.py create mode 100644 jarvis/objective/migrations/0014_alter_skill_notation_alter_skill_rotation_and_more.py create mode 100644 jarvis/objective/migrations/0015_alter_skill_position.py create mode 100644 jarvis/objective/migrations/__init__.py create mode 100644 jarvis/objective/models.py create mode 100644 jarvis/objective/templates/routines/compose.html create mode 100644 jarvis/objective/templates/routines/create.html create mode 100644 jarvis/objective/templates/routines/details.html create mode 100644 jarvis/objective/templates/routines/list.html create mode 100644 jarvis/objective/templates/skills/create.html create mode 100644 jarvis/objective/templates/skills/details.html create mode 100644 jarvis/objective/templates/skills/learning_line.html create mode 100644 jarvis/objective/templates/skills/list.html create mode 100644 jarvis/objective/tests.py create mode 100644 jarvis/objective/tests_models.py create mode 100644 jarvis/objective/tests_tools.py create mode 100644 jarvis/objective/tests_urls.py create mode 100644 jarvis/objective/tools.py create mode 100644 jarvis/objective/urls.py create mode 100644 jarvis/objective/views.py create mode 100644 jarvis/people/__init__.py create mode 100644 jarvis/people/admin.py create mode 100644 jarvis/people/apps.py create mode 100644 jarvis/people/forms.py create mode 100644 jarvis/people/migrations/0001_initial.py create mode 100644 jarvis/people/migrations/0002_gymnast_user.py create mode 100644 jarvis/people/migrations/0003_alter_gymnast_user.py create mode 100644 jarvis/people/migrations/0004_gymnast_email_trainer.py create mode 100644 jarvis/people/migrations/0005_gymnast_orientation_gymnast_year_of_practice.py create mode 100644 jarvis/people/migrations/0006_gymnast_created_at.py create mode 100644 jarvis/people/migrations/0007_remove_gymnast_club_remove_gymnast_hours_by_week_and_more.py create mode 100644 jarvis/people/migrations/0008_alter_gymnast_orientation.py create mode 100644 jarvis/people/migrations/__init__.py create mode 100644 jarvis/people/models.py create mode 100644 jarvis/people/templates/gymnasts/create.html create mode 100644 jarvis/people/templates/gymnasts/details.html create mode 100644 jarvis/people/templates/gymnasts/gymnast_level_chart_bar.html create mode 100644 jarvis/people/templates/gymnasts/gymnast_level_doughnut.html create mode 100644 jarvis/people/templates/gymnasts/link_to_routine.html create mode 100644 jarvis/people/templates/gymnasts/list.html create mode 100644 jarvis/people/templates/gymnasts/list_accident.html create mode 100644 jarvis/people/templates/gymnasts/list_events_and_notes.html create mode 100644 jarvis/people/templates/gymnasts/list_mindstate.html create mode 100644 jarvis/people/templates/gymnasts/report_choices.html create mode 100644 jarvis/people/templates/gymnasts/reports/report_evaluation.html create mode 100644 jarvis/people/templates/gymnasts/reports/report_timeline.html create mode 100644 jarvis/people/templates/gymnasts/reports/report_week.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_documents.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_events_and_notes.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_physiological.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_routines_and_routine_stats.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_scores_and_chronos.html create mode 100644 jarvis/people/templates/gymnasts/tabs/tab_skill.html create mode 100644 jarvis/people/templatetags/__init__.py create mode 100644 jarvis/people/templatetags/level_chart_bar.py create mode 100644 jarvis/people/templatetags/skill_doughnut.py create mode 100644 jarvis/people/tests.py create mode 100644 jarvis/people/tests_views.py create mode 100644 jarvis/people/urls.py create mode 100644 jarvis/people/views.py create mode 100644 jarvis/planning/__init__.py create mode 100644 jarvis/planning/admin.py create mode 100644 jarvis/planning/apps.py create mode 100644 jarvis/planning/forms.py create mode 100644 jarvis/planning/migrations/0001_initial.py create mode 100644 jarvis/planning/migrations/0002_auto_20220102_1051.py create mode 100644 jarvis/planning/migrations/0003_alter_event_datebegin.py create mode 100644 jarvis/planning/migrations/0004_alter_event_datebegin.py create mode 100644 jarvis/planning/migrations/0005_alter_event_datebegin.py create mode 100644 jarvis/planning/migrations/0006_rename_event_participation_eventparticipation.py create mode 100644 jarvis/planning/migrations/0007_eventtype_color.py create mode 100644 jarvis/planning/migrations/0008_rename_datebegin_event_date_begin_and_more.py create mode 100644 jarvis/planning/migrations/0009_alter_event_date_begin_alter_event_date_end.py create mode 100644 jarvis/planning/migrations/__init__.py create mode 100644 jarvis/planning/models.py create mode 100644 jarvis/planning/templates/events/create.html create mode 100644 jarvis/planning/templates/events/details.html create mode 100644 jarvis/planning/templates/events/grid.html create mode 100644 jarvis/planning/templates/events/list.html create mode 100644 jarvis/planning/urls.py create mode 100644 jarvis/planning/views.py create mode 100644 jarvis/profiles/__init__.py create mode 100644 jarvis/profiles/admin.py create mode 100644 jarvis/profiles/apps.py create mode 100644 jarvis/profiles/forms.py create mode 100644 jarvis/profiles/migrations/0001_initial.py create mode 100644 jarvis/profiles/migrations/0002_notification.py create mode 100644 jarvis/profiles/migrations/__init__.py create mode 100644 jarvis/profiles/models.py create mode 100644 jarvis/profiles/templates/notification_update.html create mode 100644 jarvis/profiles/templates/update.html create mode 100644 jarvis/profiles/tests_urls.py create mode 100644 jarvis/profiles/tests_views.py create mode 100644 jarvis/profiles/urls.py create mode 100644 jarvis/profiles/views.py create mode 100644 jarvis/tools/__init__.py create mode 100644 jarvis/tools/date_week_transition.py create mode 100644 jarvis/tools/models.py create mode 100644 jarvis/tools/templatetags/__init__.py create mode 100644 jarvis/tools/templatetags/has_group.py create mode 100644 jarvis/tools/templatetags/is_user_equal_to_gymnast.py create mode 100644 jarvis/tools/templatetags/menuitems.py create mode 100644 jarvis/tools/tests_date_week_transition.py create mode 100644 jarvis/tools/tests_models.py create mode 100755 manage.py create mode 100644 requirements.txt create mode 100644 requirements/base.txt create mode 100644 requirements/ci.txt create mode 100644 requirements/dev.txt create mode 100644 static/css/black-dashboard.css create mode 100644 static/css/black-dashboard.css.map create mode 100644 static/css/black-dashboard.min.css create mode 100644 static/css/black-dashboard_report.css create mode 100644 static/css/font_awesome_all_5.15.3.css create mode 100644 static/css/gymnast_report.css create mode 100644 static/css/jarvis.css create mode 100644 static/css/nucleo-icons.css create mode 100644 static/files/Manuel_Utilisateur.pdf create mode 100644 static/fonts/nucleo.eot create mode 100644 static/fonts/nucleo.ttf create mode 100644 static/fonts/nucleo.woff create mode 100644 static/fonts/nucleo.woff2 create mode 100644 static/img/apple-icon.png create mode 100644 static/img/asc.gif create mode 100644 static/img/bg.gif create mode 100644 static/img/card-danger.png create mode 100644 static/img/card-info.png create mode 100644 static/img/card-primary.png create mode 100644 static/img/card-success.png create mode 100644 static/img/card-warning.png create mode 100644 static/img/default-avatar.png create mode 100644 static/img/desc.gif create mode 100644 static/img/favicon.png create mode 100644 static/img/image_placeholder.jpg create mode 100644 static/img/placeholder.jpg create mode 100644 static/js/admin/routine.js create mode 100644 static/js/admin/skill.js create mode 100644 static/js/black-dashboard.js create mode 100644 static/js/black-dashboard.js.map create mode 100644 static/js/black-dashboard.min.js create mode 100644 static/js/core/bootstrap.min.js create mode 100644 static/js/core/jquery-3.6.0.min.js create mode 100644 static/js/core/jquery-3.6.0.min.map create mode 100644 static/js/core/popper.min.js create mode 100644 static/js/plugins/D3-dag/d3-dag_0.11.1.min.js create mode 100644 static/js/plugins/D3-dag/d3_7.6.1.min.js create mode 100644 static/js/plugins/bootstrap-datetimepicker_4.17.47.js create mode 100644 static/js/plugins/bootstrap-datetimepicker_4.17.49.js create mode 100644 static/js/plugins/bootstrap-notify_3.1.5.js create mode 100644 static/js/plugins/bootstrap-selectpicker_1.12.4.js create mode 100755 static/js/plugins/bootstrap-switch_3.3.4.js create mode 100644 static/js/plugins/bootstrap-tagsinput_0.8.0.js create mode 100644 static/js/plugins/chartjs/chartjs-adapter-moment_1.0.0.js create mode 100644 static/js/plugins/chartjs/chartjs_3.9.1.min.js create mode 100644 static/js/plugins/datatables/datatables_1.12.1.min.js create mode 100644 static/js/plugins/datatables/jquery.dataTables_1.10.18.min.js create mode 100755 static/js/plugins/datatables/pdfmake.min.js.map create mode 100644 static/js/plugins/fullcalendar/main.css create mode 100644 static/js/plugins/fullcalendar/main.js create mode 100644 static/js/plugins/fullcalendar/main.min.css create mode 100644 static/js/plugins/fullcalendar/main.min.js create mode 100644 static/js/plugins/jasny-bootstrap_3.1.3.min.js create mode 100644 static/js/plugins/jquery-jvectormap_2.0.4.js create mode 100644 static/js/plugins/jquery.bootstrap-wizard_1.4.2.js create mode 100644 static/js/plugins/jquery.tablesorter_2.0.5b.js create mode 100644 static/js/plugins/jquery.validate_1.17.0.min.js create mode 100644 static/js/plugins/jqueryui/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_glass_100_e4f1fb_1x400.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_glass_50_3baae3_1x400.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_glass_80_d7ebf9_1x400.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_highlight-hard_70_000000_1x100.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_highlight-soft_100_deedf7_1x100.png create mode 100644 static/js/plugins/jqueryui/images/ui-bg_highlight-soft_25_ffef8f_1x100.png create mode 100644 static/js/plugins/jqueryui/images/ui-icons_2694e8_256x240.png create mode 100644 static/js/plugins/jqueryui/images/ui-icons_2e83ff_256x240.png create mode 100644 static/js/plugins/jqueryui/images/ui-icons_3d80b3_256x240.png create mode 100644 static/js/plugins/jqueryui/images/ui-icons_72a7cf_256x240.png create mode 100644 static/js/plugins/jqueryui/images/ui-icons_ffffff_256x240.png create mode 100644 static/js/plugins/jqueryui/index.html create mode 100644 static/js/plugins/jqueryui/jquery-ui.css create mode 100644 static/js/plugins/jqueryui/jquery-ui.js create mode 100644 static/js/plugins/jqueryui/jquery-ui.min.css create mode 100644 static/js/plugins/jqueryui/jquery-ui.min.js create mode 100644 static/js/plugins/jqueryui/jquery-ui.structure.css create mode 100644 static/js/plugins/jqueryui/jquery-ui.structure.min.css create mode 100644 static/js/plugins/jqueryui/jquery-ui.theme.css create mode 100644 static/js/plugins/jqueryui/jquery-ui.theme.min.css create mode 100644 static/js/plugins/momentjs/moment_2.29.1.min.js create mode 100644 static/js/plugins/momentjs/moment_2.29.1.min.js.map create mode 100644 static/js/plugins/momentjs/moment_locale_en-gb.js create mode 100644 static/js/plugins/nouislider_11.1.0.min.js create mode 100644 static/js/plugins/perfect-scrollbar.jquery_1.4.0.min.js create mode 100644 static/js/plugins/sweetalert2.min.js create mode 100644 static/js/template_users/country_autocomplete.js create mode 100644 static/js/template_users/country_autocomplete_black.js create mode 100644 static/js/template_users/datepicker_maxdate_today.js create mode 100644 static/js/template_users/event_autocomplete.js create mode 100644 static/js/template_users/event_autocomplete_black.js create mode 100644 static/js/template_users/gymnast_autocomplete.js create mode 100644 static/js/template_users/gymnast_autocomplete_black.js create mode 100644 static/js/template_users/routine_autocomplete.js create mode 100644 static/js/template_users/routine_autocomplete_black.js create mode 100644 static/js/template_users/skill_autocomplete.js create mode 100644 static/js/template_users/skill_autocomplete_black.js create mode 100644 static/reglement/Sans titre.rtf create mode 100644 static/reglement/info.rtf create mode 100644 static/webfonts/fa-brands-400.eot create mode 100644 static/webfonts/fa-brands-400.svg create mode 100644 static/webfonts/fa-brands-400.ttf create mode 100644 static/webfonts/fa-brands-400.woff create mode 100644 static/webfonts/fa-brands-400.woff2 create mode 100644 static/webfonts/fa-duotone-900.eot create mode 100644 static/webfonts/fa-duotone-900.svg create mode 100644 static/webfonts/fa-duotone-900.ttf create mode 100644 static/webfonts/fa-duotone-900.woff create mode 100644 static/webfonts/fa-duotone-900.woff2 create mode 100644 static/webfonts/fa-light-300.eot create mode 100644 static/webfonts/fa-light-300.svg create mode 100644 static/webfonts/fa-light-300.ttf create mode 100644 static/webfonts/fa-light-300.woff create mode 100644 static/webfonts/fa-light-300.woff2 create mode 100644 static/webfonts/fa-regular-400.eot create mode 100644 static/webfonts/fa-regular-400.svg create mode 100644 static/webfonts/fa-regular-400.ttf create mode 100644 static/webfonts/fa-regular-400.woff create mode 100644 static/webfonts/fa-regular-400.woff2 create mode 100644 static/webfonts/fa-solid-900.eot create mode 100644 static/webfonts/fa-solid-900.svg create mode 100644 static/webfonts/fa-solid-900.ttf create mode 100644 static/webfonts/fa-solid-900.woff create mode 100644 static/webfonts/fa-solid-900.woff2 diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..3422477 --- /dev/null +++ b/.drone.yml @@ -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 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc12e2b --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..c04c254 --- /dev/null +++ b/.pylintrc @@ -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*(# )??$ + +# 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 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..f77ba1d --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +release: python manage.py migrate +web: gunicorn config.wsgi diff --git a/README.md b/README.md new file mode 100644 index 0000000..3f4e63f --- /dev/null +++ b/README.md @@ -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é diff --git a/config/__init__.py b/config/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config/asgi.py b/config/asgi.py new file mode 100644 index 0000000..da38c0d --- /dev/null +++ b/config/asgi.py @@ -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() diff --git a/config/settings.py b/config/settings.py new file mode 100644 index 0000000..97f5c3c --- /dev/null +++ b/config/settings.py @@ -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) diff --git a/config/urls.py b/config/urls.py new file mode 100644 index 0000000..b160a92 --- /dev/null +++ b/config/urls.py @@ -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), +] diff --git a/config/wsgi.py b/config/wsgi.py new file mode 100644 index 0000000..82f6ff8 --- /dev/null +++ b/config/wsgi.py @@ -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() diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..70f88f7 --- /dev/null +++ b/docker-compose.yml @@ -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 diff --git a/jarvis/core/__init__.py b/jarvis/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/core/admin.py b/jarvis/core/admin.py new file mode 100644 index 0000000..de67f0c --- /dev/null +++ b/jarvis/core/admin.py @@ -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) diff --git a/jarvis/core/apps.py b/jarvis/core/apps.py new file mode 100644 index 0000000..a1b9a56 --- /dev/null +++ b/jarvis/core/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.core" diff --git a/jarvis/core/context_processors.py b/jarvis/core/context_processors.py new file mode 100644 index 0000000..1646112 --- /dev/null +++ b/jarvis/core/context_processors.py @@ -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 + } diff --git a/jarvis/core/migrations/0001_initial.py b/jarvis/core/migrations/0001_initial.py new file mode 100644 index 0000000..d20646e --- /dev/null +++ b/jarvis/core/migrations/0001_initial.py @@ -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)), + ], + ), + ] diff --git a/jarvis/core/migrations/0002_alter_citation_quote.py b/jarvis/core/migrations/0002_alter_citation_quote.py new file mode 100644 index 0000000..5caca0a --- /dev/null +++ b/jarvis/core/migrations/0002_alter_citation_quote.py @@ -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"), + ), + ] diff --git a/jarvis/core/migrations/__init__.py b/jarvis/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/core/models.py b/jarvis/core/models.py new file mode 100644 index 0000000..ca8ef52 --- /dev/null +++ b/jarvis/core/models.py @@ -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) diff --git a/jarvis/core/templates/base.html b/jarvis/core/templates/base.html new file mode 100644 index 0000000..7e147f9 --- /dev/null +++ b/jarvis/core/templates/base.html @@ -0,0 +1,349 @@ +{% load static %} +{% load menuitems %} +{% load has_group %} + + + + + + + + + + + + + + + + + • {% block page_title %}Jarvis{% endblock %} • + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block header %}{% endblock %} + + + +
+ + + + +
+ + + + + + + +
+ {% block content %}{% endblock %} +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block footerscript %}{% endblock %} + + + + + + + \ No newline at end of file diff --git a/jarvis/core/templates/dashboard/dashboard.html b/jarvis/core/templates/dashboard/dashboard.html new file mode 100644 index 0000000..1e26ec0 --- /dev/null +++ b/jarvis/core/templates/dashboard/dashboard.html @@ -0,0 +1,180 @@ +{% extends "base.html" %} +{% load static %} + +{% block page_title %}Dashboard{% endblock %} + +{% block content %} + +
+
+
+
+

Quote

+
+
+ {{ quote.to_markdown | safe }} +
+ {% if quote.author %} + + {% endif %} +
+
+
+
+
+

Hi {{ user.username }} !

+
+
+

Welcome to Jarvi v0.80 (last update : 29-01-2023)

+

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 email.

+

You can find the user manuel here (in french).

+
+
+
+
+
+
+

Statistics

+
+
+
+
+
+
+
+
+ {% if nb_active_gymnast or nb_event or nb_skill or nb_routine or nb_score or nb_club %} +
+
+
    + {% if nb_active_gymnast %}
  • {{ nb_active_gymnast }} active gymnasts
  • {% endif %} + {% if nb_event %}
  • {{ nb_event }} events
  • {% endif %} + {% if nb_score %}
  • {{ nb_score }} scores
  • {% endif %} +
+
+
+
    + {% if nb_skill %}
  • {{ nb_skill }} skills
  • {% endif %} + {% if nb_routine %}
  • {{ nb_routine }} routines
  • {% endif %} + {% if nb_club %}
  • {{ nb_club }} clubs
  • {% endif %} +
+
+
+ {% else %} +
+ No statistics to display. + {% endif %} +
+
+
+
+
+ +
+ +
+
+
+

Next Events

+
+
+ {% if event_list %} + + {% for event in event_list %} + + + + + {% endfor %} +
{{ event.name }} + {% if event.number_of_week_from_today < 0 %} + {{event.number_of_week_from_today}} + {% else %} + + {{event.number_of_week_from_today}} + {% endif %} +
+ {% else %} + No future event defined + {% endif %} +
+
+
+ +
+
+
+

Updated needed

+
+
+ {% if waiting_update_gymnast %} + + {% for gymnast in waiting_update_gymnast %} + + + + + {% endfor %} +
{{ gymnast }}{{ gymnast.club.acronym }}
+ {% else %} + No update needed. + {% endif %} +
+
+
+ +
+
+
+

Last updated gymnasts

+
+
+ {% if last_updated_gymnast %} + + {% for gymnast in last_updated_gymnast %} + + + + + {% endfor %} +
{{ gymnast }}{{ gymnast.club.acronym }}
+ {% else %} + No update since your last visit + {% endif %} +
+
+
+ +
+
+
+

Next birthday

+
+
+ {% if birthday_list %} + + {% for gymnast in birthday_list %} + + + + + + {% endfor %} +
{{ gymnast.first_name }}{{ gymnast.birthdate | date:"j M"}}{{ gymnast.next_age }} years
+ {% else %} + No next birtday (it's a bug). + {% endif %} +
+
+
+ +
+ +{% endblock %} + +{% block footerscript %} + +{% endblock%} \ No newline at end of file diff --git a/jarvis/core/templates/listing.html b/jarvis/core/templates/listing.html new file mode 100644 index 0000000..5843ef8 --- /dev/null +++ b/jarvis/core/templates/listing.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} + +{% block content %} + + + +
+
+ {% block datacontent %}{% endblock %} + + +
+
+ + + +{% endblock %} \ No newline at end of file diff --git a/jarvis/core/templates/login.html b/jarvis/core/templates/login.html new file mode 100644 index 0000000..004b663 --- /dev/null +++ b/jarvis/core/templates/login.html @@ -0,0 +1,132 @@ +{% load static %} + + + + + + + + + + + + + + + + + + • JARVIS • + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jarvis/core/templates/records_10.html b/jarvis/core/templates/records_10.html new file mode 100644 index 0000000..9f073dc --- /dev/null +++ b/jarvis/core/templates/records_10.html @@ -0,0 +1,70 @@ +{% load static %} + + + + + + + + + + + + + + + + + List of records + + + + + + + + + +
+
+
+ {{ SITE_TITLE }} - {{ CLUB_NAME }}
+ {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
+ Season {{ season }} - week {{ week_number }} +
+
+ Head Coach : {{ HEAD_COACH }}
+ {{ HEAD_COACH_EMAIL }}
+ {{ today | date:"j F Y" }} +
+
+
+
+
+ +
+
+

Top 10| chrono scores

+
+
+
+
+ {% for record in records_list %} +

{{ record.score }} - {{ record.gymnast__first_name }} {{ record.gymnast__last_name }}

+ {% endfor %} +
+
+ +
+
+ + + + diff --git a/jarvis/core/templates/search/results.html b/jarvis/core/templates/search/results.html new file mode 100644 index 0000000..d230d37 --- /dev/null +++ b/jarvis/core/templates/search/results.html @@ -0,0 +1,264 @@ +{% extends "base.html" %} + + + +{% block title %}Search results{% endblock %} + +{% block content %} +
+ {% if gymnast_list or skill_list or event_list or place_list or club_list %} + {% if gymnast_list %} +
+

Gymnasts results

+
+
+
+ + + + + + + + + + + {% for gymnast in gymnast_list %} + + + + + + + {% endfor %} + +
FirstnameLastnameGenderAge
+ {{ gymnast.first_name }} + + {{ gymnast.last_name }} + {{ gymnast.get_gender_display }}{{ gymnast.age }}
+
+
+ {% endif %} + + {% if skill_list %} +
+

Skills results

+
+
+
+ + + + + + + + + + + + + + + {% for skill in skill_list %} + + + + + + + + + + + {% endfor %} + +
 Long Label Short Label Age Girl Age BoyNotationDiff.LevelRank
 {{ skill.long_label }}{{ skill.short_label }}{{ skill.age_girl_masterised }}{{ skill.age_boy_masterised }}{{ skill.notation }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
+
+
+ {% endif %} + + {% if event_list %} +
+

Events results

+
+
+
+ + + + + + + + + + + + {% for event in event_list %} + + + + + + + + {% endfor %} + +
EventTypeDate# weekPlace
{{ event.name }}{{ event.event_type.name }}{{ event.date_begin | date:"d-m-Y"}}{% if event.number_of_week_from_today < 0 %}{{event.number_of_week_from_today}}{% else %}{{event.number_of_week_from_today}}{% endif %}{{ event.place }}
+
+
+ {% endif %} + + {% if place_list %} +
+

Places results

+
+
+
+ + + + + + + + + + + + + {% for place in place_list %} + + + + + + + + + {% endfor %} + +
NameAddressZipCity
{{ place.name }}{{ place.address }}{{ place.postal}}{{ place.city }}
+
+
+ {% endif %} + + {% if club_list %} +
+

Clubs results

+
+
+
+ + + + + + + + + + {% for club in club_list %} + + + + + + {% endfor %} + +
NameAcronymCity
{{ club.name }}{{ club.acronym }}{{ club.place.city }}
+
+
+ {% endif %} + + + {% else %} +
+

Search results

+
+
+

There are no items corresponding to your criterias : "{{ pattern }}"

+
+ {% endif %} + +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/core/tests.py b/jarvis/core/tests.py new file mode 100644 index 0000000..850d1f1 --- /dev/null +++ b/jarvis/core/tests.py @@ -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) diff --git a/jarvis/core/urls.py b/jarvis/core/urls.py new file mode 100644 index 0000000..1e3818e --- /dev/null +++ b/jarvis/core/urls.py @@ -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"), +] diff --git a/jarvis/core/user_group_check.py b/jarvis/core/user_group_check.py new file mode 100644 index 0000000..320aa79 --- /dev/null +++ b/jarvis/core/user_group_check.py @@ -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() \ No newline at end of file diff --git a/jarvis/core/views.py b/jarvis/core/views.py new file mode 100644 index 0000000..4ff5fbf --- /dev/null +++ b/jarvis/core/views.py @@ -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 diff --git a/jarvis/followup/__init__.py b/jarvis/followup/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/followup/admin.py b/jarvis/followup/admin.py new file mode 100644 index 0000000..2ced850 --- /dev/null +++ b/jarvis/followup/admin.py @@ -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) diff --git a/jarvis/followup/apps.py b/jarvis/followup/apps.py new file mode 100644 index 0000000..9a25465 --- /dev/null +++ b/jarvis/followup/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class FollowupConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.followup" diff --git a/jarvis/followup/forms.py b/jarvis/followup/forms.py new file mode 100644 index 0000000..6294bad --- /dev/null +++ b/jarvis/followup/forms.py @@ -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", + } + ), + ) diff --git a/jarvis/followup/management/__init__.py b/jarvis/followup/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/followup/management/commands/__init__.py b/jarvis/followup/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/followup/migrations/0001_initial.py b/jarvis/followup/migrations/0001_initial.py new file mode 100644 index 0000000..fc39e58 --- /dev/null +++ b/jarvis/followup/migrations/0001_initial.py @@ -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")}, + }, + ), + ] diff --git a/jarvis/followup/migrations/0002_alter_learnedskill_gymnast.py b/jarvis/followup/migrations/0002_alter_learnedskill_gymnast.py new file mode 100644 index 0000000..7737b2c --- /dev/null +++ b/jarvis/followup/migrations/0002_alter_learnedskill_gymnast.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0003_alter_learnedskill_skill.py b/jarvis/followup/migrations/0003_alter_learnedskill_skill.py new file mode 100644 index 0000000..a8fc120 --- /dev/null +++ b/jarvis/followup/migrations/0003_alter_learnedskill_skill.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0004_alter_learnedskill_skill.py b/jarvis/followup/migrations/0004_alter_learnedskill_skill.py new file mode 100644 index 0000000..2119613 --- /dev/null +++ b/jarvis/followup/migrations/0004_alter_learnedskill_skill.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0005_auto_20211205_1412.py b/jarvis/followup/migrations/0005_auto_20211205_1412.py new file mode 100644 index 0000000..7b1b02e --- /dev/null +++ b/jarvis/followup/migrations/0005_auto_20211205_1412.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0006_heightweight_numberofroutinedone.py b/jarvis/followup/migrations/0006_heightweight_numberofroutinedone.py new file mode 100644 index 0000000..04f05c1 --- /dev/null +++ b/jarvis/followup/migrations/0006_heightweight_numberofroutinedone.py @@ -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", + }, + ), + ] diff --git a/jarvis/followup/migrations/0007_auto_20211213_1445.py b/jarvis/followup/migrations/0007_auto_20211213_1445.py new file mode 100644 index 0000000..4b8dc04 --- /dev/null +++ b/jarvis/followup/migrations/0007_auto_20211213_1445.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0008_alter_learnedskill_unique_together.py b/jarvis/followup/migrations/0008_alter_learnedskill_unique_together.py new file mode 100644 index 0000000..40ae5c6 --- /dev/null +++ b/jarvis/followup/migrations/0008_alter_learnedskill_unique_together.py @@ -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")}, + ), + ] diff --git a/jarvis/followup/migrations/0009_auto_20211222_1318.py b/jarvis/followup/migrations/0009_auto_20211222_1318.py new file mode 100644 index 0000000..b8247d6 --- /dev/null +++ b/jarvis/followup/migrations/0009_auto_20211222_1318.py @@ -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')}, + ), + ] diff --git a/jarvis/followup/migrations/0010_auto_20211224_0627.py b/jarvis/followup/migrations/0010_auto_20211224_0627.py new file mode 100644 index 0000000..ff5c972 --- /dev/null +++ b/jarvis/followup/migrations/0010_auto_20211224_0627.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0011_alter_learnedskill_cando.py b/jarvis/followup/migrations/0011_alter_learnedskill_cando.py new file mode 100644 index 0000000..9d0b8c6 --- /dev/null +++ b/jarvis/followup/migrations/0011_alter_learnedskill_cando.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0012_plan.py b/jarvis/followup/migrations/0012_plan.py new file mode 100644 index 0000000..3d2943d --- /dev/null +++ b/jarvis/followup/migrations/0012_plan.py @@ -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')}, + }, + ), + ] diff --git a/jarvis/followup/migrations/0013_chronodetails.py b/jarvis/followup/migrations/0013_chronodetails.py new file mode 100644 index 0000000..a389b9c --- /dev/null +++ b/jarvis/followup/migrations/0013_chronodetails.py @@ -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'], + }, + ), + ] diff --git a/jarvis/followup/migrations/0014_auto_20220125_1751.py b/jarvis/followup/migrations/0014_auto_20220125_1751.py new file mode 100644 index 0000000..63b7dcf --- /dev/null +++ b/jarvis/followup/migrations/0014_auto_20220125_1751.py @@ -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')}, + ), + ] diff --git a/jarvis/followup/migrations/0015_auto_20220201_1633.py b/jarvis/followup/migrations/0015_auto_20220201_1633.py new file mode 100644 index 0000000..82dfdb3 --- /dev/null +++ b/jarvis/followup/migrations/0015_auto_20220201_1633.py @@ -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')]), + ), + ] diff --git a/jarvis/followup/migrations/0016_auto_20220203_1035.py b/jarvis/followup/migrations/0016_auto_20220203_1035.py new file mode 100644 index 0000000..6f8440c --- /dev/null +++ b/jarvis/followup/migrations/0016_auto_20220203_1035.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0017_auto_20220203_1037.py b/jarvis/followup/migrations/0017_auto_20220203_1037.py new file mode 100644 index 0000000..bcceb8e --- /dev/null +++ b/jarvis/followup/migrations/0017_auto_20220203_1037.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0018_auto_20220208_1648.py b/jarvis/followup/migrations/0018_auto_20220208_1648.py new file mode 100644 index 0000000..07f474d --- /dev/null +++ b/jarvis/followup/migrations/0018_auto_20220208_1648.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0019_auto_20220213_1441.py b/jarvis/followup/migrations/0019_auto_20220213_1441.py new file mode 100644 index 0000000..d9fcaac --- /dev/null +++ b/jarvis/followup/migrations/0019_auto_20220213_1441.py @@ -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'), + ), + ] diff --git a/jarvis/followup/migrations/0020_rename_datebegin_gymnasthasroutine_date_begin_and_more.py b/jarvis/followup/migrations/0020_rename_datebegin_gymnasthasroutine_date_begin_and_more.py new file mode 100644 index 0000000..5dffbb8 --- /dev/null +++ b/jarvis/followup/migrations/0020_rename_datebegin_gymnasthasroutine_date_begin_and_more.py @@ -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", + ), + ] diff --git a/jarvis/followup/migrations/0021_notes.py b/jarvis/followup/migrations/0021_notes.py new file mode 100644 index 0000000..37a2b18 --- /dev/null +++ b/jarvis/followup/migrations/0021_notes.py @@ -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, + }, + ), + ] diff --git a/jarvis/followup/migrations/0022_rename_notes_note.py b/jarvis/followup/migrations/0022_rename_notes_note.py new file mode 100644 index 0000000..3801070 --- /dev/null +++ b/jarvis/followup/migrations/0022_rename_notes_note.py @@ -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", + ), + ] diff --git a/jarvis/followup/migrations/0023_note_note_type.py b/jarvis/followup/migrations/0023_note_note_type.py new file mode 100644 index 0000000..d923a50 --- /dev/null +++ b/jarvis/followup/migrations/0023_note_note_type.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0024_rename_note_type_note_status.py b/jarvis/followup/migrations/0024_rename_note_type_note_status.py new file mode 100644 index 0000000..eeaec45 --- /dev/null +++ b/jarvis/followup/migrations/0024_rename_note_type_note_status.py @@ -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", + ), + ] diff --git a/jarvis/followup/migrations/0025_note_date_note_season_note_week_number.py b/jarvis/followup/migrations/0025_note_date_note_season_note_week_number.py new file mode 100644 index 0000000..25b307f --- /dev/null +++ b/jarvis/followup/migrations/0025_note_date_note_season_note_week_number.py @@ -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, + ), + ] diff --git a/jarvis/followup/migrations/0026_alter_note_season_alter_note_week_number.py b/jarvis/followup/migrations/0026_alter_note_season_alter_note_week_number.py new file mode 100644 index 0000000..8909491 --- /dev/null +++ b/jarvis/followup/migrations/0026_alter_note_season_alter_note_week_number.py @@ -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), + ), + ] diff --git a/jarvis/followup/migrations/0027_accident_season_accident_week_number_chrono_season_and_more.py b/jarvis/followup/migrations/0027_accident_season_accident_week_number_chrono_season_and_more.py new file mode 100644 index 0000000..9fa8e80 --- /dev/null +++ b/jarvis/followup/migrations/0027_accident_season_accident_week_number_chrono_season_and_more.py @@ -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" + ), + ), + ] diff --git a/jarvis/followup/migrations/0028_rename_cando_learnedskill_learning_step_and_more.py b/jarvis/followup/migrations/0028_rename_cando_learnedskill_learning_step_and_more.py new file mode 100644 index 0000000..fe36dce --- /dev/null +++ b/jarvis/followup/migrations/0028_rename_cando_learnedskill_learning_step_and_more.py @@ -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")}, + ), + ] diff --git a/jarvis/followup/migrations/0029_plan_informations.py b/jarvis/followup/migrations/0029_plan_informations.py new file mode 100644 index 0000000..7e6a9f9 --- /dev/null +++ b/jarvis/followup/migrations/0029_plan_informations.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0030_alter_heightweight_hips_height.py b/jarvis/followup/migrations/0030_alter_heightweight_hips_height.py new file mode 100644 index 0000000..d44fb09 --- /dev/null +++ b/jarvis/followup/migrations/0030_alter_heightweight_hips_height.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0031_alter_note_status.py b/jarvis/followup/migrations/0031_alter_note_status.py new file mode 100644 index 0000000..94d3946 --- /dev/null +++ b/jarvis/followup/migrations/0031_alter_note_status.py @@ -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", + ), + ), + ] diff --git a/jarvis/followup/migrations/0032_intensity.py b/jarvis/followup/migrations/0032_intensity.py new file mode 100644 index 0000000..d8fa079 --- /dev/null +++ b/jarvis/followup/migrations/0032_intensity.py @@ -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, + }, + ), + ] diff --git a/jarvis/followup/migrations/0033_alter_intensity_options_alter_intensity_difficulty_and_more.py b/jarvis/followup/migrations/0033_alter_intensity_options_alter_intensity_difficulty_and_more.py new file mode 100644 index 0000000..6f7b483 --- /dev/null +++ b/jarvis/followup/migrations/0033_alter_intensity_options_alter_intensity_difficulty_and_more.py @@ -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")}, + ), + ] diff --git a/jarvis/followup/migrations/0034_seasoninformation.py b/jarvis/followup/migrations/0034_seasoninformation.py new file mode 100644 index 0000000..26c7398 --- /dev/null +++ b/jarvis/followup/migrations/0034_seasoninformation.py @@ -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")}, + }, + ), + ] diff --git a/jarvis/followup/migrations/0035_rename_hours_by_week_seasoninformation_number_of_hours_per_week_and_more.py b/jarvis/followup/migrations/0035_rename_hours_by_week_seasoninformation_number_of_hours_per_week_and_more.py new file mode 100644 index 0000000..8dac2a3 --- /dev/null +++ b/jarvis/followup/migrations/0035_rename_hours_by_week_seasoninformation_number_of_hours_per_week_and_more.py @@ -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), + ), + ] diff --git a/jarvis/followup/migrations/0036_alter_numberofroutinedone_unique_together.py b/jarvis/followup/migrations/0036_alter_numberofroutinedone_unique_together.py new file mode 100644 index 0000000..4c1a7aa --- /dev/null +++ b/jarvis/followup/migrations/0036_alter_numberofroutinedone_unique_together.py @@ -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")}, + ), + ] diff --git a/jarvis/followup/migrations/0037_alter_chrono_chrono_type_and_more.py b/jarvis/followup/migrations/0037_alter_chrono_chrono_type_and_more.py new file mode 100644 index 0000000..9ba46d2 --- /dev/null +++ b/jarvis/followup/migrations/0037_alter_chrono_chrono_type_and_more.py @@ -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"), + ] + ), + ), + ] diff --git a/jarvis/followup/migrations/0038_alter_chrono_chrono_type_and_more.py b/jarvis/followup/migrations/0038_alter_chrono_chrono_type_and_more.py new file mode 100644 index 0000000..aad87d4 --- /dev/null +++ b/jarvis/followup/migrations/0038_alter_chrono_chrono_type_and_more.py @@ -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"), + ] + ), + ), + ] diff --git a/jarvis/followup/migrations/0039_competitivepointsstats.py b/jarvis/followup/migrations/0039_competitivepointsstats.py new file mode 100644 index 0000000..05b9c2b --- /dev/null +++ b/jarvis/followup/migrations/0039_competitivepointsstats.py @@ -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, + }, + ), + ] diff --git a/jarvis/followup/migrations/0040_competitivepointsstats_place.py b/jarvis/followup/migrations/0040_competitivepointsstats_place.py new file mode 100644 index 0000000..80990a3 --- /dev/null +++ b/jarvis/followup/migrations/0040_competitivepointsstats_place.py @@ -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, + ), + ] diff --git a/jarvis/followup/migrations/0041_competitivepointsstats_label.py b/jarvis/followup/migrations/0041_competitivepointsstats_label.py new file mode 100644 index 0000000..02c42c7 --- /dev/null +++ b/jarvis/followup/migrations/0041_competitivepointsstats_label.py @@ -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, + ), + ] diff --git a/jarvis/followup/migrations/0042_competitivepointsstats_event_and_more.py b/jarvis/followup/migrations/0042_competitivepointsstats_event_and_more.py new file mode 100644 index 0000000..8a5e928 --- /dev/null +++ b/jarvis/followup/migrations/0042_competitivepointsstats_event_and_more.py @@ -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"), + ), + ] diff --git a/jarvis/followup/migrations/0043_seasoninformation_number_of_s_and_c_hours_per_week_and_more.py b/jarvis/followup/migrations/0043_seasoninformation_number_of_s_and_c_hours_per_week_and_more.py new file mode 100644 index 0000000..43d9ea4 --- /dev/null +++ b/jarvis/followup/migrations/0043_seasoninformation_number_of_s_and_c_hours_per_week_and_more.py @@ -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" + ), + ), + ] diff --git a/jarvis/followup/migrations/0044_alter_seasoninformation_number_of_hours_per_week_and_more.py b/jarvis/followup/migrations/0044_alter_seasoninformation_number_of_hours_per_week_and_more.py new file mode 100644 index 0000000..5b90e13 --- /dev/null +++ b/jarvis/followup/migrations/0044_alter_seasoninformation_number_of_hours_per_week_and_more.py @@ -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"), + ), + ] diff --git a/jarvis/followup/migrations/__init__.py b/jarvis/followup/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/followup/models.py b/jarvis/followup/models.py new file mode 100644 index 0000000..d35eaaf --- /dev/null +++ b/jarvis/followup/models.py @@ -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"
  • {self.date:%d %b %Y} - New personel best {CHRONO_TYPE_CHOICE[self.chrono_type][1]}: {self.score}' ({self.tof}')
  • " + + @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"
  • {self.date:%d %b %Y} - Accident ({self.skill}): {self.nb_week_off} (weeks off)
  • " + + +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"
  • {self.date:%d %b %Y} - learning of {self.skill.long_label} ({self.skill.short_label}): {LEARNING_STEP_CHOICES[self.learning_step][1]}
  • " + + +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})" diff --git a/jarvis/followup/templates/accidents/create.html b/jarvis/followup/templates/accidents/create.html new file mode 100644 index 0000000..48fc015 --- /dev/null +++ b/jarvis/followup/templates/accidents/create.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if accident_id %}Edit{% else %}Add{% endif %} accident

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.skill }} + {{ form.skill_related }} + {% if form.skill.errors %} {% for error in form.skill.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.nb_week_off }} + {% if form.nb_week_off.errors %} {% for error in form.nb_week_off.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/accidents/details.html b/jarvis/followup/templates/accidents/details.html new file mode 100644 index 0000000..aa12282 --- /dev/null +++ b/jarvis/followup/templates/accidents/details.html @@ -0,0 +1,32 @@ +{% extends "base.html" %} + +{% block content %} + +
    +
    +
    +
    +

    Accident : {{ accident.date | date:"d-m-Y" }}

    +
    +
    + {{ accident.gymnast }}
    + {% if accident.nb_week_off %} + {{ accident.nb_week_off }} week(s) off.
    + {% endif %} +
    + + {{ accident.to_markdown | safe }} + + +
    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/accidents/list.html b/jarvis/followup/templates/accidents/list.html new file mode 100644 index 0000000..0a159e2 --- /dev/null +++ b/jarvis/followup/templates/accidents/list.html @@ -0,0 +1,90 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +
    +
    +

    Accidents Listing

    +
    +
    + +
    +
    +
    +
    +
    + {% if accident_list %} + + + + + + + + + + + + {% for accident in accident_list %} + + + + + + + + {% endfor %} + +
    DateGymnastSkill# Week off
    + + + + {{ accident.date | date:"d-m-Y" }}{{ accident.gymnast }} + {% if accident.skill %} + {{ accident.skill }} + {% else %} + - + {% endif %} + + {% if accident.nb_week_off %} + {{ accident.nb_week_off }} + {% else %} + - + {% endif %} +
    + {% else %} +

    There are no accident corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/chronos/add_details.html b/jarvis/followup/templates/chronos/add_details.html new file mode 100644 index 0000000..8c76ded --- /dev/null +++ b/jarvis/followup/templates/chronos/add_details.html @@ -0,0 +1,123 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    Add chrono details : {{ chrono.score }} - {{ chrono.routine.short_label }} on the {{ chrono.date | date:"d N Y" }}

    +
    +
    +
      + {% for jump in jump_list %} +
    1. +
      + +
      + {{ jump.value }} +
      +
      +
    2. + {% endfor %} +
    3. +
      + +
      + +
      +
      +
      +
    4. +
    +
    + +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/chronos/create.html b/jarvis/followup/templates/chronos/create.html new file mode 100644 index 0000000..266cff8 --- /dev/null +++ b/jarvis/followup/templates/chronos/create.html @@ -0,0 +1,90 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if chrono_id %}Edit{% else %}Add{% endif %} chrono

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.chrono_type }} + {% if form.chrono_type.errors %} {% for error in form.chrono_type.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.score_type }} + {% if form.score_type.errors %} {% for error in form.score_type.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.score }} + {% if form.score.errors %} {% for error in form.score.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {{ form.tof }} +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + +{% else %} + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/chronos/details.html b/jarvis/followup/templates/chronos/details.html new file mode 100644 index 0000000..86bf816 --- /dev/null +++ b/jarvis/followup/templates/chronos/details.html @@ -0,0 +1,88 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +

    {{ chrono.gymnast }}

    +

    Chrono of {{ chrono.date | date:'d N Y' }}

    +
    +
    +
    +
    + + {% for detail in chrono.details.all %} + + + + + {% endfor %} + + + + +
    {{ detail.order }}{{ detail.value }}
    {{ chrono.score }}
    + + + +
    +
    + +
    +
    +
    +
    + +{% endblock %} + +{% block footerscript %} + +{% endblock %} diff --git a/jarvis/followup/templates/chronos/list.html b/jarvis/followup/templates/chronos/list.html new file mode 100644 index 0000000..ee1c624 --- /dev/null +++ b/jarvis/followup/templates/chronos/list.html @@ -0,0 +1,99 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +
    +
    +

    Chronos listing {% if gymnast %}for {{ gymnast }}{% endif %} +

    +
    +
    + +
    +
    +
    +
    +
    + {% if chrono_list %} + + + + + + + + + + + + + + {% for chrono in chrono_list %} + + + + + + + + + + {% endfor %} + +
     DateGymnastRoutineTypeScoreTOF
    + + + +   + + + + {% if chrono.details.all %}{% endif %}{{ chrono.date | date:"d-m-Y" }}{% if chrono.details.all %}{% endif %} + + {{ chrono.gymnast }} + + + {% if chrono.routine %} + {{ chrono.routine.long_label }} + {% else %} + {{ chrono.get_chrono_type_display }} + {% endif %} + {{ chrono.get_score_type_display }}{{ chrono.score }}{{ chrono.tof }}
    + {% else %} +

    There are no chronos corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/chronos/list_details.html b/jarvis/followup/templates/chronos/list_details.html new file mode 100644 index 0000000..a11a8aa --- /dev/null +++ b/jarvis/followup/templates/chronos/list_details.html @@ -0,0 +1,256 @@ +{% extends "base.html" %} +{% load get_value_at_index %} + +{% block content %} +
    +
    +

    {{ gymnast }}

    + Chrono for + + from {{ current_season }} + {% if date_begin %} + {{ date_begin | date:'j M Y' }} to {{ date_end | date:'j M Y' }} + {% else %} + + week + + + {% endif %} +
    + {% if chrono_list %} + Compare with: chrono for + + from {{ current_season }} + {% if date_begin %} + {{ date_begin | date:'j M Y' }} to {{ date_end | date:'j M Y' }} + {% else %} + + week + + + {% endif %} + {% endif %} +
    +
    +
    + {% if chrono_list %} +
    + + {% for chrono in chrono_list %} + {% if chrono.details.all %} + + {% for detail in chrono.details.all %} + {% with stat_value=stat_values|get_value_at_index:forloop.counter0 %} + + {% endwith %} + {% endfor %} + + + {% endif %} + {% endfor %} + + {% for value in stat_values %} + + {% endfor %} + + + + +
    {{ detail.value | floatformat:2 }}{{ chrono.score | floatformat:2 }}
    {{ value.avg_score | floatformat:2 }}
    +
    +
    + +
    + {% else %} +
    + No chrono details with the selected criteria (routine type, season and week). +
    +
    +
    + {% endif %} +
    +
    +
    + +{% endblock %} + +{% block footerscript %} + +{% endblock %} diff --git a/jarvis/followup/templates/heightweight/create.html b/jarvis/followup/templates/heightweight/create.html new file mode 100644 index 0000000..b02bb6e --- /dev/null +++ b/jarvis/followup/templates/heightweight/create.html @@ -0,0 +1,97 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if heightweight_id %}Edit{% else %}Add{% endif %} height/weight couple

    +
    +
    + {% if form.errors %} +
    + {{ form.errors }} +
    + {% endif %} +
    + {% csrf_token %} + +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    + +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + +
    + +
    + {{ form.height }} + {% if form.height.errors %} {% for error in form.height.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.hips_height }} + {% if form.hips_height.errors %} {% for error in form.hips_height.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.weight }} + {% if form.weight.errors %} {% for error in form.weight.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + +{% else %} + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/heightweight/list.html b/jarvis/followup/templates/heightweight/list.html new file mode 100644 index 0000000..ac0dadd --- /dev/null +++ b/jarvis/followup/templates/heightweight/list.html @@ -0,0 +1,84 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +
    +

    Height/Weight list {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    + {% if heightweight_list %} + + + + + + + + + + + + + {% for heightweight in heightweight_list %} + + + + + + + + + {% endfor %} + +
    DateGymnastHeightHips heightWeight
    + + + + {{ heightweight.date | date:"d-m-Y" }}{{ heightweight.gymnast }}{{ heightweight.height }}{{ heightweight.hips_height }}{{ heightweight.weight }}
    + {% else %} +

    There are no scores corresponding to your criterias

    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/intensities/create.html b/jarvis/followup/templates/intensities/create.html new file mode 100644 index 0000000..88aaa4f --- /dev/null +++ b/jarvis/followup/templates/intensities/create.html @@ -0,0 +1,137 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if intensity_id %}Edit{% else %}Add{% endif %} intensity

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.time }} + {% if form.time.errors %} {% for error in form.time.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.difficulty }} + {% if form.difficulty.errors %} {% for error in form.difficulty.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.quantity_of_skill }} + {% if form.quantity_of_skill.errors %} {% for error in form.quantity_of_skill.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.number_of_passes }} + {% if form.number_of_passes.errors %} {% for error in form.number_of_passes.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/intensities/list.html b/jarvis/followup/templates/intensities/list.html new file mode 100644 index 0000000..0f0744a --- /dev/null +++ b/jarvis/followup/templates/intensities/list.html @@ -0,0 +1,96 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +
    +
    +

    Intensity listing {% if gymnast %}for {{ gymnast }}{% endif %} +

    +
    +
    + +
    +
    +
    +
    +
    + {% if intensity_list %} + + + + + + + + + + + + + + + + + + {% for intensity in intensity_list %} + + + + + + + + + + + + + + {% endfor %} + +
     DateGymnastTimeDiff# skill# passesmean diff/passemean # skill# skill/passemean diff/skill
    + + + + {{ intensity.date | date:"d-m-Y" }} + + {{ intensity.gymnast }} + + {{ intensity.time }}{{ intensity.difficulty }}{{ intensity.quantity_of_skill }}{{ intensity.number_of_passes }}{{ intensity.mean_difficulty_by_passe | floatformat:2 }}{{ intensity.mean_quantity_of_skill | floatformat:2 }}{{ intensity.quantity_of_skill_by_passe | floatformat:2 }}{{ intensity.mean_difficulty_by_skill | floatformat:2 }}
    + {% else %} +

    There are no intensity corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/learnedskills/create.html b/jarvis/followup/templates/learnedskills/create.html new file mode 100644 index 0000000..0f93236 --- /dev/null +++ b/jarvis/followup/templates/learnedskills/create.html @@ -0,0 +1,90 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    Add Jumper/Skill link

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    + +
    + +
    + {{ form.skill }} + {{ form.skill_related }} + {% if form.skill.errors %} {% for error in form.skill.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.learning_step }} + {% if form.learning_step.errors %} {% for error in form.learning_step.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {% if plan_id %} +
    + +
    + {{ form.is_done }} + {% if form.is_done.errors %} {% for error in form.is_done.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {% endif %} +
    + +
    +
    +
    +
    +
    +
    + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/mindstates/create.html b/jarvis/followup/templates/mindstates/create.html new file mode 100644 index 0000000..426dba7 --- /dev/null +++ b/jarvis/followup/templates/mindstates/create.html @@ -0,0 +1,92 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if mindstate_id %}Edit{% else %}Add{% endif %} mind state score

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} + + {% endif %} + {% else %} + + + {% endif %} +
    +
    +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.event }} + {{ form.event_related }} + {% if form.eventt.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.score }} + {% if form.score.errors %} {% for error in form.score.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/mindstates/details.html b/jarvis/followup/templates/mindstates/details.html new file mode 100644 index 0000000..3f889ba --- /dev/null +++ b/jarvis/followup/templates/mindstates/details.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block content %} + +
    +
    +
    +
    +

    Mind State {{ mindstate.date | date:"d N Y" }}

    +
    +
    + {{ mindstate.gymnast }} : {{ mindstate.score }} +
    +
    + + {{ mindstate.to_markdown | safe }} + + +
    +
    +
    +
    + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/mindstates/list.html b/jarvis/followup/templates/mindstates/list.html new file mode 100644 index 0000000..7a06cb7 --- /dev/null +++ b/jarvis/followup/templates/mindstates/list.html @@ -0,0 +1,80 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +
    +

    Mind State list {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    + {% if mindstate_list %} + + + + + + + + + + + {% for state in mindstate_list %} + + + + + + + {% endfor %} + +
    DateGymnastScore
    + + + + {{ state.date | date:"d-m-Y" }}{{ state.gymnast }}{{ state.score }}
    + {% else %} +

    There are no mindstates corresponding to your criterias

    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/notes/create.html b/jarvis/followup/templates/notes/create.html new file mode 100644 index 0000000..2255949 --- /dev/null +++ b/jarvis/followup/templates/notes/create.html @@ -0,0 +1,84 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if note_id %}Edit{% else %}Add{% endif %} notes

    +
    +
    +
    + {% csrf_token %} + {{ form.coach }} +
    + +
    + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    + {{ form.status }} + {% if form.status.errors %} {% for error in form.status.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/notes/details.html b/jarvis/followup/templates/notes/details.html new file mode 100644 index 0000000..e09da3a --- /dev/null +++ b/jarvis/followup/templates/notes/details.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +

    {{ note.gymnast }}

    +

    Note of {{ note.created_at | date:'d-m-Y' }}

    +
    +
    +
    +
    +

    {{ note.to_markdown | safe }}

    +
    +
    +

    +
    +
    + +{% endblock %} diff --git a/jarvis/followup/templates/notes/list.html b/jarvis/followup/templates/notes/list.html new file mode 100644 index 0000000..db3bbae --- /dev/null +++ b/jarvis/followup/templates/notes/list.html @@ -0,0 +1,77 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +
    +
    +

    Notes listing {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    + +
    +
    +
    +
    +
    + {% if note_list %} + + + + + + + + + + + {% for note in note_list %} + + + + + + + {% endfor %} + +
     DateGymnastCoach
    + + {{ note.created_at | date:"d-m-Y" }} + {{ note.gymnast + }}{{ note.coach }}
    + {% else %} +

    There are no chronos corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/plan/create.html b/jarvis/followup/templates/plan/create.html new file mode 100644 index 0000000..cc94bc0 --- /dev/null +++ b/jarvis/followup/templates/plan/create.html @@ -0,0 +1,129 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if plan_id %}Edit{% else %}Add{% endif %} an objective

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.date }} + {% if form.date.errors %}{% for error in form.date.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    +
    + +
    + {{ form.educative }} + {{ form.educative_related }} + {% if form.educative.errors %} {% for error in form.educative.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.learning_step }} + {% if form.learning_step.errors %} {% for error in form.learning_step.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +{% endblock %} + +{% block footerscript %} + +{% if request.session.template == 0 %} + +{% else %} + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/routinedone/create.html b/jarvis/followup/templates/routinedone/create.html new file mode 100644 index 0000000..8b5b114 --- /dev/null +++ b/jarvis/followup/templates/routinedone/create.html @@ -0,0 +1,129 @@ +{% extends "base.html" %} +{% load static%} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if routinedone_id %}Edit{% else %}Add{% endif %} Routine done

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    + +
    + +
    + {{ form.routine }} + {{ form.routine_related }} + {% if form.routine.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.routine_type }} + {% if form.routine_type.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.date }} + {% if form.date.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.number_of_successes }} + {% if form.number_of_successes.errors %} {% for error in form.number_of_successes.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + +
    + +
    + {{ form.number_of_try }} + {% if form.number_of_try.errors %} {% for error in form.number_of_try.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + + +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} diff --git a/jarvis/followup/templates/routinedone/list.html b/jarvis/followup/templates/routinedone/list.html new file mode 100644 index 0000000..ea97bf1 --- /dev/null +++ b/jarvis/followup/templates/routinedone/list.html @@ -0,0 +1,91 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +
    +

    Routine done list {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    + {% if routine_done_list %} + + + + + + + + + + + + + {% for routine_done in routine_done_list %} + + + + + + + + + {% endfor %} + +
    DateTypeRoutine# Try# Success
    + + + + {{ routine_done.date | date:"d-m-Y"}}{{ routine_done.get_routine_type_display }} + {% if routine_done.routine %} + {{ routine_done.routine.long_label }} + {% else %} + - + {% endif %} + {{ routine_done.number_of_try }}{{ routine_done.number_of_successes }}
    + + {% else %} +

    There are no routine's statistics associated to this gymnast.

    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/scores/create.html b/jarvis/followup/templates/scores/create.html new file mode 100644 index 0000000..6bb476e --- /dev/null +++ b/jarvis/followup/templates/scores/create.html @@ -0,0 +1,155 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} + +
    +
    +
    +
    +

    {% if score_id %}Edit{% else %}Add{% endif %} score

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    +
    + +
    + {{ form.event }} + {{ form.event_related }} + {% if form.event.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.routine_type }} + {% if form.routine_type.errors %} {% for error in form.routine_type.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.point_difficulty }} + {% if form.point_difficulty.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.point_time_of_flight }} + {% if form.point_time_of_flight.errors %} + + {% endif %} +
    +
    + {{ form.add_to_chrono }}    +
    +
    +
    + +
    + {{ form.point_execution }} + {% if form.point_execution.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.point_horizontal_displacement }} + {% if form.point_horizontal_displacement.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.penality }} + {% if form.penality.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.total }} + {% if form.total.errors %} + + {% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/scores/list.html b/jarvis/followup/templates/scores/list.html new file mode 100644 index 0000000..ffe548a --- /dev/null +++ b/jarvis/followup/templates/scores/list.html @@ -0,0 +1,97 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +
    +

    Scores listing {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    + {% if score_list %} + + + + + + + + + + + + + + + + + + {% for score in score_list %} + + + + + + + + + + + + + + {% endfor %} + +
    GymnastEventDateRoutineExe.Dif.ToFHDPen.Total
    + + + + {{ score.gymnast }}{{ score.event.name }}{{ score.event.date_begin | date:"d-m-Y" }}{{ score.get_routine_type_display }}{{ score.point_execution }}{{ score.point_difficulty }}{{ score.point_time_of_flight }}{{ score.point_horizontal_displacement }} + {% if score.penality > 0 %}-{{ score.penality }} + {% else %}- + {% endif %}{{ score.total }}
    + {% else %} + There are no scores corresponding to your criterias + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/seasoninformations/create.html b/jarvis/followup/templates/seasoninformations/create.html new file mode 100644 index 0000000..4d623f1 --- /dev/null +++ b/jarvis/followup/templates/seasoninformations/create.html @@ -0,0 +1,156 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {% if season_information_id %}Edit{% else %}Add{% endif %} season informations

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.season }} + {% if form.season.errors %}{% for error in form.season.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.club }} + {{ form.club_related }} + {% if form.club.errors %} {% for error in form.club.errors %}{{error}}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.number_of_training_sessions_per_week }} + {% if form.number_of_training_sessions_per_week.errors %} {% for error in form.number_of_training_sessions_per_week.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.number_of_hours_per_week }} + {% if form.number_of_hours_per_week.errors %} {% for error in form.number_of_hours_per_week.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.number_of_s_and_c_sessions_per_week }} + {% if form.number_of_s_and_c_sessions_per_week.errors %} {% for error in form.number_of_s_and_c_sessions_per_week.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.number_of_s_and_c_hours_per_week }} + {% if form.number_of_s_and_c_hours_per_week.errors %} {% for error in form.number_of_s_and_c_hours_per_week.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.category }} + {% if form.category.errors %} {% for error in form.category.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% if request.session.template == 0 %} + +{% else %} + +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templates/seasoninformations/list.html b/jarvis/followup/templates/seasoninformations/list.html new file mode 100644 index 0000000..387bd60 --- /dev/null +++ b/jarvis/followup/templates/seasoninformations/list.html @@ -0,0 +1,90 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +
    +

    Season informations listing {% if gymnast %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    + {% if season_information_list %} + + + + + + + + + + + + + + + + {% for season_information in season_information_list %} + + + + + + + + + + + + {% endfor %} + +
    GymnastSeason# training/w# hours/w# S&C/w# S&C hours/wcategoryclub
    + + + + {{ season_information.gymnast }}{{ season_information.season }}{{ season_information.number_of_training_sessions_per_week }}{{ season_information.number_of_hours_per_week }}{{ season_information.number_of_s_and_c_sessions_per_week }}{{ season_information.number_of_s_and_c_hours_per_week }}{{ season_information.get_category_display }}{{ season_information.club.name }}
    + {% else %} + There are no scores corresponding to your criterias + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/followup/templatetags/__init__.py b/jarvis/followup/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/followup/templatetags/get_value_at_index.py b/jarvis/followup/templatetags/get_value_at_index.py new file mode 100644 index 0000000..dadb23e --- /dev/null +++ b/jarvis/followup/templatetags/get_value_at_index.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + + +@register.filter +def get_value_at_index(list, index): + return list[index] diff --git a/jarvis/followup/tests_urls.py b/jarvis/followup/tests_urls.py new file mode 100644 index 0000000..2725cb6 --- /dev/null +++ b/jarvis/followup/tests_urls.py @@ -0,0 +1,146 @@ +from django.test import TestCase +from django.urls import resolve + + +class URLTestCase(TestCase): + def test_skill_url(self): + # Chrono URL + self.assertEqual(resolve("/follow-up/chrono/").view_name, "chrono_list") + self.assertEqual( + resolve("/follow-up/chrono/gymnast/1/").view_name, "chrono_list_for_gymnast" + ) + self.assertEqual(resolve("/follow-up/chrono/add/").view_name, "chrono_create") + self.assertEqual( + resolve("/follow-up/chrono/add/1/").view_name, "chrono_create_for_gymnast" + ) + self.assertEqual( + resolve("/follow-up/chrono/edit/1/").view_name, "chrono_update" + ) + self.assertEqual( + resolve("/follow-up/chrono/details/1/").view_name, "jump_chrono_details" + ) + self.assertEqual( + resolve("/follow-up/chrono/details/1/add/").view_name, + "jump_chrono_values_create_or_update", + ) + self.assertEqual( + resolve("/follow-up/chrono/add_jump_chrono_value/").view_name, + "add_jump_chrono_value", + ) + self.assertEqual( + resolve("/follow-up/chrono/remove_jump_chrono_value/").view_name, + "remove_jump_chrono_value", + ) + self.assertEqual( + resolve( + "/follow-up/chrono/detailed_score_for_date_range/1/1/2022-09-01/2022-09-20/" + ).view_name, + "average_jump_chrono_details_between_two_date", + ) + + # Learned Skill URL + self.assertEqual( + resolve("/follow-up/learnedskill/add/").view_name, "learnedskill_create" + ) + self.assertEqual( + resolve("/follow-up/learnedskill/add/1/").view_name, "learnedskill_create" + ) + self.assertEqual( + resolve("/follow-up/learnedskill/new/").view_name, "gymnast_learn_skill" + ) + + # Score URL + self.assertEqual(resolve("/follow-up/score/").view_name, "score_listing") + self.assertEqual( + resolve("/follow-up/score/gymnast/1/").view_name, "score_list_for_gymnast" + ) + self.assertEqual( + resolve("/follow-up/score/add/gymnast/1/").view_name, + "score_create_for_gymnast", + ) + self.assertEqual(resolve("/follow-up/score/add/").view_name, "score_create") + self.assertEqual(resolve("/follow-up/score/edit/1/").view_name, "score_update") + + # Accident URL + self.assertEqual( + resolve("/follow-up/accident/search/").view_name, "accident_search" + ) + self.assertEqual(resolve("/follow-up/accident/").view_name, "accident_list") + self.assertEqual( + resolve("/follow-up/accident/add/").view_name, "accident_create" + ) + self.assertEqual( + resolve("/follow-up/accident/add/1/").view_name, + "accident_create_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/accident/edit/1/").view_name, "accident_update" + ) + self.assertEqual( + resolve("/follow-up/accident/1/").view_name, "accident_details" + ) + + # Mindstate URL + self.assertEqual(resolve("/follow-up/mindstate/").view_name, "mindstate_list") + self.assertEqual( + resolve("/follow-up/mindstate/gymnast/1/").view_name, + "mindstate_list_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/mindstate/add/").view_name, "mindstate_create" + ) + self.assertEqual( + resolve("/follow-up/mindstate/add/1/").view_name, + "mindstate_create_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/mindstate/edit/1/").view_name, "mindstate_update" + ) + self.assertEqual( + resolve("/follow-up/mindstate/1/").view_name, "mindstate_details" + ) + + # HeightWeigh URL + self.assertEqual( + resolve("/follow-up/heightweight/gymnast/1/").view_name, + "heightweight_list_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/heightweight/add/").view_name, "heightweight_create" + ) + self.assertEqual( + resolve("/follow-up/heightweight/add/1/").view_name, + "heightweight_create_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/heightweight/edit/1/").view_name, "heightweight_update" + ) + + # RoutineDone URL + self.assertEqual( + resolve("/follow-up/routinedone/add/").view_name, "routinedone_create" + ) + self.assertEqual( + resolve("/follow-up/routinedone/increment/").view_name, + "increment_routinedone", + ) + self.assertEqual( + resolve("/follow-up/routinedone/add/1/").view_name, + "routinedone_create_for_gymnast", + ) + self.assertEqual( + resolve("/follow-up/routinedone/edit/1/").view_name, "routinedone_update" + ) + self.assertEqual( + resolve("/follow-up/routinedone/edit/1/").view_name, "routinedone_update" + ) + + # Plan URL + self.assertEqual(resolve("/follow-up/plan/add/").view_name, "plan_create") + self.assertEqual( + resolve("/follow-up/plan/add/1/").view_name, "add_plan_for_gymnast" + ) + self.assertEqual( + resolve("/follow-up/plan/add/1/2/").view_name, "add_skill_for_gymnast" + ) + self.assertEqual(resolve("/follow-up/plan/edit/1/").view_name, "plan_update") diff --git a/jarvis/followup/urls.py b/jarvis/followup/urls.py new file mode 100644 index 0000000..7310d1a --- /dev/null +++ b/jarvis/followup/urls.py @@ -0,0 +1,292 @@ +from django.urls import path + +from . import views + + +urlpatterns = [ + path(r"chrono/", views.chrono_listing, name="chrono_list"), + path( + r"chrono/gymnast//", + views.chrono_listing, + name="chrono_list_for_gymnast", + ), + path(r"chrono/add/", views.chrono_create_or_update, name="chrono_create"), + path( + r"chrono/add//", + views.chrono_create_or_update, + name="chrono_create_for_gymnast", + ), + path( + r"chrono/edit//", + views.chrono_create_or_update, + name="chrono_update", + ), + path( + r"chrono/details//", + views.jump_chrono_details, + name="jump_chrono_details", + ), + path( + r"chrono/details//add/", + views.jump_chrono_values_create_or_update, + name="jump_chrono_values_create_or_update", + ), + path( + r"chrono/add_jump_chrono_value/", + views.add_jump_chrono_value, + name="add_jump_chrono_value", + ), + path( + r"chrono/remove_jump_chrono_value/", + views.remove_jump_chrono_value, + name="remove_jump_chrono_value", + ), + path( + r"chrono/range///gymnast//routine_type/", + views.average_jump_chrono_details_between_two_date, + name="average_jump_chrono_details_between_two_date", + ), + path( + r"chrono/season//week//gymnast//routine_type//", + views.average_jump_chrono_details_for_gymnast, + name="average_jump_chrono_details_for_week_of_season", + ), + path( + r"chrono/statistics/gymnast//routine_type//season//week//", + views.average_jump_chrono_details_for_gymnast, + name="average_jump_chrono_details_for_gymnast_routinetype_season_and_week", + ), + path( + r"chrono/statistics/gymnast//routine_type//", + views.average_jump_chrono_details_for_gymnast, + name="average_jump_chrono_details_for_gymnast_and_routinetype", + ), + path( + r"chrono/statistics/gymnast//", + views.average_jump_chrono_details_for_gymnast, + name="average_jump_chrono_details_for_gymnast", + ), + path( + r"chrono/get_distinct_season/gymnast//", + views.get_chrono_detail_distinct_season, + name="get_chrono_detail_distinct_season", + ), + path( + r"chrono/get_distinct_weeknumber/gymnast//season//", + views.get_chrono_detail_distinct_weeknumber_for_season, + name="get_chrono_detail_distinct_weeknumber_for_season", + ), + path( + r"chrono/get_average_jump_chrono_details_for_season_and_week/gymnast//routine_type//season//week//", + views.get_average_jump_chrono_details_for_season_and_week, + name="get_average_jump_chrono_details_for_season_and_week", + ), + # + # + # NOTE + path(r"note/", views.note_listing, name="note_list"), + path( + r"note/gymnast//", + views.chrono_listing, + name="note_list_for_gymnast", + ), + path(r"note/add/", views.note_create_or_update, name="note_create"), + path( + r"note/add//", + views.note_create_or_update, + name="note_create_for_gymnast", + ), + path(r"note/edit//", views.note_create_or_update, name="note_update"), + path(r"note/details//", views.note_details, name="note_details"), + # + # + # INTENSITY + path(r"intensity/", views.chrono_listing, name="intensity_list"), + path( + r"intensity/gymnast//", + views.intensity_listing, + name="intensity_list_for_gymnast", + ), + path(r"intensity/add/", views.intensity_create_or_update, name="intensity_create"), + path( + r"intensity/add//", + views.intensity_create_or_update, + name="intensity_create_for_gymnast", + ), + path( + r"intensity//edit/", + views.intensity_create_or_update, + name="intensity_update", + ), + # + # + # LEARNEDSKILL + path( + r"learnedskill/add/", + views.learnedskill_create_or_update, + name="learnedskill_create", + ), + path( + r"learnedskill/add//", + views.learnedskill_create_or_update, + name="learnedskill_create", + ), + path(r"learnedskill/new/", views.gymnast_learn_skill, name="gymnast_learn_skill"), + # + # + # SCORE + path(r"score/", views.score_listing, name="score_listing"), + path( + r"score/gymnast//", + views.score_listing, + name="score_list_for_gymnast", + ), + path( + r"score/add/gymnast//", + views.score_create_or_update, + name="score_create_for_gymnast", + ), + path(r"score/add/", views.score_create_or_update, name="score_create"), + path( + r"score/edit//", views.score_create_or_update, name="score_update" + ), + # + # + # ACCIDENT + path(r"accident/search/", views.accident_listing, name="accident_search"), + path(r"accident/", views.accident_listing, name="accident_list"), + path(r"accident/add/", views.accident_create_or_update, name="accident_create"), + path( + r"accident/add//", + views.accident_create_or_update, + name="accident_create_for_gymnast", + ), + path( + r"accident/edit//", + views.accident_create_or_update, + name="accident_update", + ), + path( + r"accident//", views.accident_detail, name="accident_details" + ), + # + # + # MINDSTATE + path(r"mindstate/", views.mindstate_listing, name="mindstate_list"), + path( + r"mindstate/gymnast//", + views.mindstate_listing, + name="mindstate_list_for_gymnast", + ), + path(r"mindstate/add/", views.mindstate_create_or_update, name="mindstate_create"), + path( + r"mindstate/add//", + views.mindstate_create_or_update, + name="mindstate_create_for_gymnast", + ), + path( + r"mindstate/edit//", + views.mindstate_create_or_update, + name="mindstate_update", + ), + path( + r"mindstate//", + views.mindstate_detail, + name="mindstate_details", + ), + # + # + # Height/Weight + path( + r"heightweight/gymnast//", + views.heightweight_listing, + name="heightweight_list_for_gymnast", + ), + path( + r"heightweight/add/", + views.heightweight_create_or_update, + name="heightweight_create", + ), + path( + r"heightweight/add//", + views.heightweight_create_or_update, + name="heightweight_create_for_gymnast", + ), + path( + r"heightweight/edit//", + views.heightweight_create_or_update, + name="heightweight_update", + ), + # + # + # Routine Done + path( + r"routinedone/add/", + views.routinedone_create_or_update, + name="routinedone_create", + ), + path( + r"routinedone/increment/", + views.increment_routinedone, + name="increment_routinedone", + ), + path( + r"routinedone/add//", + views.routinedone_create_or_update, + name="routinedone_create_for_gymnast", + ), + path( + r"routinedone/edit//", + views.routinedone_create_or_update, + name="routinedone_update", + ), + path( + r"routinedone/gymnast//", + views.routine_done_listing, + name="routinedone_list_for_gymnast", + ), + path(r"routinedone/", views.routine_done_listing, name="routinedone_list"), + # + # + # Plan + path(r"plan/add/", views.plan_create_or_update, name="plan_create"), + path(r"plan//edit/", views.plan_create_or_update, name="plan_update"), + path( + r"plan/add//", + views.plan_create_or_update, + name="add_plan_for_gymnast", + ), + path( + r"plan/add///", + views.plan_create_or_update, + name="add_skill_for_gymnast", + ), + path( + r"plan/edit//", + views.plan_create_or_update, + name="plan_update", + ), + # + # + # Season Informations + path( + r"season-information/gymnast//", + views.season_information_listing, + name="season_information_for_gymnast", + ), + path( + r"season-information/add/", + views.season_information_create_or_update, + name="season_information_create", + ), + path( + r"season-information//edit/", + views.season_information_create_or_update, + name="season_information_update", + ), + path( + r"season-information/add//", + views.season_information_create_or_update, + name="add_season_information_for_gymnast", + ), +] diff --git a/jarvis/followup/views.py b/jarvis/followup/views.py new file mode 100644 index 0000000..2ae0bf0 --- /dev/null +++ b/jarvis/followup/views.py @@ -0,0 +1,1358 @@ +from django.shortcuts import render, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.views.decorators.http import require_http_methods +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse +from django.db.models import Q, Min, Avg, Max, Sum +from django.urls import reverse +from django.conf import settings +from django.contrib.auth import get_user_model + +from django.core.mail import send_mail + +from jarvis.people.models import Gymnast +from jarvis.planning.models import Event +from jarvis.objective.models import Skill + +from .models import ( + Plan, + Note, + Point, + Chrono, + Accident, + MindState, + Intensity, + LearnedSkill, + HeightWeight, + ChronoDetails, + GymnastHasRoutine, + SeasonInformation, + NumberOfRoutineDone, +) + +from .forms import ( + PlanForm, + NoteForm, + ScoreForm, + ChronoForm, + AccidentForm, + MindStateForm, + IntensityForm, + HeightWeightForm, + LearnedSkillForm, + SeasonInformationForm, + NumberOfRoutineDoneForm, +) + +from jarvis.tools.date_week_transition import ( + from_date_to_week_number, + from_week_number_to_date, +) + +from jarvis.tools.models import Season + +from datetime import date, datetime +import pendulum + +User = get_user_model() + + +@login_required +@require_http_methods(["GET"]) +def jump_chrono_details(request, chrono_id): + """Récupère toutes les informations détaillées d'un chrono. La fonction en profite pour + recalculer le total et s'assure que cela correspond à la valeur stockée dans le model + Chrono. + + Args: + chrono_id (int) identifiant chrono + + QTF : Est-ce que je ne devrais pas faire un prefetch_related sur mon objet chrono pour optimiser mon affichage ? + chrono = Chrono.object.get(pk=chrono_id).prefetch_related('chrono_details') ? + """ + + chrono = get_object_or_404(Chrono, pk=chrono_id) + sum_value = chrono.details.all().aggregate(total=Sum("value")) + + if chrono.score != sum_value["total"]: + chrono.score = sum_value["total"] + if chrono.score_type == 0: + chrono.tof = Chrono.compute_tof(sum_value["total"]) + chrono.save() + + mean_value = chrono.details.all().aggregate(mean=Avg("value"))["mean"] + tmp_min_value = chrono.details.all().aggregate(min=Min("value"))["min"] + tmp_max_value = chrono.details.all().aggregate(max=Max("value"))["max"] + chart_min_value = mean_value - (tmp_min_value / 20) + chart_max_value = mean_value - (tmp_max_value / 20) + + context = { + "chrono": chrono, + "mean_value": mean_value, + "chart_min_value": chart_min_value, + "chart_max_value": chart_max_value, + } + return render(request, "chronos/details.html", context) + + +@login_required +@require_http_methods(["GET"]) +def average_jump_chrono_details_for_gymnast( + request, gymnast_id, routine_type=1, season=None, week_number=1 +): + """Récupère tout les chronos entre deux date pour un gymnaste et un type de série + + Args: + gymnast_id int Identifiant d'un gymnaste + routine_type int Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE) + season string Saison sous forme "xxxx-xxxy" + week_number int numéro de semaine (1, …, 52) + """ + if season is None: + today = pendulum.now().date() + season, week_number = from_date_to_week_number(today) + else: + season = Season(season) + + if week_number > 52: + week_number = 52 + + if week_number < 1: + week_number = 1 + + date_begin, date_end = from_week_number_to_date(season.label, week_number) + + return average_jump_chrono_details_for_season_and_week( + request, + gymnast_id, + routine_type, + season, + week_number, + ) + + +@require_http_methods(["POST"]) +def remove_jump_chrono_value(request): + """ + Recoit trois informations permettant d'ajouter le chrono d'un saut à un chrono + """ + chrono_id = request.POST.get("chrono_id", None) + order = request.POST.get("order", None) + chrono = get_object_or_404(Chrono, pk=chrono_id) + try: + ChronoDetails.objects.filter(chrono=chrono, order=order).delete() + except Exception: + return HttpResponse(409) + + return HttpResponse(200) + + +@require_http_methods(["POST"]) +def add_jump_chrono_value(request): + """ + Recoit trois informations permettant d'ajouter le chrono d'un saut à un chrono + """ + chrono_id = request.POST.get("chrono_id", None) + order = request.POST.get("order", None) + value = request.POST.get("value", None) + + chrono = get_object_or_404(Chrono, pk=chrono_id) + row, created = ChronoDetails.objects.get_or_create( + chrono=chrono, order=order, value=value + ) + + if created: + return HttpResponse(200, (row, created)) # devrait être un 201 + else: + return HttpResponse(400, (row, created)) + + +@login_required +@require_http_methods(["GET"]) +def jump_chrono_values_create_or_update(request, chrono_id): + """ + Ajoute des scores de saut à un chrono. + + Args: + chrono_id (int) identifiant chrono + """ + + chrono = get_object_or_404(Chrono, pk=chrono_id) + jump_list = chrono.details.all() + number_of_jump = jump_list.count() + context = { + "chrono": chrono, + "jump_list": jump_list, + "number_of_jump": number_of_jump, + "score_type": chrono.score_type, + } + return render(request, "chronos/add_details.html", context) + + +@login_required +@require_http_methods(["GET"]) +def average_jump_chrono_details_between_two_date( + request, gymnast_id, routine_type=1, date_begin=None, date_end=None +): + """Récupère tout les chronos entre deux date pour un gymnaste et un type de série + Args: + gymnast_id (int) Identifiant d'un gymnaste + routine_type (int) type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE) + date_begin (date) date de début + date_end (date) date de fin + TODO: le cast en date devrait être dans un try ! + """ + + if date_end: + try: + date_end = datetime.strptime(date_end, "%Y-%m-%d").date() + except (ValueError, TypeError): + date_end = pendulum.now().date() + else: + date_end = pendulum.now().date() + + if date_begin: + try: + date_begin = datetime.strptime(date_begin, "%Y-%m-%d").date() + except (ValueError, TypeError): + date_begin = datetime(date_end.year, 9, 1) + else: + date_begin = datetime(date_end.year, 9, 1) + + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + + stat_values = ( + ChronoDetails.objects.filter( + chrono__gymnast=gymnast_id, + chrono__chrono_type=routine_type, + chrono__date__gte=date_begin, + chrono__date__lte=date_end, + ) + .values("order") + .annotate( + avg_score=Avg("value"), max_score=Max("value"), min_score=Min("value") + ) + .order_by("order") + ) + + chrono_list = Chrono.objects.filter( + gymnast=gymnast_id, + date__gte=date_begin, + date__lte=date_end, + chrono_type=routine_type, + ) + + context = { + "gymnast": gymnast, + "date_begin": date_begin, + "date_end": date_end, + "chrono_list": chrono_list, + "stat_values": stat_values, + } + return render(request, "chronos/list_details.html", context) + + +@require_http_methods(["GET"]) +def get_chrono_detail_distinct_season(request, gymnast_id): + """Récupère toutes les saisons pour lesquelles le gymnaste a des chronos détaillés.""" + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + season_list = list( + gymnast.chronos.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + return JsonResponse(season_list, safe=False) + + +@require_http_methods(["GET"]) +def get_chrono_detail_distinct_weeknumber_for_season(request, gymnast_id, season): + """Récupère toutes les week_number pour lesquelles le gymnaste a des chronos détaillés au cours + d'une saison.""" + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + weeknumber_list = list( + gymnast.chronos.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + return JsonResponse(weeknumber_list, safe=False) + + +@require_http_methods(["GET"]) +def get_average_jump_chrono_details_for_season_and_week( + request, gymnast_id, routine_type, season, week_number +): + """Récupère tout les chronos moyen par saut pour une saison & semaine d'un gymnaste + Args: + gymnast_id (int) Identifiant d'un gymnaste + routine_type (int) Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE) + season (string) Season + week_number (int) Numero de la semaine + TODO: le cast en date devrait être dans un try ! + """ + + stat_values = list( + ChronoDetails.objects.filter( + chrono__gymnast=gymnast_id, + chrono__chrono_type=routine_type, + chrono__season=season, + chrono__week_number=week_number, + ) + .values("order") + .annotate(avg_score=Avg("value")) + .order_by("order") + ) + return JsonResponse(stat_values, safe=False) + + +@login_required +@require_http_methods(["GET"]) +def average_jump_chrono_details_for_season_and_week( + request, gymnast_id, routine_type, season, week_number +): + """Récupère tout les chronos entre deux date pour un gymnaste et un type de série + Args: + gymnast_id (int) Identifiant d'un gymnaste + routine_type (int) Type de série (cf. jarvis/followup/models.py > ROUTINE_CHOICE) + season (string) Season + week_number (int) Numero de la semaine + TODO: le cast en date devrait être dans un try ! + """ + + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + + stat_values = ( + ChronoDetails.objects.filter( + chrono__gymnast=gymnast_id, + chrono__chrono_type=routine_type, + chrono__season=season, + chrono__week_number=week_number, + ) + .values("order") + .annotate( + avg_score=Avg("value"), max_score=Max("value"), min_score=Min("value") + ) + .order_by("order") + ) + # print(stat_values) + + distinct_season_list = ( + gymnast.chronos.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + distinct_week_number_list = ( + gymnast.chronos.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + distinct_routine_type_list = ( + gymnast.chronos.values_list("chrono_type", flat=True) + .distinct("chrono_type") + .order_by("chrono_type") + ) + + chrono_list = Chrono.objects.filter( + gymnast=gymnast_id, + season=season, + week_number=week_number, + chrono_type=routine_type, + ) + # print(chrono_list) + + context = { + "gymnast": gymnast, + "selected_season": season, + "selected_week_number": week_number, + "selected_routine_type": routine_type, + "chrono_list": chrono_list, + "stat_values": stat_values, + "distinct_season_list": distinct_season_list, + "distinct_week_number_list": distinct_week_number_list, + "distinct_routine_type_list": distinct_routine_type_list, + } + return render(request, "chronos/list_details.html", context) + + +@login_required +@require_http_methods(["GET"]) +def chrono_listing(request, gymnast_id=None): + """ + Si la personne connectée est un entraîneur, la fonction récupère la liste des chronos d'un + gymnaste précis ou de tout le monde. + Si la personne connectée est un gymnaste, la fonction récupère la liste de ses chronos. + + Args: + gymnast_id (int) identifiant d'un gymnaste + """ + + gymnast = None + if request.user.groups.filter(name="trainer").exists(): + if gymnast_id: + chrono_list = Chrono.objects.filter(gymnast=gymnast_id) + gymnast = Gymnast.objects.get(pk=gymnast_id) + else: + chrono_list = Chrono.objects.all() + else: + chrono_list = Chrono.objects.filter( + Q(gymnast__last_name=request.user.last_name) + & Q(gymnast__first_name=request.user.first_name) + ) + + context = {"chrono_list": chrono_list, "gymnast": gymnast} + return render(request, "chronos/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def note_listing(request, gymnast_id=None): + """ + Si la personne connectée est un entraîneur, la fonction récupère la liste des notes d'un + gymnaste précis ou de tout le monde. + Si la personne connectée est un gymnaste, la fonction récupère la liste de ses notes à + lui/elle. + """ + + gymnast = None + if request.user.groups.filter(name="trainer").exists(): + if gymnast_id: + note_list = Note.objects.filter(gymnast=gymnast_id) + gymnast = Gymnast.objects.get(pk=gymnast_id) + else: + note_list = Note.objects.all() + else: + note_list = Note.objects.filter( + Q(gymnast__last_name=request.user.last_name) + & Q(gymnast__first_name=request.user.first_name) + ) + + context = {"note_list": note_list, "gymnast": gymnast} + return render(request, "notes/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def note_details(request, note_id): + """ + Récupère toutes les informations d'un note. + """ + note = get_object_or_404(Note, pk=note_id) + return render(request, "notes/details.html", {"note": note}) + + +@login_required +@require_http_methods(["GET", "POST"]) +def note_create_or_update(request, note_id=None, gymnast_id=None): + """Création ou modification d'un chrono + + Args: + note_id (int) identifiant d'une note + gymnast_id (int) identifiant d'un gymnaste + + TODO: pq ne puis-je pas récuperer l'idantifiant du caoch via le form. (cf ##..) + """ + + coach = User.objects.get(pk=request.user.id) + if note_id: + note = get_object_or_404(Note, pk=note_id) + data = { + "coach": coach.id, + "gymnast": note.gymnast.id, + "gymnast_related": str(note.gymnast), + } + else: + note = None + data = { + "coach": coach.id, + } + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data["gymnast"] = gymnast_id + data["gymnast_related"] = str(gymnast) + + if request.method == "POST": + form = NoteForm(request.POST, instance=note) + + if form.is_valid(): + new_note = form.save() + if ( + (new_note.gymnast.user.email or new_note.gymnast.email_trainer) + and ((not note_id) or (note_id and note.status == 0)) + and new_note.status == 1 + ): + send_mail( + "Nouvelle note", + "Une nouvelle note vous a été envoyée", + settings.EMAIL_HOST_USER, + [new_note.gymnast.user.email, new_note.gymnast.email_trainer], + fail_silently=False, + html_message="""

    Bonjour,

    +

    Une nouvelle note vous a été envoyée. Vous pouvez la consulter en liquant ici.


    Excellente journée

    Jarvis

    """, + ) + return HttpResponseRedirect( + reverse("gymnast_details_tab", args=(new_note.gymnast.id, "event")) + ) + else: + return render(request, "notes/create.html", {"form": form}) + else: + last_note = ( + Note.objects.filter(gymnast=gymnast_id, status=1).order_by("-date").first() + ) + if last_note: + data["informations"] = last_note.informations + + form = NoteForm(instance=note, initial=data) + context = {"form": form, "note_id": note_id} + return render(request, "notes/create.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def chrono_create_or_update(request, chrono_id=None, gymnast_id=None): + """Création ou modification d'un chrono""" + + if chrono_id: + chrono = get_object_or_404(Chrono, pk=chrono_id) + data = { + "gymnast": chrono.gymnast.id, + "gymnast_related": str(chrono.gymnast), + } + else: + chrono = None + data = None + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": gymnast} + + if request.method == "POST": + form = ChronoForm(request.POST, instance=chrono) + + if form.is_valid(): + new_chrono = form.save(commit=False) + + if new_chrono.score_type == 1: + new_chrono.tof = new_chrono.score + else: + new_chrono.tof = Chrono.compute_tof(new_chrono.score) + + new_chrono.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=0): + send_mail( + f"{gymnast} : Nouveau chrono", + f"Un nouveau chrono enregistré pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouveau chrono enregistré pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("gymnast_details_tab", args=(new_chrono.gymnast.id, "scores")) + ) + else: + return render(request, "chronos/create.html", {"form": form}) + + form = ChronoForm(instance=chrono, initial=data) + context = {"form": form, "chrono_id": chrono_id} + return render(request, "chronos/create.html", context) + + +@login_required +@require_http_methods(["POST"]) +def gymnast_learn_skill(request): + """ + Lie un gymnast à une figure. + """ + # utiliser un FORM pour cette fonction. + gymnast_id = request.POST.get("gymnast_id", None) + skill_id = request.POST.get("skill_id", None) + learning_step = request.POST.get("learning_step", 0) + + if gymnast_id and skill_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + skill = Skill.objects.get(pk=skill_id) + learned_skill = LearnedSkill( + gymnast=gymnast, + skill=skill, + learning_step=learning_step, + date=datetime.now(), + ) + learned_skill.save() + return HttpResponse(status=200) + + if gymnast_id: + print("Error : can not link Gymnast and skill. Missing Skill_ID.") + else: + print("Error : can not link Gymnast and skill. Missing Gymnast_ID.") + return HttpResponse(status=500) + + +@login_required +@require_http_methods(["GET", "POST"]) +def learnedskill_create_or_update(request, gymnast_id=None): + """ + Formulaire de creation et modification d'un lien skill/gymnaste. + """ + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = { + "gymnast": gymnast.id, + "gymnast_related": str(gymnast), + } + else: + data = None + + if request.method == "POST": + form = LearnedSkillForm(request.POST) + + if form.is_valid(): + form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=2): + send_mail( + f"{gymnast} : Nouveau skill appris", + f"Un nouveau skill a été appris par {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouveau skill () a été appris par {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("gymnast_details", args=(form.cleaned_data["gymnast"].id,)) + ) + else: + render(request, "followup/learnedskills/create.html", {"form": form}) + + form = LearnedSkillForm(initial=data) + context = {"form": form, "gymnast_id": gymnast_id} + return render(request, "learnedskills/create.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def score_create_or_update(request, score_id=None, gymnast_id=None): + """ + Formulaire de création d'un nouveau score. + """ + + if score_id: + score = get_object_or_404(Point, pk=score_id) + data = { + "gymnast_related": str(score.gymnast), + "event_related": str(score.event), + } + else: + score = None + data = None + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)} + + if request.method == "POST": + form = ScoreForm(request.POST, instance=score) + + if form.is_valid(): + form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=4): + send_mail( + f"{gymnast} : Nouveau score enregistré", + f"Un nouveau score a été enregistré pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouveau score a été enregistré pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + if form.cleaned_data["add_to_chrono"]: + new_chrono = Chrono( + gymnast=form.cleaned_data["gymnast"], + chrono_type=form.cleaned_data["routine_type"], + score_type=1, + score=form.cleaned_data["point_time_of_flight"], + tof=form.cleaned_data["point_time_of_flight"], + date=form.cleaned_data["event"].date_begin, + ) + new_chrono.save() + + return HttpResponseRedirect( + reverse( + "gymnast_details_tab", + args=(form.cleaned_data["gymnast"].id, "scores"), + ) + ) + else: + render(request, "followup/scores/create.html", {"form": form}) + + form = ScoreForm(instance=score, initial=data) + context = {"form": form, "score_id": score_id} + return render(request, "scores/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def score_listing(request, gymnast_id=None): + """ + Revoie la liste des scores + """ + + pattern = request.GET.get("pattern", None) + gymnast = None + + if pattern: + score_list = Point.objects.filter( + Q(event__icontains=pattern) | Q(gymnast__icontains=pattern) + ) + elif gymnast_id: + score_list = Point.objects.filter(gymnast=gymnast_id) + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + else: + score_list = Point.objects.all() + + context = {"score_list": score_list, "gymnast": gymnast} + return render(request, "scores/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def accident_listing(request): + """ + Récupère la liste des accidents. + Si c'est un gymnaste qui est connecté, il ne peut récupérer que la liste de ses accidents. + Si c'est un autre utilisateur (entraîneur), la liste peut répondre à un pattern si celui-ci est + définit. + """ + + if request.user.groups.filter(name="trainer").exists(): + pattern = request.GET.get("pattern", None) + if pattern: + accident_list = Accident.objects.filter( + Q(gymnast__last_name__icontains=pattern) + | Q(gymnast__first_name__icontains=pattern) + ) + else: + accident_list = Accident.objects.all() + else: + accident_list = Accident.objects.filter( + Q(gymnast__last_name=request.user.last_name) + & Q(gymnast__first_name=request.user.first_name) + ) + + context = {"accident_list": accident_list} + return render(request, "accidents/list.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def accident_create_or_update(request, accident_id=None, gymnast_id=None): + """ + Formulaire de création d'un nouvel accident. + """ + + if accident_id: + accident = get_object_or_404(Accident, pk=accident_id) + data = { + "gymnast_related": accident.gymnast, + "skill_related": accident.skill, + } + else: + accident = None + data = None + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)} + + if request.method == "POST": + form = AccidentForm(request.POST, instance=accident) + + if form.is_valid(): + accident = form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=1): + send_mail( + f"{gymnast} : Nouvel accident enregistré", + f"Un nouvel accident enregistré pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouvel accident enregistré pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("accident_details", args=(accident.pk,)) + ) + else: + return render(request, "accidents/create.html", {"form": form}) + + form = AccidentForm(instance=accident, initial=data) + context = {"form": form, "accident_id": accident_id} + return render(request, "accidents/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def accident_detail(request, accident_id): + """ + Récupère toutes les informations d'un accident. + """ + accident = get_object_or_404(Accident, pk=accident_id) + return render(request, "accidents/details.html", {"accident": accident}) + + +@login_required +@require_http_methods(["GET"]) +def mindstate_listing(request, gymnast_id=None): + """ + Récupère la liste des évaluations mentales suivant (d'un gymnaste si définit en paramètre). + """ + gymnast = None + if gymnast_id: + mindstate_list = MindState.objects.filter(gymnast=gymnast_id) + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + else: + mindstate_list = MindState.objects.all() + + context = {"mindstate_list": mindstate_list, "gymnast": gymnast} + return render(request, "mindstates/list.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def mindstate_create_or_update( + request, mindstate_id=None, gymnast_id=None, event_id=None +): + """ + Formulaire de création d'un nouvel accident. + """ + + if mindstate_id: + mindstate = get_object_or_404(MindState, pk=mindstate_id) + data = {"gymnast_related": mindstate.gymnast, "event_related": mindstate.event} + else: + mindstate = None + data = None + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)} + if event_id is not None: + event = get_object_or_404(Event, pk=event_id) + data = {"event": event_id, "event_related": str(event)} + + if request.method == "POST": + form = MindStateForm(request.POST, instance=mindstate) + + if form.is_valid(): + mindstate = form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=5): + send_mail( + f"{gymnast} : Nouvel état d'esprit enregistré", + f"Un nouvel état d'esprit enregistré pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouvel état d'esprit enregistré pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("mindstate_details", args=(mindstate.pk,)) + ) + else: + return render(request, "mindstates/create.html", {"form": form}) + + form = MindStateForm(instance=mindstate, initial=data) + context = {"form": form, "mindstate_id": mindstate_id} + return render(request, "mindstates/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def mindstate_detail(request, mindstate_id): + """ + Récupère toutes les informations d'une évaluation psychologique. + """ + mindstate = get_object_or_404(MindState, pk=mindstate_id) + return render(request, "mindstates/details.html", {"mindstate": mindstate}) + + +@login_required +@require_http_methods(["GET"]) +def heightweight_listing(request, gymnast_id=None): + """ + Récupère la liste des couples taille/poids suivant (d'un gymnast si définit en paramètre). + """ + gymnast = None + if gymnast_id: + heightweight_list = HeightWeight.objects.filter(gymnast=gymnast_id) + gymnast = Gymnast.objects.get(pk=gymnast_id) + else: + heightweight_list = HeightWeight.objects.all() + + context = {"heightweight_list": heightweight_list, "gymnast": gymnast} + return render(request, "heightweight/list.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def heightweight_create_or_update(request, heightweight_id=None, gymnast_id=None): + """ + Formulaire de creation et modification d'un couple taille/couple. + """ + + if heightweight_id: + heightweight = get_object_or_404(HeightWeight, pk=heightweight_id) + data = {"gymnast_related": heightweight.gymnast} + else: + heightweight = None + data = None + if gymnast_id: + heightweight = ( + HeightWeight.objects.filter(gymnast=gymnast_id) + .order_by("-date") + .first() + ) + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": str(gymnast)} + + if request.method == "POST": + form = HeightWeightForm(request.POST, instance=heightweight) + + if form.is_valid(): + form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=8): + send_mail( + f"{gymnast} : Nouveau poids/taille enregistré", + f"Un nouveau poids/taille enregistré pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Un nouveau poids/taille enregistré pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse( + "gymnast_details_tab", + args=(form.cleaned_data["gymnast"].id, "physiological"), + ) + ) + else: + print(form.errors) + return render(request, "heightweight/create.html", {"form": form}) + + form = HeightWeightForm(instance=heightweight, initial=data) + context = { + "form": form, + "gymnast_id": gymnast_id, + "heightweight_id": heightweight_id, + } + return render(request, "heightweight/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def routine_done_listing(request, gymnast_id=None): + """ + Liste tous les record de la table NumberOfRoutineDone + """ + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + routine_done_list = gymnast.number_of_routine_done.all() + else: + gymnast = None + routine_done_list = NumberOfRoutineDone.objects.all() + + context = {"routine_done_list": routine_done_list, "gymnast": gymnast} + return render(request, "routinedone/list.html", context) + + +@require_http_methods(["POST"]) +def increment_routinedone(request): + """ + Incrémente le nombre de routine faite pour aujourd'hui et incrémente, si besoin, le nombre + de routine réussie + """ + + data = { + "gymnast": get_object_or_404(Gymnast, pk=request.POST.get("gymnast_id", None)), + "routine_type": request.POST.get("routine_type", 1), + "date": request.POST.get("date", date.today()), + } + + routine = ( + data["gymnast"] + .has_routine.filter(routine_type=request.POST.get("routine_type", 1)) + .first() + ) + data["routine"] = routine.routine + + routinedone, _ = NumberOfRoutineDone.objects.get_or_create( + date=data["date"], gymnast=data["gymnast"], routine_type=data["routine_type"] + ) + data["number_of_try"] = routinedone.number_of_try + 1 + success = request.POST.get("success", 0) + if int(success) == 1: + data["number_of_successes"] = routinedone.number_of_successes + 1 + else: + data["number_of_successes"] = routinedone.number_of_successes + form = NumberOfRoutineDoneForm(data, instance=routinedone) + + if form.is_valid(): + form.save() + return HttpResponse(status=200) + else: + return HttpResponse(status=406) + + +@login_required +@require_http_methods(["GET", "POST"]) +def routinedone_create_or_update(request, routinedone_id=None, gymnast_id=None): + """Création ou modification d'un chrono""" + + if routinedone_id: + routinedone = get_object_or_404(NumberOfRoutineDone, pk=routinedone_id) + data = { + "gymnast": routinedone.gymnast.id, + "gymnast_related": str(routinedone.gymnast), + } + if routinedone.routine: + data["routine"] = routinedone.routine.id + data["routine_related"] = str(routinedone.routine) + else: + routine = ( + GymnastHasRoutine.objects.filter(gymnast=routinedone.gymnast) + .filter(routine_type=routinedone.routine_type) + .filter(date_begin__lte=routinedone.date) + .filter(Q(date_end__gte=routinedone.date) | Q(date_end__isnull=True)) + .first() + ) + if routine: + data["routine"] = routine.id + data["routine_related"] = str(routine) + else: + data = None + routinedone = None + if gymnast_id is not None: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = {"gymnast": gymnast_id, "gymnast_related": gymnast} + + if request.method == "POST": + form = NumberOfRoutineDoneForm(request.POST, instance=routinedone) + + if form.is_valid(): + form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=7): + send_mail( + f"{gymnast} : Nouvelle série comptabilisée", + f"Nouvelle série comptabilisée pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Nouvelle série comptabilisée pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse( + "gymnast_details_tab", + args=(form.cleaned_data["gymnast"].id, "routine"), + ) + ) + else: + return render(request, "routinedone/create.html", {"form": form}) + + form = NumberOfRoutineDoneForm(instance=routinedone, initial=data) + context = {"form": form, "routinedone_id": routinedone_id} + return render(request, "routinedone/create.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def plan_create_or_update(request, plan_id=None, gymnast_id=None, skill_id=None): + """Création d'un plan. + + Args: + plan_id (int): identifiant d'un plan (classe ). + gymnast_id (int): identifiant d'un gymnaste (classe ). + skill_id (int): identifiant d'un skill (classe ). + """ + + if plan_id: + plan = get_object_or_404(Plan, pk=plan_id) + data = { + "gymnast": plan.gymnast.id, + "gymnast_related": str(plan.gymnast), + "educative": plan.educative.id, + "educative_related": str(plan.educative), + } + else: + plan = None + data = {} + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data["gymnast"] = gymnast_id + data["gymnast_related"] = str(gymnast) + + if skill_id: + skill = get_object_or_404(Skill, pk=skill_id) + data["educative"] = skill_id + data["educative_related"] = str(skill) + + if request.method == "POST": + form = PlanForm(request.POST, instance=plan) + + if form.is_valid(): + plan = form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=7): + send_mail( + f"{gymnast} : Nouvelle série comptabilisée", + f"Nouvelle série comptabilisée pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Nouvelle série comptabilisée pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("gymnast_details", args=(form.cleaned_data["gymnast"].id,)) + ) + else: + return render(request, "plan/create.html", {"form": form}) + + form = PlanForm(instance=plan, initial=data) + context = {"form": form, "plan_id": plan_id} + return render(request, "plan/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def intensity_listing(request, gymnast_id=None): + """ + Si la personne connectée est un entraîneur, la fonction récupère la liste des intensités d'un + gymnaste précis ou de tout le monde. + Si la personne connectée est un gymnaste, la fonction récupère la liste de ses intensités à + lui/elle. + + Args: + gymnast_id (int) identifiant d'un gymnaste + """ + + gymnast = None + if request.user.groups.filter(name="trainer").exists(): + if gymnast_id: + intensity_list = Intensity.objects.filter(gymnast=gymnast_id) + gymnast = Gymnast.objects.get(pk=gymnast_id) + else: + intensity_list = Intensity.objects.all() + else: + intensity_list = Intensity.objects.filter( + Q(gymnast__last_name=request.user.last_name) + & Q(gymnast__first_name=request.user.first_name) + ) + + context = {"intensity_list": intensity_list, "gymnast": gymnast} + return render(request, "intensities/list.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def intensity_create_or_update(request, intensity_id=None, gymnast_id=None): + """Création d'un record de la class Intentity. + + Args: + intensity_id (int): identifiant d'un plan (classe ). + gymnast_id (int): identifiant d'un gymnaste (classe ). + """ + + if intensity_id: + intensity = get_object_or_404(Intensity, pk=intensity_id) + data = { + "gymnast": intensity.gymnast.id, + "gymnast_related": str(intensity.gymnast), + } + else: + intensity = None + data = {} + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data["gymnast"] = gymnast_id + data["gymnast_related"] = str(gymnast) + + if request.method == "POST": + form = IntensityForm(request.POST, instance=intensity) + + if form.is_valid(): + intensity = form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=10): + send_mail( + f"{gymnast} : Nouvelle intensité", + f"Une nouvelle note vous a été envoyée pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Une nouvelle intensité a été encodée pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse( + "gymnast_details_tab", + args=(form.cleaned_data["gymnast"].id, "routine"), + ) + ) + else: + return render(request, "intensities/create.html", {"form": form}) + + form = IntensityForm(instance=intensity, initial=data) + context = {"form": form, "intensity_id": intensity_id} + return render(request, "intensities/create.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def season_information_create_or_update( + request, season_information_id=None, gymnast_id=None +): + """Création d'un record de la class Intentity. + + Args: + season_information_id (int): identifiant d'un plan (classe ). + gymnast_id (int): identifiant d'un gymnaste (classe ). + """ + + if season_information_id: + season_information = get_object_or_404( + SeasonInformation, pk=season_information_id + ) + data = { + "gymnast": season_information.gymnast.id, + "gymnast_related": str(season_information.gymnast), + "club": season_information.club.id, + "club_related": str(season_information.club), + } + else: + season_information = None + data = {} + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data["gymnast"] = gymnast_id + data["gymnast_related"] = str(gymnast) + + if request.method == "POST": + form = SeasonInformationForm(request.POST, instance=season_information) + + if form.is_valid(): + season_information = form.save() + + # notification + gymnast = Gymnast.objects.get(pk=form.cleaned_data["gymnast"].id) + for notification in gymnast.notifications.filter(functionality=11): + send_mail( + f"{gymnast} : Nouvelle information de saison", + f"Une nouvelle information de saison enregistrée pour {gymnast}", + settings.EMAIL_HOST_USER, + [ + notification.user.email, + ], + fail_silently=False, + html_message=f"""

    Bonjour,

    +

    Une nouvelle information de saison enregistrée pour {gymnast}.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse( + "gymnast_details", + args=(form.cleaned_data["gymnast"].id,), + ) + ) + else: + return render( + request, "followup/seasoninformations/create.html", {"form": form} + ) + + form = SeasonInformationForm(instance=season_information, initial=data) + context = {"form": form, "season_information_id": season_information_id} + return render(request, "seasoninformations/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def season_information_listing(request, gymnast_id=None): + """Liste toutes les informations de saison pour un gymnaste""" + + if gymnast_id is not None: + gymnast = Gymnast.objects.get(pk=gymnast_id) + season_information_list = SeasonInformation.objects.filter(gymnast=gymnast_id) + else: + season_information_list = SeasonInformation.objects.all() + + context = {"season_information_list": season_information_list, "gymnast": gymnast} + return render(request, "seasoninformations/list.html", context) diff --git a/jarvis/location/__init__.py b/jarvis/location/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/location/admin.py b/jarvis/location/admin.py new file mode 100644 index 0000000..6fef19d --- /dev/null +++ b/jarvis/location/admin.py @@ -0,0 +1,35 @@ +from django.contrib import admin +from .models import Place, Club, Country + + +class CountryAdmin(admin.ModelAdmin): + model = Country + + list_display = ("name", "iso3", "iso2", "isonum") + ordering = ("name",) + search_fields = ("name", "nationality") + + +class ClubAdmin(admin.ModelAdmin): + model = Club + + list_display = ("name", "acronym", "place", "is_active") + ordering = ("name",) + list_filter = ("is_active",) + search_fields = ("name",) + autocomplete_fields = ("place",) + + +class PlaceAdmin(admin.ModelAdmin): + model = Place + + list_display = ("name", "address", "postal", "city", "is_active") + ordering = ("name",) + list_filter = ("is_active",) + search_fields = ("name", "address", "postal", "city") + autocomplete_fields = ("country",) # , "place" + + +admin.site.register(Place, PlaceAdmin) +admin.site.register(Club, ClubAdmin) +admin.site.register(Country, CountryAdmin) diff --git a/jarvis/location/apps.py b/jarvis/location/apps.py new file mode 100644 index 0000000..fb87555 --- /dev/null +++ b/jarvis/location/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class LocationConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.location" diff --git a/jarvis/location/forms.py b/jarvis/location/forms.py new file mode 100644 index 0000000..9dabbd3 --- /dev/null +++ b/jarvis/location/forms.py @@ -0,0 +1,53 @@ +from django import forms + +from .models import Place + + +class PlaceForm(forms.ModelForm): + class Meta: + model = Place + fields = ( + "name", + "address", + "postal", + "city", + "country", + # "nbkm", + # "timing", + "is_active", + ) + widgets = { + "name": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Place's name"} + ), + "address": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Address"} + ), + "postal": forms.TextInput( + attrs={"class": "form-control", "placeholder": "ZIP"} + ), + "city": forms.TextInput( + attrs={"class": "form-control", "placeholder": "City"} + ), + "country": forms.HiddenInput(), + # "nbkm": forms.TextInput( + # attrs={"class": "form-control", "placeholder": "Distance"} + # ), + # "timing": forms.TextInput( + # attrs={"class": "form-control", "placeholder": "Travel time"} + # ), + "is_active": forms.CheckboxInput( + attrs={"class": "form-control form-check-input ml-0 mt-0"} + ), + } + + country_related = forms.CharField( + required=False, + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching…", + "data-ref": "#id_country", + } + ), + ) diff --git a/jarvis/location/migrations/0001_initial.py b/jarvis/location/migrations/0001_initial.py new file mode 100644 index 0000000..2f7a0fa --- /dev/null +++ b/jarvis/location/migrations/0001_initial.py @@ -0,0 +1,102 @@ +# Generated by Django 3.2.8 on 2021-12-01 13:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Country", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="English name")), + ( + "nationality", + models.CharField(max_length=255, verbose_name="Nationality"), + ), + ("iso2", models.CharField(max_length=2)), + ("iso3", models.CharField(max_length=3)), + ("isonum", models.PositiveSmallIntegerField()), + ], + options={ + "verbose_name": "Country", + "verbose_name_plural": "Countries", + }, + ), + migrations.CreateModel( + name="Place", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="Name")), + ("address", models.CharField(max_length=255, verbose_name="Address")), + ("postal", models.PositiveIntegerField(verbose_name="Postal code")), + ("city", models.CharField(max_length=255, verbose_name="City")), + ("active", models.BooleanField(default=1, verbose_name="Active")), + ( + "country", + models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + to="location.country", + verbose_name="Country", + ), + ), + ], + options={ + "verbose_name": "Place", + "verbose_name_plural": "Places", + }, + ), + migrations.CreateModel( + name="Club", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="Name")), + ("acronym", models.CharField(max_length=4, verbose_name="Acronym")), + ("active", models.BooleanField(default=1, verbose_name="Active")), + ( + "place", + models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + to="location.place", + verbose_name="Place", + ), + ), + ], + options={ + "verbose_name": "Club", + "verbose_name_plural": "Clubs", + }, + ), + ] diff --git a/jarvis/location/migrations/0002_alter_club_acronym.py b/jarvis/location/migrations/0002_alter_club_acronym.py new file mode 100644 index 0000000..b92df20 --- /dev/null +++ b/jarvis/location/migrations/0002_alter_club_acronym.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2021-12-20 08:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('location', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='club', + name='acronym', + field=models.CharField(max_length=6, verbose_name='Acronym'), + ), + ] diff --git a/jarvis/location/migrations/0003_auto_20220109_1001.py b/jarvis/location/migrations/0003_auto_20220109_1001.py new file mode 100644 index 0000000..b8e69d0 --- /dev/null +++ b/jarvis/location/migrations/0003_auto_20220109_1001.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2022-01-09 10:01 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('location', '0002_alter_club_acronym'), + ] + + operations = [ + migrations.RenameField( + model_name='club', + old_name='active', + new_name='is_active', + ), + migrations.RenameField( + model_name='place', + old_name='active', + new_name='is_active', + ), + ] diff --git a/jarvis/location/migrations/__init__.py b/jarvis/location/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/location/models.py b/jarvis/location/models.py new file mode 100644 index 0000000..444b2e4 --- /dev/null +++ b/jarvis/location/models.py @@ -0,0 +1,73 @@ +from django.db import models + + +class Country(models.Model): + """Classe représentant les pays (basée sur la liste ISO 3166 de 2015). + + References: + https://fr.wikipedia.org/wiki/ISO_3166 + """ + + class Meta: + verbose_name = "Country" + verbose_name_plural = "Countries" + + name = models.CharField(max_length=255, verbose_name="English name") + nationality = models.CharField(max_length=255, verbose_name="Nationality") + iso2 = models.CharField(max_length=2) + iso3 = models.CharField(max_length=3) + isonum = models.PositiveSmallIntegerField() + + def __str__(self): + return "%s (%s)" % (self.name, self.iso2) + + +class Place(models.Model): + """ + Représente un lieu. En plus de l'adresse, un lieu est spécifié par un nombre + de kilomètres et une durée de trajet. Pour faciliter les filtres, un lieu peut être + actif ou non. + """ + + class Meta: + verbose_name = "Place" + verbose_name_plural = "Places" + + name = models.CharField(max_length=255, verbose_name="Name") + address = models.CharField(max_length=255, verbose_name="Address") + postal = models.PositiveIntegerField(verbose_name="Postal code") + city = models.CharField(max_length=255, verbose_name="City") + country = models.ForeignKey( + Country, verbose_name="Country", on_delete=models.CASCADE, default=None + ) + # nbkm = models.PositiveIntegerField(blank=True, null=True, help_text="in km") + # timing = models.PositiveIntegerField(blank=True, null=True, help_text="in minutes") + is_active = models.BooleanField(default=1, verbose_name="Active") + + def __str__(self): + return "%s (%s)" % (self.name, self.city if self.city else "?") + + +class Club(models.Model): + """ + Représente un club. Un club est associé à un lieu. Pour faciliter les filtres, + un club peut être actif ou non. + + .. todo:: Un club peut avoir plusieurs salle + et une salle peut-être louée par plusieurs clubs... M2M ? + """ + + class Meta: + verbose_name = "Club" + verbose_name_plural = "Clubs" + + # place=models.ManyToManyField(Place, through="clubs") + place = models.ForeignKey( + Place, verbose_name="Place", on_delete=models.CASCADE, default=None + ) + name = models.CharField(max_length=255, verbose_name="Name") + acronym = models.CharField(max_length=6, verbose_name="Acronym") + is_active = models.BooleanField(default=1, verbose_name="Active") + + def __str__(self): + return "%s (%s)" % (self.name, self.place.city if self.place.city else "?") diff --git a/jarvis/location/templates/clubs/create.html b/jarvis/location/templates/clubs/create.html new file mode 100644 index 0000000..e540904 --- /dev/null +++ b/jarvis/location/templates/clubs/create.html @@ -0,0 +1,97 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    +
    +
    +
    +

    {% if place_id %}Edit{% else %}Create{% endif %} Place

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.name }} + {% if form.name.errors %} + + {% for error in form.name.errors %}{{ error }}{% endfor %} + + {% endif %} +
    +
    +
    + +
    + {{ form.address }} + {% if form.address.errors %}{% for error in form.address.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    +
    + {{ form.postal }} + {% if form.postal.errors %} {% for error in form.postal.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {{ form.city }} + {% if form.city.errors %} {% for error in form.city.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    +
    +
    + +
    + {{ form.country }} + {{ form.country_related }} + {% if form.country.errors %} {% endif %} +
    +
    +
    + +
    +
    +
    + {{ form.nbkm }} + {% if form.nbkm.errors %} {% for error in form.nbkm.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {{ form.timing }} + {% if form.timing.errors %} {% for error in form.timing.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    +
    +
    + +
    + {{ form.active }} + {% if form.active.errors %} {% for error in form.active.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + + Cancel +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + {% if request.session.template == 0 %} + + {% else %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/templates/clubs/details.html b/jarvis/location/templates/clubs/details.html new file mode 100644 index 0000000..27d9856 --- /dev/null +++ b/jarvis/location/templates/clubs/details.html @@ -0,0 +1,62 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    {{ place.name }}

    +
    +
    +
    +
    +
    + {{ place.address }}
    + {{ place.postal }} {{ place.city }}
    + {{ place.country.nameus }} +
    +
    +
    + {% if place.nbkm %}{{ place.nbkm }}km
    {% endif %} + {% if place.timing %}{{ place.timing }}min.
    {% endif %} + {% if not place.active %}Inactive{% endif %} +
    +
    + +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/templates/clubs/list.html b/jarvis/location/templates/clubs/list.html new file mode 100644 index 0000000..86094dc --- /dev/null +++ b/jarvis/location/templates/clubs/list.html @@ -0,0 +1,53 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +

    Club Listing

    +
    +
    +
    + {% if club_list %} + + + + + + + + + + {% for club in club_list %} + + + + + + {% endfor %} + +
    NameAcronymCity
    {{ club.name }}{{ club.acronym }}{{ club.city }}
    + {% else %} + + + + +
    There are no places corresponding to your criterias
    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/templates/places/create.html b/jarvis/location/templates/places/create.html new file mode 100644 index 0000000..907f96a --- /dev/null +++ b/jarvis/location/templates/places/create.html @@ -0,0 +1,82 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    +
    +
    +
    +

    {% if place_id %}Edit{% else %}Create{% endif %} Place

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.name }} + {% if form.name.errors %} + + {% for error in form.name.errors %}{{ error }}{% endfor %} + + {% endif %} +
    +
    +
    + +
    + {{ form.address }} + {% if form.address.errors %}{% for error in form.address.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    + {{ form.postal }} + {% if form.postal.errors %} {% for error in form.postal.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {{ form.city }} + {% if form.city.errors %} {% for error in form.city.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    +
    + +
    + {{ form.country }} + {{ form.country_related }} + {% if form.country.errors %} {% endif %} +
    +
    + {% if placeid %} +
    + +
    + {{ form.active }} + {% if form.active.errors %} {% for error in form.active.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {% endif %} +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + {% if request.session.template == 0 %} + + {% else %} + + {% endif %} +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/templates/places/details.html b/jarvis/location/templates/places/details.html new file mode 100644 index 0000000..48b5aed --- /dev/null +++ b/jarvis/location/templates/places/details.html @@ -0,0 +1,59 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    {{ place.name }}

    +
    +
    +
    +
    +
    +
    +

    + {{ place.address }}
    + {{ place.postal }} {{ place.city }}
    + {{ place.country.nameus }} +

    +
    +
    + {% if place.nbkm %}{{ place.nbkm }}km
    {% endif %} + {% if place.timing %}{{ place.timing }}min
    {% endif %} + {% if not place.active %}Inactive{% endif %} +
    +
    + +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/templates/places/list.html b/jarvis/location/templates/places/list.html new file mode 100644 index 0000000..c95aeb0 --- /dev/null +++ b/jarvis/location/templates/places/list.html @@ -0,0 +1,90 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +

    Places Listing

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    +
    +
    + {% if place_list %} + + + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + {% for place in place_list %} + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + {% endfor %} + +
    NameAddressZipCity
    + + + + {{ place.name }}{{ place.address }}{{ place.postal}}{{ place.city }}
    + {% else %} +

    There are no places corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/location/tests_urls.py b/jarvis/location/tests_urls.py new file mode 100644 index 0000000..c7930c5 --- /dev/null +++ b/jarvis/location/tests_urls.py @@ -0,0 +1,20 @@ +from django.test import TestCase +from django.urls import reverse, resolve + + +class URLTestCase(TestCase): + def test_place_url(self): + self.assertEqual(resolve("/location/place/lookup/").view_name, "place_lookup") + self.assertEqual(resolve("/location/place/add/").view_name, "place_create") + self.assertEqual(resolve("/location/place/edit/1/").view_name, "place_update") + self.assertEqual(resolve("/location/place/1/").view_name, "place_details") + self.assertEqual(resolve("/location/place/").view_name, "place_list") + + def test_country_url(self): + self.assertEqual( + resolve("/location/country/lookup/").view_name, "country_lookup" + ) + + def test_club_url(self): + self.assertEqual(resolve("/location/club/lookup/").view_name, "club_lookup") + self.assertEqual(resolve("/location/club/").view_name, "club_list") diff --git a/jarvis/location/urls.py b/jarvis/location/urls.py new file mode 100644 index 0000000..0bb86ce --- /dev/null +++ b/jarvis/location/urls.py @@ -0,0 +1,16 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path(r"place/lookup/", views.place_lookup, name="place_lookup"), + path(r"place/add/", views.place_create_or_update, name="place_create"), + path( + r"place/edit//", views.place_create_or_update, name="place_update" + ), + path(r"place//", views.place_details, name="place_details"), + path(r"place/", views.place_listing, name="place_list"), + path(r"country/lookup/", views.country_lookup, name="country_lookup"), + path(r"club/", views.club_listing, name="club_list"), + path(r"club/lookup/", views.club_lookup, name="club_lookup"), +] diff --git a/jarvis/location/views.py b/jarvis/location/views.py new file mode 100644 index 0000000..732dbe2 --- /dev/null +++ b/jarvis/location/views.py @@ -0,0 +1,139 @@ +from datetime import datetime + +from django.contrib.auth.decorators import login_required +from django.db.models import Q +from django.http import HttpResponseRedirect, JsonResponse +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse + +from .models import ( + Club, + Place, + Country, +) +from .forms import PlaceForm + + +def __difference_between_times(end, start): + """ + Prend deux `datetime.time` en paramètre et calcul la différence entre les deux. + """ + startdate = datetime(2000, 1, 1, start.hour, start.minute) + enddate = datetime(2000, 1, 1, end.hour, end.minute) + + return enddate - startdate + + +@login_required +@require_http_methods(["POST"]) +def place_lookup(request): + """ + Récupère la liste des lieux à la volée suivant des caractères de recherche entrés. + """ + place_list = [] + pattern = request.POST.get("pattern", None) + if pattern is not None and len(pattern) >= 3: + results = Place.objects.filter( + Q(name__icontains=pattern) | Q(city__icontains=pattern) + ) + place_list = [{"ID": x.id, "Label": str(x)} for x in results] + return JsonResponse(place_list, safe=False) + + +@login_required +@require_http_methods(["POST"]) +def country_lookup(request): + """ + Récupère la liste des pays à la volée suivant des caractères de recherche entrés. + """ + country_list = [] + pattern = request.POST.get("pattern", None) + print(pattern) + if pattern is not None and len(pattern) >= 3: + results = Country.objects.filter( + Q(name__icontains=pattern) | Q(nationality__icontains=pattern) + ) + country_list = [{"id": x.id, "Label": str(x)} for x in results] + return JsonResponse(country_list, safe=False) + + +@login_required +@require_http_methods(["GET"]) +def place_listing(request): + """ + Liste tous les lieux connus + """ + place_list = Place.objects.all() + context = {"place_list": place_list} + return render(request, "places/list.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def place_create_or_update(request, place_id=None): + """ + Formulaire de création d'un nouveau lieu. + """ + + if place_id: + place = get_object_or_404(Place, pk=place_id) + data = {"country_related": place.country} + else: + place = None + data = {} + + if request.method == "POST": + form = PlaceForm(request.POST, instance=place) + + if form.is_valid(): + place = form.save() + return HttpResponseRedirect(reverse("place_details", args=(place.pk,))) + else: + return render(request, "places/create.html", {"form": form}) + + form = PlaceForm(instance=place, initial=data) + context = {"form": form, "place_id": place_id} + return render(request, "places/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def place_details(request, place_id): + """ + Récupère toutes les informations d'un lieu. + """ + place = get_object_or_404(Place, pk=place_id) + context = {"place": place} + return render(request, "places/details.html", context) + + +@login_required +@require_http_methods(["GET"]) +def club_listing(request): + """Liste tous les clubs connus""" + club_list = Club.objects.all() + context = {"club_list": club_list} + return render(request, "clubs/list.html", context) + + +@login_required +@require_http_methods(["POST"]) +def club_lookup(request): + """ + Récupère la liste des gymnastes à la volée suivant des caractères de + recherche entrés. (min 3 caractères) + """ + + results = [] + pattern = request.POST.get("pattern", None) + + if pattern is not None and len(pattern) > 3: + model_results = Club.objects.filter( + Q(name__icontains=pattern) + | Q(place__city__icontains=pattern) + | Q(acronym__icontains=pattern) + ) + results = [{"ID": x.id, "Name": str(x)} for x in model_results] + + return JsonResponse(results, safe=False) diff --git a/jarvis/objective/__init__.py b/jarvis/objective/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/objective/admin.py b/jarvis/objective/admin.py new file mode 100644 index 0000000..5d6fbb8 --- /dev/null +++ b/jarvis/objective/admin.py @@ -0,0 +1,193 @@ +from django.contrib import admin + +# from django_extensions.admin import ForeignKeyAutocompleteAdmin + +from django_admin_listfilter_dropdown.filters import ( + DropdownFilter, + ChoiceDropdownFilter, + RelatedDropdownFilter, +) + +from .models import ( + TouchPosition, + Skill, + Routine, + RoutineSkill, + PrerequisiteClosure, +) + + +class TouchPositionAdmin(admin.ModelAdmin): + model = TouchPosition + + list_display = ("long_label", "short_label", "allowed_in_competition", "is_default") + ordering = ("long_label", "short_label") + search_fields = ("long_label", "short_label") + list_filter = ("allowed_in_competition",) + + +def duplicate_skill(modeladmin, request, queryset): # pylint: disable=unused-argument + for obj in queryset: + obj.id = None + obj.save() + + +class SkillAdmin(admin.ModelAdmin): + model = Skill + + fields = ( + "long_label", + "short_label", + "informations", + "departure", + "landing", + "rotation_type", + "position", + "rotation", + "twist", + "difficulty", + "level", + "rank", + "notation", + "simplified_notation", + "is_competitive", + "age_boy_with_help", + "age_boy_without_help", + "age_boy_chained", + "age_boy_masterised", + "age_girl_with_help", + "age_girl_without_help", + "age_girl_chained", + "age_girl_masterised", + "prerequisites", + "educatives", + ) + + list_display = ( + "long_label", + "difficulty", + "is_competitive", + "level", + "rank", + "notation", + ) + + ordering = ("long_label", "short_label") + search_fields = ("rank", "long_label", "short_label", "notation") + list_filter = ( + "is_competitive", + ("difficulty", DropdownFilter), + ("departure", RelatedDropdownFilter), + ("landing", RelatedDropdownFilter), + ("level", DropdownFilter), + ("rank", DropdownFilter), + "rotation_type", + ("position", DropdownFilter), + ("rotation", DropdownFilter), + ("twist", DropdownFilter), + ) + + filter_horizontal = ("educatives", "prerequisites") + + duplicate_skill.short_description = "Duplicate selected record" + + class Media: + js = ( + "js/core/jquery-3.6.0.min.js", + "js/admin/skill.js", + ) + + +class RoutineAdmin(admin.ModelAdmin): + model = Routine + + fields = ( + "long_label", + "short_label", + "difficulty", + "level", + "rank", + "educatives", + "prerequisites", + "age_boy_with_help", + "age_boy_without_help", + "age_boy_chained", + "age_boy_masterised", + "age_girl_with_help", + "age_girl_without_help", + "age_girl_chained", + "age_girl_masterised", + "is_active", + "is_competitive", + ) + list_display = ( + "long_label", + "short_label", + "is_competitive", + "is_active", + "level", + "rank", + "difficulty", + ) + list_filter = ( + ("level", DropdownFilter), + "difficulty", + "is_competitive", + "is_active", + ) + search_fields = ( + "long_label", + "short_label", + ) + + filter_horizontal = ("educatives",) + + class Media: + js = ( + "js/core/jquery-3.6.0.min.js", + "js/admin/routine.js", + ) + + # TODO: ne proposer QUE les SKILL comme educatif + # def get_related_filter(self, model, request): + # # print('boum') + # if not issubclass(model, Educative): + # return super(Skill, self).get_related_filter(model, request) + + +class RoutineSkillAdmin(admin.ModelAdmin): + model = RoutineSkill + + list_display = ("routine", "skill", "rank") + list_filter = (("rank", DropdownFilter),) + search_fields = ( + "routine__long_label", + "routine__short_label", + "skill__long_label", + "skill__short_label", + ) + ordering = ("routine",) + autocomplete_fields = ("routine", "skill") + + +class PrerequisiteClosureAdmin(admin.ModelAdmin): + model = PrerequisiteClosure() + + list_display = ("descendant", "ancestor", "level", "path") + search_fields = ( + "descendant__long_label", + "descendant__short_label", + "ancestor__long_label", + "ancestor__short_label", + ) + list_filter = ( + ("level", DropdownFilter), + ("path", DropdownFilter), + ) + + +admin.site.register(TouchPosition, TouchPositionAdmin) +admin.site.register(Skill, SkillAdmin) +admin.site.register(Routine, RoutineAdmin) +admin.site.register(RoutineSkill, RoutineSkillAdmin) +admin.site.register(PrerequisiteClosure, PrerequisiteClosureAdmin) diff --git a/jarvis/objective/apps.py b/jarvis/objective/apps.py new file mode 100644 index 0000000..9790a8b --- /dev/null +++ b/jarvis/objective/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ObjectiveConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.objective" diff --git a/jarvis/objective/forms.py b/jarvis/objective/forms.py new file mode 100644 index 0000000..3f112ff --- /dev/null +++ b/jarvis/objective/forms.py @@ -0,0 +1,62 @@ +from django import forms + +from .models import Skill, Routine, RoutineSkill + + +class SkillForm(forms.ModelForm): + class Meta: + model = Skill + fields = ("informations",) + widgets = { + "informations": forms.Textarea( + attrs={ + "class": "form-control", + "placeholder": "Informations about the skill : attention point, methodology, biomecanics, …", # pylint: disable=line-too-long + } + ), + } + + +class RoutineForm(forms.ModelForm): + class Meta: + model = Routine + fields = ( + "long_label", + "short_label", + "difficulty", + "level", + "is_active", + "informations", + ) + widgets = { + "long_label": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Routine's long name"} + ), + "short_label": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Routine's short name"} + ), + "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 + } + ), + "difficulty": forms.HiddenInput(), + "level": forms.HiddenInput(), + "is_active": forms.HiddenInput(), + } + + +class RoutineSkillForm(forms.ModelForm): + class Meta: + model = RoutineSkill + fields = ( + "routine", + "skill", + "rank", + ) + widgets = { + "routine": forms.HiddenInput(), + "skill": forms.HiddenInput(), + "rank": forms.NumberInput(), + } diff --git a/jarvis/objective/management/commands/rebuild_closure_table_tree_for_routine.py b/jarvis/objective/management/commands/rebuild_closure_table_tree_for_routine.py new file mode 100644 index 0000000..6955bdc --- /dev/null +++ b/jarvis/objective/management/commands/rebuild_closure_table_tree_for_routine.py @@ -0,0 +1,122 @@ +"""This command manages Closure Tables implementation + +It adds new levels and cleans links between Educatives. +This way, it's relatively easy to fetch an entire tree with just one tiny request. + +""" + +from django.core.management.base import BaseCommand + +from jarvis.objective.models import Routine, PrerequisiteClosure + + +class Command(BaseCommand): + def handle(self, *args, **options): + routine_list = Routine.objects.all() + count = 0 + + for routine in routine_list: + count += 1 + updated = False + + max_level = routine.difficulty * 10 + # if educative. + + max_rank = 0 + age_boy_with_help = 0 + age_boy_without_help = 0 + age_boy_chained = 0 + age_boy_masterised = 0 + + age_girl_with_help = 0 + age_girl_without_help = 0 + age_girl_chained = 0 + age_girl_masterised = 0 + + print(str(count) + " - Traitement de " + str(routine.long_label)) + + breadcrumb = routine.breadcrumb() + for path in range(0, len(breadcrumb)): + tree = set( + PrerequisiteClosure.objects.filter(descendant=educative, path=path) + ) + + for position, ancestor in enumerate(breadcrumb[path]): + tree_path, _ = PrerequisiteClosure.objects.get_or_create( + ancestor=ancestor, + descendant=educative, + level=position, + path=path, + ) + max_level = max(max_level, position) + max_rank = max(max_rank, max_level, ancestor.rank + 1) + age_boy_with_help = max( + age_boy_with_help, ancestor.age_boy_with_help + ) + age_boy_without_help = max( + age_boy_without_help, ancestor.age_boy_without_help + ) + age_boy_chained = max(age_boy_chained, ancestor.age_boy_chained) + age_boy_masterised = max( + age_boy_masterised, ancestor.age_boy_masterised + ) + + age_girl_with_help = max( + age_girl_with_help, ancestor.age_girl_with_help + ) + age_girl_without_help = max( + age_girl_without_help, ancestor.age_girl_without_help + ) + age_girl_chained = max(age_girl_chained, ancestor.age_girl_chained) + age_girl_masterised = max( + age_girl_masterised, ancestor.age_girl_masterised + ) + + if tree_path in tree: + tree.remove(tree_path) + + for tree_path in tree: + tree_path.delete() + + if routine.level != max_level: + updated = True + routine.level = max_level + + if routine.rank != max_rank: + updated = True + routine.rank = max_rank + + if routine.age_boy_with_help < age_boy_with_help: + updated = True + routine.age_boy_with_help = age_boy_with_help + + if routine.age_boy_without_help < age_boy_without_help: + updated = True + routine.age_boy_without_help = age_boy_without_help + + if routine.age_boy_chained < age_boy_chained: + updated = True + routine.age_boy_chained = age_boy_chained + + if routine.age_boy_masterised < age_boy_masterised: + updated = True + routine.age_boy_masterised = age_boy_masterised + + if routine.age_girl_with_help < age_girl_with_help: + updated = True + routine.age_girl_with_help = age_girl_with_help + + if routine.age_girl_without_help < age_girl_without_help: + updated = True + routine.age_girl_without_help = age_girl_without_help + + if routine.age_girl_chained < age_girl_chained: + updated = True + routine.age_girl_chained = age_girl_chained + + if routine.age_girl_masterised < age_girl_masterised: + updated = True + routine.age_girl_masterised = age_girl_masterised + + if updated: + routine.save() diff --git a/jarvis/objective/management/commands/rebuild_closure_table_tree_for_skill.py b/jarvis/objective/management/commands/rebuild_closure_table_tree_for_skill.py new file mode 100644 index 0000000..b23ee1e --- /dev/null +++ b/jarvis/objective/management/commands/rebuild_closure_table_tree_for_skill.py @@ -0,0 +1,123 @@ +"""This command manages Closure Tables implementation + +It adds new levels and cleans links between Educatives. +This way, it's relatively easy to fetch an entire tree with just one tiny request. + +""" + +from django.core.management.base import BaseCommand + +from jarvis.objective.models import Skill, PrerequisiteClosure + + +class Command(BaseCommand): + def handle(self, *args, **options): + skill_list = Skill.objects.all() + count = 0 + + for skill in skill_list: + count += 1 + updated = False + + max_level = skill.difficulty * 10 + if skill.position == "/": + max_level += 1 + + max_rank = 0 + age_boy_with_help = 0 + age_boy_without_help = 0 + age_boy_chained = 0 + age_boy_masterised = 0 + + age_girl_with_help = 0 + age_girl_without_help = 0 + age_girl_chained = 0 + age_girl_masterised = 0 + + print(str(count) + " - Traitement de " + str(skill.long_label)) + + breadcrumb = skill.breadcrumb() + for path in range(0, len(breadcrumb)): + tree = set( + PrerequisiteClosure.objects.filter(descendant=skill, path=path) + ) + + for position, ancestor in enumerate(breadcrumb[path]): + tree_path, _ = PrerequisiteClosure.objects.get_or_create( + ancestor=ancestor, + descendant=skill, + level=position, + path=path, + ) + max_level = max(max_level, position) + max_rank = max(max_rank, max_level, ancestor.rank + 1) + age_boy_with_help = max( + age_boy_with_help, ancestor.age_boy_with_help + ) + age_boy_without_help = max( + age_boy_without_help, ancestor.age_boy_without_help + ) + age_boy_chained = max(age_boy_chained, ancestor.age_boy_chained) + age_boy_masterised = max( + age_boy_masterised, ancestor.age_boy_masterised + ) + + age_girl_with_help = max( + age_girl_with_help, ancestor.age_girl_with_help + ) + age_girl_without_help = max( + age_girl_without_help, ancestor.age_girl_without_help + ) + age_girl_chained = max(age_girl_chained, ancestor.age_girl_chained) + age_girl_masterised = max( + age_girl_masterised, ancestor.age_girl_masterised + ) + + if tree_path in tree: + tree.remove(tree_path) + + for tree_path in tree: + tree_path.delete() + + if skill.level != max_level: + updated = True + skill.level = max_level + + if skill.rank != max_rank: + updated = True + skill.rank = max_rank + + if skill.age_boy_with_help < age_boy_with_help: + updated = True + skill.age_boy_with_help = age_boy_with_help + + if skill.age_boy_without_help < age_boy_without_help: + updated = True + skill.age_boy_without_help = age_boy_without_help + + if skill.age_boy_chained < age_boy_chained: + updated = True + skill.age_boy_chained = age_boy_chained + + if skill.age_boy_masterised < age_boy_masterised: + updated = True + skill.age_boy_masterised = age_boy_masterised + + if skill.age_girl_with_help < age_girl_with_help: + updated = True + skill.age_girl_with_help = age_girl_with_help + + if skill.age_girl_without_help < age_girl_without_help: + updated = True + skill.age_girl_without_help = age_girl_without_help + + if skill.age_girl_chained < age_girl_chained: + updated = True + skill.age_girl_chained = age_girl_chained + + if skill.age_girl_masterised < age_girl_masterised: + updated = True + skill.age_girl_masterised = age_girl_masterised + + if updated: + skill.save() diff --git a/jarvis/objective/management/commands/remove_rank_from_skill.py b/jarvis/objective/management/commands/remove_rank_from_skill.py new file mode 100644 index 0000000..5d2c341 --- /dev/null +++ b/jarvis/objective/management/commands/remove_rank_from_skill.py @@ -0,0 +1,13 @@ +""" supprime le rank des skill +""" + +from django.core.management.base import BaseCommand +from jarvis.objective.models import Skill + + +class Command(BaseCommand): + def handle(self, *args, **options): + skill_list = Skill.objects.all() + for skill in skill_list: + skill.rank = 0 + skill.save() diff --git a/jarvis/objective/management/commands/remove_space_from_skill_notation.py b/jarvis/objective/management/commands/remove_space_from_skill_notation.py new file mode 100644 index 0000000..6aa00a9 --- /dev/null +++ b/jarvis/objective/management/commands/remove_space_from_skill_notation.py @@ -0,0 +1,27 @@ +""" Retire les espaces contenu dans le champ "notation" et "simplified notation" de la class skill +dans le but de faciliter les recherches (skill_lookup). +""" + +from django.core.management.base import BaseCommand + +from jarvis.objective.models import Skill + + +class Command(BaseCommand): + def handle(self, *args, **options): + skill_list = Skill.objects.all() + for skill in skill_list: + need_update = False + notation = skill.notation.replace(" ", "") + simplified_notation = skill.simplified_notation.replace(" ", "") + + if notation != skill.notation: + need_update = True + skill.notation = notation + + if simplified_notation != skill.simplified_notation: + need_update = True + skill.simplified_notation = simplified_notation + + if need_update: + skill.save() diff --git a/jarvis/objective/migrations/0001_initial.py b/jarvis/objective/migrations/0001_initial.py new file mode 100644 index 0000000..6ff8394 --- /dev/null +++ b/jarvis/objective/migrations/0001_initial.py @@ -0,0 +1,274 @@ +# Generated by Django 3.2.8 on 2021-12-01 13:02 + +from django.db import migrations, models +import django.db.models.deletion +import jarvis.objective.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Educative", + 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", + ), + ), + ( + "long_label", + models.CharField(max_length=255, verbose_name="Long Name"), + ), + ( + "short_label", + models.CharField(max_length=255, verbose_name="Short Name"), + ), + ( + "difficulty", + models.DecimalField( + decimal_places=1, + default=0.0, + max_digits=3, + verbose_name="Difficulty", + ), + ), + ( + "level", + models.PositiveSmallIntegerField(default=0, verbose_name="Level"), + ), + ( + "rank", + models.PositiveSmallIntegerField(default=0, verbose_name="Rank"), + ), + ( + "age_boy", + models.PositiveSmallIntegerField( + blank=True, null=True, verbose_name="Boy's age" + ), + ), + ( + "age_girl", + models.PositiveSmallIntegerField( + blank=True, null=True, verbose_name="Girl's age" + ), + ), + ( + "educatives", + models.ManyToManyField( + blank=True, + related_name="educatives_of", + to="objective.Educative", + ), + ), + ( + "prerequisites", + models.ManyToManyField( + blank=True, + related_name="prerequisite_of", + to="objective.Educative", + ), + ), + ], + options={ + "verbose_name": "Educatif", + "verbose_name_plural": "Educatifs", + "ordering": ["long_label", "short_label"], + }, + ), + migrations.CreateModel( + name="TouchPosition", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "long_label", + models.CharField(max_length=30, verbose_name="Long label"), + ), + ( + "short_label", + models.CharField(max_length=15, verbose_name="Short label"), + ), + ( + "allowed_in_competition", + models.BooleanField(verbose_name="Allowed in competition"), + ), + ("is_default", models.BooleanField(verbose_name="Défault ?")), + ], + options={ + "verbose_name": "Landing", + "verbose_name_plural": "Landings", + "ordering": [ + "long_label", + "short_label", + "is_default", + "allowed_in_competition", + ], + }, + ), + migrations.CreateModel( + name="Routine", + fields=[ + ( + "educative_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="objective.educative", + ), + ), + ("active", models.BooleanField()), + ("is_competitive", models.BooleanField(default=False)), + ], + options={ + "verbose_name": "Routine", + "verbose_name_plural": "Routines", + }, + bases=("objective.educative",), + ), + migrations.CreateModel( + name="Skill", + fields=[ + ( + "educative_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="objective.educative", + ), + ), + ( + "position", + models.CharField( + choices=[ + ("0", "None"), + ("o", "Tuck"), + ("c", "Puck"), + ("<", "Pike"), + ("/", "Straight"), + ("//", "Straddle"), + ], + max_length=2, + ), + ), + ( + "rotation_type", + models.PositiveSmallIntegerField( + choices=[(0, "None"), (1, "Frontward"), (2, "Backward")], + verbose_name="Type de rotation", + ), + ), + ( + "rotation", + models.PositiveSmallIntegerField(verbose_name="1/4 de rotation"), + ), + ("twist", models.PositiveSmallIntegerField(verbose_name="1/2 Vrille")), + ("notation", models.CharField(max_length=25)), + ( + "simplified_notation", + models.CharField(max_length=25, verbose_name="Notation simplifiée"), + ), + ("is_competitive", models.BooleanField(default=False)), + ( + "departure", + models.ForeignKey( + default=jarvis.objective.models.get_default_position, + on_delete=django.db.models.deletion.CASCADE, + related_name="depart_of", + to="objective.touchposition", + verbose_name="Take-off position", + ), + ), + ( + "landing", + models.ForeignKey( + default=jarvis.objective.models.get_default_position, + on_delete=django.db.models.deletion.CASCADE, + related_name="landing_of", + to="objective.touchposition", + verbose_name="Landing position", + ), + ), + ], + options={ + "verbose_name": "Skill", + "verbose_name_plural": "Skills", + }, + bases=("objective.educative",), + ), + migrations.CreateModel( + name="RoutineSkill", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("rank", models.PositiveSmallIntegerField()), + ( + "routine", + models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + related_name="skill_links", + to="objective.routine", + ), + ), + ( + "skill", + models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + related_name="routine_links", + to="objective.skill", + ), + ), + ], + options={ + "ordering": ("rank",), + }, + ), + migrations.AddField( + model_name="routine", + name="jumps", + field=models.ManyToManyField( + through="objective.RoutineSkill", + to="objective.Skill", + verbose_name="routine", + ), + ), + ] diff --git a/jarvis/objective/migrations/0002_gymnasthasroutine.py b/jarvis/objective/migrations/0002_gymnasthasroutine.py new file mode 100644 index 0000000..ac596c1 --- /dev/null +++ b/jarvis/objective/migrations/0002_gymnasthasroutine.py @@ -0,0 +1,85 @@ +# Generated by Django 3.2.8 on 2021-12-05 12:01 + +import datetime +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0001_initial"), + ("objective", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="GymnastHasRoutine", + 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", + ), + ), + ( + "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="has_routine", + to="people.gymnast", + verbose_name="Gymnast", + ), + ), + ( + "routine", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="used_by_gymnast", + to="objective.routine", + verbose_name="Routine", + ), + ), + ], + options={ + "verbose_name": "Gymnast Has Routine", + "verbose_name_plural": "Gymnast Has Routines", + }, + ), + ] diff --git a/jarvis/objective/migrations/0003_delete_gymnasthasroutine.py b/jarvis/objective/migrations/0003_delete_gymnasthasroutine.py new file mode 100644 index 0000000..4f96af6 --- /dev/null +++ b/jarvis/objective/migrations/0003_delete_gymnasthasroutine.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.8 on 2021-12-05 14:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("objective", "0002_gymnasthasroutine"), + ] + + operations = [ + migrations.DeleteModel( + name="GymnastHasRoutine", + ), + ] diff --git a/jarvis/objective/migrations/0004_plan.py b/jarvis/objective/migrations/0004_plan.py new file mode 100644 index 0000000..3806a1d --- /dev/null +++ b/jarvis/objective/migrations/0004_plan.py @@ -0,0 +1,58 @@ +# Generated by Django 3.2.8 on 2021-12-17 08:16 + +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"), + ] + + 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"), + ), + ( + "educative", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="plan", + to="objective.educative", + verbose_name="Skill", + ), + ), + ( + "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")}, + }, + ), + ] diff --git a/jarvis/objective/migrations/0005_alter_plan_educative.py b/jarvis/objective/migrations/0005_alter_plan_educative.py new file mode 100644 index 0000000..0c51d93 --- /dev/null +++ b/jarvis/objective/migrations/0005_alter_plan_educative.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.8 on 2021-12-19 19:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0004_plan'), + ] + + operations = [ + migrations.AlterField( + model_name='plan', + name='educative', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plan', to='objective.educative', verbose_name='Educative'), + ), + ] diff --git a/jarvis/objective/migrations/0006_delete_plan.py b/jarvis/objective/migrations/0006_delete_plan.py new file mode 100644 index 0000000..bcdc708 --- /dev/null +++ b/jarvis/objective/migrations/0006_delete_plan.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.8 on 2022-01-05 15:31 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0005_alter_plan_educative'), + ] + + operations = [ + migrations.DeleteModel( + name='Plan', + ), + ] diff --git a/jarvis/objective/migrations/0007_auto_20220108_0956.py b/jarvis/objective/migrations/0007_auto_20220108_0956.py new file mode 100644 index 0000000..6864b9a --- /dev/null +++ b/jarvis/objective/migrations/0007_auto_20220108_0956.py @@ -0,0 +1,61 @@ +# Generated by Django 3.2.8 on 2022-01-08 09:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0006_delete_plan'), + ] + + operations = [ + migrations.RemoveField( + model_name='educative', + name='age_boy', + ), + migrations.RemoveField( + model_name='educative', + name='age_girl', + ), + migrations.AddField( + model_name='educative', + name='age_boy_chained', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Boy's age chained"), + ), + migrations.AddField( + model_name='educative', + name='age_boy_masterized', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Boy's age masterised"), + ), + migrations.AddField( + model_name='educative', + name='age_boy_with_help', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Boy's age with help"), + ), + migrations.AddField( + model_name='educative', + name='age_boy_without_help', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Boy's age without help"), + ), + migrations.AddField( + model_name='educative', + name='age_girl_chained', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Girl's age chained"), + ), + migrations.AddField( + model_name='educative', + name='age_girl_masterized', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Girl's age masterised"), + ), + migrations.AddField( + model_name='educative', + name='age_girl_with_help', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Girl's age with help"), + ), + migrations.AddField( + model_name='educative', + name='age_girl_without_help', + field=models.PositiveSmallIntegerField(choices=[(6, '6-7'), (7, '7-8'), (8, '8-9'), (9, '9-10'), (10, '10-11'), (11, '11-12'), (12, '12-13'), (13, '13-14'), (14, '14-15'), (15, '15-16'), (16, '16-17'), (17, '17+')], default=6, verbose_name="Girl's age without help"), + ), + ] diff --git a/jarvis/objective/migrations/0008_auto_20220108_1011.py b/jarvis/objective/migrations/0008_auto_20220108_1011.py new file mode 100644 index 0000000..097d23d --- /dev/null +++ b/jarvis/objective/migrations/0008_auto_20220108_1011.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2022-01-08 10:11 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0007_auto_20220108_0956'), + ] + + operations = [ + migrations.RenameField( + model_name='educative', + old_name='age_boy_masterized', + new_name='age_boy_masterised', + ), + migrations.RenameField( + model_name='educative', + old_name='age_girl_masterized', + new_name='age_girl_masterised', + ), + ] diff --git a/jarvis/objective/migrations/0009_auto_20220108_1956.py b/jarvis/objective/migrations/0009_auto_20220108_1956.py new file mode 100644 index 0000000..a2d0bff --- /dev/null +++ b/jarvis/objective/migrations/0009_auto_20220108_1956.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2022-01-08 19:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0008_auto_20220108_1011'), + ] + + operations = [ + migrations.AlterField( + model_name='skill', + name='position', + field=models.CharField(choices=[('0', 'none'), ('o', 'tuck'), ('c', 'puck'), ('<', 'pike'), ('/', 'straight'), ('//', 'straddle')], max_length=2), + ), + migrations.AlterField( + model_name='skill', + name='rotation_type', + field=models.PositiveSmallIntegerField(choices=[(0, 'none'), (1, 'frontward'), (2, 'backward')], verbose_name='Type de rotation'), + ), + ] diff --git a/jarvis/objective/migrations/0010_auto_20220109_1001.py b/jarvis/objective/migrations/0010_auto_20220109_1001.py new file mode 100644 index 0000000..a910763 --- /dev/null +++ b/jarvis/objective/migrations/0010_auto_20220109_1001.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.8 on 2022-01-09 10:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0009_auto_20220108_1956'), + ] + + operations = [ + migrations.RemoveField( + model_name='routine', + name='active', + ), + migrations.AddField( + model_name='routine', + name='is_active', + field=models.BooleanField(default=True), + ), + ] diff --git a/jarvis/objective/migrations/0011_auto_20220109_1016.py b/jarvis/objective/migrations/0011_auto_20220109_1016.py new file mode 100644 index 0000000..5f9bbc9 --- /dev/null +++ b/jarvis/objective/migrations/0011_auto_20220109_1016.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.8 on 2022-01-09 10:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0010_auto_20220109_1001'), + ] + + operations = [ + migrations.AlterField( + model_name='touchposition', + name='allowed_in_competition', + field=models.BooleanField(default=True, verbose_name='Allowed in competition'), + ), + migrations.AlterField( + model_name='touchposition', + name='is_default', + field=models.BooleanField(default=False, verbose_name='Défault ?'), + ), + ] diff --git a/jarvis/objective/migrations/0012_prerequisiteclosure.py b/jarvis/objective/migrations/0012_prerequisiteclosure.py new file mode 100644 index 0000000..331e562 --- /dev/null +++ b/jarvis/objective/migrations/0012_prerequisiteclosure.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.8 on 2022-01-12 20:30 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0011_auto_20220109_1016'), + ] + + operations = [ + migrations.CreateModel( + name='PrerequisiteClosure', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('level', models.PositiveIntegerField()), + ('path', models.PositiveIntegerField()), + ('ancestor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='descendants', to='objective.educative')), + ('descendant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ancestor', to='objective.educative')), + ], + options={ + 'unique_together': {('descendant', 'ancestor', 'level', 'path')}, + }, + ), + ] diff --git a/jarvis/objective/migrations/0013_alter_skill_position.py b/jarvis/objective/migrations/0013_alter_skill_position.py new file mode 100644 index 0000000..11b46e1 --- /dev/null +++ b/jarvis/objective/migrations/0013_alter_skill_position.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2022-01-18 09:36 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('objective', '0012_prerequisiteclosure'), + ] + + operations = [ + migrations.AlterField( + model_name='skill', + name='position', + field=models.CharField(choices=[('0', 'none'), ('o', 'tuck'), ('c', 'puck'), ('<', 'pike'), ('L', 'Half pike'), ('/', 'straight'), ('//', 'straddle')], max_length=2), + ), + ] diff --git a/jarvis/objective/migrations/0014_alter_skill_notation_alter_skill_rotation_and_more.py b/jarvis/objective/migrations/0014_alter_skill_notation_alter_skill_rotation_and_more.py new file mode 100644 index 0000000..3c8c543 --- /dev/null +++ b/jarvis/objective/migrations/0014_alter_skill_notation_alter_skill_rotation_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 4.1.1 on 2022-10-18 04:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("objective", "0013_alter_skill_position"), + ] + + operations = [ + migrations.AlterField( + model_name="skill", + name="notation", + field=models.CharField(max_length=10), + ), + migrations.AlterField( + model_name="skill", + name="rotation", + field=models.PositiveSmallIntegerField(verbose_name="¼ de rotation"), + ), + migrations.AlterField( + model_name="skill", + name="simplified_notation", + field=models.CharField(max_length=10, verbose_name="Notation simplifiée"), + ), + migrations.AlterField( + model_name="skill", + name="twist", + field=models.PositiveSmallIntegerField(verbose_name="½ Vrille"), + ), + ] diff --git a/jarvis/objective/migrations/0015_alter_skill_position.py b/jarvis/objective/migrations/0015_alter_skill_position.py new file mode 100644 index 0000000..ce1b64c --- /dev/null +++ b/jarvis/objective/migrations/0015_alter_skill_position.py @@ -0,0 +1,29 @@ +# Generated by Django 4.1.1 on 2022-11-01 14:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("objective", "0014_alter_skill_notation_alter_skill_rotation_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="skill", + name="position", + field=models.CharField( + choices=[ + ("0", "none"), + ("o", "tuck"), + ("c", "puck"), + ("<", "pike"), + ("L", "half pike"), + ("/", "straight"), + ("//", "straddle"), + ], + max_length=2, + ), + ), + ] diff --git a/jarvis/objective/migrations/__init__.py b/jarvis/objective/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/objective/models.py b/jarvis/objective/models.py new file mode 100644 index 0000000..891c1e7 --- /dev/null +++ b/jarvis/objective/models.py @@ -0,0 +1,442 @@ +from django.db import models +from django.db.models import Q, Count + +from jarvis.tools.models import Markdownizable + + +class Educative(Markdownizable): + """ + Classe `mère` educative. En trampoline tout est un éducatif : un saut, un enchainement, une + série de compétition, …. + + Level (skill) : + Toutes les figures appartiennent à un niveau. Un niveau peut contenir plusieurs figures. + Par défaut, le niveau d'une figure est son coéfficient de difficulté (exprimé en 10ème + pour avoir des nombres entiers) auquel on ajoute 1 pour les positions tendue. + + Exemples : + - saut groupé, saut carpé joint et saut écart ==> niveau 0 + - salto avant groupé, salto arrière groupé ==> niveau 5 + - salto avant carpé, barani groupé, salto arrière carpé ==> niveau 6 + - salto avant tendu, salto arrière tendu, barani tendu ==> niveau 7 + + En plus de cela, il y a une limite minimum : le niveau d’une figure ne peut pas être plus + petit que le niveau maximum de ses prérequis. + Le niveau, avec le rang, ont pour but d’aider les coaches à planifier l’évolution et l’ + apprentissage des figures les unes par rapport aux autres. + + Level (routine) : + Toutes les séries ont également un niveau. Par défaut le niveau d'une série est le niveau + maximum des figures qui composent la série. + + Rank (skill) : + Le rang permet, en plus du `level` (niveau), de classer les figures entre elles, de leur + donner un ordre (informatif). Le rang d’une figure est calculé par rapport aux prérequis et + au niveau : par défaut le rang d’une figure est le maximum entre le niveau maximum de ses + prérequis plus un et le niveau de la figure. + Le rang, avec le niveau, ont pour but d’aider les coaches à planifier l’évolution et + l’apprentissage des figures les unes par rapport aux autres. + + + """ + + AGE_CHOICES = ( + (6, "6-7"), + (7, "7-8"), + (8, "8-9"), + (9, "9-10"), + (10, "10-11"), + (11, "11-12"), + (12, "12-13"), + (13, "13-14"), + (14, "14-15"), + (15, "15-16"), + (16, "16-17"), + (17, "17+"), + ) + + class Meta: + verbose_name = "Educatif" + verbose_name_plural = "Educatifs" + ordering = ["long_label", "short_label"] # 'level', + + long_label = models.CharField(max_length=255, verbose_name="Long Name") + short_label = models.CharField(max_length=255, verbose_name="Short Name") + difficulty = models.DecimalField( + max_digits=3, decimal_places=1, verbose_name="Difficulty", default=0.000 + ) + level = models.PositiveSmallIntegerField(verbose_name="Level", default=0) + rank = models.PositiveSmallIntegerField(verbose_name="Rank", default=0) + educatives = models.ManyToManyField( + "self", related_name="educatives_of", blank=True, symmetrical=False + ) + prerequisites = models.ManyToManyField( + "self", related_name="prerequisite_of", blank=True, symmetrical=False + ) + + age_boy_with_help = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Boy's age with help", default=6 + ) + age_boy_without_help = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Boy's age without help", default=6 + ) + age_boy_chained = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Boy's age chained", default=6 + ) + age_boy_masterised = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Boy's age masterised", default=6 + ) + + age_girl_with_help = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Girl's age with help", default=6 + ) + age_girl_without_help = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Girl's age without help", default=6 + ) + age_girl_chained = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Girl's age chained", default=6 + ) + age_girl_masterised = models.PositiveSmallIntegerField( + choices=AGE_CHOICES, verbose_name="Girl's age masterised", default=6 + ) + # is_competitive = models.BooleanField(default=False) + + def __str__(self): + return "%s (%s - %s)" % ( + self.long_label, + self.short_label, + self.difficulty, + ) + + def breadcrumb(self, path=[]): + """ + Renvoie le breadcrumb pour l'édutatif courant. + Exemple : + >>> s = Skill.objects.get(pk=44) + >>> s.breadcrumb() + """ + + path = [self] + path + if self.prerequisites.all().count() == 0: + return [path] + + path_list = [] + for prerequisite in self.prerequisites.all(): + if prerequisite.id == self.id: + return [self] + new_paths = prerequisite.breadcrumb(path) + for new_path in new_paths: + path_list.append(new_path) + + return path_list + + +class PrerequisiteClosure(models.Model): + """ + Closure table de prérequis + """ + + class Meta: + unique_together = ("descendant", "ancestor", "level", "path") + + descendant = models.ForeignKey( + Educative, on_delete=models.CASCADE, related_name="ancestor" + ) + ancestor = models.ForeignKey( + Educative, on_delete=models.CASCADE, related_name="descendants" + ) + level = models.PositiveIntegerField() + path = models.PositiveIntegerField() + + def __str__(self): + return "%s -> %s (%s|%s)" % ( + self.ancestor.long_label, + self.descendant.long_label, + self.level, + self.path, + ) + + +class TouchPosition(models.Model): + """ + Classe représentant les différentes position d'arrivée/départ (landing position) en trampoline. + """ + + class Meta: + verbose_name = "Landing" + verbose_name_plural = "Landings" + ordering = ["long_label", "short_label", "is_default", "allowed_in_competition"] + + long_label = models.CharField(max_length=30, verbose_name="Long label") + short_label = models.CharField(max_length=15, verbose_name="Short label") + allowed_in_competition = models.BooleanField( + verbose_name="Allowed in competition", default=True + ) + is_default = models.BooleanField(verbose_name="Défault ?", default=False) + + def __str__(self): + return "%s" % (self.long_label) + + +def get_default_position(): + """ + Renvoie la position d'arrivée/départ par définie par défaut si elle existe. Sinon, renvoie None. + """ + try: + return TouchPosition.objects.get(is_default=True).id + except TouchPosition.DoesNotExist: + return None + + +class Skill(Educative): + """ + Classe représentant une figure (un mouvement, un saut acrobatique). Elle hérite de la classe + `Educative`. + """ + + # SELECT * FROM `objective_skill` + # WHERE educative_ptr_id NOT IN ( + # SELECT DISTINCT(from_educative_id) FROM `objective_educative_prerequisite` + # ) + # + # SELECT * FROM `objective_skill`, `objective_educative` + # WHERE `objective_educative`.id = `objective_skill`.educative_ptr_id + + class Meta: + verbose_name = "Skill" + verbose_name_plural = "Skills" + + POSITION_CHOICES = ( + ("0", "none"), + ("o", "tuck"), + ("c", "puck"), + ("<", "pike"), + ("L", "half pike"), + ("/", "straight"), + ("//", "straddle"), + ) + + ROTATION_CHOICES = ( + (0, "none"), + (1, "frontward"), + (2, "backward"), + ) + + position = models.CharField(max_length=2, choices=POSITION_CHOICES) + departure = models.ForeignKey( + TouchPosition, + related_name="depart_of", + default=get_default_position, + verbose_name="Take-off position", + on_delete=models.CASCADE, + ) + landing = models.ForeignKey( + TouchPosition, + related_name="landing_of", + default=get_default_position, + verbose_name="Landing position", + on_delete=models.CASCADE, + ) + rotation_type = models.PositiveSmallIntegerField( + choices=ROTATION_CHOICES, verbose_name="Type de rotation" + ) + rotation = models.PositiveSmallIntegerField(verbose_name="¼ de rotation") + twist = models.PositiveSmallIntegerField(verbose_name="½ Vrille") + notation = models.CharField(max_length=10) + simplified_notation = models.CharField( + max_length=10, verbose_name="Notation simplifiée" + ) + is_competitive = models.BooleanField(default=False) + # importance = models.PositiveSmallIntegerField(default = 1) + + def __str__(self): + return "%s (%s)" % (self.long_label, self.notation) + + +class Routine(Educative): + """ + Classe représentant une série (enchainement de plusieurs figures). Elle hérite de la classe + `Educative`. + """ + + class Meta: + verbose_name = "Routine" + verbose_name_plural = "Routines" + + jumps = models.ManyToManyField( + Skill, through="RoutineSkill", verbose_name="routine" + ) + is_active = models.BooleanField(default=True) + is_competitive = models.BooleanField(default=False) + + def __str__(self): + return "%s (%s)" % (self.long_label, self.short_label) + + def compute_informations(self): + """Calcule les informations (rank, niveau, ages, …) d'une routine.""" + rank = 0 + level = 0 + age_boy_with_help = 0 + age_girl_with_help = 0 + age_boy_without_help = 0 + age_girl_without_help = 0 + age_boy_chained = 0 + age_girl_chained = 0 + age_boy_masterised = 0 + age_girl_masterised = 0 + difficulty = 0 + is_competitive = True + + for skill_link in self.skill_links.all(): + skill = skill_link.skill + + difficulty += skill.difficulty + level = max(skill.level, level) + rank = max(skill.rank + 1, rank) + + if not skill.is_competitive: + is_competitive = False + + # Age boy computing + age_boy_with_help = max_even_if_none( + skill.age_boy_with_help, age_boy_with_help + ) + age_boy_without_help = max_even_if_none( + skill.age_boy_without_help, age_boy_without_help + ) + age_boy_chained = max_even_if_none(skill.age_boy_chained, age_boy_chained) + age_boy_masterised = max_even_if_none( + skill.age_boy_masterised, age_boy_masterised + ) + + # Age girl computing + age_girl_with_help = max_even_if_none( + skill.age_girl_with_help, age_girl_with_help + ) + age_girl_without_help = max_even_if_none( + skill.age_girl_without_help, age_girl_without_help + ) + age_girl_chained = max_even_if_none( + skill.age_girl_chained, age_girl_chained + ) + age_girl_masterised = max_even_if_none( + skill.age_girl_masterised, age_girl_masterised + ) + + if self.skill_links.all().count() != 10: + is_competitive = False + self.is_competitive = is_competitive + + self.difficulty = difficulty + self.level = max(self.level, level) + self.rank = max(self.rank, rank) + + self.age_boy_with_help = max(self.age_boy_with_help, age_boy_with_help) + self.age_boy_without_help = max(self.age_boy_without_help, age_boy_without_help) + self.age_boy_chained = max(self.age_boy_chained, age_boy_chained) + self.age_boy_masterised = max(self.age_boy_masterised, age_boy_masterised) + + self.age_girl_with_help = max(self.age_girl_with_help, age_girl_with_help) + self.age_girl_without_help = max( + self.age_girl_without_help, age_girl_without_help + ) + self.age_girl_chained = max(self.age_girl_chained, age_girl_chained) + self.age_girl_masterised = max(self.age_girl_masterised, age_girl_masterised) + + self.save() + + def contains_basic_jumps(self): + """ + Renvoie True si la série contient au moins un saut de base, False sinon. + """ + return self.skill_links.filter(skill__notation__in=["//", "<", "o"]).exists() + + def contains_basic_fall(self): + """ + Renvoie True si la série contient au moins un tomber de base, False sinon. + """ + return self.skill_links.filter( + skill__landing__long_label__in=["Assis", "Dos", "Ventre"] + ).exists() + + def contains_basic_salto(self): + """ + Renvoie True si la série contient au moins un salto/barani de base, + False sinon. + """ + return self.skill_links.filter( + Q(skill__notation__icontains=".41") | Q(skill__notation__icontains="4.-") + ).exists() + + def contains_basic_three_quarters(self): + """ + Renvoie True si la série contient au moins un 3/4 de salto, False sinon. + """ + return self.skill_links.filter( + Q(skill__notation__icontains=".3") | Q(skill__notation__icontains="3.") + ).exists() + + def contains_basic_twist(self): + """ + Renvoie True si la série contient au moins une vrille de base, False sinon. + """ + return self.skill_links.filter( + Q(skill__notation__icontains=".43") | Q(skill__notation__icontains="4.2") + ).exists() + + def contains_double(self): + """ + Renvoie True si la série contient au moins un double, False sinon. + """ + return self.skill_links.filter( + Q(skill__notation__icontains=".8") | Q(skill__notation__icontains="8.") + ).exists() + + def contains_triple(self): + """ + Renvoie True si la série contient au moins un triple, False sinon. + """ + return self.skill_links.filter( + Q(skill__notation__icontains=".12") | Q(skill__notation__icontains="12.") + ).exists() + + +class RoutineSkill(models.Model): + """ + Classe de liaison permettant de liée une figure à une série. (relation n-n) + """ + + class Meta: + ordering = ("rank",) + + routine = models.ForeignKey( + Routine, on_delete=models.CASCADE, default=None, related_name="skill_links" + ) + skill = models.ForeignKey( + Skill, on_delete=models.CASCADE, default=None, related_name="routine_links" + ) + rank = models.PositiveSmallIntegerField() + + def __str__(self): + return "%s - %s : %s" % ( + self.rank, + self.routine.short_label, + self.skill.short_label, + ) + + +def max_even_if_none(value_1, value_2): + """ + Renvoie le maximum de deux valeurs même si l'une des deux vaut None. + """ + if value_1 is not None and value_2 is not None: + if value_1 > value_2: + return value_1 + else: + return value_2 + elif value_1 is not None: + return value_1 + elif value_2 is not None: + return value_2 + else: + return 0 diff --git a/jarvis/objective/templates/routines/compose.html b/jarvis/objective/templates/routines/compose.html new file mode 100644 index 0000000..9be54e7 --- /dev/null +++ b/jarvis/objective/templates/routines/compose.html @@ -0,0 +1,135 @@ +{% extends "base.html" %} + + + + +{% block content %} +
    +
    +
    +
    +

    Compose Routine : {{ routine.long_label }}

    +
    +
    +
      + {% for link in skill_link_list %} +
    1. +
      + +
      + {{ link.skill.notation }} +
      +
      +
    2. + {% endfor %} +
    3. +
      + +
      + +
      +
      +
    4. +
    +
    + +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/objective/templates/routines/create.html b/jarvis/objective/templates/routines/create.html new file mode 100644 index 0000000..c26dfb5 --- /dev/null +++ b/jarvis/objective/templates/routines/create.html @@ -0,0 +1,61 @@ +{% extends "base.html" %} + + + + + +{% block content %} +
    +
    +
    +
    +

    {% if routine_id %}Edit{% else %}Add{% endif %} Routine

    +
    +
    +
    + {% csrf_token %} + {% for hidden in form.hidden_fields %} + {{ hidden }} + {% endfor %} +
    + +
    + {{ form.long_label }} {% if form.long_label.errors %} + {% for error in form.long_label.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + +
    + +
    + {{ form.short_label }} {% if form.short_label.errors %}{% for error in form.short_label.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + +{% endblock %} diff --git a/jarvis/objective/templates/routines/details.html b/jarvis/objective/templates/routines/details.html new file mode 100644 index 0000000..3a05ab0 --- /dev/null +++ b/jarvis/objective/templates/routines/details.html @@ -0,0 +1,89 @@ +{% extends "base.html" %} +{% load has_group %} + +{% block page_title %}{{ routine.short_label }}{% endblock %} + +{% block content %} +
    +
    +
    +
    +

    {{ routine.short_label }}

    +
    {{ routine.long_label }}
    +
    +
    + {% if skill_link_list %} + + {% for link in skill_link_list %} + + + + + + + {% endfor %} + + + + +
    {{ link.skill.notation }}{{ link.skill.short_label }}{% if link.skill.difficulty != 0.0 %}{{ link.skill.difficulty }}{% endif %}
    {{ routine.difficulty }}
    + + {% else %} +

    No skill defined for this routine.

    + {% endif %} + +
    + + {% if routine.informations %} +
    +
    +

    Informations

    + + + {{ routine.to_markdown | safe }} + +
    + {% endif %} + +
    +
    +
    + + +{% endblock %} \ No newline at end of file diff --git a/jarvis/objective/templates/routines/list.html b/jarvis/objective/templates/routines/list.html new file mode 100644 index 0000000..9bfe4d9 --- /dev/null +++ b/jarvis/objective/templates/routines/list.html @@ -0,0 +1,101 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +

    Routines' Listing {% if gymnast_id %}for {{ gymnast }}{% endif %}

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    +
    +
    + {% if routine_list %} + + + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + + + + + {% for routine in routine_list %} + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + + + {% endfor %} + +
     Long Label Short LabelCompetitive ?Diff.LevelRank
    + + + + {{ routine.long_label }}{{ routine.short_label }} + {% if routine.is_competitive %} + {% else %} + {% endif %} + {{ routine.age_girl_masterised }}{{ routine.age_boy_masterised }}{{ routine.difficulty }}{{ routine.level }}{{ routine.rank }}
    + {% else %} +

    There are no events corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/objective/templates/skills/create.html b/jarvis/objective/templates/skills/create.html new file mode 100644 index 0000000..4f6a171 --- /dev/null +++ b/jarvis/objective/templates/skills/create.html @@ -0,0 +1,28 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    {% if skill_id %}Edit{% else %}Add{% endif %} Skill informations

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.informations }} +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +{% endblock %} diff --git a/jarvis/objective/templates/skills/details.html b/jarvis/objective/templates/skills/details.html new file mode 100644 index 0000000..f675c3d --- /dev/null +++ b/jarvis/objective/templates/skills/details.html @@ -0,0 +1,127 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +

    {{ skill.short_label }}

    +

    {{ skill.notation }}

    +
    +
    +
    +
    + +
    +
    +

    Notation : {{ skill.notation }}

    +
    +
    +

    Difficulty : {{ skill.difficulty }}

    +
    +
    +

    Level : {{ skill.level }}

    +
    +
    +

    Rank : {{ skill.level }}

    +
    +
    +
    + +
    + {% if skill.prerequisites.all or skill.educatives.all %} +
    +
    +

    Prerequisites

    +
      + {% if skill.prerequisites.all %} + {% for prerequisites in skill.prerequisites.all %} +
    • {{ prerequisites.short_label }}
    • + {% endfor %} + {% else %} +

      No prerequisites defined.

      + {% endif %} +
    +
    +
    +

    Educatives

    +
      + {% if skill.educatives.all %} + {% for educatives in skill.educatives.all %} +
    • {{ educatives.short_label }}
    • + {% endfor %} + {% else %} +

      No educative defined.

      + {% endif %} +
    +
    +
    + {% endif %} +
    + Learning Line
    +
    +
    +
    +

    From {{ skill.departure }}, + {% if skill.rotation %} + {{ skill.rotation }} quart of {{ skill.get_rotation_type_display }} rotation + {% else %} + straight jump + {% endif %} + {% if skill.twist %} + with {{ skill.twist }} half-twist + {% endif %} + {% if skill.get_position_display %} + in a {{ skill.get_position_display }} position + {% endif %} + , landing on {{ skill.landing }}. +

    +
    + {% if skill.informations %} + {{ skill.to_markdown | safe }} + {% else %} +

    No more informations provided for this skill.

    + {% endif %} +
    +
    +
    + +
    + +{% endblock %} + +{% block footerscript %} + +{% endblock %} diff --git a/jarvis/objective/templates/skills/learning_line.html b/jarvis/objective/templates/skills/learning_line.html new file mode 100644 index 0000000..7874425 --- /dev/null +++ b/jarvis/objective/templates/skills/learning_line.html @@ -0,0 +1,193 @@ +{% extends "base.html" %} +{% load static %} +{% block header %} + +{% endblock %} + +{% block content %} +
    +
    +

    {{ skill.short_label }}

    +

    {{ skill.notation }}

    +
    +
    +
    + +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + + +{% endblock %} diff --git a/jarvis/objective/templates/skills/list.html b/jarvis/objective/templates/skills/list.html new file mode 100644 index 0000000..1f45d1b --- /dev/null +++ b/jarvis/objective/templates/skills/list.html @@ -0,0 +1,87 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +

    Skills Listing

    +
    +
    +
    + {% if skill_list %} + + + + + + + + + + + + + + + {% for skill in skill_list %} + + + + + + + + + + + {% endfor %} + +
     Long Label Short LabelNotationDiff.LevelRank
     {{ skill.long_label }}{{ skill.short_label }}{{ skill.notation }}{{ skill.age_girl_without_help }}{{ skill.age_boy_without_help }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    + {% else %} +

    There are no skills corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block pagination %} + {% if skills.has_previous and skills.has_next %} +
    +
    + {% if skills.has_previous %} + + {% endif %} + + {% if skills.has_next %} + + {% endif %} +
    +
    + {% endif %} +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/objective/tests.py b/jarvis/objective/tests.py new file mode 100644 index 0000000..1abca22 --- /dev/null +++ b/jarvis/objective/tests.py @@ -0,0 +1,84 @@ +from django.test import TestCase +from jarvis.objective.models import Educative + + +class EducativeTestCase(TestCase): + def setUp(self): + """ + Structure finale : + 1 -> 2 + 1 -> 6 + 3 -> 4 -> 5 -> 6 -> 7 + 1 -> 2 -> 9 + 3 -> 4 -> 5 -> 6 -> 7 -> 9 + """ + # 1 et 3 Eductative sans pre-requis + educ_1 = Educative.objects.create( + long_label="1/2 vrille", difficulty=0.1, level=1, rank=1 + ) + educ_3 = Educative.objects.create( + long_label="4 pattes", difficulty=0.1, level=1, rank=1 + ) + + educ_2 = Educative.objects.create( + long_label="tour", difficulty=0.2, level=1, rank=2 + ) + educ_2.prerequisites.add(educ_1) + + educ_4 = Educative.objects.create( + long_label="Ventre", difficulty=0.1, level=1, rank=1 + ) + educ_4.prerequisites.add(educ_3) + educ_5 = Educative.objects.create( + long_label="3/4 Avant /", difficulty=0.3, level=1, rank=1 + ) + educ_5.prerequisites.add(educ_4) + educ_6 = Educative.objects.create( + long_label="Avant /", difficulty=0.6, level=1, rank=1 + ) + educ_6.prerequisites.add(educ_5) + educ_7 = Educative.objects.create( + long_label="Barani /", difficulty=0.6, level=1, rank=1 + ) + educ_7.prerequisites.add(educ_6) + educ_7.prerequisites.add(educ_1) + # educ_8 = Educative.objects.create(long_label="3/4 Avant vrille", difficulty=0.5, level=1, rank=1) + # educ_8.prerequisites.add(educ_6) + # educ_8.prerequisites.add(educ_2) + educ_9 = Educative.objects.create( + long_label="Rudy", difficulty=0.8, level=1, rank=1 + ) + educ_9.prerequisites.add(educ_2) + educ_9.prerequisites.add(educ_7) + + def test_educative_breadcrumb(self): + + # Cas "None" d'un objet qui n'a pas de prerequis + educ_1 = Educative.objects.get(long_label="1/2 vrille") + self.assertEqual(educ_1.breadcrumb(), [[educ_1]]) + + # Cas simplise : un objet avec un seul encetre + educ_2 = Educative.objects.get(long_label="tour") + self.assertEqual(educ_2.breadcrumb(), [[educ_1, educ_2]]) + + # Cas simple : chaque skill n'a qu'un seul encetre + educ_3 = Educative.objects.get(long_label="4 pattes") + educ_4 = Educative.objects.get(long_label="Ventre") + educ_5 = Educative.objects.get(long_label="3/4 Avant /") + educ_6 = Educative.objects.get(long_label="Avant /") + educ_7 = Educative.objects.get(long_label="Barani /") + self.assertEqual( + educ_7.breadcrumb(), + [[educ_1, educ_7], [educ_3, educ_4, educ_5, educ_6, educ_7]], + ) + + # Cas plus complexe : l'éduc 8 a deux encetres qui ont chacun une lignée d'encêtres + educ_9 = Educative.objects.get(long_label="Rudy") + self.assertEqual( + educ_9.breadcrumb(), + [ + [educ_1, educ_7, educ_9], + [educ_3, educ_4, educ_5, educ_6, educ_7, educ_9], + [educ_1, educ_2, educ_9], + ], + ) diff --git a/jarvis/objective/tests_models.py b/jarvis/objective/tests_models.py new file mode 100644 index 0000000..765911f --- /dev/null +++ b/jarvis/objective/tests_models.py @@ -0,0 +1,111 @@ +from django.test import TestCase +from jarvis.objective.models import ( + Educative, + Skill, + max_even_if_none, + TouchPosition, + Routine, + RoutineSkill, +) + + +class ToolsModels(TestCase): + def setUp(self): + """ """ + touch_position_1 = TouchPosition.objects.create( + long_label="debout", short_label="debout", is_default=True + ) + touch_position_2 = TouchPosition.objects.create( + long_label="quadrupédique", short_label="4 patte", is_default=False + ) + educ_1 = Educative.objects.create( + long_label="1/2 vrille", short_label="1/2T", difficulty=0.1, level=1, rank=1 + ) + educ_3 = Educative.objects.create( + long_label="4 pattes", short_label="4P", difficulty=0.1, level=1, rank=1 + ) + skill_1 = Skill.objects.create( + long_label="Salto arrière groupé", + short_label="Arrière o", + rotation_type=2, + notation="4.-o", + rotation=4, + twist=0, + departure=touch_position_1, + landing=touch_position_1, + difficulty=0.5, + ) + skill_2 = Skill.objects.create( + long_label="Barani groupé", + short_label="Barani o", + rotation_type=2, + notation=".41o", + rotation=4, + twist=1, + departure=touch_position_1, + landing=touch_position_1, + difficulty=0.5, + ) + routine_1 = Routine.objects.create( + long_label="BOT Bronze", + short_label="Bronze", + is_active=True, + is_competitive=True, + ) + routine_skill_1 = RoutineSkill.objects.create( + routine=routine_1, skill=skill_1, rank=1 + ) + routine_skill_1 = RoutineSkill.objects.create( + routine=routine_1, skill=skill_2, rank=2 + ) + + def test_max_even_if_none(self): + self.assertEqual(max_even_if_none(None, None), 0) + self.assertEqual(max_even_if_none(1, None), 1) + self.assertEqual(max_even_if_none(1, 2), 2) + self.assertEqual(max_even_if_none(None, 2), 2) + + def test_touch_position_to_string(self): + touch_position = TouchPosition.objects.get(long_label="debout") + self.assertEqual(str(touch_position), touch_position.long_label) + + def test_touch_position_get_default_position(self): + # result = + pass + + def test_educative_to_string(self): + educ_1 = Educative.objects.get(long_label="1/2 vrille") + self.assertEqual( + str(educ_1), + educ_1.long_label + + " (" + + educ_1.short_label + + " - " + + str(educ_1.difficulty) + + ")", + ) + + def test_skill_to_string(self): + skill_1 = Skill.objects.get(long_label="Salto arrière groupé") + self.assertEqual( + str(skill_1), skill_1.long_label + " (" + skill_1.notation + ")" + ) + + def test_routine_to_string(self): + routine_1 = Routine.objects.get(long_label="BOT Bronze") + self.assertEqual( + str(routine_1), routine_1.long_label + " (" + routine_1.short_label + ")" + ) + + def test_routine_skill_to_string(self): + routine_1 = Routine.objects.get(long_label="BOT Bronze") + skill_1 = Skill.objects.get(long_label="Salto arrière groupé") + routine_skill_1 = RoutineSkill.objects.get(routine=routine_1, skill=skill_1) + self.assertEqual( + str(routine_skill_1), + str(routine_skill_1.rank) + + " - " + + routine_skill_1.routine.short_label + + " : " + + routine_skill_1.skill.short_label, + ) diff --git a/jarvis/objective/tests_tools.py b/jarvis/objective/tests_tools.py new file mode 100644 index 0000000..88ee1e0 --- /dev/null +++ b/jarvis/objective/tests_tools.py @@ -0,0 +1,139 @@ +from django.test import TestCase +from jarvis.objective.models import Skill, TouchPosition +from jarvis.objective.tools import ( + nb_skill_by_type, + nb_skill_lte_type, + compute_completude, + compute_statistics_by_type, +) + + +class ToolsTestCase(TestCase): + def setUp(self): + """ """ + departure_and_landing, _ = TouchPosition.objects.get_or_create( + long_label="debout", short_label="debout" + ) + + skill_1 = Skill.objects.create( + long_label="1/2 vrille", + difficulty=0.1, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=1, + rank=1, + ) + skill_3 = Skill.objects.create( + long_label="4 pattes", + difficulty=0.1, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=1, + rank=1, + ) + skill_2 = Skill.objects.create( + long_label="tour", + difficulty=0.2, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=2, + rank=2, + ) + skill_4 = Skill.objects.create( + long_label="Ventre", + difficulty=0.1, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=2, + rank=2, + ) + skill_5 = Skill.objects.create( + long_label="3/4 Avant /", + difficulty=0.3, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=3, + rank=3, + ) + skill_6 = Skill.objects.create( + long_label="Avant /", + difficulty=0.6, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=3, + rank=3, + ) + skill_7 = Skill.objects.create( + long_label="Barani /", + difficulty=0.6, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=4, + rank=4, + ) + skill_8 = Skill.objects.create( + long_label="3/4 Avant vrille", + difficulty=0.5, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=4, + rank=4, + ) + skill_9 = Skill.objects.create( + long_label="Rudy", + difficulty=0.8, + departure=departure_and_landing, + landing=departure_and_landing, + position="0", + rotation_type="0", + rotation="0", + twist="1", + level=4, + rank=4, + ) + + def test_nb_skill_lte_type(self): + # Cas "None" + self.assertEqual(nb_skill_lte_type(2, "unkown_string"), None) + # Cas normal + self.assertEqual(nb_skill_lte_type(2, "level"), 4) + self.assertEqual(nb_skill_lte_type(2, "rank"), 4) + self.assertEqual(nb_skill_lte_type(4, "level"), 9) + + def test_compute_completude(self): + self.assertEqual(compute_completude(10, 1, 4), ("10%", 0)) + self.assertEqual(compute_completude(10, 1, 4), ("10%", 0)) + self.assertEqual(compute_completude(10, 5, 4), ("50%", 2)) + self.assertEqual(compute_completude(10, 10, 4), ("100%", 4)) diff --git a/jarvis/objective/tests_urls.py b/jarvis/objective/tests_urls.py new file mode 100644 index 0000000..f41a3a3 --- /dev/null +++ b/jarvis/objective/tests_urls.py @@ -0,0 +1,55 @@ +from django.test import TestCase +from django.urls import reverse, resolve + + +class URLTestCase(TestCase): + def test_skill_url(self): + self.assertEqual( + resolve("/objective/skill/level/exact/1/").view_name, "skill_listing_by_key" + ) + self.assertEqual( + resolve("/objective/skill/rank/lte/1/").view_name, "skill_listing_by_key" + ) + self.assertEqual( + resolve("/objective/skill/difficulty/gt/1/").view_name, + "skill_listing_by_key", + ) + + self.assertEqual(resolve("/objective/skill/lookup/").view_name, "skill_lookup") + self.assertEqual(resolve("/objective/skill/search/").view_name, "skill_search") + self.assertEqual(resolve("/objective/skill/1/").view_name, "skill_details") + self.assertEqual(resolve("/objective/skill/1/tree/").view_name, "skill_tree") + self.assertEqual( + resolve("/objective/skill/prerequisiteless/").view_name, + "skill_without_prerequisite", + ) + self.assertEqual(resolve("/objective/skill/").view_name, "skill_list") + + def test_routine_url(self): + self.assertEqual( + resolve("/objective/routine/lookup/").view_name, "routine_lookup" + ) + self.assertEqual( + resolve("/objective/routine/search/").view_name, "routine_search" + ) + self.assertEqual(resolve("/objective/routine/add/").view_name, "routine_create") + self.assertEqual( + resolve("/objective/routine/edit/1/").view_name, "routine_update" + ) + self.assertEqual(resolve("/objective/routine/1/").view_name, "routine_details") + self.assertEqual( + resolve("/objective/routine/compose/1/").view_name, "compose_routine" + ) + self.assertEqual( + resolve("/objective/routine/compose/link_skill/").view_name, + "link_skill_to_routine", + ) + self.assertEqual( + resolve("/objective/routine/compose/unlink_skill/").view_name, + "unlink_skill_from_routine", + ) + self.assertEqual(resolve("/objective/routine/").view_name, "routine_list") + self.assertEqual( + resolve("/objective/routine/gymnast/1/").view_name, + "routine_list_for_gymnast", + ) diff --git a/jarvis/objective/tools.py b/jarvis/objective/tools.py new file mode 100644 index 0000000..cf328a7 --- /dev/null +++ b/jarvis/objective/tools.py @@ -0,0 +1,162 @@ +from django.db.models import Count +from jarvis.objective.models import Skill + + +def nb_skill_by_type(max_value, desired_type="level"): + """ + Renvoie le nombre de Skill qui ont un niveau/rang inférieur ou égal à celui passé en paramètre, + groupé par niveau/rank. + + Args: + max_value (int): valeur du niveau/rang maximum désiré. + desired_type (string): type ("level" ou "rank") des Skills désirés. + + Returns: + QuerySet + + Example: + >>> from jarvis.objective.tools import nb_skill_by_type + >>> nb_skill_by_type(2) + + """ + if desired_type != "level" and desired_type != "rank": + return None + + nb_skill_by_level = ( + Skill.objects.values(desired_type) + .filter(level__lte=max_value) + .order_by(desired_type) + .annotate(nb_skill=Count("id")) + ) + return nb_skill_by_level + + +def nb_skill_lte_type(max_value, desired_type="level"): + """ + Renvoie le nombre de Skill qui ont un niveau inférieur ou égal à un niveau/rang passé en + paramètre. + + Args: + max_value (int): valeur du niveau/rang maximum désiré. + desired_type (string): type ("level" ou "rank") des Skills désirés. + + Returns: + int + + Example: + >>> from jarvis.objective.tools import nb_skill_lte_type + >>> nb_skill_lte_type(2) + 62 + """ + if desired_type != "level" and desired_type != "rank": + return None + + if desired_type == "level": + nb_skill = Skill.objects.filter(level__lte=max_value) + else: + nb_skill = Skill.objects.filter(rank__lte=max_value) + + return nb_skill.count() + + +def compute_completude(total_skill, gymnast_nb_known_skills, max_level_skill): + """ + Calcule la complétude et le niveau estimé d'un gymnaste. + + Args: + total_skill (int): nombre totale de skill. + gymnast_nb_known_skills (int): nombre de skill connus du gymnaste. + max_skill (int): nombre maximum de skill. + + Returns: + string: pourcentage de skill connus. + int: niveau estimé du gymnaste. + + Example: + >>> from jarvis.objective.tools import compute_completude + >>> compute_completude(62, 12, 2) + ('19%', 0) + """ + if total_skill: + percentage = gymnast_nb_known_skills / total_skill + completude = "%s%%" % (int(percentage * 100)) + evaluated_level = int(max_level_skill * percentage) + else: + completude = None + evaluated_level = None + return completude, evaluated_level + + +def compute_statistics_by_type( + nb_skill_by_type, nb_known_skill_by_type, desired_type="level" +): + """ + Calcule les statistiques par niveau. + + Args: + nb_skill_by_type (QuerySet): nombre de skill par niveau/rang. + + ou + + + nb_known_skill_by_type (QuerySet): nombre de skill connus par niveau/rang. + + ou + + + desired_type (string): type ("level" ou "rank") des Skills désirés. + + Returns: + array of list (todo: why not a (list of) dictionnary?) + + Examples: + >>> from jarvis.objective.tools import nb_skill_by_type, compute_statistics_by_type + >>> gymnast = Gymnast.objects.get(pk=1) + >>> nb_skill_by_level = nb_skill_by_type(1, "level") + >>> nb_known_skill_by_level = gymnast.nb_known_skill_by_level() + >>> compute_statistics_by_type(nb_skill_by_level, nb_known_skill_by_level, "level") + [(0, 23, 15, 65), (1, 20, 5, 25), …] + + >>> from jarvis.objective.tools import nb_skill_by_type, compute_statistics_by_type + >>> gymnast = Gymnast.objects.get(pk=1) + >>> nb_skill_by_rank = nb_skill_by_type(1, "rank") + >>> nb_known_skill_by_rank = gymnast.nb_known_skill_by_rank() + >>> compute_statistics_by_type(nb_skill_by_rank, nb_known_skill_by_rank, "rank") + [(1, 5, 2, 40), (2, 5, 4, 80), …] + """ + + if desired_type != "level" and desired_type != "rank": + return None + + j = 0 + percentages = [] + for skill in nb_skill_by_type: + + # les deux lignes (nécessaires pour les skill "rank") ci-dessous devraient partir + if desired_type == "rank" and j >= nb_known_skill_by_type.count(): + break + + tmp = None + if skill[desired_type] == nb_known_skill_by_type[j][desired_type]: + if skill["nb_skill"] != nb_known_skill_by_type[j]["nb_known_skill"]: + tmp = ( + skill[desired_type], + skill["nb_skill"], + nb_known_skill_by_type[j]["nb_known_skill"], + int( + ( + nb_known_skill_by_type[j]["nb_known_skill"] + / skill["nb_skill"] + ) + * 100 + ), + ) + + j += 1 + else: + tmp = (skill[desired_type], skill["nb_skill"], 0, 0) + + if tmp: + percentages.append(tmp) + + return percentages diff --git a/jarvis/objective/urls.py b/jarvis/objective/urls.py new file mode 100644 index 0000000..b053a64 --- /dev/null +++ b/jarvis/objective/urls.py @@ -0,0 +1,50 @@ +from django.urls import path, re_path +from . import views + + +urlpatterns = [ + re_path( + r"skill/(?P(level|rank|difficulty))/(?P[\w]+)/(?P[\w]+)/", + views.skill_listing, + name="skill_listing_by_key", + ), + path(r"skill/lookup/", views.skill_lookup, name="skill_lookup"), + path(r"skill/search/", views.skill_listing, name="skill_search"), + path(r"skill//", views.skill_details, name="skill_details"), + path( + r"skill//edit/", + views.skill_create_or_update, + name="skill_update" + ), + path(r"skill//tree/", views.skill_tree, name="skill_tree"), + path( + r"skill/prerequisiteless/", + views.skill_without_prerequisite_listing, + name="skill_without_prerequisite" + ), + path(r"skill/", views.skill_listing, name="skill_list"), + + # Routines + path(r"routine/lookup/", views.routine_lookup, name="routine_lookup"), + path(r"routine/search/", views.routine_listing, name="routine_search"), + path(r"routine/add/", views.routine_create_or_update, name="routine_create"), + path( + r"routine/edit//", + views.routine_create_or_update, + name="routine_update" + ), + path(r"routine//", views.routine_details, name="routine_details"), + path(r"routine/compose//", views.compose_routine, name="compose_routine"), + path( + r"routine/compose/link_skill/", + views.link_skill_to_routine, + name="link_skill_to_routine", + ), + path( + r"routine/compose/unlink_skill/", + views.unlink_skill_from_routine, + name="unlink_skill_from_routine", + ), + path(r"routine/", views.routine_listing, name="routine_list"), + path(r"routine/gymnast//", views.routine_listing, name="routine_list_for_gymnast"), +] diff --git a/jarvis/objective/views.py b/jarvis/objective/views.py new file mode 100644 index 0000000..61bee0d --- /dev/null +++ b/jarvis/objective/views.py @@ -0,0 +1,322 @@ +from django.contrib.auth.decorators import login_required +from django.db.models import Q +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse + +from jarvis.people.models import Gymnast + +from .forms import ( + SkillForm, + RoutineForm, + RoutineSkillForm, +) +from .models import ( + Skill, + Routine, + RoutineSkill, + PrerequisiteClosure, +) + + +@login_required +@require_http_methods(["POST"]) +def skill_lookup(request): + """ + Récupère la liste des skill à la volée suivant des caractères de + recherche entrés. (min 3 caractères) + """ + results = [] + pattern = request.POST.get("pattern", None) + + # Ignore queries shorter than length 2 + if pattern is not None and len(pattern) > 2: + model_results = Skill.objects.filter( + Q(short_label__icontains=pattern) + | Q(long_label__icontains=pattern) + | Q(notation__icontains=pattern) + ) + results = [ + {"ID": x.id, "Name": str(x), "Notation": x.notation} for x in model_results + ] + + return JsonResponse(results, safe=False) + + +@login_required +@require_http_methods(["GET"]) +def skill_without_prerequisite_listing(request): + """ + Récupère la liste des skills suivant un pattern si celui-ci est définit. + """ + skill_list = Skill.objects.filter(prerequisites=None) + context = {"skill_list": skill_list} + return render(request, "skills/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def skill_listing(request, field=None, expression=None, value=None, level=None): + """ + Récupère la liste des skills suivant un pattern si celui-ci est définit. + """ + + pattern = None + + if not field or not value or not expression: + pattern = request.GET.get("pattern", None) + + if pattern: + skill_list = Skill.objects.filter( + Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern) + ) + elif field and expression and value: + kwargs = {"{0}__{1}".format(field, expression): value} + skill_list = Skill.objects.filter(**kwargs) + elif level is not None: + skill_list = Skill.objects.filter(level=level) + else: + skill_list = Skill.objects.all() + + context = {"skill_list": skill_list} + return render(request, "skills/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def skill_tree(request, skill_id): + """ """ + skill = get_object_or_404(Skill, pk=skill_id) + node_dict = {} + skill_closure = PrerequisiteClosure.objects.filter(descendant=skill) + for closure in skill_closure: + node_dict[closure.ancestor] = closure.ancestor.prerequisites.all() + + context = {"skill": skill, "node_dict": node_dict} + return render(request, "skills/learning_line.html", context) + + +@login_required +@require_http_methods(["GET"]) +def skill_details(request, skill_id): + """Récupère toutes les informations d'un skill. + + La méthode en profite pour vérifier les champs level, rank, age_boy et age_girl + par rapport aux pré-requis. + + :param skill_id: id d'un `skill` + :type skill_id: int + + :return: skill + """ + skill = get_object_or_404(Skill, pk=skill_id) + + for prerequisite in skill.prerequisites.all(): + skill.level = max(prerequisite.level + 1, skill.level) + skill.rank = max(prerequisite.rank + 1, skill.rank) + + skill.age_boy_with_help = max( + skill.age_boy_with_help, prerequisite.age_boy_with_help + ) + skill.age_boy_without_help = max( + skill.age_boy_without_help, prerequisite.age_boy_without_help + ) + skill.age_boy_chained = max(skill.age_boy_chained, prerequisite.age_boy_chained) + skill.age_boy_masterised = max( + skill.age_boy_masterised, prerequisite.age_boy_masterised + ) + + skill.age_girl_with_help = max( + skill.age_girl_with_help, prerequisite.age_girl_with_help + ) + skill.age_girl_without_help = max( + skill.age_girl_without_help, prerequisite.age_girl_without_help + ) + skill.age_girl_chained = max( + skill.age_girl_chained, prerequisite.age_girl_chained + ) + skill.age_girl_masterised = max( + skill.age_girl_masterised, prerequisite.age_girl_masterised + ) + + skill.save() + + context = {"skill": skill} + return render(request, "skills/details.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def skill_create_or_update(request, skill_id=None): + """Création ou modification d'un saut. + + Args: + skill_id (int): identifiant d'un object de classe . + """ + + if skill_id: + skill = get_object_or_404(Skill, pk=skill_id) + else: + skill = None + + if request.method == "POST": + form = SkillForm(request.POST, instance=skill) + + if form.is_valid(): + skill = form.save() + return HttpResponseRedirect(reverse("skill_details", args=(skill.pk,))) + else: + return render(request, "skill/create.html", {"form": form}) + + form = SkillForm(instance=skill) + context = {"form": form, "skill_id": skill_id} + return render(request, "skills/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def routine_listing(request, gymnast_id=None): + """Récupère la liste des routines suivant un pattern si celui-ci est défini""" + + gymnast = None + pattern = request.GET.get("pattern", None) + if pattern: + routine_list = Routine.objects.filter( + Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern) + ) + else: + if gymnast_id: + routine_list = Routine.objects.filter(done_by_gymnast__gymnast=gymnast_id) + gymnast = Gymnast.objects.get(pk=gymnast_id) + else: + routine_list = Routine.objects.all() + + context = { + "routine_list": routine_list, + "gymnast_id": gymnast_id, + "gymnast": gymnast, + } + return render(request, "routines/list.html", context) + + +@login_required +@require_http_methods(["POST"]) +def routine_lookup(request): + """ + Récupère la liste des lieux à la volée suivant des caractères de recherche entrés. + """ + pattern = request.POST.get("pattern", None) + + if pattern is not None and len(pattern) >= 3: + results = Routine.objects.filter( + Q(long_label__icontains=pattern) | Q(short_label__icontains=pattern) + ) + place_list = [{"id": x.id, "label": str(x)} for x in results] + + return JsonResponse(place_list, safe=False) + + +@login_required +def routine_details(request, routine_id): + """ + Récupère toutes les informations d'une routine (série). + + Args: + routine_id int identifiant d'une routine + """ + + routine = get_object_or_404(Routine, pk=routine_id) + routine.compute_informations() + context = {"routine": routine, "skill_link_list": routine.skill_links.all()} + return render(request, "routines/details.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def routine_create_or_update(request, routine_id=None): + """Création d'une série. + + Args: + routine_id (int): identifiant d'un object de classe . + """ + + if routine_id: + routine = get_object_or_404(Routine, pk=routine_id) + else: + routine = None + + if request.method == "POST": + form = RoutineForm(request.POST, instance=routine) + + if form.is_valid(): + routine = form.save() + # ici faire un FOR skill in form_skills_list: + # record.save() # ca sauve le record dans la table RoutineSkill + # something like this : http://stackoverflow.com/questions/3074938/django-m2m-form-save-through-table + # TO_FRED : can you help me ? + return HttpResponseRedirect(reverse("routine_details", args=(routine.pk,))) + else: + return render(request, "routines/create.html", {"form": form}) + + form = RoutineForm(instance=routine) + context = {"form": form, "routine_id": routine_id} + return render(request, "routines/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def compose_routine(request, routine_id): + """ + Récupère une routine et les sauts associés. + """ + + routine = get_object_or_404(Routine, pk=routine_id) + skill_link_list = routine.skill_links.all() + skill_list = Skill.objects.all() + context = { + "routine": routine, + "skill_link_list": skill_link_list, + "number_of_skill": skill_link_list.count(), + "skill_list": skill_list, + } + return render(request, "routines/compose.html", context) + + +@require_http_methods(["POST"]) +def link_skill_to_routine(request): + """ + Recoit trois informations permettant de lier complètement un saut à une routine + """ + data = { + "routine": get_object_or_404(Routine, pk=request.POST.get("routine_id", 0)), + "skill": get_object_or_404(Skill, pk=request.POST.get("skill_id", 0)), + "rank": request.POST.get("rank", 0), + } + form = RoutineSkillForm(data) + + if form.is_valid(): + link, created = RoutineSkill.objects.get_or_create( + routine=form.cleaned_data["routine"], + skill=form.cleaned_data["skill"], + rank=form.cleaned_data["rank"], + ) + return HttpResponse(200, (link, created)) + else: + return HttpResponse(406) + + +@require_http_methods(["POST"]) +def unlink_skill_from_routine(request): + """ + Recoit deux informations (routine_id & order) permettant d'enlever un skill d'une routine + """ + order = request.POST.get("order", None) + routine_id = request.POST.get("routine_id", None) + routine = get_object_or_404(Routine, pk=routine_id) + try: + RoutineSkill.objects.get(routine=routine, rank=order).delete() + except Exception: + return HttpResponse(409) + + return HttpResponse(200) diff --git a/jarvis/people/__init__.py b/jarvis/people/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/people/admin.py b/jarvis/people/admin.py new file mode 100644 index 0000000..9b58403 --- /dev/null +++ b/jarvis/people/admin.py @@ -0,0 +1,43 @@ +from django.contrib import admin +from .models import Gymnast + + +class GymnastAdmin(admin.ModelAdmin): + model = Gymnast + + def last_name(self, obj): + return obj.user.last_name + + def first_name(self, obj): + return obj.user.first_name + + @admin.display(ordering='user__email', description='Email') + def email(self, obj): + if obj.user: + return obj.user.email + + return None + + def is_active(self, obj): + return obj.user.is_active + + fields = ( + "last_name", + "first_name", + "user", + "birthdate", + "gender", + # "email", + "is_active", + "orientation", + "year_of_practice", + "email_trainer", + "informations", + ) + + list_display = ("last_name", "first_name", "age", "email", "is_active") + list_filter = ("gender", "user__is_active") + search_fields = ("last_name", "first_name") + + +admin.site.register(Gymnast, GymnastAdmin) diff --git a/jarvis/people/apps.py b/jarvis/people/apps.py new file mode 100644 index 0000000..d699fa4 --- /dev/null +++ b/jarvis/people/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PeopleConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.people" diff --git a/jarvis/people/forms.py b/jarvis/people/forms.py new file mode 100644 index 0000000..d81965c --- /dev/null +++ b/jarvis/people/forms.py @@ -0,0 +1,83 @@ +"""Formulaires de gestion des données entrantes pour les gymnastes et accidents.""" + +from django import forms +from django.contrib.auth import get_user_model + +from .models import Gymnast + +User = get_user_model() + + +class GymnastForm(forms.ModelForm): + class Meta: + model = Gymnast + fields = ( + "last_name", + "first_name", + "birthdate", + "gender", + "is_active", + "orientation", + "year_of_practice", + "email_trainer", + "informations", + ) + + widgets = { + "last_name": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Lastname"} + ), + "first_name": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Firstname"} + ), + "birthdate": forms.DateInput(attrs={"class": "form-control datepicker"}), + "gender": forms.Select(attrs={"class": "form-control selectpicker"}), + "email_trainer": forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "trainer_email@email.com", + } + ), + "is_active": forms.CheckboxInput( + attrs={"class": "form-control form-check-input ml-0 mt-0"} + ), + "orientation": forms.Select(attrs={"class": "form-control selectpicker"}), + "year_of_practice": forms.TextInput( + attrs={"class": "form-control", "placeholder": "3"} + ), + "informations": forms.Textarea( + attrs={ + "class": "form-control", + "placeholder": "Informations about gymnast: fear, lost skill syndrom, …", # pylint: disable=line-too-long + } + ), + } + + last_name = forms.CharField( + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Lastname"} + ) + ) + first_name = forms.CharField( + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "Firstname"} + ) + ) + email = forms.EmailField( + required=False, + widget=forms.TextInput( + attrs={"class": "form-control", "placeholder": "my_email@email.com"} + ), + ) + + +class UserForm(forms.ModelForm): + class Meta: + model = User + fields = ( + "last_name", + "first_name", + "email", + "is_active", + "username", + ) diff --git a/jarvis/people/migrations/0001_initial.py b/jarvis/people/migrations/0001_initial.py new file mode 100644 index 0000000..03e934c --- /dev/null +++ b/jarvis/people/migrations/0001_initial.py @@ -0,0 +1,70 @@ +# Generated by Django 3.2.8 on 2021-12-01 13:02 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("location", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Gymnast", + 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", + ), + ), + ("last_name", models.CharField(max_length=40)), + ("first_name", models.CharField(max_length=25)), + ("birthdate", models.DateField(verbose_name="Date de naissance")), + ( + "gender", + models.PositiveSmallIntegerField( + choices=[(0, "Male"), (1, "Female")], verbose_name="Sexe" + ), + ), + ("is_active", models.BooleanField(default=1, verbose_name="Active")), + ( + "trainings_by_week", + models.PositiveSmallIntegerField(verbose_name="# Training by week"), + ), + ( + "hours_by_week", + models.PositiveSmallIntegerField(verbose_name="# Hours by week"), + ), + ( + "club", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="gymnast", + to="location.club", + ), + ), + ], + options={ + "verbose_name": "Gymnast", + "verbose_name_plural": "Gymnasts", + }, + ), + ] diff --git a/jarvis/people/migrations/0002_gymnast_user.py b/jarvis/people/migrations/0002_gymnast_user.py new file mode 100644 index 0000000..dec776d --- /dev/null +++ b/jarvis/people/migrations/0002_gymnast_user.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.8 on 2022-02-04 10:29 + +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', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='gymnast', + name='user', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='gymnast', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/jarvis/people/migrations/0003_alter_gymnast_user.py b/jarvis/people/migrations/0003_alter_gymnast_user.py new file mode 100644 index 0000000..9a23e5b --- /dev/null +++ b/jarvis/people/migrations/0003_alter_gymnast_user.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.8 on 2022-02-06 17:40 + +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', '0002_gymnast_user'), + ] + + operations = [ + migrations.AlterField( + model_name='gymnast', + name='user', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='gymnast', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/jarvis/people/migrations/0004_gymnast_email_trainer.py b/jarvis/people/migrations/0004_gymnast_email_trainer.py new file mode 100644 index 0000000..ab88abc --- /dev/null +++ b/jarvis/people/migrations/0004_gymnast_email_trainer.py @@ -0,0 +1,20 @@ +# Generated by Django 4.1.1 on 2022-09-07 06:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0003_alter_gymnast_user"), + ] + + operations = [ + migrations.AddField( + model_name="gymnast", + name="email_trainer", + field=models.EmailField( + blank=True, max_length=254, null=True, verbose_name="Trainer's email" + ), + ), + ] diff --git a/jarvis/people/migrations/0005_gymnast_orientation_gymnast_year_of_practice.py b/jarvis/people/migrations/0005_gymnast_orientation_gymnast_year_of_practice.py new file mode 100644 index 0000000..acb76a5 --- /dev/null +++ b/jarvis/people/migrations/0005_gymnast_orientation_gymnast_year_of_practice.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.1 on 2022-10-31 13:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0004_gymnast_email_trainer"), + ] + + operations = [ + migrations.AddField( + model_name="gymnast", + name="orientation", + field=models.PositiveSmallIntegerField( + blank=True, + choices=[(None, "Unknown"), (0, "Left"), (1, "Right")], + null=True, + verbose_name="Twist side", + ), + ), + migrations.AddField( + model_name="gymnast", + name="year_of_practice", + field=models.PositiveSmallIntegerField(default=0), + ), + ] diff --git a/jarvis/people/migrations/0006_gymnast_created_at.py b/jarvis/people/migrations/0006_gymnast_created_at.py new file mode 100644 index 0000000..c732003 --- /dev/null +++ b/jarvis/people/migrations/0006_gymnast_created_at.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.1 on 2022-10-31 13:48 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0005_gymnast_orientation_gymnast_year_of_practice"), + ] + + operations = [ + migrations.AddField( + model_name="gymnast", + name="created_at", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), + preserve_default=False, + ), + ] diff --git a/jarvis/people/migrations/0007_remove_gymnast_club_remove_gymnast_hours_by_week_and_more.py b/jarvis/people/migrations/0007_remove_gymnast_club_remove_gymnast_hours_by_week_and_more.py new file mode 100644 index 0000000..ceb77db --- /dev/null +++ b/jarvis/people/migrations/0007_remove_gymnast_club_remove_gymnast_hours_by_week_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 4.1.1 on 2022-11-02 13:54 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0006_gymnast_created_at"), + ] + + operations = [ + migrations.RemoveField( + model_name="gymnast", + name="club", + ), + migrations.RemoveField( + model_name="gymnast", + name="hours_by_week", + ), + migrations.RemoveField( + model_name="gymnast", + name="trainings_by_week", + ), + ] diff --git a/jarvis/people/migrations/0008_alter_gymnast_orientation.py b/jarvis/people/migrations/0008_alter_gymnast_orientation.py new file mode 100644 index 0000000..eec20e9 --- /dev/null +++ b/jarvis/people/migrations/0008_alter_gymnast_orientation.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.1 on 2022-11-02 13:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0007_remove_gymnast_club_remove_gymnast_hours_by_week_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="gymnast", + name="orientation", + field=models.PositiveSmallIntegerField( + blank=True, + choices=[(None, "Unknown"), (1, "Left"), (2, "Right")], + null=True, + verbose_name="Twist side", + ), + ), + ] diff --git a/jarvis/people/migrations/__init__.py b/jarvis/people/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/people/models.py b/jarvis/people/models.py new file mode 100644 index 0000000..31e85c1 --- /dev/null +++ b/jarvis/people/models.py @@ -0,0 +1,269 @@ +""" Modelisation des gymnastes """ + +from django.contrib.auth import get_user_model +from django.db.models import Count +from django.db import models + +User = get_user_model() + +from datetime import date +import pendulum + +from jarvis.location.models import Club +from jarvis.objective.models import Skill +from jarvis.tools.models import Markdownizable +from jarvis.objective.tools import ( + nb_skill_by_type, + nb_skill_lte_type, + compute_completude, + compute_statistics_by_type, +) + +GENDER_CHOICES = ((0, "Male"), (1, "Female")) + + +class Gymnast(Markdownizable): + """Représente un gymnaste. + Un gymnaste peut être actif ou inactif. + """ + + class Meta: + verbose_name = "Gymnast" + verbose_name_plural = "Gymnasts" + + ORIENTATION_CHOICES = ((None, "Unknown"), (1, "Left"), (2, "Right")) + + user = models.OneToOneField( + User, on_delete=models.SET_NULL, related_name="gymnast", blank=True, null=True + ) + last_name = models.CharField(max_length=40, null=False, blank=False) + first_name = models.CharField(max_length=25, null=False, blank=False) + birthdate = models.DateField(verbose_name="Date de naissance") + gender = models.PositiveSmallIntegerField( + choices=GENDER_CHOICES, verbose_name="Sexe" + ) + is_active = models.BooleanField(default=1, verbose_name="Active") + year_of_practice = models.PositiveSmallIntegerField(default=0) + orientation = models.PositiveSmallIntegerField( + choices=ORIENTATION_CHOICES, + null=True, + blank=True, + verbose_name="Twist side", + ) + email_trainer = models.EmailField( + max_length=254, null=True, blank=True, verbose_name="Trainer's email" + ) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return "%s %s" % (self.first_name, self.last_name) + + @property + def next_birthday(self): + """Définit la prochaine date (de fête) d'anniversaire pour cette personne. + + Returns: + Soit le jour/mois pour cette année + Soit le jour/mois pour l'année prochaine. + + Example: (en supposant qu'on soit le 23/05/2019) + >>> from datetime import date + >>> gymnast = Gymnast(name='Tru', firstname='Gregg', birthdate=date(1982, 2, 5)) + >>> gymnast.next_birthday() + Date(2020, 2, 5) + """ + + now = pendulum.now() + + this_year_birthday = pendulum.date( + now.year, self.birthdate.month, self.birthdate.day + ) + + if this_year_birthday.is_past(): + return pendulum.date(now.year + 1, self.birthdate.month, self.birthdate.day) + return this_year_birthday + + @property + def next_birthday_in_days(self): + now = pendulum.now() + return self.next_birthday.diff(now).in_days() + + @property + def age(self): + """Renvoie l'âge d'un gymnaste.""" + today = date.today() + return ( + today.year + - self.birthdate.year + - ((today.month, today.day) < (self.birthdate.month, self.birthdate.day)) + ) + + @property + def next_age(self): + """Renvoie l'âge prochain du gymnaste.""" + return (self.age) + 1 + + def skill_max_for_type(self, desired_type="level"): + """ + Renvoie le niveau/rank maximum des skill que le gymnaste sait faire suivant le type passé + en paramètre. + + Args: + desired_type (string): type ("level" ou "rank") des Skills désirés. + + Returns: + int + + Examples: + >>> from jarvis.people.models import Gymnast + >>> gymnast = Gymnast.objects.get(pk=1) + >>> gymnast.skill_max_for_type("level") + 1 + >>> gymnast.skill_max_for_type("rank") + 19 + """ + if desired_type != "level" and desired_type != "rank": + return None + + tmp = ( + Skill.objects.filter(known_by__gymnast=self.id) + .order_by("-" + desired_type) + .values(desired_type)[:1] + ) + if tmp: + skill_max = tmp[0][desired_type] + else: + skill_max = 0 + + return skill_max + + def nb_known_skill_by_type(self, desired_type="level"): + """ + Renvoie le nombre de Skill qu'un gymnast sait faire par niveau/rang suivant le type passé + en paramètre + + Args: + desired_type (string): type ("level" ou "rank") des Skills désirés. + + Returns: + QuerySet + + Examples: + >>> from jarvis.people.models import Gymnast + >>> gymnast = Gymnast.objects.get(pk=1) + >>> gymnast.nb_known_skill_by_type("level") + + >>> gymnast.nb_known_skill_by_type("rank") + + """ + if desired_type != "level" and desired_type != "rank": + return None + + nb_known_skill_by_type = ( + Skill.objects.values(desired_type) + .filter(known_by__gymnast=self.id) + .order_by(desired_type) + .annotate(nb_known_skill=Count("id")) + ) + return nb_known_skill_by_type + + def unknown_skill_lte_level(self, max_level_skill): + """ + Liste des Skill que le gymnaste ne sait PAS faire/ + + Ces skills sont classé par niveau. + """ + return Skill.objects.filter(level__lte=max_level_skill).exclude( + known_by__gymnast=self.id + ) + + def unknown_skill_gt_level(self, max_level_skill): + """ + Liste des Skill que le gymnaste ne sait PAS faire, + classé par niveau (ayant un niveau inférieur ou égal au niveau max du gym) + """ + return Skill.objects.filter(level__gt=max_level_skill).exclude( + known_by__gymnast=self.id + ) + + def unknown_skill_lte_rank(self, max_rank_skill): + # Liste des Skill que le gymnaste ne sait PAS faire, classé par niveau + # (ayant un niveau inférieur ou égal au niveau max du gym) + return Skill.objects.filter(level__lte=max_rank_skill).exclude( + known_by__gymnast=self.id + ) + + def unknown_skill_gt_rank(self, max_rank_skill): + """ + Liste des Skill que le gymnaste ne sais PAS faire + (ayant un niveau plus grand que le niveau max du gym) + """ + return Skill.objects.filter(level__gt=max_rank_skill).exclude( + known_by__gymnast=self.id + ) + + def get_informations_from_type(self, desired_type="level"): + """ + Calcule toutes les statistiques par rapport au niveau/rang suivant le paramètre transmis. + + 1. On va chercher le niveau/rang maximum de skill que le gymnast sait faire + 2. 0n va chercher le nombre de skill par niveau/rang que le gymnast sait faire + nb_known_skill_by_type = [ + { + 'level': 1, + 'nb_known_skill': 1 + }, + { + 'level': 2, + 'nb_known_skill': 2 + } + ] + 3. Si le niveau/rang maximum est supérieur à 0: + a. on va chercher le nombre de skill qu'il y a par niveau/rang : + [ + { + 'level': 1, 'nb_skill': 1 + }, + { + 'level': 2, 'nb_skill': 1 + } + ] + + b. on va chercher le nombre total des skills dont le niveau/rang est inférieur ou + égale au niveau max. + + c. on calcule les statistiques du gymnaste par rapport aux skill + + SINON: + on va chercher tous les Skill qui existent et on les compte. + + """ + if desired_type != "level" and desired_type != "rank": + return None + + context = {} + skill_max = self.skill_max_for_type(desired_type) + gymnast_nb_known_skills = self.known_skills.distinct("skill").count() + + if skill_max > 0: + cpt_known_skill_by_type = self.nb_known_skill_by_type( + desired_type + ) # à remonter ??? + cpt_skill_by_type = nb_skill_by_type(skill_max, desired_type) + context["total_skill"] = nb_skill_lte_type(skill_max, desired_type) + context["percentages"] = compute_statistics_by_type( + cpt_skill_by_type, cpt_known_skill_by_type, desired_type + ) + context["skill_by_level"] = self.unknown_skill_lte_level(skill_max) + context["unknown_skill"] = self.unknown_skill_gt_level(skill_max) + else: + tmp = Skill.objects.all() + context["total_skill"] = tmp.count() + context["unknown_skill"] = tmp + + context["completude"], context["evaluated_level"] = compute_completude( + context["total_skill"], gymnast_nb_known_skills, skill_max + ) + + context["max_" + desired_type + "_skill"] = skill_max + return context diff --git a/jarvis/people/templates/gymnasts/create.html b/jarvis/people/templates/gymnasts/create.html new file mode 100644 index 0000000..07559b4 --- /dev/null +++ b/jarvis/people/templates/gymnasts/create.html @@ -0,0 +1,171 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    {% if gymnast_id %}Edit{% else %}Add{% endif %} Gymnast

    +
    +
    +
    + {% csrf_token %} +
    + +
    +
    +
    + {{ form.first_name }} + {% if form.first_name.errors %} {% for error in form.first_name.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {{ form.last_name }} + {% if form.last_name.errors %} {% for error in form.last_name.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    +
    +
    + +
    + {{ form.birthdate }} + {% if form.birthdate.errors %} {% for error in form.birthdate.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.gender }} + {% if form.gender.errors %} {% for error in form.gender.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.email }} + {% if form.email.errors %} {% for error in form.email.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.orientation }} + {% if form.orientation.errors %} {% for error in form.orientation.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    + {{ form.year_of_practice }} + {% if form.year_of_practice.errors %} + + {% endif %} +
    +
    +
    +
    + +
    + {{ form.email_trainer }} + {% if form.email_trainer.errors %} {% for error in form.email_trainer.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.informations }} +
    +
    + {% if gymnast_id %} +
    + +
    + {{ form.is_active }} + {% if form.is_active.errors %} {% for error in form.is_active.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    + {% else %} + + {% endif %} +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% endblock %} \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/details.html b/jarvis/people/templates/gymnasts/details.html new file mode 100644 index 0000000..4883589 --- /dev/null +++ b/jarvis/people/templates/gymnasts/details.html @@ -0,0 +1,195 @@ +{% extends "base.html" %} + +{% load skill_doughnut %} +{% load level_chart_bar %} +{% load has_group %} +{% load is_user_equal_to_gymnast %} + +{% block page_title %}{{ gymnast.first_name }} {{ gymnast.last_name }}{% endblock %} + +{% block content %} +
    +
    +
    +
    +
    +
    +
    +
    +
    + + {% if gymnast.picture %} + {{ gymnast }} + {% endif %} +

    {{ gymnast.first_name }} {{ gymnast.last_name }}

    +
    +

    + {{ last_season_information.club.name }}
    + {{ last_season_information.get_category_display }} +

    +
    +
    + {% if gymnast.orientation %} + Twisting side : {{ gymnast.get_orientation_display }}
    + {% endif %} + {% if height_weight %} + {{ height_weight.0.height }}cm - {{ height_weight.0.weight }}kg ({{ height_weight.0.date | date:"d-m-Y" }})
    + {% endif %} + {{ last_season_information.number_of_training_sessions_per_week }} training/week for {{ last_season_information.number_of_hours_per_week }} hours/week
    +
    + {% if user_is_trainer and gymnast.informations %} +

    {{ gymnast.to_markdown | safe }}

    +
    + {% endif %} +
  • + 10 | : {{ best_straightjump.0.tof }} ({{ best_straightjump.0.date | date:"d-m-Y" }}) +
  • +
  • + Routine : {% if best_routine %}{{ best_routine.0.tof }} ({{ best_routine.0.date | date:"d-m-Y" }}){% else %} (no information){% endif %} +
  • + {% if user_is_trainer %} + Add season details - + Seasons details + {% endif %} +
    +
    +
    +
    + +
    + {% if gymnast_nb_known_skills %} + {% generate_skill_doughnut gymnast.id %} + {% else %} +

    No learned skill statistics.

    + {% endif %} +
    + +
    + {% if gymnast_nb_known_skills %} + {% generate_level_chart_bar gymnast.id %} + {% else %} +

    No level/rank information.

    + {% endif %} +
    + +
    + +
    +
    + +
    +
    +
    +
    + {% if user_is_trainer or request.user|is_user_equal_to_gymnast:gymnast.id %} +
    + {% endif %} +
    +
    + {% if user_is_trainer or request.user|is_user_equal_to_gymnast:gymnast.id %} +
    + {% endif %} +
    +
    +
    +
    + +{% endblock %} + +{% block footerscript %} + + +{% endblock %} diff --git a/jarvis/people/templates/gymnasts/gymnast_level_chart_bar.html b/jarvis/people/templates/gymnasts/gymnast_level_chart_bar.html new file mode 100644 index 0000000..8fd33f2 --- /dev/null +++ b/jarvis/people/templates/gymnasts/gymnast_level_chart_bar.html @@ -0,0 +1,18 @@ +

    + Estimated level : {% if gymnast_nb_known_skills %}{{ evaluated_level }}{% else %}0{% endif %} +

    +
    +
    +
    +
    +
    + +{% for level in percentages %} + {% if level.2 > 0 and level.2 <= level.1 %} +
    +
    +
      Level {{ level.0 }}
    +
    +
    + {% endif %} +{% endfor %} \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/gymnast_level_doughnut.html b/jarvis/people/templates/gymnasts/gymnast_level_doughnut.html new file mode 100644 index 0000000..4632ab4 --- /dev/null +++ b/jarvis/people/templates/gymnasts/gymnast_level_doughnut.html @@ -0,0 +1,41 @@ + + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/link_to_routine.html b/jarvis/people/templates/gymnasts/link_to_routine.html new file mode 100644 index 0000000..09bed26 --- /dev/null +++ b/jarvis/people/templates/gymnasts/link_to_routine.html @@ -0,0 +1,107 @@ +{% extends "base.html" %} +{% load static %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    Link gymnast to routine

    +
    +
    +
    + {% csrf_token %} + +
    + +
    + {% if request.user|has_group:"trainer" %} + {{ form.gymnast }} + {{ form.gymnast_related }} + {% if form.gymnast.errors %} {% for error in form.gymnast.errors %}{{ error }}{% endfor %}{% endif %} + {% else %} + + + {% endif %} +
    +
    + +
    + +
    + {{ form.routine }} + {{ form.routine_related }} + {% if form.routine.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.routine_type }} + {% if form.routine_type.errors %} + + {% endif %} +
    +
    + +
    + +
    + {{ form.date_begin }} + {% if form.date_begin.errors %} + + {% endif %} +
    +
    + +
    + +
    +
    + {{ form.date_end }} + {% if form.date_end.errors %} + + {% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% if request.session.template == 0 %} + + +{% else %} + + +{% endif %} +{% endblock %} diff --git a/jarvis/people/templates/gymnasts/list.html b/jarvis/people/templates/gymnasts/list.html new file mode 100644 index 0000000..9dac769 --- /dev/null +++ b/jarvis/people/templates/gymnasts/list.html @@ -0,0 +1,93 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +

    Gymnast Listing

    +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    +
    +
    + {% if season_information_list %} + + + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + + + {% for season_information in season_information_list %} + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + {% endfor %} + +
    LastnameFirstnameGenderAgeCategoryClub
    + + + + {{ season_information.gymnast.last_name }}{{ season_information.gymnast.first_name }}{{ season_information.gymnast.get_gender_display }}{{ season_information.gymnast.age }}{{ season_information.get_category_display }}{{ season_information.club.name }}
    + {% else %} + There are no gymnast corresponding to your criterias. + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/list_accident.html b/jarvis/people/templates/gymnasts/list_accident.html new file mode 100644 index 0000000..535aa73 --- /dev/null +++ b/jarvis/people/templates/gymnasts/list_accident.html @@ -0,0 +1,58 @@ +
    +
    +
    +
    +

    Accidents

    +
    +
    + {% if accident_list %} +
    + + + + + + + + + + {% for accident in accident_list %} + + + + + + {% endfor %} + +
    DateSkill
    + +   + + {{ accident.date | date:"d-m-Y" }}{{ accident.skill }}
    +
    + {% else %} +

    No accident known for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/list_events_and_notes.html b/jarvis/people/templates/gymnasts/list_events_and_notes.html new file mode 100644 index 0000000..763ca94 --- /dev/null +++ b/jarvis/people/templates/gymnasts/list_events_and_notes.html @@ -0,0 +1,210 @@ +{% load has_group %} + +
    +
    +
    +
    +

    Next events

    +
    +
    + {% if next_event_list %} + + + + + + + + + + + + + {% for event in next_event_list %} + + + + + + + + + {% endfor %} + +
    NameTypeDate# weeksPlace
    + +   + + {{ event.name }}{{ event.event_type.name }}{{ event.date_begin | date:"d-m-Y"}}{% if event.number_of_week_from_today < 0 + %}{{event.number_of_week_from_today}}{% else %} + {{event.number_of_week_from_today}}{% endif %}{{ event.place.name + }}
    + {% else %} +

    No next event associated to this gymnast.

    + {% endif %} +
    +
    +
    +
    + + +
    + +
    +
    +
    +
    +

    Notes

    +
    +
    + {% if last_notes_list %} + + + + + + + + + + {% for note in last_notes_list %} + + + + + + {% endfor %} + +
    DateCoach
    + + + + {{ note.date | + date:"d-m-Y" }}{{ note.coach }}
    + {% else %} +

    No note for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + + +{% if previous_events %} +
    +
    +
    +
    +

    Previous event

    +
    +
    + + + + + + + + + + + + + {% for event in previous_event %} + + + + + + + + + {% endfor %} + +
    Event's nameTypeDate# weeksPlace
    + + + + {{ event.name }}{{ event.event_type.name }}{{ event.date_begin | date:"d-m-Y"}}{{ event.number_of_week_from_today }}{{ event.place.name + }}
    +
    +
    +
    +
    +
    +
    +

    Last notes

    +
    +
    + + + + + + + + + + + {% for note in last_notes_list %} + + + + + + {% endfor %} + +
    DateCoach
    + + + + {{ note.created_at | + date:"d-m-Y" }}{{ note.coach }}
    +
    +
    +
    +
    + + +{% endif %} \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/list_mindstate.html b/jarvis/people/templates/gymnasts/list_mindstate.html new file mode 100644 index 0000000..59d0859 --- /dev/null +++ b/jarvis/people/templates/gymnasts/list_mindstate.html @@ -0,0 +1,121 @@ +{% load has_group %} +{% load is_user_equal_to_gymnast %} + +
    +
    +
    +
    +

    Mindstates

    +
    +
    + {% if mindstate_list %} +
    + +
    +
    + + + + + + + + + + {% for state in mindstate_list %} + + + + + + {% endfor %} + +
    DateScore
    + + + + {{ state.date | date:"d-m-Y" }}{{ state.score }} 
    +
    +
    + + {% else %} +

    No mindstate recorded for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/report_choices.html b/jarvis/people/templates/gymnasts/report_choices.html new file mode 100644 index 0000000..a97adf5 --- /dev/null +++ b/jarvis/people/templates/gymnasts/report_choices.html @@ -0,0 +1,96 @@ +{% extends "base.html" %} + +{% block page_title %}{{ gymnast.first_name }} {{ gymnast.last_name }}{% endblock %} + +{% block content %} +
    + +
    +
    +
    + +
    + +
    + +
    + +
    +
    + + + + + +
    +
    +
    + +
    +
    +
    +
    + + + +{% endblock %} + +{% block footerscript %} + +{% endblock %} diff --git a/jarvis/people/templates/gymnasts/reports/report_evaluation.html b/jarvis/people/templates/gymnasts/reports/report_evaluation.html new file mode 100644 index 0000000..fe373b0 --- /dev/null +++ b/jarvis/people/templates/gymnasts/reports/report_evaluation.html @@ -0,0 +1,380 @@ +{% load static %} + + + + + + + + + + + + + + + + + {{ gymnast.first_name }} {{ gymnast.last_name }} + + + + + + + + + +
    +
    +
    + {{ SITE_TITLE }} - {{ CLUB_NAME }}
    + {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
    + Season {{ season }} - week {{ week_number }} +
    +
    + Head Coach : {{ HEAD_COACH }}
    + {{ HEAD_COACH_EMAIL }}
    + {{ today | date:"j F Y" }} +
    +
    +
    +
    + +
    +
    + +
    +
    +

    {{ gymnast.first_name }} {{ gymnast.last_name }}

    +

    {{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.

    + {% if gymnast.informations %} + {{ gymnast.to_markdown | safe }} + {% endif %} +
    +
    + {% if last_mindstate or last_height_weigth or mindstate_analyse or height_analyse or weight_analyse %} +

    Physiological

    + + {% if last_mindstate %} + + + + + + {% endif %} + {% if last_height and last_weight %} + + + + + + + + + + + {% endif %} +
    Mind state{{ last_mindstate }}{% if mindstate_analyse %}{{ mindstate_analyse }}{% endif %}
    Height{{ last_height }}{% if height_analyse %}{{ height_analyse }}{% endif %}
    Weight{{ last_weigth }}{% if weight_analyse %}{{ weight_analyse }}{% endif %}
    + {% endif %} +
    +
    +
    +
    +
    +
    +

    Best ToF

    + {% if best_tof_straightjump or best_tof_routine_1 or best_tof_routine_2 %} + + + + + + + + {% if best_tof_straightjump %} + + + + + + + {% endif %} + {% if best_tof_routine_1 %} + + + + + + + {% endif %} + {% if best_tof_routine_2 %} + + + + + + + {% endif %} +
    ChronoToF
    ToF |{{ best_tof_straightjump.score }}{{ best_tof_straightjump.tof }}{{ best_tof_straightjump.date|date:"j M Y" }}
    Routine 1{{ best_tof_routine_1.score }}{{ best_tof_routine_1.tof }}{{ best_tof_routine_1.date|date:"j M Y" }}
    Routine 2{{ best_tof_routine_2.score }}{{ best_tof_routine_2.tof }}{{ best_tof_routine_2.date|date:"j M Y" }}
    + {% endif %} +
    +
    +
    +

    Best Scores

    + {% if best_point_routine_1 or best_point_routine_2 %} + + + + + + + + + + + {% if best_point_routine_1 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_2 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_3 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_4 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_5 %} + + + + + + + + + + {% endif %} +
    Exe.Diff.HDToFTotal
    Routine 1{{ best_point_routine_1.point_execution }}{{ best_point_routine_1.point_difficulty }}{{ best_point_routine_1.point_horizontal_displacement }}{{ best_point_routine_1.point_time_of_flight }}{{ best_point_routine_1.total }}{{ best_point_routine_1.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_2.point_execution }}{{ best_point_routine_2.point_difficulty }}{{ best_point_routine_2.point_horizontal_displacement }}{{ best_point_routine_2.point_time_of_flight }}{{ best_point_routine_2.total }}{{ best_point_routine_2.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_3.point_execution }}{{ best_point_routine_3.point_difficulty }}{{ best_point_routine_3.point_horizontal_displacement }}{{ best_point_routine_3.point_time_of_flight }}{{ best_point_routine_3.total }}{{ best_point_routine_3.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_4.point_execution }}{{ best_point_routine_4.point_difficulty }}{{ best_point_routine_4.point_horizontal_displacement }}{{ best_point_routine_4.point_time_of_flight }}{{ best_point_routine_4.total }}{{ best_point_routine_4.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_5.point_execution }}{{ best_point_routine_5.point_difficulty }}{{ best_point_routine_5.point_horizontal_displacement }}{{ best_point_routine_5.point_time_of_flight }}{{ best_point_routine_5.total }}{{ best_point_routine_5.event.date_begin|date:"j M Y" }}
    + {% endif %} +
    +
    +
    +
    +
    +
    +

    Last learned skills

    + + {% for learned_skill in learned_skills %} + + + + + + + {% endfor %} +
    {{ learned_skill.skill.short_label }}({{ learned_skill.get_learning_step_display }}){{ learned_skill.skill.notation }}{{ learned_skill.date|date:"j M Y" }}
    +
    +
    +

    Objectives

    + {% if plan_list %} + + {% for plan in plan_list %} + + + + + + + {% endfor %} +
    {{ plan.educative.short_label }}({{ plan.get_learning_step_display }}){{ plan.skill.notation }}{{ plan.date | date:"j M Y" }}
    + {% else %} + No objective defined. + {% endif %} +
    +
    +
    +
    +
    +

    Next Events

    + {% if next_event_list %} + + {% for event in next_event_list %} + + + + + + {% endfor %} +
    {{ event.date_begin | date:"j M Y" }}in {{ event.number_of_week_from_today }} week(s){{ event.name }}
    + {% endif %} +
    +
    +
    +
    +
    +

    Notes

    + {% if notes %} + {% for note in notes %} + {{ note.to_markdown | safe }} + {% endfor %} + {% else %} + No note this week. + {% endif %} +
    +
    +
    +
    + {% if routine_1 or routine_2 or routine_3 or routine_4 or routine_5 %} +
    +

    Routines

    +
    +
    +
    Q1 routine 1
    + {% if routine_1 %} + + {% for routine_skill in routine_1.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_1.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_1_done_stat.total_succeeded %} + {{ routine_1_done_stat.total_succeeded }} | {{ routine_1_done_stat.total_try }} ({% widthratio routine_1_done_stat.total_succeeded routine_1_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Q1 routine 2
    + {% if routine_2 %} + + {% for routine_skill in routine_2.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_2.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_2_done_stat.total_succeeded %} + {{ routine_2_done_stat.total_succeeded }} | {{ routine_2_done_stat.total_try }} ({% widthratio routine_2_done_stat.total_succeeded routine_2_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Q2 routine 1
    + {% if routine_3 %} + + {% for routine_skill in routine_3.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_3.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_3_done_stat.total_succeeded %} + {{ routine_3_done_stat.total_succeeded }} | {{ routine_3_done_stat.total_try }} ({% widthratio routine_3_done_stat.total_succeeded routine_3_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Semi-final routine
    + {% if routine_4 %} + + {% for routine_skill in routine_4.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_4.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_4_done_stat.total_succeeded %} + {{ routine_4_done_stat.total_succeeded }} | {{ routine_4_done_stat.total_try }} ({% widthratio routine_4_done_stat.total_succeeded routine_4_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Final routine
    + {% if routine_5 %} + + {% for routine_skill in routine_5.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_5.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_5_done_stat.total_succeeded %} + {{ routine_5_done_stat.total_succeeded }} | {{ routine_5_done_stat.total_try }} ({% widthratio routine_5_done_stat.total_succeeded routine_5_done_stat.total_try 100 %}%) + {% endif %} +
    + {% endif %} +
    + + + + diff --git a/jarvis/people/templates/gymnasts/reports/report_timeline.html b/jarvis/people/templates/gymnasts/reports/report_timeline.html new file mode 100644 index 0000000..b2ca10d --- /dev/null +++ b/jarvis/people/templates/gymnasts/reports/report_timeline.html @@ -0,0 +1,67 @@ +{% load static %} + + + + + + + + + + + + + + + + + {{ gymnast.first_name }} {{ gymnast.last_name }} + + + + + + + + + +
    +
    +
    + {{ SITE_TITLE }} - {{ CLUB_NAME }}
    + {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
    + Season {{ season }} - week {{ week_number }} +
    +
    + Head Coach : {{ HEAD_COACH }}
    + {{ HEAD_COACH_EMAIL }}
    + {{ today | date:"j F Y" }} +
    +
    +
    + +
    +
    + +
    +
    +

    {{ gymnast.first_name }} {{ gymnast.last_name }}

    +

    {{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.

    + {% if gymnast.informations %} + {{ gymnast.to_markdown | safe }} + {% endif %} +
    +
    +

    Timeline

    + {% if sorted_records %} +
      + {% for record in sorted_records %} + {{ record.timeline_representation | safe }} + {% endfor %} +
    + {% endif %} +
    +
    + + + diff --git a/jarvis/people/templates/gymnasts/reports/report_week.html b/jarvis/people/templates/gymnasts/reports/report_week.html new file mode 100644 index 0000000..fe373b0 --- /dev/null +++ b/jarvis/people/templates/gymnasts/reports/report_week.html @@ -0,0 +1,380 @@ +{% load static %} + + + + + + + + + + + + + + + + + {{ gymnast.first_name }} {{ gymnast.last_name }} + + + + + + + + + +
    +
    +
    + {{ SITE_TITLE }} - {{ CLUB_NAME }}
    + {{ ADDRESS }} - {{ ZIP }} {{ CITY }}
    + Season {{ season }} - week {{ week_number }} +
    +
    + Head Coach : {{ HEAD_COACH }}
    + {{ HEAD_COACH_EMAIL }}
    + {{ today | date:"j F Y" }} +
    +
    +
    +
    + +
    +
    + +
    +
    +

    {{ gymnast.first_name }} {{ gymnast.last_name }}

    +

    {{ gymnast.age }} years - {% if gymnast.orientation %}{{ gymnast.get_orientation_display }}{% else %}unknown{% endif %} twisting side.

    + {% if gymnast.informations %} + {{ gymnast.to_markdown | safe }} + {% endif %} +
    +
    + {% if last_mindstate or last_height_weigth or mindstate_analyse or height_analyse or weight_analyse %} +

    Physiological

    + + {% if last_mindstate %} + + + + + + {% endif %} + {% if last_height and last_weight %} + + + + + + + + + + + {% endif %} +
    Mind state{{ last_mindstate }}{% if mindstate_analyse %}{{ mindstate_analyse }}{% endif %}
    Height{{ last_height }}{% if height_analyse %}{{ height_analyse }}{% endif %}
    Weight{{ last_weigth }}{% if weight_analyse %}{{ weight_analyse }}{% endif %}
    + {% endif %} +
    +
    +
    +
    +
    +
    +

    Best ToF

    + {% if best_tof_straightjump or best_tof_routine_1 or best_tof_routine_2 %} + + + + + + + + {% if best_tof_straightjump %} + + + + + + + {% endif %} + {% if best_tof_routine_1 %} + + + + + + + {% endif %} + {% if best_tof_routine_2 %} + + + + + + + {% endif %} +
    ChronoToF
    ToF |{{ best_tof_straightjump.score }}{{ best_tof_straightjump.tof }}{{ best_tof_straightjump.date|date:"j M Y" }}
    Routine 1{{ best_tof_routine_1.score }}{{ best_tof_routine_1.tof }}{{ best_tof_routine_1.date|date:"j M Y" }}
    Routine 2{{ best_tof_routine_2.score }}{{ best_tof_routine_2.tof }}{{ best_tof_routine_2.date|date:"j M Y" }}
    + {% endif %} +
    +
    +
    +

    Best Scores

    + {% if best_point_routine_1 or best_point_routine_2 %} + + + + + + + + + + + {% if best_point_routine_1 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_2 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_3 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_4 %} + + + + + + + + + + {% endif %} + {% if best_point_routine_5 %} + + + + + + + + + + {% endif %} +
    Exe.Diff.HDToFTotal
    Routine 1{{ best_point_routine_1.point_execution }}{{ best_point_routine_1.point_difficulty }}{{ best_point_routine_1.point_horizontal_displacement }}{{ best_point_routine_1.point_time_of_flight }}{{ best_point_routine_1.total }}{{ best_point_routine_1.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_2.point_execution }}{{ best_point_routine_2.point_difficulty }}{{ best_point_routine_2.point_horizontal_displacement }}{{ best_point_routine_2.point_time_of_flight }}{{ best_point_routine_2.total }}{{ best_point_routine_2.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_3.point_execution }}{{ best_point_routine_3.point_difficulty }}{{ best_point_routine_3.point_horizontal_displacement }}{{ best_point_routine_3.point_time_of_flight }}{{ best_point_routine_3.total }}{{ best_point_routine_3.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_4.point_execution }}{{ best_point_routine_4.point_difficulty }}{{ best_point_routine_4.point_horizontal_displacement }}{{ best_point_routine_4.point_time_of_flight }}{{ best_point_routine_4.total }}{{ best_point_routine_4.event.date_begin|date:"j M Y" }}
    Routine 2{{ best_point_routine_5.point_execution }}{{ best_point_routine_5.point_difficulty }}{{ best_point_routine_5.point_horizontal_displacement }}{{ best_point_routine_5.point_time_of_flight }}{{ best_point_routine_5.total }}{{ best_point_routine_5.event.date_begin|date:"j M Y" }}
    + {% endif %} +
    +
    +
    +
    +
    +
    +

    Last learned skills

    + + {% for learned_skill in learned_skills %} + + + + + + + {% endfor %} +
    {{ learned_skill.skill.short_label }}({{ learned_skill.get_learning_step_display }}){{ learned_skill.skill.notation }}{{ learned_skill.date|date:"j M Y" }}
    +
    +
    +

    Objectives

    + {% if plan_list %} + + {% for plan in plan_list %} + + + + + + + {% endfor %} +
    {{ plan.educative.short_label }}({{ plan.get_learning_step_display }}){{ plan.skill.notation }}{{ plan.date | date:"j M Y" }}
    + {% else %} + No objective defined. + {% endif %} +
    +
    +
    +
    +
    +

    Next Events

    + {% if next_event_list %} + + {% for event in next_event_list %} + + + + + + {% endfor %} +
    {{ event.date_begin | date:"j M Y" }}in {{ event.number_of_week_from_today }} week(s){{ event.name }}
    + {% endif %} +
    +
    +
    +
    +
    +

    Notes

    + {% if notes %} + {% for note in notes %} + {{ note.to_markdown | safe }} + {% endfor %} + {% else %} + No note this week. + {% endif %} +
    +
    +
    +
    + {% if routine_1 or routine_2 or routine_3 or routine_4 or routine_5 %} +
    +

    Routines

    +
    +
    +
    Q1 routine 1
    + {% if routine_1 %} + + {% for routine_skill in routine_1.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_1.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_1_done_stat.total_succeeded %} + {{ routine_1_done_stat.total_succeeded }} | {{ routine_1_done_stat.total_try }} ({% widthratio routine_1_done_stat.total_succeeded routine_1_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Q1 routine 2
    + {% if routine_2 %} + + {% for routine_skill in routine_2.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_2.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_2_done_stat.total_succeeded %} + {{ routine_2_done_stat.total_succeeded }} | {{ routine_2_done_stat.total_try }} ({% widthratio routine_2_done_stat.total_succeeded routine_2_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Q2 routine 1
    + {% if routine_3 %} + + {% for routine_skill in routine_3.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_3.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_3_done_stat.total_succeeded %} + {{ routine_3_done_stat.total_succeeded }} | {{ routine_3_done_stat.total_try }} ({% widthratio routine_3_done_stat.total_succeeded routine_3_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Semi-final routine
    + {% if routine_4 %} + + {% for routine_skill in routine_4.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_4.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_4_done_stat.total_succeeded %} + {{ routine_4_done_stat.total_succeeded }} | {{ routine_4_done_stat.total_try }} ({% widthratio routine_4_done_stat.total_succeeded routine_4_done_stat.total_try 100 %}%) + {% endif %} +
    +
    +
    Final routine
    + {% if routine_5 %} + + {% for routine_skill in routine_5.routine.skill_links.all %} + + + + + {% endfor %} + + + + +
    {{ routine_skill.skill.notation }}{{ routine_skill.skill.difficulty }}
    {{ routine_5.routine.difficulty }}
    + {% else %} + No routine defined. + {% endif %} + {% if routine_5_done_stat.total_succeeded %} + {{ routine_5_done_stat.total_succeeded }} | {{ routine_5_done_stat.total_try }} ({% widthratio routine_5_done_stat.total_succeeded routine_5_done_stat.total_try 100 %}%) + {% endif %} +
    + {% endif %} +
    + + + + diff --git a/jarvis/people/templates/gymnasts/tabs/tab_documents.html b/jarvis/people/templates/gymnasts/tabs/tab_documents.html new file mode 100644 index 0000000..072731e --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_documents.html @@ -0,0 +1,27 @@ +{% load has_group %} + +
    +
    +
    +
    +

    Documents

    +
    +
    +

    Reports

    +
      +
    • Weekly
    • +
    • monthly (under construction)
    • +
    • seasonly (under construction)
    • +
    +

    Career

    +

    (Under construction)

    +

    Evaluation

    +

    Report

    +
    +
    +
    +
    + + diff --git a/jarvis/people/templates/gymnasts/tabs/tab_events_and_notes.html b/jarvis/people/templates/gymnasts/tabs/tab_events_and_notes.html new file mode 100644 index 0000000..4f60281 --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_events_and_notes.html @@ -0,0 +1,208 @@ +{% load has_group %} + +{% if latest_published_note %} +
    +
    +
    +
    +

    Latest note ({{ latest_published_note.date | date:'d-m-Y' }})

    +
    +
    +

    {{ latest_published_note.to_markdown | safe }}

    +
    +
    +
    +
    +{% endif %} + + +
    +
    +
    +
    +

    Next events

    +
    +
    + {% if next_event_list %} + + + + + + + + + + {% for event in next_event_list %} + + + + + + {% endfor %} + +
    NameDate# weeks
    {{ event.name }}{{ event.date_begin | date:"d-m-Y"}}{% if event.number_of_week_from_today < 0 %}{{event.number_of_week_from_today}}{% else %} + {{event.number_of_week_from_today}}{% endif %}
    + {% else %} +

    No next event associated to this gymnast.

    + {% endif %} +
    +
    +
    + +
    +
    +
    +

    Notes

    +
    +
    + {% if last_notes_list %} + + + + + + + + + + {% for note in last_notes_list %} + + + + + + {% endfor %} + +
    DateCoach
    + + + +   {% if not note.status %}{% endif %} + + {{ note.created_at | date:"d-m-Y" }} + {{ note.coach }}
    + {% else %} +

    No note for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + +{% if previous_events %} +
    +
    +
    +
    +

    Previous event

    +
    +
    + + + + + + + + + + + + + {% for event in previous_event %} + + + + + + + + + {% endfor %} + +
    Event's nameTypeDate# weeksPlace
    + + + + {{ event.name }}{{ event.event_type.name }}{{ event.date_begin | date:"d-m-Y"}}{{ event.number_of_week_from_today }}{{ event.place.name }}
    +
    +
    +
    +
    +
    +
    +

    Last notes

    +
    +
    + + + + + + + + + + + {% for note in last_notes_list %} + + + + + + {% endfor %} + +
    DateCoach
    + + + + {{ note.created_at | + date:"d-m-Y" }}{{ note.coach }}
    +
    +
    +
    +
    + + +{% endif %} \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/tabs/tab_physiological.html b/jarvis/people/templates/gymnasts/tabs/tab_physiological.html new file mode 100644 index 0000000..b282f78 --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_physiological.html @@ -0,0 +1,266 @@ +
    +
    +
    +
    +

    Height/Weight

    +
    +
    + {% if height_weight_list %} +
    + {% else %} +

    No height/weight recorded for this gymnast.

    + {% endif %} +
    + +
    +
    + +
    +
    +
    +

    Mindstates

    +
    +
    + {% if mindstate_list %} +
    + {% else %} +

    No mindstate recorded for this gymnast.

    + {% endif %} +
    + +
    +
    +
    +
    +
    +
    +
    +

    Accidents

    +
    +
    + {% if accident_list %} + + + + + + + + + + + {% for accident in accident_list %} + + + + + + + {% endfor %} + +
    DateSkill# Week Off
    + +   + + {{ accident.date | date:"d-m-Y" }}{% if accident.skill %}{{ accident.skill }}{% else %}-{% endif %}{{ accident.nb_week_off }}
    + {% else %} +

    No accident known for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/tabs/tab_routines_and_routine_stats.html b/jarvis/people/templates/gymnasts/tabs/tab_routines_and_routine_stats.html new file mode 100644 index 0000000..b3d9cb6 --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_routines_and_routine_stats.html @@ -0,0 +1,435 @@ +{% load has_group %} +{% load is_user_equal_to_gymnast %} + +
    +
    +
    +
    +

    Intensity statistics

    +
    +
    + {% if intensity_list %} +
    + {% else %} +

    No intensity recorded for this gymnast.

    + {% endif %} +
    + +
    +
    +
    +
    +
    +

    Routine's statistics

    +
    +
    + {% if routine_one_done_list or routine_two_done_list %} +
    + {% else %} +

    There are no routine's statistics associated to this gymnast.

    + {% endif %} +
    + +
    +
    +
    +
    +
    +

    Active routines

    +
    +
    + {% if ghr_list %} +
    + + + + + + + + + + + + + {% for ghr in ghr_list %} + + + + + + + + + + {% endfor %} + +
    TypeLabelFromDiff.LevelRank
    {{ ghr.get_routine_type_display }}{{ ghr.routine.short_label }}{{ ghr.date_begin | date:"d-m-Y"}}{{ ghr.routine.difficulty }}{{ ghr.routine.level }}{{ ghr.routine.rank }}
    +
    + {% else %} +

    There are no routines associated to this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/tabs/tab_scores_and_chronos.html b/jarvis/people/templates/gymnasts/tabs/tab_scores_and_chronos.html new file mode 100644 index 0000000..252a47b --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_scores_and_chronos.html @@ -0,0 +1,357 @@ +{% load has_group %} +{% load is_user_equal_to_gymnast %} + +
    +
    +
    +
    +

    Scores

    +
    +
    + {% if score_list %} +
    + {% else %} +

    No score recorded for this gymnast.

    + {% endif %} +
    + + +
    +
    + +
    +
    +
    +

    Chrono

    +
    +
    + {% if chrono_list %} +
    + {% else %} +

    No chrono recorded for this gymnast.

    + {% endif %} +
    + +
    +
    +
    + + \ No newline at end of file diff --git a/jarvis/people/templates/gymnasts/tabs/tab_skill.html b/jarvis/people/templates/gymnasts/tabs/tab_skill.html new file mode 100644 index 0000000..118009a --- /dev/null +++ b/jarvis/people/templates/gymnasts/tabs/tab_skill.html @@ -0,0 +1,656 @@ +{% load has_group %} + +
    +
    +
    +
    + {% if planned_skill or skill_whith_help or skill_without_help or skill_chained or skill_masterised or skill_by_rank or skill_by_level or skill_by_age or unknown_skill %} + + +
    + {% if planned_skill %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + + + {% for plan in planned_skill %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% endfor %} + +
    NotationLabelPhaseDeadlineDiff.LevelRank
    + + + + + + + + {{ plan.educative.skill.notation }} + {{ plan.educative.long_label }} + + {{ plan.get_learning_step_display }} + + {% if plan.is_past %}{% endif %} + {{ plan.date | date:"d-m-Y" }} + {% if plan.is_past %}{% endif %} + {{ plan.educative.difficulty }}{{ plan.educative.level }}{{ plan.educative.rank }}
    +
    + {% endif %} + + {% if confused_skill %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in confused_skill %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    NotationLabelDiff.LevelRank
    + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_whith_help %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_whith_help %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    NotationLabelDiff.LevelRank
    + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_without_help %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_without_help %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    NotationLabelDiff.LevelRank
    + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_chained %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_chained %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    NotationLabelDiff.LevelRank
    + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_masterised %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_masterised %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    NotationLabelDiff.LevelRank
    + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_by_rank %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_by_rank %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    ActionsNotationLabelDiff.LevelRank
    + + + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_by_level %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in skill_by_level %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    ActionsNotationLabelDiff.LevelRank
    + + + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if skill_by_age %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + + {% for skill in skill_by_age %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    ActionsNotationLabelDiff.LevelRank
    + + + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% if unknown_skill %} +
    + + + + {% if user_is_trainer %} + + {% endif %} + + + + + + + + + {% for skill in unknown_skill %} + + {% if user_is_trainer %} + + {% endif %} + + + + + + + {% endfor %} + +
    ActionsNotationLabelDiff.LevelRank
    + + + + + + + + {{ skill.notation }}{{ skill.long_label }}{{ skill.difficulty }}{{ skill.level }}{{ skill.rank }}
    +
    + {% endif %} + + {% else %} +
    +

    No skill to display.

    +
    + {% endif %} +
    +
    +
    +
    +
    + + diff --git a/jarvis/people/templatetags/__init__.py b/jarvis/people/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/people/templatetags/level_chart_bar.py b/jarvis/people/templatetags/level_chart_bar.py new file mode 100644 index 0000000..fff18c7 --- /dev/null +++ b/jarvis/people/templatetags/level_chart_bar.py @@ -0,0 +1,17 @@ +from django import template +from django.shortcuts import get_object_or_404 +from jarvis.people.models import Gymnast + +register = template.Library() + + +@register.inclusion_tag("gymnasts/gymnast_level_chart_bar.html") +def generate_level_chart_bar(gymnast_id): + """ + Tag permettant l'affichage des statistiques d'un gymnaste : le nombre de saut qu'il sait faire + (total, par niveau, par rank, …), calcule la complétude, … + """ + + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + context = gymnast.get_informations_from_type("level") + return context diff --git a/jarvis/people/templatetags/skill_doughnut.py b/jarvis/people/templatetags/skill_doughnut.py new file mode 100644 index 0000000..805f38d --- /dev/null +++ b/jarvis/people/templatetags/skill_doughnut.py @@ -0,0 +1,34 @@ +from django import template +from jarvis.objective.models import Skill +from jarvis.followup.models import LearnedSkill + +register = template.Library() + + +@register.inclusion_tag("gymnasts/gymnast_level_doughnut.html") +def generate_skill_doughnut(gymnast_id): + """Récupère pour un gymnaste la quantité de skill dans chaque phase d'apprentissage (with help, + withoutRécupère les différents nombres de skill du gymnaste (masterised, chained, without help, + with help or no) à la dernière date connue et les renvoie pour en faire un graphique. + """ + skill_learned_by_phase = [0] * 5 + learned_skills = ( + LearnedSkill.objects.filter(gymnast=gymnast_id) + .order_by("skill_id", "-date") + .distinct("skill_id") + ) + + for learned_skill in learned_skills: + skill_learned_by_phase[learned_skill.learning_step] += 1 + + nb_skill = Skill.objects.all().count() + nb_unknown_skill = nb_skill - learned_skills.count() + + return { + "lost_skill": skill_learned_by_phase[0], + "nb_skill_whith_help": skill_learned_by_phase[1], + "nb_skill_without_help": skill_learned_by_phase[2], + "nb_skill_chained": skill_learned_by_phase[3], + "nb_skill_masterised": skill_learned_by_phase[4], + "nb_unknown_skill": nb_unknown_skill, + } diff --git a/jarvis/people/tests.py b/jarvis/people/tests.py new file mode 100644 index 0000000..4de3e52 --- /dev/null +++ b/jarvis/people/tests.py @@ -0,0 +1,70 @@ +from datetime import datetime + +from django.test import TestCase + +from jarvis.people.models import Gymnast + + +class GymnastTestCase(TestCase): + # def setUp(self): + # gymnast = Gymnast( + # last_name="Pauchou", + # first_name="Fred", + # birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + # ) + + def test_gymnast_tostring(self): + gymnast = Gymnast(last_name="Pauchou", first_name="Fred") + self.assertEqual(str(gymnast), "Fred Pauchou") + + def test_gymnaste_get_age(self): + gymnast = Gymnast( + last_name="Pauchou", + first_name="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) + self.assertEqual(gymnast.age, 37) + + def test_gymnaste_get_next_age(self): + gymnast = Gymnast( + last_name="Pauchou", + first_name="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) + self.assertEqual(gymnast.next_age, 38) + + def test_gymnaste_next_birthday(self): + gymnast = Gymnast( + last_name="Pauchou", + first_name="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) + self.assertEqual( + gymnast.next_birthday, datetime.strptime("03/07/2023", "%d/%m/%Y") + ) + + def test_next_birthday_in_days(self): + gymnast = Gymnast( + last_name="Pauchou", + first_name="Fred", + birthdate=datetime.strptime("03/07/1985", "%d/%m/%Y"), + ) + self.assertEqual(gymnast.next_birthday_in_days, 72) + + def skill_max_for_type(self): + pass + + def test_nb_known_skill_by_type(self): + pass + + def test_unknown_skill_gt_level(self): + pass + + def test_unknown_skill_lte_rank(self): + pass + + def test_unknown_skill_gt_rank(self): + pass + + def get_informations_from_type(self): + pass diff --git a/jarvis/people/tests_views.py b/jarvis/people/tests_views.py new file mode 100644 index 0000000..3e10126 --- /dev/null +++ b/jarvis/people/tests_views.py @@ -0,0 +1,23 @@ +from django.test import TestCase + +# from django.urls import reverse, resolve +# from jarvis.profiles.views import profile_update + + +# class URLTestCase(TestCase): +# def setUp(self): +# # Every test needs access to the request factory. +# self.factory = RequestFactory() +# self.user = User.objects.create_user( +# username="test", email="test@gmail.com", password="top_secret" +# ) + +# def test_view_profile_update(self): +# request = self.factory.get("/profile/edit/") +# request.user = self.user + +# response = my_view(request) + +# url = reverse("profile_update") +# response = self.client.get(url) +# self.assertEqual(response.status_code, 200) diff --git a/jarvis/people/urls.py b/jarvis/people/urls.py new file mode 100644 index 0000000..9b36764 --- /dev/null +++ b/jarvis/people/urls.py @@ -0,0 +1,103 @@ +from django.urls import path + +from . import views + +gymnast_urlpatterns = [ + path(r"", views.gymnast_listing, name="gymnast_list"), + path(r"lookup/", views.gymnast_lookup, name="gymnast_lookup"), + path( + r"details//tab//", + views.gymnast_details, + name="gymnast_details_tab", + ), + path(r"details//", views.gymnast_details, name="gymnast_details"), + path( + r"details//skill/", + views.gymnast_display_skill, + name="gymnast_display_skill", + ), + path( + r"details//document/", + views.gymnast_report_list, + name="gymnast_report_list", + ), + path( + r"details//routine/", + views.gymnast_display_routine_statistics, + name="gymnast_display_routine", + ), + path( + r"details//to-routine/", + views.link_routine_to_gymnast, + name="link_routine_to_gymnast", + ), + path( + r"details//event/", + views.gymnast_display_events_and_notes, + name="gymnast_display_events_and_notes", + ), + path( + r"details//accident/", + views.gymnast_display_accident, + name="gymnast_display_accident", + ), + path( + r"details//scores_chrono/", + views.gymnast_display_scores_chrono, + name="gymnast_display_scores_chrono", + ), + path( + r"details//mindstate/", + views.gymnast_display_mindstate, + name="gymnast_display_mindstate", + ), + path( + r"details//physiological/", + views.gymnast_display_physiological, + name="gymnast_display_physiological", + ), + path( + r"report/choice//", + views.report_choice, + name="report_choice", + ), + path( + r"report/choice//get_distinct_week_number_for_season/", + views.get_distinct_week_number_for_season_and_gymnast, + name="get_distinct_week_number_for_season_and_gymnast", + ), + path( + r"report//", + views.generate_week_report, + name="gymnast_report_export", + ), + path( + r"report//season//week_number//", + views.generate_week_report, + name="gymnast_report_export_for_week_number", + ), + path( + r"report/timeline//", + views.generate_timeline_report, + name="gymnast_timeline_export", + ), + path( + r"report/timeline//season//week_number//", + views.generate_timeline_report, + name="gymnast_timeline_export_from_week_number", + ), + path( + r"report/timeline//date//", + views.generate_timeline_report, + name="gymnast_timeline_export_from_date", + ), + path( + r"report/evaluation//", + views.gymnast_report_evaluation, + name="gymnast_report_evaluation", + ), + path(r"add/", views.gymnast_create_or_update, name="gymnast_create"), + path( + r"edit//", views.gymnast_create_or_update, name="gymnast_update" + ), +] diff --git a/jarvis/people/views.py b/jarvis/people/views.py new file mode 100644 index 0000000..eb5d421 --- /dev/null +++ b/jarvis/people/views.py @@ -0,0 +1,1178 @@ +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import Group +from django.contrib.auth import get_user_model + +from django.db.models import ( + Q, + Avg, + Sum, +) +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse +from django.template.loader import render_to_string +from django.conf import settings + +from django.core.mail import send_mail + +from weasyprint import HTML, CSS + +# from weasyprint.fonts import FontConfiguration + +import pendulum +from datetime import date, timedelta +from statistics import mean + +from jarvis.followup.models import Event +from jarvis.followup.forms import GymnastHasRoutineForm +from jarvis.followup.models import ( + Note, + Plan, + Skill, + Point, + Chrono, + Accident, + MindState, + Intensity, + LearnedSkill, + HeightWeight, + SeasonInformation, + NumberOfRoutineDone, +) + +from jarvis.followup.models import ( + CATEGORY_CHOICES, + LEARNING_STEP_CHOICES, +) + +from jarvis.tools.models import Season + +# from jarvis.tools.pdf_generator import GymnastReportDocument + +from jarvis.tools.date_week_transition import ( + from_date_to_week_number, + from_week_number_to_date, +) + +from .models import Gymnast +from .forms import GymnastForm, UserForm + +User = get_user_model() + + +@login_required +@require_http_methods(["POST"]) +def gymnast_lookup(request): + """ + Récupère la liste des gymnastes à la volée suivant des caractères de + recherche entrés. (min 3 caractères) + """ + + results = [] + pattern = request.POST.get("pattern", None) + + if pattern is not None and len(pattern) > 3: + model_results = Gymnast.objects.filter( + Q(last_name__icontains=pattern) | Q(first_name__icontains=pattern) + ) + results = [{"ID": x.id, "Name": str(x)} for x in model_results] + + return JsonResponse(results, safe=False) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_listing(request): + """ + Si la personne connectée est un entraîneur : liste tous les gymnasts connus. + Si la personne connectée est un gymnaste : renvoie sa fiche détaillée. + """ + season = Season() + if request.user.groups.filter(name="trainer").exists(): + season_information_list = SeasonInformation.objects.filter( + season=season.label + ).select_related("gymnast") + context = {"season_information_list": season_information_list} + return render(request, "gymnasts/list.html", context) + else: + gymnast = Gymnast.objects.get(user=request.user) # a mettre dans un TRY + return gymnast_details(request, gymnast.id) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_details(request, gymnast_id, tab=None): + """ + Récupère toutes les informations d'un gymnaste si la personne connectée est un "trainer". + Si la personne connectée est un gymnaste : renvoie sa fiche détaillée. + """ + + if request.user.groups.filter(name="trainer").exists(): + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + else: + gymnast = Gymnast.objects.get(user=request.user) + + last_season_information = gymnast.season_informations.order_by("-season").first() + + gymnast_nb_known_skills = gymnast.known_skills.distinct( + "skill" + ).count() # devrait disparaitre + + nb_skill = Skill.objects.all().count() + nb_known_skill = ( + LearnedSkill.objects.filter(gymnast=gymnast_id).distinct("skill").count() + ) + + if nb_skill != 0: + percentage_known_skill = (nb_known_skill / nb_skill) * 100 + else: + percentage_known_skill = 0 + + # base_queryset = Chrono.objects.filter(gymnast=gymnast_id).order_by("-date") + chronos_list = Chrono.objects.filter(gymnast=gymnast_id).order_by("-date")[:10] + straightjump_score = ( + Chrono.objects.filter(gymnast=gymnast_id) + .filter(chrono_type=0) + .order_by("-date") + ) + best_straightjump = ( + Chrono.objects.filter(gymnast=gymnast_id) + .filter(chrono_type=0) + .order_by("-score")[:1] + ) + best_routine = ( + Chrono.objects.filter(gymnast=gymnast_id) + .filter(chrono_type=1) + .order_by("-score")[:1] + ) + + context = { + "gymnast": gymnast, + "last_season_information": last_season_information, + "gymnast_nb_known_skills": gymnast_nb_known_skills, + "chronos_list": chronos_list, + "straightjump_score": straightjump_score, + "best_routine": best_routine, + "best_straightjump": best_straightjump, + "nb_skill": nb_skill, + "nb_known_skill": nb_known_skill, + "percentage_known_skill": percentage_known_skill, + "tab": tab, + } + context["user_is_trainer"] = request.user.groups.filter( + name="trainer" + ).exists() # TODO: utiliser les {{ perms }} + return render(request, "gymnasts/details.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_report_list(request, gymnast_id): + """ """ + + context = { + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/tabs/tab_documents.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_report_evaluation(request, gymnast_id): + """ """ + 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, + # "gymnast": gymnast, + # "today": date_begin, + # "season": season, + # "sorted_records": sorted_records, + } + return render(request, "gymnasts/reports/report_evaluation.html", context) + # response = HttpResponse(content_type="application/pdf") + # response[ + # "Content-Disposition" + # ] = "attachment; filename={lastname}-{firstname}-report-timeline.pdf".format( + # lastname=gymnast.last_name, + # firstname=gymnast.first_name, + # ) + + # html = render_to_string("people/gymnasts/reports/report_timeline.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 + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_events_and_notes(request, gymnast_id): + """ + Renvoie deux listes d'évènements : ceux à venir et ceux passés. + """ + today = pendulum.now().date() + next_event_list = Event.objects.filter(gymnasts=gymnast_id, date_begin__gte=today) + previous_event_list = Event.objects.filter( + gymnasts=gymnast_id, date_begin__lte=today + ) + + base_queryset = Note.objects.filter(gymnast=gymnast_id) + if not request.user.groups.filter(name="trainer").exists(): + notes_list = base_queryset.filter(status=1) + else: + notes_list = base_queryset + notes_list = notes_list.order_by("-created_at") + + last_notes_list = notes_list[:5] + latest_published_note = ( + base_queryset.filter(status=1).order_by("-created_at").first() + ) + + context = { + "next_event_list": next_event_list, + "previous_event_list": previous_event_list, + "last_notes_list": last_notes_list, + "latest_published_note": latest_published_note, + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/tabs/tab_events_and_notes.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_accident(request, gymnast_id): + """ + Renvoie la liste des accidents d'un gymnaste. + """ + accident_list = Accident.objects.filter(gymnast=gymnast_id) + context = {"accident_list": accident_list, "gymnast_id": gymnast_id} + return render(request, "gymnasts/list_accident.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_physiological(request, gymnast_id): + """ + Renvoie les listes des tailles/poids, état d'esprit et accidents. + """ + accident_list = Accident.objects.filter(gymnast=gymnast_id).order_by("date") + mindstate_list = MindState.objects.filter(gymnast=gymnast_id).order_by("date") + height_weight_list = HeightWeight.objects.filter(gymnast=gymnast_id).order_by( + "date" + ) + + context = { + "accident_list": accident_list, + "mindstate_list": mindstate_list, + "height_weight_list": height_weight_list, + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/tabs/tab_physiological.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_scores_chrono(request, gymnast_id): + """ + Selectionne tous les scores réalisés par le gymnaste + + Afin de ne pas avoir plusieurs valeurs pour une même date, nous faisons une + moyenne par date. + """ + score_list = Point.objects.filter(gymnast=gymnast_id).order_by("-event__date_begin") + chrono_list = Chrono.objects.filter(gymnast=gymnast_id).order_by("date") + base_queryset = chrono_list.values("date").annotate(score_avg=Avg("tof")) + + context = { + "score_list": score_list, + "score_routine1_list": score_list.filter(routine_type=1), + "score_routine2_list": score_list.filter(routine_type=2), + "score_routine3_list": score_list.filter(routine_type=3), + "chrono_list": chrono_list, + "chrono_10c": base_queryset.filter(chrono_type=0), + "chrono_r1": base_queryset.filter(chrono_type=1), + "chrono_r2": base_queryset.filter(chrono_type=2), + "chrono_rf": base_queryset.filter(chrono_type=3), + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/tabs/tab_scores_and_chronos.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_mindstate(request, gymnast_id): + """ + Selectionne tous les scores réalisés par le gymnaste + """ + mindstate_list = MindState.objects.filter(gymnast=gymnast_id).order_by("-date") + + context = { + "mindstate_list": mindstate_list, + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/list_mindstate.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_routine_statistics(request, gymnast_id): + """ + Tag affichant les statistiques et informations de séries d'un gymnaste. + """ + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + ghr_list = gymnast.has_routine.prefetch_related("routine").filter( + Q(date_end__gte=date.today()) | Q(date_end__isnull=True) + ) + + has_routine_1 = ghr_list.filter(routine_type=1) + has_routine_2 = ghr_list.filter(routine_type=2) + has_routine_3 = ghr_list.filter(routine_type=3) + has_routine_4 = ghr_list.filter(routine_type=4) + has_routine_5 = ghr_list.filter(routine_type=5) + + routine_one_done_list = NumberOfRoutineDone.objects.filter( + gymnast=gymnast_id, routine_type=1 + ).order_by("date") + routine_two_done_list = NumberOfRoutineDone.objects.filter( + gymnast=gymnast_id, routine_type=2 + ).order_by("date") + intensity_list = Intensity.objects.filter(gymnast=gymnast_id).order_by("date") + + context = { + "ghr_list": ghr_list, + "has_routine_1": has_routine_1, + "has_routine_2": has_routine_2, + "has_routine_3": has_routine_3, + "has_routine_4": has_routine_4, + "has_routine_5": has_routine_5, + "routine_one_done_list": routine_one_done_list, + "routine_two_done_list": routine_two_done_list, + "intensity_list": intensity_list, + "gymnast_id": gymnast_id, + } + return render(request, "gymnasts/tabs/tab_routines_and_routine_stats.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def link_routine_to_gymnast(request, gymnast_id=None): + """ """ + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + data = { + "gymnast": gymnast_id, + "gymnast_related": str(gymnast), + "date_end": None, + } + else: + gymnast = None + data = {"date_end": None} + + if request.method == "POST": + form = GymnastHasRoutineForm(request.POST) + + if form.is_valid(): + form.save() + if not gymnast: + gymnast = get_object_or_404(Gymnast, pk=form.cleaned_data["gymnast"]) + send_mail( + "Nouvelle série", + "Une nouvelle série vous a été associée.", + settings.EMAIL_HOST_USER, + [gymnast.user.email, gymnast.email_trainer], + fail_silently=False, + html_message="""

    Bonjour,

    +

    Une nouvelle série vous a été associée.


    +

    Excellente journée

    Jarvis

    """, + ) + + return HttpResponseRedirect( + reverse("gymnast_details_tab", args=(gymnast_id, "routine")) + ) + + else: + form = GymnastHasRoutineForm(instance=gymnast, initial=data) + + context = {"form": form, "gymnast_id": gymnast_id} + return render(request, "gymnasts/link_to_routine.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def gymnast_create_or_update(request, gymnast_id=None): + """ + Formulaire de creation et modification d'un gymnaste. + """ + + if gymnast_id: + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + else: + gymnast = None + + if request.method == "POST": + gymnast_form = GymnastForm(request.POST, instance=gymnast) + + if gymnast_form.is_valid(): + gymnast = gymnast_form.save() + form_data = request.POST.dict() + user_data = {} + user_data["first_name"] = form_data["first_name"] + user_data["last_name"] = form_data["last_name"] + user_data["username"] = ( + form_data["first_name"].lower() + "_" + form_data["last_name"].lower() + ) + user_data["email"] = form_data["email"].lower() + user_data["is_active"] = True + user_form = UserForm(user_data, instance=gymnast.user) + + if user_form.is_valid(): + user = user_form.save() + + gymnast_group, _ = Group.objects.get_or_create(name="gymnast") + user.groups.add(gymnast_group) + gymnast.user = user + gymnast.save() + + # if not user.has_usable_password(): + # user.set_password(gymnast.last_name.lower() + _ + str(gymnast.birthdate)[-2:]) + + return HttpResponseRedirect(reverse("gymnast_details", args=(gymnast.pk,))) + else: + return render( + request, "people/gymnasts/create.html", {"form": gymnast_form} + ) + + form = GymnastForm(instance=gymnast) + context = {"form": form, "gymnast_id": gymnast_id} + return render(request, "gymnasts/create.html", context) + + +@login_required +@require_http_methods(["GET"]) +def gymnast_display_skill(request, gymnast_id): + """Tag affichant les statistiques de skill d'un gymnaste + + Le nombre de saut qu'il sait faire (total, par niveau, par rank, …), calcule la complétude, … + + .. todo:: Générer UNE fois la liste de skill que le gymnaste ne sait pas faire (1 query) + et les counts puis, dans le template on parcourt plusieurs fois cette même liste mais on + affiche conditionnellement (par age, par rank, ...) + + Il y a 4 étapes dans l'apprentissage : + - skill confusion syndrom "No" - 0 + - avec aide (tapis, manip, …) "with help" - 1 + - sans aide () "without help" - 2 + - enchaîné (un saut avec et après) "chained" - 3 + - maitrisé (dans n'importe quelle condition) "masterised" - 4 + """ + + context = {} + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + gymnast_nb_known_skills = gymnast.known_skills.distinct("skill").count() + context = gymnast.get_informations_from_type("level") + context.update(gymnast.get_informations_from_type("rank")) + + planned_skill = ( + Plan.objects.filter(gymnast=gymnast.id, educative__in=(Skill.objects.all())) + .select_related("educative", "educative__skill") + .order_by("-date", "educative__long_label") + ) + + context["planned_skill"] = planned_skill + + if gymnast.gender: + context["skill_by_age"] = Skill.objects.filter( + age_girl_masterised__lte=gymnast.age + ).exclude(known_by__gymnast=gymnast.id) + else: + context["skill_by_age"] = Skill.objects.filter( + age_boy_masterised__lte=gymnast.age + ).exclude(known_by__gymnast=gymnast.id) + + learned_skills = ( + LearnedSkill.objects.filter(gymnast=gymnast_id) + .select_related("skill") + .order_by("skill_id", "-date") + .distinct("skill_id") + ) + + # skill_learned_by_phase = [[]] * 5 + # print(skill_learned_by_phase) + confused_skill = list() + skill_whith_help = list() + skill_without_help = list() + skill_chained = list() + skill_masterised = list() + for learned_skill in learned_skills: + # print('Add skill for ' + str(learned_skill.learning_step) + ' phase') + # skill_learned_by_phase[learned_skill.learning_step].append(learned_skill.skill) + if learned_skill.learning_step == 0: + confused_skill.append(learned_skill.skill) + elif learned_skill.learning_step == 1: + skill_whith_help.append(learned_skill.skill) + elif learned_skill.learning_step == 2: + skill_without_help.append(learned_skill.skill) + elif learned_skill.learning_step == 3: + skill_chained.append(learned_skill.skill) + else: + skill_masterised.append(learned_skill.skill) + + # for i in range(0,4): + # print(skill_learned_by_phase[i]) + + # context["confused_skill"] = skill_learned_by_phase[0] + # context["skill_whith_help"] = skill_learned_by_phase[1] + # context["skill_without_help"] = skill_learned_by_phase[2] + # context["skill_chained"] = skill_learned_by_phase[3] + # context["skill_masterised"] = skill_learned_by_phase[4] + context["confused_skill"] = confused_skill + context["skill_whith_help"] = skill_whith_help + context["skill_without_help"] = skill_without_help + context["skill_chained"] = skill_chained + context["skill_masterised"] = skill_masterised + + context["gymnast"] = gymnast + context["gymnast_nb_known_skills"] = gymnast_nb_known_skills + context["user_is_trainer"] = request.user.groups.filter( + name="trainer" + ).exists() # TODO: utiliser les {{ perms }} + return render(request, "gymnasts/tabs/tab_skill.html", context) + + +def analyse_score(value, value_list): + """Analyse une value (value) par rapport à la moyenne de value_list et à la dernière + valeur de value_list. + + Args: + value float valeur + value_list array liste de valeurs + + Returns: + string + + Examples: + """ + result = "" + mean_value = mean(value_list) + + if value > value_list[-1]: + result += "+" + elif value < value_list[-1]: + result += "-" + else: + result += "=" + + if value > mean_value: + result = "+" + result + elif value < mean_value: + result = "-" + result + else: + result = "=" + result + + return result + + +def __get_distinct_followup_season_for_gymnast(gymnast_id): + """Recupère les saisons pour lesquelles le gymnastes à des followup.""" + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + season_list = list( + gymnast.chronos.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.accident.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.known_skills.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.todo.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.mindstate.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.number_of_routine_done.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.height_weight.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.remarks.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + season_list += list( + gymnast.intensities.values_list("season", flat=True) + .distinct("season") + .order_by("season") + ) + return list(dict.fromkeys(season_list)) + + +def __get_distinct_week_number_for_season_and_gymnast(gymnast_id, season): + """Récupère les numéro de semaines pour lesquelles le gymnaste à des followup.""" + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + weeknumber_list = list( + gymnast.chronos.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.accident.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.known_skills.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.todo.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.mindstate.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.number_of_routine_done.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.height_weight.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.remarks.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + weeknumber_list += list( + gymnast.intensities.values_list("week_number", flat=True) + .filter(season=season) + .distinct("week_number") + .order_by("week_number") + ) + return list(dict.fromkeys(weeknumber_list)) + + +def get_distinct_week_number_for_season_and_gymnast(gymnast_id, season): + """ """ + if not season: + season = Season() + + weeknumber_list = __get_distinct_week_number_for_season_and_gymnast( + gymnast_id, season + ) + return JsonResponse(weeknumber_list, safe=False) + + +@require_http_methods(["GET"]) +def report_choice(request, gymnast_id): + """Recupère les saisons pour lesquelles le gymnastes à des followup.""" + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + + today = pendulum.now().date() + season, week_number = from_date_to_week_number(today) + + season_list = __get_distinct_followup_season_for_gymnast(gymnast_id) + week_number_list = sorted( + __get_distinct_week_number_for_season_and_gymnast(gymnast_id, season) + ) + context = { + "gymnast": gymnast, + "season": season, + "season_list": season_list, + "week_number": week_number, + "week_number_list": week_number_list, + } + return render(request, "gymnasts/report_choices.html", context) + + +@login_required +@require_http_methods(["GET"]) +def generate_week_report(request, gymnast_id, season=None, week_number=None): + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + + if season is None: + date_begin = pendulum.now().date() + season, week_number = from_date_to_week_number(date_begin) + else: + date_begin, _ = from_week_number_to_date(season, week_number) + date_begin = date_begin.date() + + # + # PHYSIOLOGICAL INFORMATIONS + # + # Mindstate Score + mindstate_week_score = ( + gymnast.mindstate.filter(season=season) + .filter(week_number=week_number) + .aggregate(mean_mindstate_value=Avg("score")) + ) + + mindstate_queryset = MindState.objects.filter(gymnast=gymnast).order_by("-date") + have_physiological = False + if mindstate_week_score["mean_mindstate_value"]: + lasts_mindstate = list(mindstate_queryset.values_list("score", flat=True)[1:6]) + mindstate_analyse = analyse_score( + mindstate_week_score["mean_mindstate_value"], lasts_mindstate + ) + else: + mindstate_analyse = None + + height_weight_week_value = ( + gymnast.height_weight.filter(season=season) + .filter(week_number=week_number) + .aggregate(mean_height_value=Avg("height"), mean_weight_value=Avg("weight")) + ) + + height_weight_queryset = HeightWeight.objects.filter(gymnast=gymnast).order_by( + "-date" + ) + + if height_weight_week_value["mean_height_value"]: + lasts_height = list( + height_weight_queryset.values_list("height", flat=True)[1:6] + ) + height_analyse = analyse_score( + height_weight_week_value["mean_height_value"], lasts_height + ) + else: + height_analyse = None + + if height_weight_week_value["mean_weight_value"]: + lasts_weight = list( + height_weight_queryset.values_list("weight", flat=True)[1:6] + ) + weight_analyse = analyse_score( + height_weight_week_value["mean_weight_value"], lasts_weight + ) + else: + weight_analyse = None + + intensity_week_value = ( + gymnast.intensities.filter(season=season) + .filter(week_number=week_number) + .aggregate( + mean_intensity_time__value=Avg("time"), + mean_intensity_difficulty_value=Avg("difficulty"), + mean_quantity_of_skill_value=Avg("quantity_of_skill"), + mean_number_of_passes_value=Avg("number_of_passes"), + ) + ) + + # + # BEST TOF + # + best_tof_straightjump = ( + Chrono.objects.filter(gymnast=gymnast) + .filter(chrono_type=0) + .order_by("-score") + .first() + ) + best_tof_routine_1 = ( + Chrono.objects.filter(gymnast=gymnast) + .filter(chrono_type=1) + .order_by("-score") + .first() + ) + best_tof_routine_2 = ( + Chrono.objects.filter(gymnast=gymnast) + .filter(chrono_type=2) + .order_by("-score") + .first() + ) + + # + # BEST SCORES + # + best_point_routine_1 = ( + Point.objects.filter(gymnast=gymnast) + .filter(routine_type=1) + .order_by("-total") + .first() + ) + best_point_routine_2 = ( + Point.objects.filter(gymnast=gymnast) + .filter(routine_type=2) + .order_by("-total") + .first() + ) + best_point_routine_3 = ( + Point.objects.filter(gymnast=gymnast) + .filter(routine_type=3) + .order_by("-total") + .first() + ) + best_point_routine_4 = ( + Point.objects.filter(gymnast=gymnast) + .filter(routine_type=4) + .order_by("-total") + .first() + ) + best_point_routine_5 = ( + Point.objects.filter(gymnast=gymnast) + .filter(routine_type=5) + .order_by("-total") + .first() + ) + + # + # ROUTINES + # + routine_1 = ( + gymnast.has_routine.filter(routine_type=1) + .filter(date_begin__lte=date_begin) + .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) + .first() + ) + routine_1_done_stat = ( + gymnast.number_of_routine_done.filter(routine_type=1) + .filter(season=season) + .filter(week_number=week_number) + .aggregate( + total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") + ) + ) + + routine_2 = ( + gymnast.has_routine.filter(routine_type=2) + .filter(date_begin__lte=date_begin) + .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) + .first() + ) + routine_2_done_stat = ( + gymnast.number_of_routine_done.filter(routine_type=2) + .filter(season=season) + .filter(week_number=week_number) + .aggregate( + total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") + ) + ) + + routine_3 = ( + gymnast.has_routine.filter(routine_type=3) + .filter(date_begin__lte=date_begin) + .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) + .first() + ) + routine_3_done_stat = ( + gymnast.number_of_routine_done.filter(routine_type=3) + .filter(season=season) + .filter(week_number=week_number) + .aggregate( + total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") + ) + ) + + routine_4 = ( + gymnast.has_routine.filter(routine_type=4) + .filter(date_begin__lte=date_begin) + .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) + .first() + ) + routine_4_done_stat = ( + gymnast.number_of_routine_done.filter(routine_type=4) + .filter(season=season) + .filter(week_number=week_number) + .aggregate( + total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") + ) + ) + + routine_5 = ( + gymnast.has_routine.filter(routine_type=5) + .filter(date_begin__lte=date_begin) + .filter(Q(date_end__gte=date_begin) | Q(date_end__isnull=True)) + .first() + ) + routine_5_done_stat = ( + gymnast.number_of_routine_done.filter(routine_type=5) + .filter(season=season) + .filter(week_number=week_number) + .aggregate( + total_try=Sum("number_of_try"), total_succeeded=Sum("number_of_successes") + ) + ) + + # + # LAST LEARNED SKILLS + # + # TODO: il faudrait que ce soit classer d'abord par DATE et pas par skill. + # + # learned_skills = ( + # LearnedSkill.objects.filter(gymnast=gymnast.id) + # .annotate(skill_notation=F("skill__notation")) + # .order_by("skill_notation", "-date") + # .distinct("skill_notation")[:6] + # ) + learned_skills = LearnedSkill.objects.filter(gymnast=gymnast.id).order_by("-date")[ + :6 + ] + + # + # PLANNED SKILLS + # + plan_list = ( + Plan.objects.filter(gymnast=gymnast, educative__in=(Skill.objects.all())) + .filter(Q(is_done=False) | Q(date__gte=date.today())) + .order_by("educative", "-date") + .distinct()[:6] + ) + + # + # NEXT EVENTS + # + next_event_list = Event.objects.filter( + gymnasts=gymnast.id, date_begin__gte=date_begin + ).order_by("date_begin")[:5] + + # + # NOTES + # + begin_of_the_week = date_begin + if date_begin.weekday() != 0: + begin_of_the_week -= timedelta(date_begin.weekday()) + + notes = ( + gymnast.remarks.filter(date__gte=begin_of_the_week) + .filter(status=1) + .order_by("date") + ) + + 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, + "gymnast": gymnast, + "today": date_begin, + "season": season, + "last_mindstate": mindstate_week_score["mean_mindstate_value"], + "mindstate_analyse": mindstate_analyse, + "last_height": height_weight_week_value["mean_height_value"], + "last_weigth": height_weight_week_value["mean_weight_value"], + "height_analyse": height_analyse, + "weight_analyse": weight_analyse, + "best_tof_straightjump": best_tof_straightjump, + "best_tof_routine_1": best_tof_routine_1, + "best_tof_routine_2": best_tof_routine_2, + "best_point_routine_1": best_point_routine_1, + "best_point_routine_2": best_point_routine_2, + "best_point_routine_3": best_point_routine_3, + "best_point_routine_4": best_point_routine_4, + "best_point_routine_5": best_point_routine_5, + "routine_1": routine_1, + "routine_2": routine_2, + "routine_3": routine_3, + "routine_4": routine_4, + "routine_5": routine_5, + "routine_1_done_stat": routine_1_done_stat, + "routine_2_done_stat": routine_2_done_stat, + "routine_3_done_stat": routine_3_done_stat, + "routine_4_done_stat": routine_4_done_stat, + "routine_5_done_stat": routine_5_done_stat, + "LEARNING_STEP_CHOICES": LEARNING_STEP_CHOICES, + "learned_skills": learned_skills, + "plan_list": plan_list, + "next_event_list": next_event_list, + "notes": notes, + } + + # return render(request, "gymnasts/reports/report_week.html", context) + + response = HttpResponse(content_type="application/pdf") + response[ + "Content-Disposition" + ] = "attachment; filename={lastname}-{firstname}-report.pdf".format( + lastname=gymnast.last_name, + firstname=gymnast.first_name, + ) + + html = render_to_string("people/gymnasts/reports/report_week.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 + + +@login_required +@require_http_methods(["GET"]) +def generate_timeline_report( + request, gymnast_id, season=None, week_number=None, date=None +): + """Génère une timeline pour un gymnaste. On va chercher tous les + - records (Chrono/ToF), + - points (compétition), + - nouveau apprentissage (learned skill) + - accident + - GymnastHasRoutine + eton les trie par date. + + """ + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + + if season is None: + date_begin = pendulum.now().date() + season, week_number = from_date_to_week_number(date_begin) + else: + date_begin, _ = from_week_number_to_date(season, week_number) + date_begin = date_begin.date() + + selected_record = [] + + list_record_straightjumps = list( + gymnast.chronos.filter(chrono_type=0).order_by("date") + ) + last_top_score = 0 + for record in list_record_straightjumps: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + list_record_first_routine = list( + gymnast.chronos.filter(chrono_type=1).order_by("date") + ) + last_top_score = 0 + for record in list_record_first_routine: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + list_record_second_routine = list( + gymnast.chronos.filter(chrono_type=2).order_by("date") + ) + last_top_score = 0 + for record in list_record_second_routine: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + list_record_third_routine = list( + gymnast.chronos.filter(chrono_type=3).order_by("date") + ) + last_top_score = 0 + for record in list_record_third_routine: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + list_record_fourth_routine = list( + gymnast.chronos.filter(chrono_type=4).order_by("date") + ) + last_top_score = 0 + for record in list_record_fourth_routine: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + list_record_fifth_routine = list( + gymnast.chronos.filter(chrono_type=5).order_by("date") + ) + last_top_score = 0 + for record in list_record_fifth_routine: + if record.score > last_top_score: + last_top_score = record.score + selected_record.append(record) + + # print(selected_record) + list_record = selected_record + list_record.extend(list(gymnast.accident.all().order_by("date"))) + # list_record.extend(list(gymnast.points.all().order_by("date"))) + list_record.extend(list(gymnast.known_skills.all().order_by("date"))) + + # print(list_record) + sorted_records = sorted(list_record, key=lambda x: x.date) + # print(sorted_records) + 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, + "gymnast": gymnast, + "today": date_begin, + "season": season, + "sorted_records": sorted_records, + } + # return render(request, "gymnasts/reports/report_timeline.html", context) + response = HttpResponse(content_type="application/pdf") + response[ + "Content-Disposition" + ] = "attachment; filename={lastname}-{firstname}-report-timeline.pdf".format( + lastname=gymnast.last_name, + firstname=gymnast.first_name, + ) + + html = render_to_string("gymnasts/reports/report_timeline.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 diff --git a/jarvis/planning/__init__.py b/jarvis/planning/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/planning/admin.py b/jarvis/planning/admin.py new file mode 100644 index 0000000..79fe3ad --- /dev/null +++ b/jarvis/planning/admin.py @@ -0,0 +1,58 @@ +from django.contrib import admin + +from .models import ( + EventType, + Event, + EventParticipation, +) + + +# def duplicate_record(modeladmin, request, queryset): +# '''*Custom action* permettant de dupliquer plusieurs enregistrements. +# ''' +# for object in queryset: +# object.id = None +# object.save() + + +# duplicate_record.short_description = 'Duplicate selected records' + + +class EventTypeAdmin(admin.ModelAdmin): + model = EventType + + list_display = ("name", "acronym") + ordering = ("name",) + search_fields = ("name", "acronym") + + +class EventAdmin(admin.ModelAdmin): + model = Event + + fields = ("name", "event_type", "place", "date_begin", "date_end", "informations") + list_display = ("name", "event_type", "place", "date_begin") + ordering = ("name",) + list_filter = ("event_type",) + search_fields = ("name",) + autocomplete_fields = ("event_type", "place") + date_hierarchy = "date_begin" + + # related_search_fields = { + # 'place': ('name', 'city'), + # } + + # filter_horizontal = ('gymnasts', 'club') + # filter_horizontal = ('gymnasts',) + + +class EventParticipationAdmin(admin.ModelAdmin): + model = EventParticipation + + fields = ("event", "gymnast", "rank") + list_display = ("event", "gymnast", "rank") + autocomplete_fields = ("event", "gymnast") + + +admin.site.register(EventType, EventTypeAdmin) +admin.site.register(Event, EventAdmin) +admin.site.register(EventParticipation, EventParticipationAdmin) diff --git a/jarvis/planning/apps.py b/jarvis/planning/apps.py new file mode 100644 index 0000000..e770dd4 --- /dev/null +++ b/jarvis/planning/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class PlanningConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.planning" diff --git a/jarvis/planning/forms.py b/jarvis/planning/forms.py new file mode 100644 index 0000000..9153802 --- /dev/null +++ b/jarvis/planning/forms.py @@ -0,0 +1,58 @@ +from datetime import date + +from django import forms + +from .models import Event + + +class EventForm(forms.ModelForm): + + # gymnasts = forms.ModelMultipleChoiceField(queryset=Gymnast.objects.all(), + # widget=FilteredSelectMultiple('Gymnast(s)', is_stacked=False)) + + class Meta: + model = Event + fields = ( + "name", + "date_begin", + "date_end", + "place", + "event_type", + "informations", + ) + widgets = { + "place": forms.HiddenInput(), + "event_type": forms.Select(attrs={"class": "form-control selectpicker"}), + "name": forms.TextInput( + attrs={"class": "form-control", "placeholder": "Even's name"} + ), + "date_begin": forms.DateTimeInput( + attrs={ + "class": "form-control datetimepicker", + "placeholder": date.today().strftime("%Y-%m-%d 08:00"), + } + ), + "date_end": forms.DateTimeInput( + attrs={ + "class": "form-control datetimepicker", + "placeholder": date.today().strftime("%Y-%m-%d 18:00"), + } + ), + "informations": forms.Textarea( + attrs={ + "class": "form-control", + "placeholder": "Informations about the event…", + } + ), + } + + place_related = forms.CharField( + required=False, + widget=forms.TextInput( + attrs={ + "class": "form-control", + "placeholder": "Searching place…", + "data-ref": "#id_place", + } + ), + ) diff --git a/jarvis/planning/migrations/0001_initial.py b/jarvis/planning/migrations/0001_initial.py new file mode 100644 index 0000000..2a50dbf --- /dev/null +++ b/jarvis/planning/migrations/0001_initial.py @@ -0,0 +1,126 @@ +# Generated by Django 3.2.8 on 2021-12-01 13:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("people", "0001_initial"), + ("location", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Event", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("datebegin", models.DateTimeField(verbose_name="Début")), + ("dateend", models.DateTimeField(blank=True, verbose_name="Fin")), + ( + "informations", + models.TextField( + blank=True, + help_text="Only MarkDown is authorized", + null=True, + verbose_name="Comments", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="Nom")), + ], + options={ + "verbose_name": "Event", + "verbose_name_plural": "Event", + }, + ), + migrations.CreateModel( + name="EventType", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="Nom")), + ("acronym", models.CharField(max_length=15, verbose_name="Acronyme")), + ], + options={ + "verbose_name": "Event Type", + "verbose_name_plural": "Event Types", + }, + ), + migrations.CreateModel( + name="Event_Participation", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("rank", models.PositiveSmallIntegerField(default=0)), + ( + "event", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="planning.event" + ), + ), + ( + "gymnast", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="people.gymnast" + ), + ), + ], + options={ + "verbose_name": "Event Participation", + }, + ), + migrations.AddField( + model_name="event", + name="eventtype", + field=models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + to="planning.eventtype", + verbose_name="Type", + ), + ), + migrations.AddField( + model_name="event", + name="gymnasts", + field=models.ManyToManyField( + related_name="participate_to", + through="planning.Event_Participation", + to="people.Gymnast", + verbose_name="Participants", + ), + ), + migrations.AddField( + model_name="event", + name="place", + field=models.ForeignKey( + default=None, + on_delete=django.db.models.deletion.CASCADE, + to="location.place", + ), + ), + ] diff --git a/jarvis/planning/migrations/0002_auto_20220102_1051.py b/jarvis/planning/migrations/0002_auto_20220102_1051.py new file mode 100644 index 0000000..6a1086d --- /dev/null +++ b/jarvis/planning/migrations/0002_auto_20220102_1051.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.8 on 2022-01-02 10:51 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('planning', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='datebegin', + field=models.DateTimeField(default=datetime.datetime(2022, 1, 2, 10, 51, 6, 441608, tzinfo=utc), verbose_name='Début'), + ), + migrations.AlterField( + model_name='event', + name='dateend', + field=models.DateTimeField(blank=True, null=True, verbose_name='Fin'), + ), + ] diff --git a/jarvis/planning/migrations/0003_alter_event_datebegin.py b/jarvis/planning/migrations/0003_alter_event_datebegin.py new file mode 100644 index 0000000..2fa751f --- /dev/null +++ b/jarvis/planning/migrations/0003_alter_event_datebegin.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2022-01-05 10:00 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('planning', '0002_auto_20220102_1051'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='datebegin', + field=models.DateTimeField(default=datetime.datetime(2022, 1, 5, 10, 0, 20, 481306, tzinfo=utc), verbose_name='Début'), + ), + ] diff --git a/jarvis/planning/migrations/0004_alter_event_datebegin.py b/jarvis/planning/migrations/0004_alter_event_datebegin.py new file mode 100644 index 0000000..b40dcd1 --- /dev/null +++ b/jarvis/planning/migrations/0004_alter_event_datebegin.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.8 on 2022-01-05 15:31 + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('planning', '0003_alter_event_datebegin'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='datebegin', + field=models.DateTimeField(default=datetime.datetime(2022, 1, 5, 15, 31, 53, 132264, tzinfo=utc), verbose_name='Début'), + ), + ] diff --git a/jarvis/planning/migrations/0005_alter_event_datebegin.py b/jarvis/planning/migrations/0005_alter_event_datebegin.py new file mode 100644 index 0000000..3526f4f --- /dev/null +++ b/jarvis/planning/migrations/0005_alter_event_datebegin.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.8 on 2022-01-06 09:11 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('planning', '0004_alter_event_datebegin'), + ] + + operations = [ + migrations.AlterField( + model_name='event', + name='datebegin', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='Début'), + ), + ] diff --git a/jarvis/planning/migrations/0006_rename_event_participation_eventparticipation.py b/jarvis/planning/migrations/0006_rename_event_participation_eventparticipation.py new file mode 100644 index 0000000..b85cea9 --- /dev/null +++ b/jarvis/planning/migrations/0006_rename_event_participation_eventparticipation.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.8 on 2022-01-08 07:30 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('people', '0001_initial'), + ('planning', '0005_alter_event_datebegin'), + ] + + operations = [ + migrations.RenameModel( + old_name='Event_Participation', + new_name='EventParticipation', + ), + ] diff --git a/jarvis/planning/migrations/0007_eventtype_color.py b/jarvis/planning/migrations/0007_eventtype_color.py new file mode 100644 index 0000000..0e41f69 --- /dev/null +++ b/jarvis/planning/migrations/0007_eventtype_color.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.8 on 2022-02-03 18:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('planning', '0006_rename_event_participation_eventparticipation'), + ] + + operations = [ + migrations.AddField( + model_name='eventtype', + name='color', + field=models.PositiveSmallIntegerField(choices=[(0, 'default'), (1, 'azure'), (2, 'green'), (3, 'orange'), (4, 'red')], default=0), + preserve_default=False, + ), + ] diff --git a/jarvis/planning/migrations/0008_rename_datebegin_event_date_begin_and_more.py b/jarvis/planning/migrations/0008_rename_datebegin_event_date_begin_and_more.py new file mode 100644 index 0000000..e50500c --- /dev/null +++ b/jarvis/planning/migrations/0008_rename_datebegin_event_date_begin_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.1 on 2022-09-23 07:42 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("planning", "0007_eventtype_color"), + ] + + operations = [ + migrations.RenameField( + model_name="event", + old_name="datebegin", + new_name="date_begin", + ), + migrations.RenameField( + model_name="event", + old_name="dateend", + new_name="date_end", + ), + migrations.RenameField( + model_name="event", + old_name="eventtype", + new_name="event_type", + ), + ] diff --git a/jarvis/planning/migrations/0009_alter_event_date_begin_alter_event_date_end.py b/jarvis/planning/migrations/0009_alter_event_date_begin_alter_event_date_end.py new file mode 100644 index 0000000..24b70a4 --- /dev/null +++ b/jarvis/planning/migrations/0009_alter_event_date_begin_alter_event_date_end.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.1 on 2022-10-06 12:51 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ("planning", "0008_rename_datebegin_event_date_begin_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="event", + name="date_begin", + field=models.DateTimeField( + default=django.utils.timezone.now, verbose_name="Begin" + ), + ), + migrations.AlterField( + model_name="event", + name="date_end", + field=models.DateTimeField( + default=django.utils.timezone.now, verbose_name="End" + ), + ), + ] diff --git a/jarvis/planning/migrations/__init__.py b/jarvis/planning/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/planning/models.py b/jarvis/planning/models.py new file mode 100644 index 0000000..94d7321 --- /dev/null +++ b/jarvis/planning/models.py @@ -0,0 +1,108 @@ +from datetime import datetime, time + +from django.db import models + +import pendulum + +from jarvis.tools.models import ( + Markdownizable, + Temporizable, + get_number_of_weeks_between, +) +from jarvis.people.models import Gymnast +from jarvis.location.models import Place + + +# User = get_user_model() + + +class EventType(models.Model): + """ + Classe représentant les types d'évènements. + + C'est un dictionnaire fini : + - compétiton qualificative, + - compétition finale, + - démonstration, + - … + """ + + COLOR_CHOICES = ( + (0, "default"), + (1, "azure"), + (2, "green"), + (3, "orange"), + (4, "red"), + ) + + class Meta: + verbose_name = "Event Type" + verbose_name_plural = "Event Types" + + name = models.CharField(max_length=255, verbose_name="Nom") + acronym = models.CharField(max_length=15, verbose_name="Acronyme") + color = models.PositiveSmallIntegerField(choices=COLOR_CHOICES) + + def __str__(self): + return "%s (%s)" % (self.name, self.acronym) + + +class Event(Markdownizable, Temporizable): + """Classe représentant les évènements. + + Un évènement est caractérisé par : + - un nom, + - un lieu (place), + - un type (compétition, démonstration, …), + - des gymnastes (participation prévue). + """ + + class Meta: + verbose_name = "Event" + verbose_name_plural = "Event" + + place = models.ForeignKey(Place, on_delete=models.CASCADE, default=None) + event_type = models.ForeignKey( + EventType, verbose_name="Type", on_delete=models.CASCADE, default=None + ) + name = models.CharField(max_length=255, verbose_name="Nom") + gymnasts = models.ManyToManyField( + Gymnast, + through="EventParticipation", + related_name="participate_to", + verbose_name="Participants", + ) + + def __str__(self): + return "%s (à %s)" % (self.name, self.place) + + def save(self, *args, **kwargs): + self.checkdates() + super().save(*args, **kwargs) + + def checkdates(self): + """ + Fonction assignant la date de fin d'un évènement à la date de début, si la date + de fin n'est pas définie, l'heure de fin est par défaut 18h00. + """ + if self.date_end is None and self.date_begin is not None: + self.date_end = datetime.combine(self.date_begin.date(), time(18, 0)) + + @property + def number_of_week_from_today(self): + today = pendulum.now().date() + return get_number_of_weeks_between(today, self.date_begin.date()) + + +class EventParticipation(models.Model): + """ """ + + class Meta: + verbose_name = "Event Participation" + + event = models.ForeignKey(Event, on_delete=models.CASCADE) + gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE) + rank = models.PositiveSmallIntegerField(default=0) + + def __str__(self): + return "%s to %s" % (self.gymnast, self.event) diff --git a/jarvis/planning/templates/events/create.html b/jarvis/planning/templates/events/create.html new file mode 100644 index 0000000..b484e28 --- /dev/null +++ b/jarvis/planning/templates/events/create.html @@ -0,0 +1,147 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    {% if event_id %}Update{% else %}Create{% endif %} Event

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.name }} + {% if form.name.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.date_begin }} + {% if form.date_begin.errors %} + + {% endif %} +
    +
    +
    + +
    +
    + {{ form.date_end }} + {% if form.date_end.errors %} + + {% endif %} +
    +
    +
    +
    + +
    + {{ form.event_type }} + {% if form.event_type.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.place }} + {{ form.place_related }} + {% if form.place.errors %} + + {% endif %} +
    +
    +
    + +
    + {{ form.informations }} + {% if form.informations.errors %} {% for error in form.informations.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/planning/templates/events/details.html b/jarvis/planning/templates/events/details.html new file mode 100644 index 0000000..959b9a4 --- /dev/null +++ b/jarvis/planning/templates/events/details.html @@ -0,0 +1,204 @@ +{% extends "base.html" %} +{% load has_group %} + +{% block content %} +
    +
    +
    +
    +

    {{ event.name }}

    +
    {{ event.event_type.name }}
    +
    +
    +
    +
    + {% if event.date_begin.date == event.date_end.date %} +

    The {{ event.date_begin | date:"d-m-Y" }} from {{ event.date_begin | date:"G:i" }} to {{ event.date_end | date:"G:i" }} + {% else %} +

    From {{ event.date_begin | date:"d-m-Y, G:i" }}
    + To {{ event.date_end | date:"d-m-Y, G:i" }} + {% endif %} + + {% if event.number_of_week_from_today > 0 %} +
    In {{event.number_of_week_from_today}} week(s) + {% endif %} +

    +
    +

    + {{ event.place.address }}
    + {{ event.place.postal }} {{ event.place.city }}
    + {{ event.place.country }} +

    + {% if event.place.nbkm %} +

    {{ event.place.nbkm }}km ({{ event.place.timing }}min)

    + {% endif %} +
    +
    + + +
    Participants
    + {% if request.user|has_group:"trainer" %} + + {% endif %} + + {% for gymnast in gymnast_list %} + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + {% endfor %} +
    + + + {{ gymnast.0 }} +
    + {% if not gymnast_list %} +

    No partifcipant for now.

    + {% endif %} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + {% if event.informations %} + {{ event.to_markdown | safe }} + {% else %} + No informations for this event. + {% endif %} + {% if request.user|has_group:"trainer" %} +

    + + + +

    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + + +{% endblock %} \ No newline at end of file diff --git a/jarvis/planning/templates/events/grid.html b/jarvis/planning/templates/events/grid.html new file mode 100644 index 0000000..c057e97 --- /dev/null +++ b/jarvis/planning/templates/events/grid.html @@ -0,0 +1,52 @@ +{% extends "base.html" %} +{% load static %} + +{% block content %} +
    +
    +
    +
    + {% if event_list %} +
    + {% else %} +

    There are no events corresponding to your criterias.

    + {% endif %} +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/planning/templates/events/list.html b/jarvis/planning/templates/events/list.html new file mode 100644 index 0000000..f855e4b --- /dev/null +++ b/jarvis/planning/templates/events/list.html @@ -0,0 +1,99 @@ +{% extends "listing.html" %} +{% load has_group %} + +{% block datacontent %} +
    +
    +
    +
    +

    Event Listing

    + {% if event_list %}Calendar{% endif %} +
    +
    +
    + {% if request.user|has_group:"trainer" %} + + + + {% endif %} +
    +
    +
    +
    +
    +
    + {% if event_list %} + + + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + + + {% for event in event_list %} + + {% if request.user|has_group:"trainer" %} + + {% endif %} + + + + + + + {% endfor %} + +
    EventTypeDate# weekPlace
    + + + + {{ event.name }}{{ event.event_type.name }}{{ event.date_begin | date:"d-m-Y"}} + {% if event.number_of_week_from_today < 0 %} + {{event.number_of_week_from_today}} + {% else %} + {{event.number_of_week_from_today}} + {% endif %} + {{ event.place }}
    + {% else %} +

    There are no events corresponding to your criterias.

    + {% endif %} +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} \ No newline at end of file diff --git a/jarvis/planning/urls.py b/jarvis/planning/urls.py new file mode 100644 index 0000000..d45059b --- /dev/null +++ b/jarvis/planning/urls.py @@ -0,0 +1,25 @@ +from django.urls import path + +from . import views + + +# Event +event_urlpatterns = [ + path(r"lookup/", views.event_lookup, name="event_lookup"), + path(r"search/", views.event_listing, name="event_search"), + path(r"add/", views.event_create_or_update, name="event_create"), + path(r"edit//", views.event_create_or_update, name="event_update"), + path( + r"link_with_gymnast/", + views.link_gymnast_to_event, + name="link_gymnast_to_event", + ), + path( + r"unlink_gymnast/", + views.unlink_gymnast_from_event, + name="unlink_gymnast_from_event", + ), + path(r"/", views.event_detail, name="event_details"), + path(r"calendar/", views.calendar, name="calendar"), + path(r"", views.event_listing, name="event_list"), +] diff --git a/jarvis/planning/views.py b/jarvis/planning/views.py new file mode 100644 index 0000000..e9d7764 --- /dev/null +++ b/jarvis/planning/views.py @@ -0,0 +1,176 @@ +from django.contrib.auth.decorators import login_required +from django.http import HttpResponseRedirect, HttpResponse, JsonResponse +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse + +from jarvis.people.models import Gymnast + +from .models import ( + Event, + EventParticipation, +) + +from .forms import EventForm + + +@login_required +@require_http_methods(["POST"]) +def event_lookup(request): + """ + Récupère la liste de tous évènements suivant un pattern si celui-ci est + définit. + """ + results = [] + pattern = request.POST.get("pattern", None) + + if pattern is not None and len(pattern) >= 3: + model_results = Event.objects.filter(name__icontains=pattern).order_by( + "date_begin" + ) + results = [{"ID": x.id, "Name": str(x)} for x in model_results] + return JsonResponse(results, safe=False) + + +@login_required +@require_http_methods(["GET", "POST"]) +def event_create_or_update(request, event_id=None): + """ + Création ou mise à jour d'un évènement. + """ + + if event_id: + event = get_object_or_404(Event, pk=event_id) + data = {"place_related": event.place} + else: + event = None + data = {} + + if request.method == "POST": + form = EventForm(request.POST, instance=event) + + if form.is_valid(): + event = form.save() + return HttpResponseRedirect(reverse("event_details", args=(event.pk,))) + else: + return render(request, "objectives/routines/create.html", {"form": form}) + + form = EventForm(instance=event, initial=data) + context = {"form": form, "event_id": event_id} + return render(request, "events/create.html", context) + + +@require_http_methods(["POST"]) +def link_gymnast_to_event(request): + """Crée un lien entre un gymnaste et un évènement. + + Returns: + Si tout se passe bien, un code 200 (Success) est retourné. + + Excepts: + Si une erreur se produit lors de l'association, un code HTTP 409 (Conflict) est retourné. + + Remarks: + Tu ne veux pas retourner le lien qui vient d'être créé ? + """ + try: + event_id = request.POST.get("event_id", None) + gymnast_id = request.POST.get("gymnast_id", None) + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + event = get_object_or_404(Event, pk=event_id) + EventParticipation.objects.create(gymnast=gymnast, event=event) + except Exception: + return HttpResponse(409) + + return HttpResponse(200) + + +@require_http_methods(["POST"]) +def unlink_gymnast_from_event(request): + """Supprime le lien entre un gymnaste et un évènement. + + Remarks: + Fonctions `link_gymnast_to_event` et `unlink_gymnast_from_event` sont _très_ similaires. + Il faudrait sans doute mieux passer par une CBV, voire mieux: DRF ;-) + Surtout qu'ici, on gère directement des `link_between_g_and_e`, à ajouter ou supprimer. + """ + event_id = request.POST.get("event_id", None) + gymnast_id = request.POST.get("gymnast_id", None) + + try: + EventParticipation.objects.get(event=event_id, gymnast=gymnast_id).delete() + except Exception: + return HttpResponse(409) + + return HttpResponse(200) + + +def __get_event_list(request): + """Récupère une liste d'évènement. + + Par défaut, la liste est triée chronologiquement - le plus ancien étant le premier élément. + + Cette fonction est utilisée pour l'affichage des évènements et au niveau du calendrier. + + Args: + request (HttpRequest): La requête en entrée + pattern (str?): Optionnel. Permet de spécifier un pattern à appliquer à la recherche. + """ + pattern = request.GET.get("pattern", None) + + if pattern: + event_list = Event.objects.filter(name__icontains=pattern) + else: + event_list = Event.objects.all() + + return event_list.order_by("date_begin") + + +@login_required +@require_http_methods(["GET"]) +def calendar(request): + """Récupère la liste de tous évènements suivant un pattern si celui-ci est définit.""" + event_list = __get_event_list(request) + context = {"event_list": event_list} + return render(request, "events/grid.html", context) + + +@login_required +@require_http_methods(["GET"]) +def event_listing(request): + """ + Récupère la liste de tous évènements suivant un pattern si celui-ci est + définit. + """ + event_list = __get_event_list(request) + context = {"event_list": event_list} + return render(request, "events/list.html", context) + + +@login_required +@require_http_methods(["GET"]) +def event_detail(request, event_id=None): + """ + Récupère toutes les informations d'un event. + + :return: une instance de la classe `Event` et une liste de gymnastes liés + à l'évènement. + """ + # event = get_object_or_404(Event, pk=event_id) + event = Event.objects.get(pk=event_id) + + gymnast_list = [] + # today = timezone.now().date() + # tohour = timezone.now().time() + + # if event.date_begin.date() > today: + # for gymnast in event.gymnasts.all(): + # # pass + # gymnast_list.append((gymnast, counted, int((counted / 16) * 100))) + # else: + # gymnast_list = [(x, None, None) for x in event.gymnasts.all()] + gymnast_list = [(x, None, None) for x in event.gymnasts.all()] + + context = {"event": event, "gymnast_list": gymnast_list} + + return render(request, "events/details.html", context) diff --git a/jarvis/profiles/__init__.py b/jarvis/profiles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/profiles/admin.py b/jarvis/profiles/admin.py new file mode 100644 index 0000000..2a30c5b --- /dev/null +++ b/jarvis/profiles/admin.py @@ -0,0 +1,27 @@ +from django.contrib import admin +from .models import Profile, Notification +from django_admin_listfilter_dropdown.filters import ( + ChoiceDropdownFilter, + RelatedDropdownFilter, +) + + +class ProfileAdmin(admin.ModelAdmin): + model = Profile + list_display = ("user", "template_color", "sidebar_color") + autocomplete_fields = ("user",) + + +class NotificationAdmin(admin.ModelAdmin): + model = Notification + list_display = ("user", "gymnast", "functionality") + autocomplete_fields = ("user", "gymnast") + list_filter = ( + ("user", RelatedDropdownFilter), + ("gymnast", RelatedDropdownFilter), + ("functionality", ChoiceDropdownFilter), + ) + + +admin.site.register(Profile, ProfileAdmin) +admin.site.register(Notification, NotificationAdmin) diff --git a/jarvis/profiles/apps.py b/jarvis/profiles/apps.py new file mode 100644 index 0000000..3e12c84 --- /dev/null +++ b/jarvis/profiles/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ProfileConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "jarvis.profiles" diff --git a/jarvis/profiles/forms.py b/jarvis/profiles/forms.py new file mode 100644 index 0000000..18478f0 --- /dev/null +++ b/jarvis/profiles/forms.py @@ -0,0 +1,26 @@ +from django import forms + +from .models import Profile + + +class ProfileForm(forms.ModelForm): + class Meta: + model = Profile + fields = ( + "template_color", + "sidebar_color", + "is_sidebar_minified", + ) + widgets = { + "template_color": forms.Select( + attrs={"class": "form-control selectpicker"} + ), + "sidebar_color": forms.Select(attrs={"class": "form-control selectpicker"}), + "is_sidebar_minified": forms.CheckboxInput( + attrs={ + "class": "bootstrap-switch mt-0", + "data-on-label": '', + "data-off-label": '', + } + ), + } diff --git a/jarvis/profiles/migrations/0001_initial.py b/jarvis/profiles/migrations/0001_initial.py new file mode 100644 index 0000000..b873a39 --- /dev/null +++ b/jarvis/profiles/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 3.2.8 on 2021-12-01 13:03 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Profile", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "template_color", + models.PositiveSmallIntegerField( + choices=[(0, "Dark"), (1, "Light")], + default=0, + verbose_name="Template", + ), + ), + ( + "sidebar_color", + models.PositiveSmallIntegerField( + choices=[ + (0, "Purple"), + (1, "Blue"), + (2, "Green"), + (3, "Orange"), + (4, "Red"), + ], + default=0, + verbose_name="Sidebar", + ), + ), + ("is_sidebar_minified", models.BooleanField(default=False)), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/jarvis/profiles/migrations/0002_notification.py b/jarvis/profiles/migrations/0002_notification.py new file mode 100644 index 0000000..6267e76 --- /dev/null +++ b/jarvis/profiles/migrations/0002_notification.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2 on 2023-04-23 06:42 + +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", "0008_alter_gymnast_orientation"), + ("profiles", "0001_initial"), + ] + + operations = [ + migrations.CreateModel( + name="Notification", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "functionality", + models.PositiveSmallIntegerField( + choices=[ + (0, "Chrono"), + (1, "Accident"), + (2, "LearnedSkill"), + (3, "Plan"), + (4, "Point"), + (5, "MindState"), + (6, "GymnastHasRoutine"), + (7, "NumberOfRoutineDone"), + (8, "HeightWeight"), + (9, "Note"), + (10, "Intensity"), + (11, "SeasonInformation"), + ] + ), + ), + ( + "gymnast", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="notifications", + to="people.gymnast", + ), + ), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "unique_together": {("user", "gymnast", "functionality")}, + }, + ), + ] diff --git a/jarvis/profiles/migrations/__init__.py b/jarvis/profiles/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/profiles/models.py b/jarvis/profiles/models.py new file mode 100644 index 0000000..9e14f2c --- /dev/null +++ b/jarvis/profiles/models.py @@ -0,0 +1,74 @@ +"""Extension et gestion des profils utilisateurs. + +Les profils peuvent enregistrer les informations suivantes: + +* le type de template, +* la couleur de la barre de navigation +* si la barre de navigation doit être cachée ou non +""" + +from django.contrib.auth import get_user_model +from django.db import models +from jarvis.people.models import Gymnast + +FUNCTIONALITY_CHOICES = ( + (0, "Chrono"), + (1, "Accident"), + (2, "LearnedSkill"), + (3, "Plan"), + (4, "Point"), + (5, "MindState"), + (6, "GymnastHasRoutine"), + (7, "NumberOfRoutineDone"), + (8, "HeightWeight"), + (9, "Note"), + (10, "Intensity"), + (11, "SeasonInformation"), +) + +User = get_user_model() + + +class Profile(models.Model): + """Classe étendant les informations des utilisateurs/entraineurs de l'application. + + References: + * https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html + """ + + TEMPLATE_CHOICES = ((0, "Dark"), (1, "Light")) + SIDEBAR_CHOICES = ( + (0, "Purple"), + (1, "Blue"), + (2, "Green"), + (3, "Orange"), + (4, "Red"), + ) + + user = models.OneToOneField(User, on_delete=models.CASCADE) + template_color = models.PositiveSmallIntegerField( + choices=TEMPLATE_CHOICES, verbose_name="Template", default=0 + ) + sidebar_color = models.PositiveSmallIntegerField( + choices=SIDEBAR_CHOICES, verbose_name="Sidebar", default=0 + ) + is_sidebar_minified = models.BooleanField(default=False) + + def __str__(self): + return "%s %s" % (self.user.first_name, self.user.last_name) + + +class Notification(models.Model): + """Classe permettant de définir quelles notification un utilisateur veut recevoir.""" + + class Meta: + unique_together = ("user", "gymnast", "functionality") + + user = models.ForeignKey(User, on_delete=models.CASCADE) + gymnast = models.ForeignKey( + Gymnast, on_delete=models.CASCADE, related_name="notifications" + ) + functionality = models.PositiveSmallIntegerField(choices=FUNCTIONALITY_CHOICES) + + def __str__(self): + return f"{self.user} will be notified for add/update {self.functionality} to {self.gymnast}" diff --git a/jarvis/profiles/templates/notification_update.html b/jarvis/profiles/templates/notification_update.html new file mode 100644 index 0000000..d20c0f2 --- /dev/null +++ b/jarvis/profiles/templates/notification_update.html @@ -0,0 +1,79 @@ +{% extends "listing.html" %} + +{% block datacontent %} +
    +
    +
    +
    +
    +
    +

    Notifications

    +
    +
    +
    +
    +
    + {% for gymnast in gymnast_list %} +
    +
    + {{ gymnast }} +
    +
    +
    + {% for functionality in functionality_list %} +
    + {{ functionality.1 }} +
    +
    + +
    + {% endfor %} +
    +
    + {% endfor %} +
    +
    +
    +
    +
    +{% endblock %} + +{% block footerscript %} + +{% endblock %} diff --git a/jarvis/profiles/templates/update.html b/jarvis/profiles/templates/update.html new file mode 100644 index 0000000..70adf2b --- /dev/null +++ b/jarvis/profiles/templates/update.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} + +{% block content %} +
    +
    +
    +
    +

    Edit User Profile

    +
    +
    +
    + {% csrf_token %} +
    + +
    + {{ form.template_color }} + {% if form.template_color.errors %} {% for error in form.template_color.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.is_sidebar_minified }} + {% if form.is_sidebar_minified.errors %} {% for error in form.is_sidebar_minified.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    + +
    + {{ form.sidebar_color }} + {% if form.sidebar_color.errors %} {% for error in form.sidebar_color.errors %}{{ error }}{% endfor %}{% endif %} +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + + + +{% endblock %} \ No newline at end of file diff --git a/jarvis/profiles/tests_urls.py b/jarvis/profiles/tests_urls.py new file mode 100644 index 0000000..5c68bde --- /dev/null +++ b/jarvis/profiles/tests_urls.py @@ -0,0 +1,15 @@ +from django.test import TestCase +from django.urls import reverse, resolve + +# from jarvis.profiles.views import profile_update + + +class URLTestCase(TestCase): + def test_profile_url(self): + # self.assertEqual(reverse("profile_update"), profile_update()) + + # url = reverse("profile_update") + # response = self.client.get(url) + # self.assertEqual(response.status_code, 200) + + self.assertEqual(resolve("/profile/edit/").view_name, "profile_update") diff --git a/jarvis/profiles/tests_views.py b/jarvis/profiles/tests_views.py new file mode 100644 index 0000000..8bec857 --- /dev/null +++ b/jarvis/profiles/tests_views.py @@ -0,0 +1,40 @@ +from django.test import RequestFactory, TestCase, Client +from django.urls import reverse, resolve +from jarvis.profiles.views import profile_update +from django.contrib.auth.models import AnonymousUser, User + + +class ViewsTestCase(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.user = User.objects.create_user( + username="test", email="test@gmail.com", password="top_secret" + ) + + def test_view_profile_update(self): + request = self.factory.get("/profile/edit/") + # request = self.client.get('/profile/edit/') ## Ne fonctionne pas à cause de whitenoise ? + + # Not connected user + request.user = AnonymousUser() + response = profile_update(request) + self.assertEqual(response.status_code, 302) + + # Connected user + # no idea… :'( + # request.user = self.user + # response = profile_update(request) + # self.assertEqual(response.status_code, 200) + + # client = Client() + # response = client.get(reverse('profile_update')) + # self.assertEqual(response.status_code, 200) # 302 + + # client = Client() + # response = self.client.get(reverse('profile_update')) + # # print(response) + # self.assertContains(response, 'font_awesome_all_5.15.3.css') + + client = Client() + response = client.get("/profile/edit/") + self.assertEqual(response.status_code, 302) diff --git a/jarvis/profiles/urls.py b/jarvis/profiles/urls.py new file mode 100644 index 0000000..0c7591b --- /dev/null +++ b/jarvis/profiles/urls.py @@ -0,0 +1,17 @@ +"""URLs définissant la gestion des profils utilisateurs.""" + +from django.urls import path + +from . import views + + +profile_urlpatterns = [ + path(r"edit/", views.profile_update, name="profile_update"), + path( + r"notification_update/", views.notification_update, name="notification_update" + ), + path(r"notification_add/", views.notification_add, name="notification_add"), + path( + r"notification_remove/", views.notification_remove, name="notification_remove" + ), +] diff --git a/jarvis/profiles/views.py b/jarvis/profiles/views.py new file mode 100644 index 0000000..128ddb3 --- /dev/null +++ b/jarvis/profiles/views.py @@ -0,0 +1,93 @@ +"""Vues de gestion des profils utilisateurs.""" + +from django.contrib.auth.decorators import login_required +from django.contrib.auth import get_user_model +from django.http import HttpResponseRedirect, HttpResponse +from django.shortcuts import render, get_object_or_404 +from django.views.decorators.http import require_http_methods +from django.urls import reverse + +from .forms import ProfileForm +from .models import Profile, Notification, FUNCTIONALITY_CHOICES +from jarvis.people.models import Gymnast + + +User = get_user_model() + + +@login_required +@require_http_methods(["GET", "POST"]) +def profile_update(request): + """Modification du profil de l'utilisateur connecté""" + + try: + profile = request.user.profile + except Exception: # don't do this ! + profile = Profile.objects.create(user=request.user) + + if request.method == "POST": + form = ProfileForm(request.POST, instance=profile) + + if form.is_valid(): + form.save() + request.session["template"] = profile.template_color + request.session["sidebar"] = profile.sidebar_color + request.session["is_sidebar_minified"] = profile.is_sidebar_minified + return HttpResponseRedirect(reverse("home")) + + else: + form = ProfileForm(instance=profile) + + context = { + "form": form, + } + return render(request, "update.html", context) + + +@login_required +@require_http_methods(["GET", "POST"]) +def notification_update(request): + gymnast_list = Gymnast.objects.filter(is_active=True) + + context = { + "gymnast_list": gymnast_list, + "functionality_list": FUNCTIONALITY_CHOICES, + } + return render(request, "notification_update.html", context) + + +@require_http_methods(["POST"]) +def notification_add(request): + """ + Ajoute une demande de notification + """ + gymnast_id = request.POST.get("gymnast_id", None) + notification_id = request.POST.get("notification_id", None) + + gymnast = get_object_or_404(Gymnast, pk=gymnast_id) + row, created = Notification.objects.get_or_create( + user=request.user, gymnast=gymnast, functionality=notification_id + ) + + if created: + return HttpResponse(200, (row, created)) # devrait être un 201 + else: + return HttpResponse(400, (row, created)) + + +@require_http_methods(["POST"]) +def notification_remove(request): + """ + Supprime une demande de notification + """ + gymnast_id = request.POST.get("gymnast_id", None) + notification_id = request.POST.get("notification_id", None) + + try: + Notification.objects.get( + user=request.user, gymnast=gymnast_id, functionality=notification_id + ).delete() + except Exception: + return HttpResponse(409) + + return HttpResponse(200) diff --git a/jarvis/tools/__init__.py b/jarvis/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/tools/date_week_transition.py b/jarvis/tools/date_week_transition.py new file mode 100644 index 0000000..dbe12e9 --- /dev/null +++ b/jarvis/tools/date_week_transition.py @@ -0,0 +1,138 @@ +""" Module de manipulation de dates/semaines. +Ce module contient un ensemble de fonction permettant de passer de date en numéro de semaine et +vice-versa (de numéro de semaine en date). +""" + +import pendulum + + +def from_week_number_to_date(season, week_number): + """Renvoie la date du début de semaine (lundi) et la date de fin de semaine (dimanche) à + partir d'une saison et d'un numéro de semaine. + + Args: + season str saison (ex : "2022-2023") + week_number int numéro de semaine + + Returns : + start_date Date du lundi de la semaine + end_date Date du dimanche de la semaine + + Examples: + >>> from jarvis.tools.date_week_transition import from_week_number_to_date + >>> from_week_number_to_date("2022-2023", 6) + >>> (DateTime(2022, 10, 3, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2022, 10, 9, 0, 0, 0, tzinfo=Timezone('UTC'))) + + >>> from_week_number_to_date("2022-2023", 22) + >>> (DateTime(2023, 1, 30, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2023, 2, 5, 0, 0, 0, tzinfo=Timezone('UTC'))) + + >>> from_week_number_to_date("2022-2023", 44) + >>> (DateTime(2023, 7, 3, 0, 0, 0, tzinfo=Timezone('UTC')), DateTime(2023, 7, 9, 0, 0, 0, tzinfo=Timezone('UTC'))) + """ + + dash = season.find("-") + first_year = season[:dash] + season_begin = pendulum.parse(str(first_year) + "09-01") + season_first_week = season_begin.week_of_year + rest_of_week = 52 - season_first_week + + week_string = "-W" + if week_number <= rest_of_week: + week_number += season_first_week - 1 + year = first_year + else: + week_number -= rest_of_week + year = str(season[dash + 1 :]) + + if week_number - rest_of_week < 10: + week_string += "0" + + start_date = pendulum.parse(year + week_string + str(week_number)) + end_date = start_date.add(days=6) + return start_date, end_date + + +def from_date_to_week_number(the_date=None): + """Renvoie le numéro de la semaine depuis le début de la saison. + Une saison commence le 1er septembre. + La semaine contenant le 1er septembre est la semaine 1, même si le 1er septembre est un + diamnche. + + Args: + date date date à convertir + + Returns : + int nombre de semaine entre la date en paramètre et le début de la saison. + + Examples: + >>> from jarvis.tools.date_week_transition import from_date_to_week_number + >>> import pendulum + >>> from_date_to_week_number(pendulum.date(2022, 9, 1)) + >>> 1 + + >>> from_date_to_week_number(pendulum.date(2023, 2, 5)) + >>> 22 + + >>> from_date_to_week_number(pendulum.date(2023, 7, 5)) + >>> 44 + """ + + if the_date is None: + the_date = pendulum.today().date() + else: + the_date = pendulum.parse(the_date.strftime("%Y%m%d")) + + number_of_year_week = the_date.week_of_year + if the_date.month >= 9: # nouvelle saison + season = str(the_date.year) + "-" + str(the_date.year + 1) + begin_season = pendulum.date(the_date.year, 9, 1) + season_first_week = begin_season.week_of_year + number_of_season_week = number_of_year_week + 1 - season_first_week + else: # "ancienne" saison + season = str(the_date.year - 1) + "-" + str(the_date.year) + begin_season = pendulum.date(the_date.year - 1, 9, 1) + season_first_week = begin_season.week_of_year + number_of_season_week = number_of_year_week + (52 - season_first_week) + + return season, number_of_season_week + + +def get_number_of_weeks_between(first_date, second_date): + """ + Renvoie le nombre de semaines entre deux dates. + Par extension, cela permet de connaitre le nombre d'occurence d'un + évènement (entraînement, par exemple) hebdromadaire entre deux dates + et ainsi pouvoir plannifier. + + Args: + first_date date date du premier évènement + second_date date date du second évènement + + Returns : + int nombre de semaine entre les deux dates + + Examples: + >>> from jarvis.tools.date_week_transition import get_number_of_weeks_between + >>> import pendulum + >>> get_number_of_weeks_between(pendulum.date(2022, 9, 1), pendulum.date(2022, 9, 4)) + >>> 0 + + >>> get_number_of_weeks_between(pendulum.date(2022, 9, 1), pendulum.date(2023, 2, 5)) + >>> 22 + + >>> get_number_of_weeks_between(pendulum.date(2022, 9, 1), pendulum.date(2023, 7, 5)) + >>> 44 + + >>> get_number_of_weeks_between(pendulum.date(2023, 2, 5), pendulum.date(2023, 7, 5)) + >>> 22 + """ + + first = pendulum.parse(first_date.strftime("%Y%m%d")) + second = pendulum.parse(second_date.strftime("%Y%m%d")) + + if first_date.year == second.year: + return second.week_of_year - first.week_of_year + elif second_date.year > first_date.year: + return (52 - first.week_of_year) + second.week_of_year + else: + return (52 - second.week_of_year) + first.week_of_year diff --git a/jarvis/tools/models.py b/jarvis/tools/models.py new file mode 100644 index 0000000..2501abe --- /dev/null +++ b/jarvis/tools/models.py @@ -0,0 +1,218 @@ +"""Ensemble des classes d'utilité publique :-)""" + +from django.db import models +from django.utils import timezone + +from datetime import date +from .date_week_transition import ( + get_number_of_weeks_between, + from_date_to_week_number, +) +import markdown +import pendulum +import re + + +class Season: + """Class pour représenter une saison""" + + def __init__(self, label=None): + self.label = label + if self.label is None or not self.is_valid(): + the_date = pendulum.today().date() + if the_date.month >= 9: # nouvelle saison + self.label = str(the_date.year) + "-" + str(the_date.year + 1) + else: + self.label = str(the_date.year - 1) + "-" + str(the_date.year) + + def is_valid(self): + """Test si une chaine de caractère correspond bien à une saison + + Args: + season string saison sous la forme "xxxx-xxxy" + + Returns: + bool vrai si la chaine de caractère correspond à une saison. + + Examples: + >>> from jarvis.tools.date_week_transition import is_valid_season + >>> is_valid_season("2022-2023") + >>> True + + >>> is_valid_season("2022-2024") + >>> False + + >>> is_valid_season("2024-2023") + >>> False + + >>> is_valid_season("1358-5682") + >>> False + + >>> is_valid_season("fgkrs-gkml") + >>> False + + >>> is_valid_season("drgnldsjgklfdtngl") + >>> False + """ + dash_position = self.label.find("-") + if dash_position < 0: + return False + + pattern = "^[0-9]{4}-[0-9]{4}$" + if not re.search(pattern, self.label): + return False + + first_year = int(self.label[:dash_position]) + second_year = int(self.label[dash_position + 1 :]) + + if first_year != second_year - 1: + return False + + return True + + def __str__(self): + return "%s" % (self.label) + + +def get_default_date(): + return pendulum.now().date() + + +class Seasonisable(models.Model): + """ """ + + class Meta: + abstract = True + + date = models.DateField(default=get_default_date, verbose_name="Date") + season = models.CharField(max_length=9, editable=False) + week_number = models.PositiveSmallIntegerField(editable=False) + + def save(self, *args, **kwargs): + """Calcule les valeurs `season` et `week_number` sur base d'une date lors de l' + enregistrement d'un object enfant. + """ + if self.date is None: + self.date = get_default_date() + self.season, self.week_number = from_date_to_week_number(self.date) + super().save(*args, **kwargs) + + @property + def is_past(self): + return pendulum.now().date() > self.date + + +class TemporizableQuerySet(models.QuerySet): + """ + Classe permettant de spécifier le `QuerySet` de la classe `Temporizable`. + """ + + def next(self, limit): + """ + Renvoie la liste des prochains "temporizable" (par rapport à la date du jour). + + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables. + """ + return self.filter(date_begin__gte=timezone.now()).order_by("date_begin")[ + 0:limit + ] + + def last(self, limit): + """ + Renvoie la liste des derniers "temporizable" (par rapport à la date du jour). + + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables + """ + return self.filter(date_end__lte=timezone.now()).order_by("-date_end")[0:limit] + + # def get(self, date_string): + # """ + # """ + # try: + # selected_object = self.get(date_begin__lte=date_string, date_end__gte=date_string) + # except self.DoesNotExist: + # return None + # except self.MultipleObjectsReturned: + # return None + + # return selected_object + + +class Temporizable(models.Model): + """ + Classe abstraite définissant une période comprise entre deux dates. + """ + + class Meta: + abstract = True + + date_begin = models.DateTimeField(verbose_name="Begin", default=timezone.now) + date_end = models.DateTimeField(verbose_name="End", default=timezone.now) + + objects = models.Manager.from_queryset(TemporizableQuerySet)() + + def get_total_occurence(self): + """ + Renvoie le nombre de semaines entre les deux dates d'une instance de la + classe `Temporizable`. + + :return: nombre de semaines. + """ + return get_number_of_weeks_between(self.date_begin.date(), self.date_end.date()) + + def get_number_of_occurence_to_event(self, the_date): + """ + Renvoie le nombre semaines entre une date choisie et le début + (date_begin) d'une instance de la classe `Temporizable`. + + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :return: nombre de semaines. + """ + return get_number_of_weeks_between(the_date, self.date_begin.date()) + + def get_number_of_occurence_inbetween(self, the_date, rest=True): + """ + Renvoie le nombre semaines entre une date choisie et une instance de la + classe `Temporizable`. Le calcul peut se faire soit entre la date + choisie et le date de fin d'une occurence de la classe, soit entre la + date de début d'une occurence de la classe et la date choisie. + + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :param rest: paramètre définissant s'il faut calculer le reste des + occurences à venir (depuis `the_date` jusqu'à la date de fin) ou + les occurences déjà passées (depuis la date de début jusqu'à + `the_date`) + :type rest: booléen + :return: nombre de semaines. + """ + if rest: + return get_number_of_weeks_between(the_date, self.date_end.date()) + + return get_number_of_weeks_between(self.date_begin.date(), the_date) + + +class Markdownizable(models.Model): + """ + Classe abstraite ajoutant un champ `informations`, convertible de .md -> .html. + """ + + class Meta: + abstract = True + + informations = models.TextField( + null=True, + blank=True, + verbose_name="Comments", + help_text="Only MarkDown is authorized", + ) + + def to_markdown(self): + """Convertit le champ `informations` en (Github-flavored) Markdown.""" + + return markdown.markdown(self.informations) diff --git a/jarvis/tools/templatetags/__init__.py b/jarvis/tools/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jarvis/tools/templatetags/has_group.py b/jarvis/tools/templatetags/has_group.py new file mode 100644 index 0000000..dd9045d --- /dev/null +++ b/jarvis/tools/templatetags/has_group.py @@ -0,0 +1,9 @@ +from django import template + + +register = template.Library() + + +@register.filter +def has_group(user, group_name): + return user.groups.filter(name=group_name).exists() diff --git a/jarvis/tools/templatetags/is_user_equal_to_gymnast.py b/jarvis/tools/templatetags/is_user_equal_to_gymnast.py new file mode 100644 index 0000000..d4208ed --- /dev/null +++ b/jarvis/tools/templatetags/is_user_equal_to_gymnast.py @@ -0,0 +1,9 @@ +from django import template + + +register = template.Library() + + +@register.filter +def is_user_equal_to_gymnast(user, gymnast_id): + return user.gymnast.id == gymnast_id diff --git a/jarvis/tools/templatetags/menuitems.py b/jarvis/tools/templatetags/menuitems.py new file mode 100644 index 0000000..d2ded07 --- /dev/null +++ b/jarvis/tools/templatetags/menuitems.py @@ -0,0 +1,50 @@ +from django import template +from django.utils.html import format_html +from django.urls import reverse + + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def menuitem(context, url, css_class, title): + url = reverse(url) + # css_class = "" + css_class + + if len(url) > 1: + if context.request.path.startswith(url): + return format_html( + '
  • {}

  • ', + url, + css_class, + title, + ) + + if url == "/admin/": + return format_html( + '
  • {}

  • ', + url, + css_class, + title, + ) + return format_html( + '
  • {}

  • ', + url, + css_class, + title, + ) + + if context.request.path == url: # si le contexte est "/" + return format_html( + '
  • {}

  • ', + url, + css_class, + title, + ) + + return format_html( + '
  • {}

  • ', + url, + css_class, + title, + ) diff --git a/jarvis/tools/tests_date_week_transition.py b/jarvis/tools/tests_date_week_transition.py new file mode 100644 index 0000000..8a2a4b8 --- /dev/null +++ b/jarvis/tools/tests_date_week_transition.py @@ -0,0 +1,73 @@ +""" Test du module date_week_transition """ + +from django.test import TestCase +from datetime import datetime +import pytz + +from jarvis.tools.date_week_transition import ( + from_date_to_week_number, + from_week_number_to_date, + get_number_of_weeks_between, +) + +import pendulum + + +class FunctionTestCase(TestCase): + def test_from_week_number_to_date(self): + timezone = pytz.timezone("UTC") + + start_date = datetime(2022, 10, 3, 0, 0, 0, tzinfo=pytz.UTC) + end_date = datetime(2022, 10, 9, 0, 0, 0, tzinfo=pytz.UTC) + self.assertEqual( + from_week_number_to_date("2022-2023", 6), (start_date, end_date) + ) + + start_date = datetime(2023, 1, 30, 0, 0, 0, tzinfo=pytz.UTC) + end_date = datetime(2023, 2, 5, 0, 0, 0, tzinfo=pytz.UTC) + self.assertEqual( + from_week_number_to_date("2022-2023", 22), (start_date, end_date) + ) + + start_date = datetime(2023, 7, 3, 0, 0, 0, tzinfo=pytz.UTC) + end_date = datetime(2023, 7, 9, 0, 0, 0, tzinfo=pytz.UTC) + self.assertEqual( + from_week_number_to_date("2022-2023", 44), (start_date, end_date) + ) + + def test_from_date_to_week_number(self): + self.assertEqual( + from_date_to_week_number(pendulum.date(2022, 9, 1)), ("2022-2023", 1) + ) + self.assertEqual( + from_date_to_week_number(pendulum.date(2023, 2, 5)), ("2022-2023", 22) + ) + self.assertEqual( + from_date_to_week_number(pendulum.date(2023, 7, 5)), ("2022-2023", 44) + ) + + def test_get_number_of_weeks_between(self): + self.assertEqual( + get_number_of_weeks_between( + pendulum.date(2022, 9, 1), pendulum.date(2022, 9, 4) + ), + 0, + ) + self.assertEqual( + get_number_of_weeks_between( + pendulum.date(2022, 9, 1), pendulum.date(2023, 2, 5) + ), + 22, + ) + self.assertEqual( + get_number_of_weeks_between( + pendulum.date(2022, 9, 1), pendulum.date(2023, 7, 5) + ), + 44, + ) + self.assertEqual( + get_number_of_weeks_between( + pendulum.date(2023, 2, 5), pendulum.date(2023, 7, 5) + ), + 22, + ) diff --git a/jarvis/tools/tests_models.py b/jarvis/tools/tests_models.py new file mode 100644 index 0000000..360ab6a --- /dev/null +++ b/jarvis/tools/tests_models.py @@ -0,0 +1,55 @@ +""" Test des models """ + +from django.test import TestCase +from .models import Season +import pendulum + + +class ModelTestCase(TestCase): + def test_init_season(self): + season = Season("2022-2023") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("2022-2024") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("2024-2023") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("1358-5682") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("fgkrs-gkml") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("drgnldsjgklfdtngl") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + season = Season("12675353878354") + self.assertEqual(season.is_valid(), True) + self.assertEqual(season.label, "2022-2023") + + def test_season_is_valid(self): + season = Season("2022-2023") + self.assertEqual(season.is_valid(), True) + + season.label = "2022-2024" + self.assertEqual(season.is_valid(), False) + + season.label = "1358-5682" + self.assertEqual(season.is_valid(), False) + + season.label = "fgkrs-gkml" + self.assertEqual(season.is_valid(), False) + + season.label = "drgnldsjgklfdtngl" + self.assertEqual(season.is_valid(), False) + + season.label = "drgnldsjgklfdtngl" + self.assertEqual(season.is_valid(), False) diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..8e7ac79 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b0c3540 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +-r requirements/dev.txt diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..8be75bb --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,12 @@ +Django==3.2.8 +django-environ==0.8.1 +django-extensions==3.1.3 +django-admin-list-filter-dropdown==1.0.3 +Markdown==3.3.4 +pendulum==2.1.2 +PyYAML==6.0 +reportlab==3.6.11 +simplejson==3.17.5 +whitenoise==5.3.0 +sentry-sdk==1.5.5 +weasyprint==56.1 diff --git a/requirements/ci.txt b/requirements/ci.txt new file mode 100644 index 0000000..c836ef8 --- /dev/null +++ b/requirements/ci.txt @@ -0,0 +1,5 @@ +-r base.txt + +astroid==2.9.0 +pylint==2.12.2 +pylint_django==2.4.4 diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 0000000..ef7c176 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,12 @@ +-r base.txt + +asgiref==3.4.1 +install==1.3.4 +python-dateutil==2.8.2 +pytz==2021.3 +pytzdata==2020.1 +six==1.16.0 +sqlparse==0.4.2 +gunicorn==20.1.0 +psycopg2==2.9.2 +pytest-django==4.5.2 \ No newline at end of file diff --git a/static/css/black-dashboard.css b/static/css/black-dashboard.css new file mode 100644 index 0000000..0ff765e --- /dev/null +++ b/static/css/black-dashboard.css @@ -0,0 +1,29678 @@ +/*! + + ========================================================= + * Black Dashboard Pro - v1.0.0 + ========================================================= + + * Product Page: https://www.creative-tim.com/product/black-dashboard-pro + * Copyright 2018 Creative Tim (http://www.creative-tim.com) + + + ========================================================= + + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + */ + +/* brand Colors */ + +/* navbar color */ + +:root { + --blue: #5e72e4; + --indigo: #5603ad; + --purple: #8965e0; + --pink: #f3a4b5; + --red: #f5365c; + --orange: #fb6340; + --yellow: #ffd600; + --green: #2dce89; + --teal: #11cdef; + --cyan: #2bffc6; + --white: #ffffff; + --gray: #6c757d; + --gray-dark: #32325d; + --light: #ced4da; + --lighter: #e9ecef; + --primary: #e14eca; + --secondary: #f4f5f7; + --success: #00f2c3; + --info: #1d8cf8; + --warning: #ff8d72; + --danger: #fd5d93; + --light: #adb5bd; + --dark: #212529; + --default: #344675; + --white: #ffffff; + --neutral: #ffffff; + --darker: black; + --breakpoint-xs: 0; + --breakpoint-sm: 576px; + --breakpoint-md: 768px; + --breakpoint-lg: 992px; + --breakpoint-xl: 1200px; + --font-family-sans-serif: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + font-family: sans-serif; + line-height: 1.15; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -ms-overflow-style: scrollbar; + -webkit-tap-highlight-color: rgba(34, 42, 66, 0); +} + +@-ms-viewport { + width: device-width; +} + +article, +aside, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section { + display: block; +} + +body { + margin: 0; + font-family: "Poppins", sans-serif; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.5; + color: #525f7f; + text-align: left; + background-color: #1e1e2f; +} + +[tabindex="-1"]:focus { + outline: 0 !important; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 0; + margin-bottom: 0.5rem; +} + +p { + margin-top: 0; + margin-bottom: 1rem; +} + +abbr[title], +abbr[data-original-title] { + text-decoration: underline; + text-decoration: underline dotted; + cursor: help; + border-bottom: 0; +} + +address { + margin-bottom: 1rem; + font-style: normal; + line-height: inherit; +} + +ol, +ul, +dl { + margin-top: 0; + margin-bottom: 1rem; +} + +ol ol, +ul ul, +ol ul, +ul ol { + margin-bottom: 0; +} + +dt { + font-weight: 600; +} + +dd { + margin-bottom: .5rem; + margin-left: 0; +} + +blockquote { + margin: 0 0 1rem; +} + +dfn { + font-style: italic; +} + +b, +strong { + font-weight: bolder; +} + +small { + font-size: 80%; +} + +sub, +sup { + position: relative; + font-size: 75%; + line-height: 0; + vertical-align: baseline; +} + +sub { + bottom: -.25em; +} + +sup { + top: -.5em; +} + +a { + color: #e14eca; + text-decoration: none; + background-color: transparent; + -webkit-text-decoration-skip: objects; +} + +a:hover { + color: #c221a9; + text-decoration: none; +} + +a:not([href]):not([tabindex]) { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):hover, +a:not([href]):not([tabindex]):focus { + color: inherit; + text-decoration: none; +} + +a:not([href]):not([tabindex]):focus { + outline: 0; +} + +pre, +code, +kbd, +samp { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} + +pre { + margin-top: 0; + margin-bottom: 1rem; + overflow: auto; + -ms-overflow-style: scrollbar; +} + +figure { + margin: 0 0 1rem; +} + +img { + vertical-align: middle; + border-style: none; +} + +svg { + overflow: hidden; + vertical-align: middle; +} + +table { + border-collapse: collapse; +} + +caption { + padding-top: 1rem; + padding-bottom: 1rem; + color: #6c757d; + text-align: left; + caption-side: bottom; +} + +th { + text-align: inherit; +} + +label { + display: inline-block; + margin-bottom: 0.5rem; +} + +button { + border-radius: 0; +} + +button:focus { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} + +input, +button, +select, +optgroup, +textarea { + margin: 0; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +html [type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + padding: 0; + border-style: none; +} + +input[type="radio"], +input[type="checkbox"] { + box-sizing: border-box; + padding: 0; +} + +input[type="date"], +input[type="time"], +input[type="datetime-local"], +input[type="month"] { + -webkit-appearance: listbox; +} + +textarea { + overflow: auto; + resize: vertical; +} + +fieldset { + min-width: 0; + padding: 0; + margin: 0; + border: 0; +} + +legend { + display: block; + width: 100%; + max-width: 100%; + padding: 0; + margin-bottom: .5rem; + font-size: 1.5rem; + line-height: inherit; + color: inherit; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + outline-offset: -2px; + -webkit-appearance: none; +} + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + font: inherit; + -webkit-appearance: button; +} + +output { + display: inline-block; +} + +summary { + display: list-item; + cursor: pointer; +} + +template { + display: none; +} + +[hidden] { + display: none !important; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + margin-bottom: 0.5rem; + font-family: inherit; + font-weight: 400; + line-height: 1.2; + color: #32325d; +} + +h1, +.h1 { + font-size: 2.0625rem; +} + +h2, +.h2 { + font-size: 1.6875rem; +} + +h3, +.h3 { + font-size: 1.4375rem; +} + +h4, +.h4 { + font-size: 1.0625rem; +} + +h5, +.h5 { + font-size: 0.8125rem; +} + +h6, +.h6 { + font-size: 0.75rem; +} + +.lead { + font-size: 0.78125rem; + font-weight: 300; +} + +.display-1 { + font-size: 3.3rem; + font-weight: 600; + line-height: 1.2; +} + +.display-2 { + font-size: 2.75rem; + font-weight: 600; + line-height: 1.2; +} + +.display-3 { + font-size: 2.1875rem; + font-weight: 600; + line-height: 1.2; +} + +.display-4 { + font-size: 1.6275rem; + font-weight: 600; + line-height: 1.2; +} + +hr { + margin-top: 2rem; + margin-bottom: 2rem; + border: 0; + border-top: 0.0625rem solid rgba(34, 42, 66, 0.1); +} + +small, +.small { + font-size: 80%; + font-weight: 400; +} + +mark, +.mark { + padding: 0.2em; + background-color: #fcf8e3; +} + +.list-unstyled { + padding-left: 0; + list-style: none; +} + +.list-inline { + padding-left: 0; + list-style: none; +} + +.list-inline-item { + display: inline-block; +} + +.list-inline-item:not(:last-child) { + margin-right: 0.5rem; +} + +.initialism { + font-size: 90%; + text-transform: uppercase; +} + +.blockquote { + margin-bottom: 1rem; + font-size: 0.9625rem; +} + +.blockquote-footer { + display: block; + font-size: 80%; + color: #6c757d; +} + +.blockquote-footer::before { + content: "\2014 \00A0"; +} + +.img-fluid { + max-width: 100%; + height: auto; +} + +.img-thumbnail { + padding: 0.25rem; + background-color: #1e1e2f; + border: 0.0625rem solid #e3e3e3; + border-radius: 0.25rem; + box-shadow: 0 1px 2px rgba(34, 42, 66, 0.075); + max-width: 100%; + height: auto; +} + +.figure { + display: inline-block; +} + +.figure-img { + margin-bottom: 0.5rem; + line-height: 1; +} + +.figure-caption { + font-size: 90%; + color: #6c757d; +} + +code { + font-size: 87.5%; + color: #f3a4b5; + word-break: break-word; +} + +a>code { + color: inherit; +} + +kbd { + padding: 0.2rem 0.4rem; + font-size: 87.5%; + color: #ffffff; + background-color: #212529; + border-radius: 0.2857rem; + box-shadow: inset 0 -0.1rem 0 rgba(34, 42, 66, 0.25); +} + +kbd kbd { + padding: 0; + font-size: 100%; + font-weight: 600; + box-shadow: none; +} + +pre { + display: block; + font-size: 87.5%; + color: #212529; +} + +pre code { + font-size: inherit; + color: inherit; + word-break: normal; +} + +.pre-scrollable { + max-height: 340px; + overflow-y: scroll; +} + +.container { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +@media (min-width: 576px) { + .container { + max-width: 540px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 720px; + } +} + +@media (min-width: 992px) { + .container { + max-width: 960px; + } +} + +@media (min-width: 1200px) { + .container { + max-width: 1140px; + } +} + +.container-fluid { + width: 100%; + padding-right: 15px; + padding-left: 15px; + margin-right: auto; + margin-left: auto; +} + +.row { + display: flex; + flex-wrap: wrap; + margin-right: -15px; + margin-left: -15px; +} + +.no-gutters { + margin-right: 0; + margin-left: 0; +} + +.no-gutters>.col, +.no-gutters>[class*="col-"] { + padding-right: 0; + padding-left: 0; +} + +.col-1, +.col-2, +.col-3, +.col-4, +.col-5, +.col-6, +.col-7, +.col-8, +.col-9, +.col-10, +.col-11, +.col-12, +.col, +.col-auto, +.col-sm-1, +.col-sm-2, +.col-sm-3, +.col-sm-4, +.col-sm-5, +.col-sm-6, +.col-sm-7, +.col-sm-8, +.col-sm-9, +.col-sm-10, +.col-sm-11, +.col-sm-12, +.col-sm, +.col-sm-auto, +.col-md-1, +.col-md-2, +.col-md-3, +.col-md-4, +.col-md-5, +.col-md-6, +.col-md-7, +.col-md-8, +.col-md-9, +.col-md-10, +.col-md-11, +.col-md-12, +.col-md, +.col-md-auto, +.col-lg-1, +.col-lg-2, +.col-lg-3, +.col-lg-4, +.col-lg-5, +.col-lg-6, +.col-lg-7, +.col-lg-8, +.col-lg-9, +.col-lg-10, +.col-lg-11, +.col-lg-12, +.col-lg, +.col-lg-auto, +.col-xl-1, +.col-xl-2, +.col-xl-3, +.col-xl-4, +.col-xl-5, +.col-xl-6, +.col-xl-7, +.col-xl-8, +.col-xl-9, +.col-xl-10, +.col-xl-11, +.col-xl-12, +.col-xl, +.col-xl-auto { + position: relative; + width: 100%; + min-height: 1px; + padding-right: 15px; + padding-left: 15px; +} + +.col { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; +} + +.col-auto { + flex: 0 0 auto; + width: auto; + max-width: none; +} + +.col-1 { + flex: 0 0 8.333333%; + max-width: 8.333333%; +} + +.col-2 { + flex: 0 0 16.666667%; + max-width: 16.666667%; +} + +.col-3 { + flex: 0 0 25%; + max-width: 25%; +} + +.col-4 { + flex: 0 0 33.333333%; + max-width: 33.333333%; +} + +.col-5 { + flex: 0 0 41.666667%; + max-width: 41.666667%; +} + +.col-6 { + flex: 0 0 50%; + max-width: 50%; +} + +.col-7 { + flex: 0 0 58.333333%; + max-width: 58.333333%; +} + +.col-8 { + flex: 0 0 66.666667%; + max-width: 66.666667%; +} + +.col-9 { + flex: 0 0 75%; + max-width: 75%; +} + +.col-10 { + flex: 0 0 83.333333%; + max-width: 83.333333%; +} + +.col-11 { + flex: 0 0 91.666667%; + max-width: 91.666667%; +} + +.col-12 { + flex: 0 0 100%; + max-width: 100%; +} + +.order-first { + order: -1; +} + +.order-last { + order: 13; +} + +.order-0 { + order: 0; +} + +.order-1 { + order: 1; +} + +.order-2 { + order: 2; +} + +.order-3 { + order: 3; +} + +.order-4 { + order: 4; +} + +.order-5 { + order: 5; +} + +.order-6 { + order: 6; +} + +.order-7 { + order: 7; +} + +.order-8 { + order: 8; +} + +.order-9 { + order: 9; +} + +.order-10 { + order: 10; +} + +.order-11 { + order: 11; +} + +.order-12 { + order: 12; +} + +.offset-1 { + margin-left: 8.333333%; +} + +.offset-2 { + margin-left: 16.666667%; +} + +.offset-3 { + margin-left: 25%; +} + +.offset-4 { + margin-left: 33.333333%; +} + +.offset-5 { + margin-left: 41.666667%; +} + +.offset-6 { + margin-left: 50%; +} + +.offset-7 { + margin-left: 58.333333%; +} + +.offset-8 { + margin-left: 66.666667%; +} + +.offset-9 { + margin-left: 75%; +} + +.offset-10 { + margin-left: 83.333333%; +} + +.offset-11 { + margin-left: 91.666667%; +} + +@media (min-width: 576px) { + .col-sm { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-sm-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-sm-1 { + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-sm-2 { + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-sm-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-sm-4 { + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-sm-5 { + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-sm-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-sm-7 { + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-sm-8 { + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-sm-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-sm-10 { + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-sm-11 { + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-sm-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-sm-first { + order: -1; + } + .order-sm-last { + order: 13; + } + .order-sm-0 { + order: 0; + } + .order-sm-1 { + order: 1; + } + .order-sm-2 { + order: 2; + } + .order-sm-3 { + order: 3; + } + .order-sm-4 { + order: 4; + } + .order-sm-5 { + order: 5; + } + .order-sm-6 { + order: 6; + } + .order-sm-7 { + order: 7; + } + .order-sm-8 { + order: 8; + } + .order-sm-9 { + order: 9; + } + .order-sm-10 { + order: 10; + } + .order-sm-11 { + order: 11; + } + .order-sm-12 { + order: 12; + } + .offset-sm-0 { + margin-left: 0; + } + .offset-sm-1 { + margin-left: 8.333333%; + } + .offset-sm-2 { + margin-left: 16.666667%; + } + .offset-sm-3 { + margin-left: 25%; + } + .offset-sm-4 { + margin-left: 33.333333%; + } + .offset-sm-5 { + margin-left: 41.666667%; + } + .offset-sm-6 { + margin-left: 50%; + } + .offset-sm-7 { + margin-left: 58.333333%; + } + .offset-sm-8 { + margin-left: 66.666667%; + } + .offset-sm-9 { + margin-left: 75%; + } + .offset-sm-10 { + margin-left: 83.333333%; + } + .offset-sm-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 768px) { + .col-md { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-md-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-md-1 { + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-md-2 { + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-md-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-md-4 { + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-md-5 { + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-md-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-md-7 { + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-md-8 { + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-md-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-md-10 { + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-md-11 { + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-md-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-md-first { + order: -1; + } + .order-md-last { + order: 13; + } + .order-md-0 { + order: 0; + } + .order-md-1 { + order: 1; + } + .order-md-2 { + order: 2; + } + .order-md-3 { + order: 3; + } + .order-md-4 { + order: 4; + } + .order-md-5 { + order: 5; + } + .order-md-6 { + order: 6; + } + .order-md-7 { + order: 7; + } + .order-md-8 { + order: 8; + } + .order-md-9 { + order: 9; + } + .order-md-10 { + order: 10; + } + .order-md-11 { + order: 11; + } + .order-md-12 { + order: 12; + } + .offset-md-0 { + margin-left: 0; + } + .offset-md-1 { + margin-left: 8.333333%; + } + .offset-md-2 { + margin-left: 16.666667%; + } + .offset-md-3 { + margin-left: 25%; + } + .offset-md-4 { + margin-left: 33.333333%; + } + .offset-md-5 { + margin-left: 41.666667%; + } + .offset-md-6 { + margin-left: 50%; + } + .offset-md-7 { + margin-left: 58.333333%; + } + .offset-md-8 { + margin-left: 66.666667%; + } + .offset-md-9 { + margin-left: 75%; + } + .offset-md-10 { + margin-left: 83.333333%; + } + .offset-md-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 992px) { + .col-lg { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-lg-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-lg-1 { + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-lg-2 { + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-lg-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-lg-4 { + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-lg-5 { + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-lg-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-lg-7 { + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-lg-8 { + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-lg-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-lg-10 { + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-lg-11 { + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-lg-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-lg-first { + order: -1; + } + .order-lg-last { + order: 13; + } + .order-lg-0 { + order: 0; + } + .order-lg-1 { + order: 1; + } + .order-lg-2 { + order: 2; + } + .order-lg-3 { + order: 3; + } + .order-lg-4 { + order: 4; + } + .order-lg-5 { + order: 5; + } + .order-lg-6 { + order: 6; + } + .order-lg-7 { + order: 7; + } + .order-lg-8 { + order: 8; + } + .order-lg-9 { + order: 9; + } + .order-lg-10 { + order: 10; + } + .order-lg-11 { + order: 11; + } + .order-lg-12 { + order: 12; + } + .offset-lg-0 { + margin-left: 0; + } + .offset-lg-1 { + margin-left: 8.333333%; + } + .offset-lg-2 { + margin-left: 16.666667%; + } + .offset-lg-3 { + margin-left: 25%; + } + .offset-lg-4 { + margin-left: 33.333333%; + } + .offset-lg-5 { + margin-left: 41.666667%; + } + .offset-lg-6 { + margin-left: 50%; + } + .offset-lg-7 { + margin-left: 58.333333%; + } + .offset-lg-8 { + margin-left: 66.666667%; + } + .offset-lg-9 { + margin-left: 75%; + } + .offset-lg-10 { + margin-left: 83.333333%; + } + .offset-lg-11 { + margin-left: 91.666667%; + } +} + +@media (min-width: 1200px) { + .col-xl { + flex-basis: 0; + flex-grow: 1; + max-width: 100%; + } + .col-xl-auto { + flex: 0 0 auto; + width: auto; + max-width: none; + } + .col-xl-1 { + flex: 0 0 8.333333%; + max-width: 8.333333%; + } + .col-xl-2 { + flex: 0 0 16.666667%; + max-width: 16.666667%; + } + .col-xl-3 { + flex: 0 0 25%; + max-width: 25%; + } + .col-xl-4 { + flex: 0 0 33.333333%; + max-width: 33.333333%; + } + .col-xl-5 { + flex: 0 0 41.666667%; + max-width: 41.666667%; + } + .col-xl-6 { + flex: 0 0 50%; + max-width: 50%; + } + .col-xl-7 { + flex: 0 0 58.333333%; + max-width: 58.333333%; + } + .col-xl-8 { + flex: 0 0 66.666667%; + max-width: 66.666667%; + } + .col-xl-9 { + flex: 0 0 75%; + max-width: 75%; + } + .col-xl-10 { + flex: 0 0 83.333333%; + max-width: 83.333333%; + } + .col-xl-11 { + flex: 0 0 91.666667%; + max-width: 91.666667%; + } + .col-xl-12 { + flex: 0 0 100%; + max-width: 100%; + } + .order-xl-first { + order: -1; + } + .order-xl-last { + order: 13; + } + .order-xl-0 { + order: 0; + } + .order-xl-1 { + order: 1; + } + .order-xl-2 { + order: 2; + } + .order-xl-3 { + order: 3; + } + .order-xl-4 { + order: 4; + } + .order-xl-5 { + order: 5; + } + .order-xl-6 { + order: 6; + } + .order-xl-7 { + order: 7; + } + .order-xl-8 { + order: 8; + } + .order-xl-9 { + order: 9; + } + .order-xl-10 { + order: 10; + } + .order-xl-11 { + order: 11; + } + .order-xl-12 { + order: 12; + } + .offset-xl-0 { + margin-left: 0; + } + .offset-xl-1 { + margin-left: 8.333333%; + } + .offset-xl-2 { + margin-left: 16.666667%; + } + .offset-xl-3 { + margin-left: 25%; + } + .offset-xl-4 { + margin-left: 33.333333%; + } + .offset-xl-5 { + margin-left: 41.666667%; + } + .offset-xl-6 { + margin-left: 50%; + } + .offset-xl-7 { + margin-left: 58.333333%; + } + .offset-xl-8 { + margin-left: 66.666667%; + } + .offset-xl-9 { + margin-left: 75%; + } + .offset-xl-10 { + margin-left: 83.333333%; + } + .offset-xl-11 { + margin-left: 91.666667%; + } +} + +.table { + width: 100%; + margin-bottom: 1rem; + background-color: transparent; +} + +.table th, +.table td { + padding: 1rem; + vertical-align: top; + border-top: 0.0625rem solid #e3e3e3; +} + +.table thead th { + vertical-align: bottom; + border-bottom: 0.125rem solid #e3e3e3; +} + +.table tbody+tbody { + border-top: 0.125rem solid #e3e3e3; +} + +.table .table { + background-color: #1e1e2f; +} + +.table-sm th, +.table-sm td { + padding: 0.3rem; +} + +.table-bordered { + border: 0.0625rem solid #e3e3e3; +} + +.table-bordered th, +.table-bordered td { + border: 0.0625rem solid #e3e3e3; +} + +.table-bordered thead th, +.table-bordered thead td { + border-bottom-width: 0.125rem; +} + +.table-borderless th, +.table-borderless td, +.table-borderless thead th, +.table-borderless tbody+tbody { + border: 0; +} + +.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(34, 42, 66, 0.05); +} + +.table-hover tbody tr:hover { + background-color: rgba(34, 42, 66, 0.075); +} + +.table-primary, +.table-primary>th, +.table-primary>td { + background-color: #f7cdf0; +} + +.table-hover .table-primary:hover { + background-color: #f3b7e9; +} + +.table-hover .table-primary:hover>td, +.table-hover .table-primary:hover>th { + background-color: #f3b7e9; +} + +.table-secondary, +.table-secondary>th, +.table-secondary>td { + background-color: #fcfcfd; +} + +.table-hover .table-secondary:hover { + background-color: #ededf3; +} + +.table-hover .table-secondary:hover>td, +.table-hover .table-secondary:hover>th { + background-color: #ededf3; +} + +.table-success, +.table-success>th, +.table-success>td { + background-color: #b8fbee; +} + +.table-hover .table-success:hover { + background-color: #a0fae8; +} + +.table-hover .table-success:hover>td, +.table-hover .table-success:hover>th { + background-color: #a0fae8; +} + +.table-info, +.table-info>th, +.table-info>td { + background-color: #c0dffd; +} + +.table-hover .table-info:hover { + background-color: #a7d2fc; +} + +.table-hover .table-info:hover>td, +.table-hover .table-info:hover>th { + background-color: #a7d2fc; +} + +.table-warning, +.table-warning>th, +.table-warning>td { + background-color: #ffdfd8; +} + +.table-hover .table-warning:hover { + background-color: #ffcabf; +} + +.table-hover .table-warning:hover>td, +.table-hover .table-warning:hover>th { + background-color: #ffcabf; +} + +.table-danger, +.table-danger>th, +.table-danger>td { + background-color: #fed2e1; +} + +.table-hover .table-danger:hover { + background-color: #fdb9d0; +} + +.table-hover .table-danger:hover>td, +.table-hover .table-danger:hover>th { + background-color: #fdb9d0; +} + +.table-light, +.table-light>th, +.table-light>td { + background-color: #e8eaed; +} + +.table-hover .table-light:hover { + background-color: #dadde2; +} + +.table-hover .table-light:hover>td, +.table-hover .table-light:hover>th { + background-color: #dadde2; +} + +.table-dark, +.table-dark>th, +.table-dark>td { + background-color: #c1c2c3; +} + +.table-hover .table-dark:hover { + background-color: #b4b5b6; +} + +.table-hover .table-dark:hover>td, +.table-hover .table-dark:hover>th { + background-color: #b4b5b6; +} + +.table-default, +.table-default>th, +.table-default>td { + background-color: #c6cbd8; +} + +.table-hover .table-default:hover { + background-color: #b7bdce; +} + +.table-hover .table-default:hover>td, +.table-hover .table-default:hover>th { + background-color: #b7bdce; +} + +.table-white, +.table-white>th, +.table-white>td { + background-color: white; +} + +.table-hover .table-white:hover { + background-color: #f2f2f2; +} + +.table-hover .table-white:hover>td, +.table-hover .table-white:hover>th { + background-color: #f2f2f2; +} + +.table-neutral, +.table-neutral>th, +.table-neutral>td { + background-color: white; +} + +.table-hover .table-neutral:hover { + background-color: #f2f2f2; +} + +.table-hover .table-neutral:hover>td, +.table-hover .table-neutral:hover>th { + background-color: #f2f2f2; +} + +.table-darker, +.table-darker>th, +.table-darker>td { + background-color: #b8b8b8; +} + +.table-hover .table-darker:hover { + background-color: #ababab; +} + +.table-hover .table-darker:hover>td, +.table-hover .table-darker:hover>th { + background-color: #ababab; +} + +.table-active, +.table-active>th, +.table-active>td { + background-color: rgba(34, 42, 66, 0.075); +} + +.table-hover .table-active:hover { + background-color: rgba(25, 31, 49, 0.075); +} + +.table-hover .table-active:hover>td, +.table-hover .table-active:hover>th { + background-color: rgba(25, 31, 49, 0.075); +} + +.table .thead-dark th { + color: #1e1e2f; + background-color: #212529; + border-color: #32383e; +} + +.table .thead-light th { + color: #525f7f; + background-color: #e9ecef; + border-color: #e3e3e3; +} + +.table-dark { + color: #1e1e2f; + background-color: #212529; +} + +.table-dark th, +.table-dark td, +.table-dark thead th { + border-color: #32383e; +} + +.table-dark.table-bordered { + border: 0; +} + +.table-dark.table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(255, 255, 255, 0.05); +} + +.table-dark.table-hover tbody tr:hover { + background-color: rgba(255, 255, 255, 0.075); +} + +@media (max-width: 575.98px) { + .table-responsive-sm { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-sm>.table-bordered { + border: 0; + } +} + +@media (max-width: 767.98px) { + .table-responsive-md { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-md>.table-bordered { + border: 0; + } +} + +@media (max-width: 991.98px) { + .table-responsive-lg { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-lg>.table-bordered { + border: 0; + } +} + +@media (max-width: 1199.98px) { + .table-responsive-xl { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; + } + .table-responsive-xl>.table-bordered { + border: 0; + } +} + +.table-responsive { + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; + -ms-overflow-style: -ms-autohiding-scrollbar; +} + +.table-responsive>.table-bordered { + border: 0; +} + +.form-control { + display: block; + width: 100%; + height: calc(2.25rem + 2px); + padding: 0.5rem 0.7rem; + font-size: 0.875rem; + line-height: 1.428571; + color: rgba(255, 255, 255, 0.8); + background-color: transparent; + background-clip: padding-box; + border: 1px solid #cad1d7; + border-radius: 0.25rem; + box-shadow: none; + transition: all 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +@media screen and (prefers-reduced-motion: reduce) { + .form-control { + transition: none; + } +} + +.form-control::-ms-expand { + background-color: transparent; + border: 0; +} + +.form-control:focus { + color: rgba(255, 255, 255, 0.8); + background-color: #ffffff; + border-color: rgba(50, 151, 211, 0.25); + outline: 0; + box-shadow: none, none; +} + +.form-control::placeholder { + color: #adb5bd; + opacity: 1; +} + +.form-control:disabled, +.form-control[readonly] { + background-color: #e9ecef; + opacity: 1; +} + +select.form-control:focus::-ms-value { + color: rgba(255, 255, 255, 0.8); + background-color: transparent; +} + +.form-control-file, +.form-control-range { + display: block; + width: 100%; +} + +.col-form-label { + padding-top: calc(0.5rem + 1px); + padding-bottom: calc(0.5rem + 1px); + margin-bottom: 0; + font-size: inherit; + line-height: 1.5; +} + +.col-form-label-lg { + padding-top: calc(0.875rem + 1px); + padding-bottom: calc(0.875rem + 1px); + font-size: 0.99925rem; + line-height: 1.35; +} + +.col-form-label-sm { + padding-top: calc(0.25rem + 1px); + padding-bottom: calc(0.25rem + 1px); + font-size: 0.75rem; + line-height: 1.35; +} + +.form-control-plaintext { + display: block; + width: 100%; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + margin-bottom: 0; + line-height: 1.428571; + color: #525f7f; + background-color: transparent; + border: solid transparent; + border-width: 1px 0; +} + +.form-control-plaintext.form-control-sm, +.form-control-plaintext.form-control-lg { + padding-right: 0; + padding-left: 0; +} + +.form-control-sm { + height: calc(1.5125rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + line-height: 1.35; + border-radius: 0.2857rem; +} + +.form-control-lg { + height: calc(3.098987rem + 2px); + padding: 0.875rem 1rem; + font-size: 0.99925rem; + line-height: 1.35; + border-radius: 0.4285rem; +} + +select.form-control[size], +select.form-control[multiple] { + height: auto; +} + +textarea.form-control { + height: auto; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-text { + display: block; + margin-top: 0.25rem; +} + +.form-row { + display: flex; + flex-wrap: wrap; + margin-right: -5px; + margin-left: -5px; +} + +.form-row>.col, +.form-row>[class*="col-"] { + padding-right: 5px; + padding-left: 5px; +} + +.form-check { + position: relative; + display: block; + padding-left: 1.25rem; +} + +.form-check-input { + position: absolute; + margin-top: 0.3rem; + margin-left: -1.25rem; +} + +.form-check-input:disabled~.form-check-label { + color: #6c757d; +} + +.form-check-label { + margin-bottom: 0; +} + +.form-check-inline { + display: inline-flex; + align-items: center; + padding-left: 0; + margin-right: 0.75rem; +} + +.form-check-inline .form-check-input { + position: static; + margin-top: 0; + margin-right: 0.3125rem; + margin-left: 0; +} + +.valid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #00f2c3; +} + +.valid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.75rem; + line-height: 1.5; + color: #ffffff; + background-color: rgba(0, 242, 195, 0.9); + border-radius: 0.25rem; +} + +.was-validated .form-control:valid, +.form-control.is-valid, +.was-validated .custom-select:valid, +.custom-select.is-valid { + border-color: #00f2c3; +} + +.was-validated .form-control:valid:focus, +.form-control.is-valid:focus, +.was-validated .custom-select:valid:focus, +.custom-select.is-valid:focus { + border-color: #00f2c3; + box-shadow: 0 0 0 0 rgba(0, 242, 195, 0.25); +} + +.was-validated .form-control:valid~.valid-feedback, +.was-validated .form-control:valid~.valid-tooltip, +.form-control.is-valid~.valid-feedback, +.form-control.is-valid~.valid-tooltip, +.was-validated .custom-select:valid~.valid-feedback, +.was-validated .custom-select:valid~.valid-tooltip, +.custom-select.is-valid~.valid-feedback, +.custom-select.is-valid~.valid-tooltip { + display: block; +} + +.was-validated .form-control-file:valid~.valid-feedback, +.was-validated .form-control-file:valid~.valid-tooltip, +.form-control-file.is-valid~.valid-feedback, +.form-control-file.is-valid~.valid-tooltip { + display: block; +} + +.was-validated .form-check-input:valid~.form-check-label, +.form-check-input.is-valid~.form-check-label { + color: #00f2c3; +} + +.was-validated .form-check-input:valid~.valid-feedback, +.was-validated .form-check-input:valid~.valid-tooltip, +.form-check-input.is-valid~.valid-feedback, +.form-check-input.is-valid~.valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid~.custom-control-label, +.custom-control-input.is-valid~.custom-control-label { + color: #00f2c3; +} + +.was-validated .custom-control-input:valid~.custom-control-label::before, +.custom-control-input.is-valid~.custom-control-label::before { + background-color: #73ffe4; +} + +.was-validated .custom-control-input:valid~.valid-feedback, +.was-validated .custom-control-input:valid~.valid-tooltip, +.custom-control-input.is-valid~.valid-feedback, +.custom-control-input.is-valid~.valid-tooltip { + display: block; +} + +.was-validated .custom-control-input:valid:checked~.custom-control-label::before, +.custom-control-input.is-valid:checked~.custom-control-label::before { + background-color: #26ffd5; +} + +.was-validated .custom-control-input:valid:focus~.custom-control-label::before, +.custom-control-input.is-valid:focus~.custom-control-label::before { + box-shadow: 0 0 0 1px #1e1e2f, 0 0 0 0 rgba(0, 242, 195, 0.25); +} + +.was-validated .custom-file-input:valid~.custom-file-label, +.custom-file-input.is-valid~.custom-file-label { + border-color: #00f2c3; +} + +.was-validated .custom-file-input:valid~.custom-file-label::after, +.custom-file-input.is-valid~.custom-file-label::after { + border-color: inherit; +} + +.was-validated .custom-file-input:valid~.valid-feedback, +.was-validated .custom-file-input:valid~.valid-tooltip, +.custom-file-input.is-valid~.valid-feedback, +.custom-file-input.is-valid~.valid-tooltip { + display: block; +} + +.was-validated .custom-file-input:valid:focus~.custom-file-label, +.custom-file-input.is-valid:focus~.custom-file-label { + box-shadow: 0 0 0 0 rgba(0, 242, 195, 0.25); +} + +.invalid-feedback { + display: none; + width: 100%; + margin-top: 0.25rem; + font-size: 80%; + color: #ff8d72; +} + +.invalid-tooltip { + position: absolute; + top: 100%; + z-index: 5; + display: none; + max-width: 100%; + padding: 0.25rem 0.5rem; + margin-top: .1rem; + font-size: 0.75rem; + line-height: 1.5; + color: #ffffff; + background-color: rgba(255, 141, 114, 0.9); + border-radius: 0.25rem; +} + +.was-validated .form-control:invalid, +.form-control.is-invalid, +.was-validated .custom-select:invalid, +.custom-select.is-invalid { + border-color: #ff8d72; +} + +.was-validated .form-control:invalid:focus, +.form-control.is-invalid:focus, +.was-validated .custom-select:invalid:focus, +.custom-select.is-invalid:focus { + border-color: #ff8d72; + box-shadow: 0 0 0 0 rgba(255, 141, 114, 0.25); +} + +.was-validated .form-control:invalid~.invalid-feedback, +.was-validated .form-control:invalid~.invalid-tooltip, +.form-control.is-invalid~.invalid-feedback, +.form-control.is-invalid~.invalid-tooltip, +.was-validated .custom-select:invalid~.invalid-feedback, +.was-validated .custom-select:invalid~.invalid-tooltip, +.custom-select.is-invalid~.invalid-feedback, +.custom-select.is-invalid~.invalid-tooltip { + display: block; +} + +.was-validated .form-control-file:invalid~.invalid-feedback, +.was-validated .form-control-file:invalid~.invalid-tooltip, +.form-control-file.is-invalid~.invalid-feedback, +.form-control-file.is-invalid~.invalid-tooltip { + display: block; +} + +.was-validated .form-check-input:invalid~.form-check-label, +.form-check-input.is-invalid~.form-check-label { + color: #ff8d72; +} + +.was-validated .form-check-input:invalid~.invalid-feedback, +.was-validated .form-check-input:invalid~.invalid-tooltip, +.form-check-input.is-invalid~.invalid-feedback, +.form-check-input.is-invalid~.invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid~.custom-control-label, +.custom-control-input.is-invalid~.custom-control-label { + color: #ff8d72; +} + +.was-validated .custom-control-input:invalid~.custom-control-label::before, +.custom-control-input.is-invalid~.custom-control-label::before { + background-color: #fff4f2; +} + +.was-validated .custom-control-input:invalid~.invalid-feedback, +.was-validated .custom-control-input:invalid~.invalid-tooltip, +.custom-control-input.is-invalid~.invalid-feedback, +.custom-control-input.is-invalid~.invalid-tooltip { + display: block; +} + +.was-validated .custom-control-input:invalid:checked~.custom-control-label::before, +.custom-control-input.is-invalid:checked~.custom-control-label::before { + background-color: #ffb6a5; +} + +.was-validated .custom-control-input:invalid:focus~.custom-control-label::before, +.custom-control-input.is-invalid:focus~.custom-control-label::before { + box-shadow: 0 0 0 1px #1e1e2f, 0 0 0 0 rgba(255, 141, 114, 0.25); +} + +.was-validated .custom-file-input:invalid~.custom-file-label, +.custom-file-input.is-invalid~.custom-file-label { + border-color: #ff8d72; +} + +.was-validated .custom-file-input:invalid~.custom-file-label::after, +.custom-file-input.is-invalid~.custom-file-label::after { + border-color: inherit; +} + +.was-validated .custom-file-input:invalid~.invalid-feedback, +.was-validated .custom-file-input:invalid~.invalid-tooltip, +.custom-file-input.is-invalid~.invalid-feedback, +.custom-file-input.is-invalid~.invalid-tooltip { + display: block; +} + +.was-validated .custom-file-input:invalid:focus~.custom-file-label, +.custom-file-input.is-invalid:focus~.custom-file-label { + box-shadow: 0 0 0 0 rgba(255, 141, 114, 0.25); +} + +.form-inline { + display: flex; + flex-flow: row wrap; + align-items: center; +} + +.form-inline .form-check { + width: 100%; +} + +@media (min-width: 576px) { + .form-inline label { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0; + } + .form-inline .form-group { + display: flex; + flex: 0 0 auto; + flex-flow: row wrap; + align-items: center; + margin-bottom: 0; + } + .form-inline .form-control { + display: inline-block; + width: auto; + vertical-align: middle; + } + .form-inline .form-control-plaintext { + display: inline-block; + } + .form-inline .input-group, + .form-inline .custom-select { + width: auto; + } + .form-inline .form-check { + display: flex; + align-items: center; + justify-content: center; + width: auto; + padding-left: 0; + } + .form-inline .form-check-input { + position: relative; + margin-top: 0; + margin-right: 0.25rem; + margin-left: 0; + } + .form-inline .custom-control { + align-items: center; + justify-content: center; + } + .form-inline .custom-control-label { + margin-bottom: 0; + } +} + +.btn { + display: inline-block; + font-weight: 600; + text-align: center; + white-space: nowrap; + vertical-align: middle; + user-select: none; + border: 1px solid transparent; + padding: 11px 40px; + font-size: 0.875rem; + line-height: 1.35em; + border-radius: 0.25rem; + transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .btn { + transition: none; + } +} + +.btn:hover, +.btn:focus { + text-decoration: none; +} + +.btn:focus, +.btn.focus { + outline: 0; + box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08); +} + +.btn.disabled, +.btn:disabled { + opacity: 0.65; + box-shadow: none; +} + +.btn:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.btn:not(:disabled):not(.disabled):active, +.btn:not(:disabled):not(.disabled).active { + box-shadow: none; +} + +.btn:not(:disabled):not(.disabled):active:focus, +.btn:not(:disabled):not(.disabled).active:focus { + box-shadow: 0 7px 14px rgba(50, 50, 93, 0.1), 0 3px 6px rgba(0, 0, 0, 0.08), none; +} + +a.btn.disabled, +fieldset:disabled a.btn { + pointer-events: none; +} + +.btn-primary { + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-primary:hover { + color: #ffffff; + background-color: #db2dc0; + border-color: #d725bb; +} + +.btn-primary:focus, +.btn-primary.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(225, 78, 202, 0.5); +} + +.btn-primary.disabled, +.btn-primary:disabled { + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; +} + +.btn-primary:not(:disabled):not(.disabled):active, +.btn-primary:not(:disabled):not(.disabled).active, +.show>.btn-primary.dropdown-toggle { + color: #ffffff; + background-color: #d725bb; + border-color: #cd23b2; +} + +.btn-primary:not(:disabled):not(.disabled):active:focus, +.btn-primary:not(:disabled):not(.disabled).active:focus, +.show>.btn-primary.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(225, 78, 202, 0.5); +} + +.btn-secondary { + color: #212529; + background-color: #f4f5f7; + border-color: #f4f5f7; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-secondary:hover { + color: #212529; + background-color: #dee1e7; + border-color: #d6dae2; +} + +.btn-secondary:focus, +.btn-secondary.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(244, 245, 247, 0.5); +} + +.btn-secondary.disabled, +.btn-secondary:disabled { + color: #212529; + background-color: #f4f5f7; + border-color: #f4f5f7; +} + +.btn-secondary:not(:disabled):not(.disabled):active, +.btn-secondary:not(:disabled):not(.disabled).active, +.show>.btn-secondary.dropdown-toggle { + color: #212529; + background-color: #d6dae2; + border-color: #cfd3dc; +} + +.btn-secondary:not(:disabled):not(.disabled):active:focus, +.btn-secondary:not(:disabled):not(.disabled).active:focus, +.show>.btn-secondary.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(244, 245, 247, 0.5); +} + +.btn-success { + color: #ffffff; + background-color: #00f2c3; + border-color: #00f2c3; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-success:hover { + color: #ffffff; + background-color: #00cca4; + border-color: #00bf9a; +} + +.btn-success:focus, +.btn-success.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(0, 242, 195, 0.5); +} + +.btn-success.disabled, +.btn-success:disabled { + color: #ffffff; + background-color: #00f2c3; + border-color: #00f2c3; +} + +.btn-success:not(:disabled):not(.disabled):active, +.btn-success:not(:disabled):not(.disabled).active, +.show>.btn-success.dropdown-toggle { + color: #ffffff; + background-color: #00bf9a; + border-color: #00b290; +} + +.btn-success:not(:disabled):not(.disabled):active:focus, +.btn-success:not(:disabled):not(.disabled).active:focus, +.show>.btn-success.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(0, 242, 195, 0.5); +} + +.btn-info { + color: #ffffff; + background-color: #1d8cf8; + border-color: #1d8cf8; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-info:hover { + color: #ffffff; + background-color: #0779e8; + border-color: #0772db; +} + +.btn-info:focus, +.btn-info.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(29, 140, 248, 0.5); +} + +.btn-info.disabled, +.btn-info:disabled { + color: #ffffff; + background-color: #1d8cf8; + border-color: #1d8cf8; +} + +.btn-info:not(:disabled):not(.disabled):active, +.btn-info:not(:disabled):not(.disabled).active, +.show>.btn-info.dropdown-toggle { + color: #ffffff; + background-color: #0772db; + border-color: #066ccf; +} + +.btn-info:not(:disabled):not(.disabled):active:focus, +.btn-info:not(:disabled):not(.disabled).active:focus, +.show>.btn-info.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(29, 140, 248, 0.5); +} + +.btn-warning { + color: #ffffff; + background-color: #ff8d72; + border-color: #ff8d72; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-warning:hover { + color: #ffffff; + background-color: #ff6e4c; + border-color: #ff643f; +} + +.btn-warning:focus, +.btn-warning.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(255, 141, 114, 0.5); +} + +.btn-warning.disabled, +.btn-warning:disabled { + color: #ffffff; + background-color: #ff8d72; + border-color: #ff8d72; +} + +.btn-warning:not(:disabled):not(.disabled):active, +.btn-warning:not(:disabled):not(.disabled).active, +.show>.btn-warning.dropdown-toggle { + color: #ffffff; + background-color: #ff643f; + border-color: #ff5932; +} + +.btn-warning:not(:disabled):not(.disabled):active:focus, +.btn-warning:not(:disabled):not(.disabled).active:focus, +.show>.btn-warning.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(255, 141, 114, 0.5); +} + +.btn-danger { + color: #ffffff; + background-color: #fd5d93; + border-color: #fd5d93; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-danger:hover { + color: #ffffff; + background-color: #fd377a; + border-color: #fc2b71; +} + +.btn-danger:focus, +.btn-danger.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(253, 93, 147, 0.5); +} + +.btn-danger.disabled, +.btn-danger:disabled { + color: #ffffff; + background-color: #fd5d93; + border-color: #fd5d93; +} + +.btn-danger:not(:disabled):not(.disabled):active, +.btn-danger:not(:disabled):not(.disabled).active, +.show>.btn-danger.dropdown-toggle { + color: #ffffff; + background-color: #fc2b71; + border-color: #fc1e69; +} + +.btn-danger:not(:disabled):not(.disabled):active:focus, +.btn-danger:not(:disabled):not(.disabled).active:focus, +.show>.btn-danger.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(253, 93, 147, 0.5); +} + +.btn-light { + color: #ffffff; + background-color: #adb5bd; + border-color: #adb5bd; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-light:hover { + color: #ffffff; + background-color: #98a2ac; + border-color: #919ca6; +} + +.btn-light:focus, +.btn-light.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(173, 181, 189, 0.5); +} + +.btn-light.disabled, +.btn-light:disabled { + color: #ffffff; + background-color: #adb5bd; + border-color: #adb5bd; +} + +.btn-light:not(:disabled):not(.disabled):active, +.btn-light:not(:disabled):not(.disabled).active, +.show>.btn-light.dropdown-toggle { + color: #ffffff; + background-color: #919ca6; + border-color: #8a95a1; +} + +.btn-light:not(:disabled):not(.disabled):active:focus, +.btn-light:not(:disabled):not(.disabled).active:focus, +.show>.btn-light.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(173, 181, 189, 0.5); +} + +.btn-dark { + color: #ffffff; + background-color: #212529; + border-color: #212529; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-dark:hover { + color: #ffffff; + background-color: #101214; + border-color: #0a0c0d; +} + +.btn-dark:focus, +.btn-dark.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(33, 37, 41, 0.5); +} + +.btn-dark.disabled, +.btn-dark:disabled { + color: #ffffff; + background-color: #212529; + border-color: #212529; +} + +.btn-dark:not(:disabled):not(.disabled):active, +.btn-dark:not(:disabled):not(.disabled).active, +.show>.btn-dark.dropdown-toggle { + color: #ffffff; + background-color: #0a0c0d; + border-color: #050506; +} + +.btn-dark:not(:disabled):not(.disabled):active:focus, +.btn-dark:not(:disabled):not(.disabled).active:focus, +.show>.btn-dark.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(33, 37, 41, 0.5); +} + +.btn-default { + color: #ffffff; + background-color: #344675; + border-color: #344675; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-default:hover { + color: #ffffff; + background-color: #28365b; + border-color: #243152; +} + +.btn-default:focus, +.btn-default.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(52, 70, 117, 0.5); +} + +.btn-default.disabled, +.btn-default:disabled { + color: #ffffff; + background-color: #344675; + border-color: #344675; +} + +.btn-default:not(:disabled):not(.disabled):active, +.btn-default:not(:disabled):not(.disabled).active, +.show>.btn-default.dropdown-toggle { + color: #ffffff; + background-color: #243152; + border-color: #202c49; +} + +.btn-default:not(:disabled):not(.disabled):active:focus, +.btn-default:not(:disabled):not(.disabled).active:focus, +.show>.btn-default.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(52, 70, 117, 0.5); +} + +.btn-white { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-white:hover { + color: #212529; + background-color: #ececec; + border-color: #e6e6e6; +} + +.btn-white:focus, +.btn-white.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-white.disabled, +.btn-white:disabled { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-white:not(:disabled):not(.disabled):active, +.btn-white:not(:disabled):not(.disabled).active, +.show>.btn-white.dropdown-toggle { + color: #212529; + background-color: #e6e6e6; + border-color: #dfdfdf; +} + +.btn-white:not(:disabled):not(.disabled):active:focus, +.btn-white:not(:disabled):not(.disabled).active:focus, +.show>.btn-white.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-neutral { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-neutral:hover { + color: #212529; + background-color: #ececec; + border-color: #e6e6e6; +} + +.btn-neutral:focus, +.btn-neutral.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-neutral.disabled, +.btn-neutral:disabled { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-neutral:not(:disabled):not(.disabled):active, +.btn-neutral:not(:disabled):not(.disabled).active, +.show>.btn-neutral.dropdown-toggle { + color: #212529; + background-color: #e6e6e6; + border-color: #dfdfdf; +} + +.btn-neutral:not(:disabled):not(.disabled):active:focus, +.btn-neutral:not(:disabled):not(.disabled).active:focus, +.show>.btn-neutral.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-darker { + color: #ffffff; + background-color: black; + border-color: black; + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.btn-darker:hover { + color: #ffffff; + background-color: black; + border-color: black; +} + +.btn-darker:focus, +.btn-darker.focus { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08), 0 0 0 0 rgba(0, 0, 0, 0.5); +} + +.btn-darker.disabled, +.btn-darker:disabled { + color: #ffffff; + background-color: black; + border-color: black; +} + +.btn-darker:not(:disabled):not(.disabled):active, +.btn-darker:not(:disabled):not(.disabled).active, +.show>.btn-darker.dropdown-toggle { + color: #ffffff; + background-color: black; + border-color: black; +} + +.btn-darker:not(:disabled):not(.disabled):active:focus, +.btn-darker:not(:disabled):not(.disabled).active:focus, +.show>.btn-darker.dropdown-toggle:focus { + box-shadow: none, 0 0 0 0 rgba(0, 0, 0, 0.5); +} + +.btn-outline-primary { + color: #e14eca; + background-color: transparent; + background-image: none; + border-color: #e14eca; +} + +.btn-outline-primary:hover { + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; +} + +.btn-outline-primary:focus, +.btn-outline-primary.focus { + box-shadow: 0 0 0 0 rgba(225, 78, 202, 0.5); +} + +.btn-outline-primary.disabled, +.btn-outline-primary:disabled { + color: #e14eca; + background-color: transparent; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active, +.btn-outline-primary:not(:disabled):not(.disabled).active, +.show>.btn-outline-primary.dropdown-toggle { + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; +} + +.btn-outline-primary:not(:disabled):not(.disabled):active:focus, +.btn-outline-primary:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-primary.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(225, 78, 202, 0.5); +} + +.btn-outline-secondary { + color: #f4f5f7; + background-color: transparent; + background-image: none; + border-color: #f4f5f7; +} + +.btn-outline-secondary:hover { + color: #212529; + background-color: #f4f5f7; + border-color: #f4f5f7; +} + +.btn-outline-secondary:focus, +.btn-outline-secondary.focus { + box-shadow: 0 0 0 0 rgba(244, 245, 247, 0.5); +} + +.btn-outline-secondary.disabled, +.btn-outline-secondary:disabled { + color: #f4f5f7; + background-color: transparent; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active, +.btn-outline-secondary:not(:disabled):not(.disabled).active, +.show>.btn-outline-secondary.dropdown-toggle { + color: #212529; + background-color: #f4f5f7; + border-color: #f4f5f7; +} + +.btn-outline-secondary:not(:disabled):not(.disabled):active:focus, +.btn-outline-secondary:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-secondary.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(244, 245, 247, 0.5); +} + +.btn-outline-success { + color: #00f2c3; + background-color: transparent; + background-image: none; + border-color: #00f2c3; +} + +.btn-outline-success:hover { + color: #ffffff; + background-color: #00f2c3; + border-color: #00f2c3; +} + +.btn-outline-success:focus, +.btn-outline-success.focus { + box-shadow: 0 0 0 0 rgba(0, 242, 195, 0.5); +} + +.btn-outline-success.disabled, +.btn-outline-success:disabled { + color: #00f2c3; + background-color: transparent; +} + +.btn-outline-success:not(:disabled):not(.disabled):active, +.btn-outline-success:not(:disabled):not(.disabled).active, +.show>.btn-outline-success.dropdown-toggle { + color: #ffffff; + background-color: #00f2c3; + border-color: #00f2c3; +} + +.btn-outline-success:not(:disabled):not(.disabled):active:focus, +.btn-outline-success:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-success.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(0, 242, 195, 0.5); +} + +.btn-outline-info { + color: #1d8cf8; + background-color: transparent; + background-image: none; + border-color: #1d8cf8; +} + +.btn-outline-info:hover { + color: #ffffff; + background-color: #1d8cf8; + border-color: #1d8cf8; +} + +.btn-outline-info:focus, +.btn-outline-info.focus { + box-shadow: 0 0 0 0 rgba(29, 140, 248, 0.5); +} + +.btn-outline-info.disabled, +.btn-outline-info:disabled { + color: #1d8cf8; + background-color: transparent; +} + +.btn-outline-info:not(:disabled):not(.disabled):active, +.btn-outline-info:not(:disabled):not(.disabled).active, +.show>.btn-outline-info.dropdown-toggle { + color: #ffffff; + background-color: #1d8cf8; + border-color: #1d8cf8; +} + +.btn-outline-info:not(:disabled):not(.disabled):active:focus, +.btn-outline-info:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-info.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(29, 140, 248, 0.5); +} + +.btn-outline-warning { + color: #ff8d72; + background-color: transparent; + background-image: none; + border-color: #ff8d72; +} + +.btn-outline-warning:hover { + color: #ffffff; + background-color: #ff8d72; + border-color: #ff8d72; +} + +.btn-outline-warning:focus, +.btn-outline-warning.focus { + box-shadow: 0 0 0 0 rgba(255, 141, 114, 0.5); +} + +.btn-outline-warning.disabled, +.btn-outline-warning:disabled { + color: #ff8d72; + background-color: transparent; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active, +.btn-outline-warning:not(:disabled):not(.disabled).active, +.show>.btn-outline-warning.dropdown-toggle { + color: #ffffff; + background-color: #ff8d72; + border-color: #ff8d72; +} + +.btn-outline-warning:not(:disabled):not(.disabled):active:focus, +.btn-outline-warning:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-warning.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(255, 141, 114, 0.5); +} + +.btn-outline-danger { + color: #fd5d93; + background-color: transparent; + background-image: none; + border-color: #fd5d93; +} + +.btn-outline-danger:hover { + color: #ffffff; + background-color: #fd5d93; + border-color: #fd5d93; +} + +.btn-outline-danger:focus, +.btn-outline-danger.focus { + box-shadow: 0 0 0 0 rgba(253, 93, 147, 0.5); +} + +.btn-outline-danger.disabled, +.btn-outline-danger:disabled { + color: #fd5d93; + background-color: transparent; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active, +.btn-outline-danger:not(:disabled):not(.disabled).active, +.show>.btn-outline-danger.dropdown-toggle { + color: #ffffff; + background-color: #fd5d93; + border-color: #fd5d93; +} + +.btn-outline-danger:not(:disabled):not(.disabled):active:focus, +.btn-outline-danger:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-danger.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(253, 93, 147, 0.5); +} + +.btn-outline-light { + color: #adb5bd; + background-color: transparent; + background-image: none; + border-color: #adb5bd; +} + +.btn-outline-light:hover { + color: #ffffff; + background-color: #adb5bd; + border-color: #adb5bd; +} + +.btn-outline-light:focus, +.btn-outline-light.focus { + box-shadow: 0 0 0 0 rgba(173, 181, 189, 0.5); +} + +.btn-outline-light.disabled, +.btn-outline-light:disabled { + color: #adb5bd; + background-color: transparent; +} + +.btn-outline-light:not(:disabled):not(.disabled):active, +.btn-outline-light:not(:disabled):not(.disabled).active, +.show>.btn-outline-light.dropdown-toggle { + color: #ffffff; + background-color: #adb5bd; + border-color: #adb5bd; +} + +.btn-outline-light:not(:disabled):not(.disabled):active:focus, +.btn-outline-light:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-light.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(173, 181, 189, 0.5); +} + +.btn-outline-dark { + color: #212529; + background-color: transparent; + background-image: none; + border-color: #212529; +} + +.btn-outline-dark:hover { + color: #ffffff; + background-color: #212529; + border-color: #212529; +} + +.btn-outline-dark:focus, +.btn-outline-dark.focus { + box-shadow: 0 0 0 0 rgba(33, 37, 41, 0.5); +} + +.btn-outline-dark.disabled, +.btn-outline-dark:disabled { + color: #212529; + background-color: transparent; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active, +.btn-outline-dark:not(:disabled):not(.disabled).active, +.show>.btn-outline-dark.dropdown-toggle { + color: #ffffff; + background-color: #212529; + border-color: #212529; +} + +.btn-outline-dark:not(:disabled):not(.disabled):active:focus, +.btn-outline-dark:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-dark.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(33, 37, 41, 0.5); +} + +.btn-outline-default { + color: #344675; + background-color: transparent; + background-image: none; + border-color: #344675; +} + +.btn-outline-default:hover { + color: #ffffff; + background-color: #344675; + border-color: #344675; +} + +.btn-outline-default:focus, +.btn-outline-default.focus { + box-shadow: 0 0 0 0 rgba(52, 70, 117, 0.5); +} + +.btn-outline-default.disabled, +.btn-outline-default:disabled { + color: #344675; + background-color: transparent; +} + +.btn-outline-default:not(:disabled):not(.disabled):active, +.btn-outline-default:not(:disabled):not(.disabled).active, +.show>.btn-outline-default.dropdown-toggle { + color: #ffffff; + background-color: #344675; + border-color: #344675; +} + +.btn-outline-default:not(:disabled):not(.disabled):active:focus, +.btn-outline-default:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-default.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(52, 70, 117, 0.5); +} + +.btn-outline-white { + color: #ffffff; + background-color: transparent; + background-image: none; + border-color: #ffffff; +} + +.btn-outline-white:hover { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-outline-white:focus, +.btn-outline-white.focus { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-outline-white.disabled, +.btn-outline-white:disabled { + color: #ffffff; + background-color: transparent; +} + +.btn-outline-white:not(:disabled):not(.disabled):active, +.btn-outline-white:not(:disabled):not(.disabled).active, +.show>.btn-outline-white.dropdown-toggle { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-outline-white:not(:disabled):not(.disabled):active:focus, +.btn-outline-white:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-white.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-outline-neutral { + color: #ffffff; + background-color: transparent; + background-image: none; + border-color: #ffffff; +} + +.btn-outline-neutral:hover { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-outline-neutral:focus, +.btn-outline-neutral.focus { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-outline-neutral.disabled, +.btn-outline-neutral:disabled { + color: #ffffff; + background-color: transparent; +} + +.btn-outline-neutral:not(:disabled):not(.disabled):active, +.btn-outline-neutral:not(:disabled):not(.disabled).active, +.show>.btn-outline-neutral.dropdown-toggle { + color: #212529; + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-outline-neutral:not(:disabled):not(.disabled):active:focus, +.btn-outline-neutral:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-neutral.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.5); +} + +.btn-outline-darker { + color: black; + background-color: transparent; + background-image: none; + border-color: black; +} + +.btn-outline-darker:hover { + color: #ffffff; + background-color: black; + border-color: black; +} + +.btn-outline-darker:focus, +.btn-outline-darker.focus { + box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.5); +} + +.btn-outline-darker.disabled, +.btn-outline-darker:disabled { + color: black; + background-color: transparent; +} + +.btn-outline-darker:not(:disabled):not(.disabled):active, +.btn-outline-darker:not(:disabled):not(.disabled).active, +.show>.btn-outline-darker.dropdown-toggle { + color: #ffffff; + background-color: black; + border-color: black; +} + +.btn-outline-darker:not(:disabled):not(.disabled):active:focus, +.btn-outline-darker:not(:disabled):not(.disabled).active:focus, +.show>.btn-outline-darker.dropdown-toggle:focus { + box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.5); +} + +.btn-link { + font-weight: 400; + color: #e14eca; + background-color: transparent; +} + +.btn-link:hover { + color: #c221a9; + text-decoration: none; + background-color: transparent; + border-color: transparent; +} + +.btn-link:focus, +.btn-link.focus { + text-decoration: none; + border-color: transparent; + box-shadow: none; +} + +.btn-link:disabled, +.btn-link.disabled { + color: #6c757d; + pointer-events: none; +} + +.btn-lg, +.btn-group-lg>.btn { + padding: 15px 48px; + font-size: 0.99925rem; + line-height: 1.35; + border-radius: 0.4285rem; +} + +.btn-sm, +.btn-group-sm>.btn { + padding: 5px 15px; + font-size: 0.75rem; + line-height: 1.35; + border-radius: 0.25rem; +} + +.btn-block { + display: block; + width: 100%; +} + +.btn-block+.btn-block { + margin-top: 0.5rem; +} + +input[type="submit"].btn-block, +input[type="reset"].btn-block, +input[type="button"].btn-block { + width: 100%; +} + +.fade { + transition: opacity 0.15s linear; +} + +@media screen and (prefers-reduced-motion: reduce) { + .fade { + transition: none; + } +} + +.fade:not(.show) { + opacity: 0; +} + +.collapse:not(.show) { + display: none; +} + +.collapsing { + position: relative; + height: 0; + overflow: hidden; + transition: height 0.35s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .collapsing { + transition: none; + } +} + +.dropup, +.dropright, +.dropdown, +.dropleft { + position: relative; +} + +.dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; +} + +.dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropdown-menu { + position: absolute; + top: 100%; + left: 0; + z-index: 1000; + display: none; + float: left; + min-width: 10rem; + padding: 0.5rem 0; + margin: 0.125rem 0 0; + font-size: 0.875rem; + color: #525f7f; + text-align: left; + list-style: none; + background-color: #ffffff; + background-clip: padding-box; + border: 0 solid rgba(34, 42, 66, 0.15); + border-radius: 0.1428rem; + box-shadow: 0 50px 100px rgba(50, 50, 93, 0.1), 0 15px 35px rgba(50, 50, 93, 0.15), 0 5px 15px rgba(0, 0, 0, 0.1); +} + +.dropdown-menu-right { + right: 0; + left: auto; +} + +.dropup .dropdown-menu { + top: auto; + bottom: 100%; + margin-top: 0; + margin-bottom: 0.125rem; +} + +.dropup .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0; + border-right: 0.3em solid transparent; + border-bottom: 0.3em solid; + border-left: 0.3em solid transparent; +} + +.dropup .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-menu { + top: 0; + right: auto; + left: 100%; + margin-top: 0; + margin-left: 0.125rem; +} + +.dropright .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0; + border-bottom: 0.3em solid transparent; + border-left: 0.3em solid; +} + +.dropright .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropright .dropdown-toggle::after { + vertical-align: 0; +} + +.dropleft .dropdown-menu { + top: 0; + right: 100%; + left: auto; + margin-top: 0; + margin-right: 0.125rem; +} + +.dropleft .dropdown-toggle::after { + display: inline-block; + width: 0; + height: 0; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; +} + +.dropleft .dropdown-toggle::after { + display: none; +} + +.dropleft .dropdown-toggle::before { + display: inline-block; + width: 0; + height: 0; + margin-right: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid transparent; + border-right: 0.3em solid; + border-bottom: 0.3em solid transparent; +} + +.dropleft .dropdown-toggle:empty::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle::before { + vertical-align: 0; +} + +.dropdown-menu[x-placement^="top"], +.dropdown-menu[x-placement^="right"], +.dropdown-menu[x-placement^="bottom"], +.dropdown-menu[x-placement^="left"] { + right: auto; + bottom: auto; +} + +.dropdown-divider { + height: 0; + margin: 0.5rem 0; + overflow: hidden; + border-top: 1px solid #e9ecef; +} + +.dropdown-item { + display: block; + width: 100%; + padding: 0.25rem 1.5rem; + clear: both; + font-weight: 400; + color: #212529; + text-align: inherit; + white-space: nowrap; + background-color: transparent; + border: 0; +} + +.dropdown-item:hover, +.dropdown-item:focus { + color: #16181b; + text-decoration: none; + background-color: #f6f9fc; +} + +.dropdown-item.active, +.dropdown-item:active { + color: #ffffff; + text-decoration: none; + background-color: #e14eca; +} + +.dropdown-item.disabled, +.dropdown-item:disabled { + color: #6c757d; + background-color: transparent; +} + +.dropdown-menu.show { + display: block; +} + +.dropdown-header { + display: block; + padding: 0.5rem 1.5rem; + margin-bottom: 0; + font-size: 0.75rem; + color: #6c757d; + white-space: nowrap; +} + +.dropdown-item-text { + display: block; + padding: 0.25rem 1.5rem; + color: #212529; +} + +.btn-group, +.btn-group-vertical { + position: relative; + display: inline-flex; + vertical-align: middle; +} + +.btn-group>.btn, +.btn-group-vertical>.btn { + position: relative; + flex: 0 1 auto; +} + +.btn-group>.btn:hover, +.btn-group-vertical>.btn:hover { + z-index: 1; +} + +.btn-group>.btn:focus, +.btn-group>.btn:active, +.btn-group>.btn.active, +.btn-group-vertical>.btn:focus, +.btn-group-vertical>.btn:active, +.btn-group-vertical>.btn.active { + z-index: 1; +} + +.btn-group .btn+.btn, +.btn-group .btn+.btn-group, +.btn-group .btn-group+.btn, +.btn-group .btn-group+.btn-group, +.btn-group-vertical .btn+.btn, +.btn-group-vertical .btn+.btn-group, +.btn-group-vertical .btn-group+.btn, +.btn-group-vertical .btn-group+.btn-group { + margin-left: -1px; +} + +.btn-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: flex-start; +} + +.btn-toolbar .input-group { + width: auto; +} + +.btn-group>.btn:first-child { + margin-left: 0; +} + +.btn-group>.btn:not(:last-child):not(.dropdown-toggle), +.btn-group>.btn-group:not(:last-child)>.btn { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.btn-group>.btn:not(:first-child), +.btn-group>.btn-group:not(:first-child)>.btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.dropdown-toggle-split { + padding-right: 30px; + padding-left: 30px; +} + +.dropdown-toggle-split::after, +.dropup .dropdown-toggle-split::after, +.dropright .dropdown-toggle-split::after { + margin-left: 0; +} + +.dropleft .dropdown-toggle-split::before { + margin-right: 0; +} + +.btn-sm+.dropdown-toggle-split, +.btn-group-sm>.btn+.dropdown-toggle-split { + padding-right: 11.25px; + padding-left: 11.25px; +} + +.btn-lg+.dropdown-toggle-split, +.btn-group-lg>.btn+.dropdown-toggle-split { + padding-right: 36px; + padding-left: 36px; +} + +.btn-group.show .dropdown-toggle { + box-shadow: none; +} + +.btn-group.show .dropdown-toggle.btn-link { + box-shadow: none; +} + +.btn-group-vertical { + flex-direction: column; + align-items: flex-start; + justify-content: center; +} + +.btn-group-vertical .btn, +.btn-group-vertical .btn-group { + width: 100%; +} + +.btn-group-vertical>.btn+.btn, +.btn-group-vertical>.btn+.btn-group, +.btn-group-vertical>.btn-group+.btn, +.btn-group-vertical>.btn-group+.btn-group { + margin-top: -1px; + margin-left: 0; +} + +.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle), +.btn-group-vertical>.btn-group:not(:last-child)>.btn { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.btn-group-vertical>.btn:not(:first-child), +.btn-group-vertical>.btn-group:not(:first-child)>.btn { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.btn-group-toggle>.btn, +.btn-group-toggle>.btn-group>.btn { + margin-bottom: 0; +} + +.btn-group-toggle>.btn input[type="radio"], +.btn-group-toggle>.btn input[type="checkbox"], +.btn-group-toggle>.btn-group>.btn input[type="radio"], +.btn-group-toggle>.btn-group>.btn input[type="checkbox"] { + position: absolute; + clip: rect(0, 0, 0, 0); + pointer-events: none; +} + +.input-group { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: stretch; + width: 100%; +} + +.input-group>.form-control, +.input-group>.custom-select, +.input-group>.custom-file { + position: relative; + flex: 1 1 auto; + width: 1%; + margin-bottom: 0; +} + +.input-group>.form-control+.form-control, +.input-group>.form-control+.custom-select, +.input-group>.form-control+.custom-file, +.input-group>.custom-select+.form-control, +.input-group>.custom-select+.custom-select, +.input-group>.custom-select+.custom-file, +.input-group>.custom-file+.form-control, +.input-group>.custom-file+.custom-select, +.input-group>.custom-file+.custom-file { + margin-left: -1px; +} + +.input-group>.form-control:focus, +.input-group>.custom-select:focus, +.input-group>.custom-file .custom-file-input:focus~.custom-file-label { + z-index: 3; +} + +.input-group>.custom-file .custom-file-input:focus { + z-index: 4; +} + +.input-group>.form-control:not(:last-child), +.input-group>.custom-select:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group>.form-control:not(:first-child), +.input-group>.custom-select:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group>.custom-file { + display: flex; + align-items: center; +} + +.input-group>.custom-file:not(:last-child) .custom-file-label, +.input-group>.custom-file:not(:last-child) .custom-file-label::after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group>.custom-file:not(:first-child) .custom-file-label { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.input-group-prepend, +.input-group-append { + display: flex; +} + +.input-group-prepend .btn, +.input-group-append .btn { + position: relative; + z-index: 2; +} + +.input-group-prepend .btn+.btn, +.input-group-prepend .btn+.input-group-text, +.input-group-prepend .input-group-text+.input-group-text, +.input-group-prepend .input-group-text+.btn, +.input-group-append .btn+.btn, +.input-group-append .btn+.input-group-text, +.input-group-append .input-group-text+.input-group-text, +.input-group-append .input-group-text+.btn { + margin-left: -1px; +} + +.input-group-prepend { + margin-right: -1px; +} + +.input-group-append { + margin-left: -1px; +} + +.input-group-text { + display: flex; + align-items: center; + padding: 0.5rem 0.7rem; + margin-bottom: 0; + font-size: 0.875rem; + font-weight: 400; + line-height: 1.428571; + color: #adb5bd; + text-align: center; + white-space: nowrap; + background-color: transparent; + border: 1px solid #cad1d7; + border-radius: 0.25rem; +} + +.input-group-text input[type="radio"], +.input-group-text input[type="checkbox"] { + margin-top: 0; +} + +.input-group-lg>.form-control, +.input-group-lg>.input-group-prepend>.input-group-text, +.input-group-lg>.input-group-append>.input-group-text, +.input-group-lg>.input-group-prepend>.btn, +.input-group-lg>.input-group-append>.btn { + height: calc(3.098987rem + 2px); + padding: 0.875rem 1rem; + font-size: 0.99925rem; + line-height: 1.35; + border-radius: 0.4285rem; +} + +.input-group-sm>.form-control, +.input-group-sm>.input-group-prepend>.input-group-text, +.input-group-sm>.input-group-append>.input-group-text, +.input-group-sm>.input-group-prepend>.btn, +.input-group-sm>.input-group-append>.btn { + height: calc(1.5125rem + 2px); + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + line-height: 1.35; + border-radius: 0.2857rem; +} + +.input-group>.input-group-prepend>.btn, +.input-group>.input-group-prepend>.input-group-text, +.input-group>.input-group-append:not(:last-child)>.btn, +.input-group>.input-group-append:not(:last-child)>.input-group-text, +.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle), +.input-group>.input-group-append:last-child>.input-group-text:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.input-group>.input-group-append>.btn, +.input-group>.input-group-append>.input-group-text, +.input-group>.input-group-prepend:not(:first-child)>.btn, +.input-group>.input-group-prepend:not(:first-child)>.input-group-text, +.input-group>.input-group-prepend:first-child>.btn:not(:first-child), +.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} + +.custom-control { + position: relative; + display: block; + min-height: 1.3125rem; + padding-left: 1.75rem; +} + +.custom-control-inline { + display: inline-flex; + margin-right: 1rem; +} + +.custom-control-input { + position: absolute; + z-index: -1; + opacity: 0; +} + +.custom-control-input:checked~.custom-control-label::before { + color: #ffffff; + background-color: #e14eca; + box-shadow: none; +} + +.custom-control-input:focus~.custom-control-label::before { + box-shadow: none; +} + +.custom-control-input:active~.custom-control-label::before { + color: #ffffff; + background-color: #e14eca; + box-shadow: none; +} + +.custom-control-input:disabled~.custom-control-label { + color: #6c757d; +} + +.custom-control-input:disabled~.custom-control-label::before { + background-color: #e9ecef; +} + +.custom-control-label { + position: relative; + margin-bottom: 0; +} + +.custom-control-label::before { + position: absolute; + top: 0.03125rem; + left: -1.75rem; + display: block; + width: 1.25rem; + height: 1.25rem; + pointer-events: none; + content: ""; + user-select: none; + background-color: transparent; + box-shadow: none; +} + +.custom-control-label::after { + position: absolute; + top: 0.03125rem; + left: -1.75rem; + display: block; + width: 1.25rem; + height: 1.25rem; + content: ""; + background-repeat: no-repeat; + background-position: center center; + background-size: 50% 50%; +} + +.custom-checkbox .custom-control-label::before { + border-radius: 0.2857rem; +} + +.custom-checkbox .custom-control-input:checked~.custom-control-label::before { + background-color: #e14eca; +} + +.custom-checkbox .custom-control-input:checked~.custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffffff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); +} + +.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::before { + background-color: #e14eca; + box-shadow: none; +} + +.custom-checkbox .custom-control-input:indeterminate~.custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23ffffff' d='M0 2h4'/%3E%3C/svg%3E"); +} + +.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label::before { + background-color: rgba(225, 78, 202, 0.5); +} + +.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label::before { + background-color: rgba(225, 78, 202, 0.5); +} + +.custom-radio .custom-control-label::before { + border-radius: 50%; +} + +.custom-radio .custom-control-input:checked~.custom-control-label::before { + background-color: #e14eca; +} + +.custom-radio .custom-control-input:checked~.custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffffff'/%3E%3C/svg%3E"); +} + +.custom-radio .custom-control-input:disabled:checked~.custom-control-label::before { + background-color: rgba(225, 78, 202, 0.5); +} + +.custom-select { + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + padding: 0.375rem 1.75rem 0.375rem 0.75rem; + line-height: 1.428571; + color: rgba(255, 255, 255, 0.8); + vertical-align: middle; + background: transparent url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%2332325d' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right 0.75rem center; + background-size: 8px 10px; + border: 1px solid #cad1d7; + border-radius: 0.25rem; + box-shadow: inset 0 1px 2px rgba(34, 42, 66, 0.075); + appearance: none; +} + +.custom-select:focus { + border-color: rgba(50, 151, 211, 0.25); + outline: 0; + box-shadow: inset 0 1px 2px rgba(34, 42, 66, 0.075), 0 0 0 0 rgba(50, 151, 211, 0.5); +} + +.custom-select:focus::-ms-value { + color: rgba(255, 255, 255, 0.8); + background-color: transparent; +} + +.custom-select[multiple], +.custom-select[size]:not([size="1"]) { + height: auto; + padding-right: 0.75rem; + background-image: none; +} + +.custom-select:disabled { + color: #6c757d; + background-color: #e9ecef; +} + +.custom-select::-ms-expand { + opacity: 0; +} + +.custom-select-sm { + height: calc(1.5125rem + 2px); + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: 75%; +} + +.custom-select-lg { + height: calc(3.098987rem + 2px); + padding-top: 0.375rem; + padding-bottom: 0.375rem; + font-size: 125%; +} + +.custom-file { + position: relative; + display: inline-block; + width: 100%; + height: calc(2.25rem + 2px); + margin-bottom: 0; +} + +.custom-file-input { + position: relative; + z-index: 2; + width: 100%; + height: calc(2.25rem + 2px); + margin: 0; + opacity: 0; +} + +.custom-file-input:focus~.custom-file-label { + border-color: rgba(50, 151, 211, 0.25); + box-shadow: none; +} + +.custom-file-input:focus~.custom-file-label::after { + border-color: rgba(50, 151, 211, 0.25); +} + +.custom-file-input:disabled~.custom-file-label { + background-color: #e9ecef; +} + +.custom-file-input:lang(en)~.custom-file-label::after { + content: "Browse"; +} + +.custom-file-label { + position: absolute; + top: 0; + right: 0; + left: 0; + z-index: 1; + height: calc(2.25rem + 2px); + padding: 0.5rem 0.7rem; + line-height: 1.428571; + color: rgba(255, 255, 255, 0.8); + background-color: transparent; + border: 1px solid #cad1d7; + border-radius: 0.25rem; + box-shadow: none; +} + +.custom-file-label::after { + position: absolute; + top: 0; + right: 0; + bottom: 0; + z-index: 3; + display: block; + height: 2.25rem; + padding: 0.5rem 0.7rem; + line-height: 1.428571; + color: rgba(255, 255, 255, 0.8); + content: "Browse"; + background-color: transparent; + border-left: 1px solid #cad1d7; + border-radius: 0 0.25rem 0.25rem 0; +} + +.custom-range { + width: 100%; + padding-left: 0; + background-color: transparent; + appearance: none; +} + +.custom-range:focus { + outline: none; +} + +.custom-range:focus::-webkit-slider-thumb { + box-shadow: 0 0 0 1px #1e1e2f, none; +} + +.custom-range:focus::-moz-range-thumb { + box-shadow: 0 0 0 1px #1e1e2f, none; +} + +.custom-range:focus::-ms-thumb { + box-shadow: 0 0 0 1px #1e1e2f, none; +} + +.custom-range::-moz-focus-outer { + border: 0; +} + +.custom-range::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + margin-top: -0.25rem; + background-color: #e14eca; + border: 0; + border-radius: 1rem; + box-shadow: 0 0.1rem 0.25rem rgba(34, 42, 66, 0.1); + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-webkit-slider-thumb { + transition: none; + } +} + +.custom-range::-webkit-slider-thumb:active { + background-color: #fbe7f8; +} + +.custom-range::-webkit-slider-runnable-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #e3e3e3; + border-color: transparent; + border-radius: 1rem; + box-shadow: inset 0 0.25rem 0.25rem rgba(34, 42, 66, 0.1); +} + +.custom-range::-moz-range-thumb { + width: 1rem; + height: 1rem; + background-color: #e14eca; + border: 0; + border-radius: 1rem; + box-shadow: 0 0.1rem 0.25rem rgba(34, 42, 66, 0.1); + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-moz-range-thumb { + transition: none; + } +} + +.custom-range::-moz-range-thumb:active { + background-color: #fbe7f8; +} + +.custom-range::-moz-range-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: #e3e3e3; + border-color: transparent; + border-radius: 1rem; + box-shadow: inset 0 0.25rem 0.25rem rgba(34, 42, 66, 0.1); +} + +.custom-range::-ms-thumb { + width: 1rem; + height: 1rem; + margin-top: 0; + margin-right: 0; + margin-left: 0; + background-color: #e14eca; + border: 0; + border-radius: 1rem; + box-shadow: 0 0.1rem 0.25rem rgba(34, 42, 66, 0.1); + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + appearance: none; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-range::-ms-thumb { + transition: none; + } +} + +.custom-range::-ms-thumb:active { + background-color: #fbe7f8; +} + +.custom-range::-ms-track { + width: 100%; + height: 0.5rem; + color: transparent; + cursor: pointer; + background-color: transparent; + border-color: transparent; + border-width: 0.5rem; + box-shadow: inset 0 0.25rem 0.25rem rgba(34, 42, 66, 0.1); +} + +.custom-range::-ms-fill-lower { + background-color: #e3e3e3; + border-radius: 1rem; +} + +.custom-range::-ms-fill-upper { + margin-right: 15px; + background-color: #e3e3e3; + border-radius: 1rem; +} + +.custom-control-label::before, +.custom-file-label, +.custom-select { + transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +@media screen and (prefers-reduced-motion: reduce) { + .custom-control-label::before, + .custom-file-label, + .custom-select { + transition: none; + } +} + +.nav { + display: flex; + flex-wrap: wrap; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.nav-link { + display: block; + padding: 0.5rem 1rem; +} + +.nav-link:hover, +.nav-link:focus { + text-decoration: none; +} + +.nav-link.disabled { + color: #6c757d; +} + +.nav-tabs { + border-bottom: 0.0625rem solid #e3e3e3; +} + +.nav-tabs .nav-item { + margin-bottom: -0.0625rem; +} + +.nav-tabs .nav-link { + border: 0.0625rem solid transparent; + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.nav-tabs .nav-link:hover, +.nav-tabs .nav-link:focus { + border-color: #e9ecef #e9ecef #e3e3e3; +} + +.nav-tabs .nav-link.disabled { + color: #6c757d; + background-color: transparent; + border-color: transparent; +} + +.nav-tabs .nav-link.active, +.nav-tabs .nav-item.show .nav-link { + color: #525f7f; + background-color: #1e1e2f; + border-color: #e3e3e3 #e3e3e3 #1e1e2f; +} + +.nav-tabs .dropdown-menu { + margin-top: -0.0625rem; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.nav-pills .nav-link { + border-radius: 0.25rem; +} + +.nav-pills .nav-link.active, +.nav-pills .show>.nav-link { + color: #ffffff; + background-color: #e14eca; +} + +.nav-fill .nav-item { + flex: 1 1 auto; + text-align: center; +} + +.nav-justified .nav-item { + flex-basis: 0; + flex-grow: 1; + text-align: center; +} + +.tab-content>.tab-pane { + display: none; +} + +.tab-content>.active { + display: block; +} + +.navbar { + position: relative; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0.625rem 0.9375rem; +} + +.navbar>.container, +.navbar>.container-fluid { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; +} + +.navbar-brand { + display: inline-block; + padding-top: 0.406813rem; + padding-bottom: 0.406813rem; + margin-right: 0.9375rem; + font-size: 0.99925rem; + line-height: inherit; + white-space: nowrap; +} + +.navbar-brand:hover, +.navbar-brand:focus { + text-decoration: none; +} + +.navbar-nav { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; + list-style: none; +} + +.navbar-nav .nav-link { + padding-right: 0; + padding-left: 0; +} + +.navbar-nav .dropdown-menu { + position: static; + float: none; +} + +.navbar-text { + display: inline-block; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.navbar-collapse { + flex-basis: 100%; + flex-grow: 1; + align-items: center; +} + +.navbar-toggler { + padding: 0.25rem 0.75rem; + font-size: 0.99925rem; + line-height: 1; + background-color: transparent; + border: 0.0625rem solid transparent; + border-radius: 0.25rem; +} + +.navbar-toggler:hover, +.navbar-toggler:focus { + text-decoration: none; +} + +.navbar-toggler:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.navbar-toggler-icon { + display: inline-block; + width: 1.5em; + height: 1.5em; + vertical-align: middle; + content: ""; + background: no-repeat center center; + background-size: 100% 100%; +} + +@media (max-width: 575.98px) { + .navbar-expand-sm>.container, + .navbar-expand-sm>.container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 576px) { + .navbar-expand-sm { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-sm .navbar-nav { + flex-direction: row; + } + .navbar-expand-sm .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-sm .navbar-nav .nav-link { + padding-right: 1rem; + padding-left: 1rem; + } + .navbar-expand-sm>.container, + .navbar-expand-sm>.container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-sm .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-sm .navbar-toggler { + display: none; + } +} + +@media (max-width: 767.98px) { + .navbar-expand-md>.container, + .navbar-expand-md>.container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 768px) { + .navbar-expand-md { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-md .navbar-nav { + flex-direction: row; + } + .navbar-expand-md .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-md .navbar-nav .nav-link { + padding-right: 1rem; + padding-left: 1rem; + } + .navbar-expand-md>.container, + .navbar-expand-md>.container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-md .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-md .navbar-toggler { + display: none; + } +} + +@media (max-width: 991.98px) { + .navbar-expand-lg>.container, + .navbar-expand-lg>.container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 992px) { + .navbar-expand-lg { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-lg .navbar-nav { + flex-direction: row; + } + .navbar-expand-lg .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 1rem; + padding-left: 1rem; + } + .navbar-expand-lg>.container, + .navbar-expand-lg>.container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-lg .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-lg .navbar-toggler { + display: none; + } +} + +@media (max-width: 1199.98px) { + .navbar-expand-xl>.container, + .navbar-expand-xl>.container-fluid { + padding-right: 0; + padding-left: 0; + } +} + +@media (min-width: 1200px) { + .navbar-expand-xl { + flex-flow: row nowrap; + justify-content: flex-start; + } + .navbar-expand-xl .navbar-nav { + flex-direction: row; + } + .navbar-expand-xl .navbar-nav .dropdown-menu { + position: absolute; + } + .navbar-expand-xl .navbar-nav .nav-link { + padding-right: 1rem; + padding-left: 1rem; + } + .navbar-expand-xl>.container, + .navbar-expand-xl>.container-fluid { + flex-wrap: nowrap; + } + .navbar-expand-xl .navbar-collapse { + display: flex !important; + flex-basis: auto; + } + .navbar-expand-xl .navbar-toggler { + display: none; + } +} + +.navbar-expand { + flex-flow: row nowrap; + justify-content: flex-start; +} + +.navbar-expand>.container, +.navbar-expand>.container-fluid { + padding-right: 0; + padding-left: 0; +} + +.navbar-expand .navbar-nav { + flex-direction: row; +} + +.navbar-expand .navbar-nav .dropdown-menu { + position: absolute; +} + +.navbar-expand .navbar-nav .nav-link { + padding-right: 1rem; + padding-left: 1rem; +} + +.navbar-expand>.container, +.navbar-expand>.container-fluid { + flex-wrap: nowrap; +} + +.navbar-expand .navbar-collapse { + display: flex !important; + flex-basis: auto; +} + +.navbar-expand .navbar-toggler { + display: none; +} + +.navbar-light .navbar-brand { + color: rgba(34, 42, 66, 0.9); +} + +.navbar-light .navbar-brand:hover, +.navbar-light .navbar-brand:focus { + color: rgba(34, 42, 66, 0.9); +} + +.navbar-light .navbar-nav .nav-link { + color: rgba(34, 42, 66, 0.5); +} + +.navbar-light .navbar-nav .nav-link:hover, +.navbar-light .navbar-nav .nav-link:focus { + color: rgba(34, 42, 66, 0.7); +} + +.navbar-light .navbar-nav .nav-link.disabled { + color: rgba(34, 42, 66, 0.3); +} + +.navbar-light .navbar-nav .show>.nav-link, +.navbar-light .navbar-nav .active>.nav-link, +.navbar-light .navbar-nav .nav-link.show, +.navbar-light .navbar-nav .nav-link.active { + color: rgba(34, 42, 66, 0.9); +} + +.navbar-light .navbar-toggler { + color: rgba(34, 42, 66, 0.5); + border-color: transparent; +} + +.navbar-light .navbar-toggler-icon { + background-image: url("data:image/svg+xml !default;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(34, 42, 66, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.navbar-light .navbar-text { + color: rgba(34, 42, 66, 0.5); +} + +.navbar-light .navbar-text a { + color: rgba(34, 42, 66, 0.9); +} + +.navbar-light .navbar-text a:hover, +.navbar-light .navbar-text a:focus { + color: rgba(34, 42, 66, 0.9); +} + +.navbar-dark .navbar-brand { + color: rgba(255, 255, 255, 0.65); +} + +.navbar-dark .navbar-brand:hover, +.navbar-dark .navbar-brand:focus { + color: rgba(255, 255, 255, 0.65); +} + +.navbar-dark .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.95); +} + +.navbar-dark .navbar-nav .nav-link:hover, +.navbar-dark .navbar-nav .nav-link:focus { + color: rgba(255, 255, 255, 0.65); +} + +.navbar-dark .navbar-nav .nav-link.disabled { + color: rgba(255, 255, 255, 0.25); +} + +.navbar-dark .navbar-nav .show>.nav-link, +.navbar-dark .navbar-nav .active>.nav-link, +.navbar-dark .navbar-nav .nav-link.show, +.navbar-dark .navbar-nav .nav-link.active { + color: rgba(255, 255, 255, 0.65); +} + +.navbar-dark .navbar-toggler { + color: rgba(255, 255, 255, 0.95); + border-color: transparent; +} + +.navbar-dark .navbar-toggler-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.95)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); +} + +.navbar-dark .navbar-text { + color: rgba(255, 255, 255, 0.95); +} + +.navbar-dark .navbar-text a { + color: rgba(255, 255, 255, 0.65); +} + +.navbar-dark .navbar-text a:hover, +.navbar-dark .navbar-text a:focus { + color: rgba(255, 255, 255, 0.65); +} + +.card { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + word-wrap: break-word; + background-color: #ffffff; + background-clip: border-box; + border: 0.0625rem solid rgba(34, 42, 66, 0.05); + border-radius: 0.2857rem; +} + +.card>hr { + margin-right: 0; + margin-left: 0; +} + +.card>.list-group:first-child .list-group-item:first-child { + border-top-left-radius: 0.2857rem; + border-top-right-radius: 0.2857rem; +} + +.card>.list-group:last-child .list-group-item:last-child { + border-bottom-right-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; +} + +.card-body { + flex: 1 1 auto; + padding: 1.5rem; +} + +.card-title { + margin-bottom: 1.25rem; +} + +.card-subtitle { + margin-top: -0.625rem; + margin-bottom: 0; +} + +.card-text:last-child { + margin-bottom: 0; +} + +.card-link:hover { + text-decoration: none; +} + +.card-link+.card-link { + margin-left: 1.5rem; +} + +.card-header { + padding: 1.25rem 1.5rem; + margin-bottom: 0; + background-color: #f6f9fc; + border-bottom: 0.0625rem solid rgba(34, 42, 66, 0.05); +} + +.card-header:first-child { + border-radius: calc(0.2857rem - 0.0625rem) calc(0.2857rem - 0.0625rem) 0 0; +} + +.card-header+.list-group .list-group-item:first-child { + border-top: 0; +} + +.card-footer { + padding: 1.25rem 1.5rem; + background-color: #f6f9fc; + border-top: 0.0625rem solid rgba(34, 42, 66, 0.05); +} + +.card-footer:last-child { + border-radius: 0 0 calc(0.2857rem - 0.0625rem) calc(0.2857rem - 0.0625rem); +} + +.card-header-tabs { + margin-right: -0.75rem; + margin-bottom: -1.25rem; + margin-left: -0.75rem; + border-bottom: 0; +} + +.card-header-pills { + margin-right: -0.75rem; + margin-left: -0.75rem; +} + +.card-img-overlay { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: 1.25rem; +} + +.card-img { + width: 100%; + border-radius: calc(0.2857rem - 0.0625rem); +} + +.card-img-top { + width: 100%; + border-top-left-radius: calc(0.2857rem - 0.0625rem); + border-top-right-radius: calc(0.2857rem - 0.0625rem); +} + +.card-img-bottom { + width: 100%; + border-bottom-right-radius: calc(0.2857rem - 0.0625rem); + border-bottom-left-radius: calc(0.2857rem - 0.0625rem); +} + +.card-deck { + display: flex; + flex-direction: column; +} + +.card-deck .card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-deck { + flex-flow: row wrap; + margin-right: -15px; + margin-left: -15px; + } + .card-deck .card { + display: flex; + flex: 1 0 0%; + flex-direction: column; + margin-right: 15px; + margin-bottom: 0; + margin-left: 15px; + } +} + +.card-group { + display: flex; + flex-direction: column; +} + +.card-group>.card { + margin-bottom: 15px; +} + +@media (min-width: 576px) { + .card-group { + flex-flow: row wrap; + } + .card-group>.card { + flex: 1 0 0%; + margin-bottom: 0; + } + .card-group>.card+.card { + margin-left: 0; + border-left: 0; + } + .card-group>.card:first-child { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .card-group>.card:first-child .card-img-top, + .card-group>.card:first-child .card-header { + border-top-right-radius: 0; + } + .card-group>.card:first-child .card-img-bottom, + .card-group>.card:first-child .card-footer { + border-bottom-right-radius: 0; + } + .card-group>.card:last-child { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .card-group>.card:last-child .card-img-top, + .card-group>.card:last-child .card-header { + border-top-left-radius: 0; + } + .card-group>.card:last-child .card-img-bottom, + .card-group>.card:last-child .card-footer { + border-bottom-left-radius: 0; + } + .card-group>.card:only-child { + border-radius: 0.2857rem; + } + .card-group>.card:only-child .card-img-top, + .card-group>.card:only-child .card-header { + border-top-left-radius: 0.2857rem; + border-top-right-radius: 0.2857rem; + } + .card-group>.card:only-child .card-img-bottom, + .card-group>.card:only-child .card-footer { + border-bottom-right-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; + } + .card-group>.card:not(:first-child):not(:last-child):not(:only-child) { + border-radius: 0; + } + .card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top, + .card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom, + .card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header, + .card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer { + border-radius: 0; + } +} + +.card-columns .card { + margin-bottom: 1.25rem; +} + +@media (min-width: 576px) { + .card-columns { + column-count: 3; + column-gap: 1.25rem; + orphans: 1; + widows: 1; + } + .card-columns .card { + display: inline-block; + width: 100%; + } +} + +.accordion .card:not(:first-of-type):not(:last-of-type) { + border-bottom: 0; + border-radius: 0; +} + +.accordion .card:not(:first-of-type) .card-header:first-child { + border-radius: 0; +} + +.accordion .card:first-of-type { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} + +.accordion .card:last-of-type { + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.breadcrumb { + display: flex; + flex-wrap: wrap; + padding: 0.75rem 1rem; + margin-bottom: 1rem; + list-style: none; + background-color: #1d253b; + border-radius: 0.25rem; +} + +.breadcrumb-item+.breadcrumb-item { + padding-left: 0.5rem; +} + +.breadcrumb-item+.breadcrumb-item::before { + display: inline-block; + padding-right: 0.5rem; + color: #ffffff; + content: "/"; +} + +.breadcrumb-item+.breadcrumb-item:hover::before { + text-decoration: underline; +} + +.breadcrumb-item+.breadcrumb-item:hover::before { + text-decoration: none; +} + +.breadcrumb-item.active { + color: #ffffff; +} + +.pagination { + display: flex; + padding-left: 0; + list-style: none; + border-radius: 0.25rem; +} + +.page-link { + position: relative; + display: block; + padding: 0 0.6875rem; + margin-left: -0.0625rem; + line-height: 1.25; + color: #ffffff; + background-color: transparent; + border: 0.0625rem solid #e3e3e3; +} + +.page-link:hover { + z-index: 2; + color: #6c757d; + text-decoration: none; + background-color: #e3e3e3; + border-color: #e3e3e3; +} + +.page-link:focus { + z-index: 2; + outline: 0; + box-shadow: none; +} + +.page-link:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.page-item:first-child .page-link { + margin-left: 0; + border-top-left-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.page-item:last-child .page-link { + border-top-right-radius: 0.25rem; + border-bottom-right-radius: 0.25rem; +} + +.page-item.active .page-link { + z-index: 1; + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; +} + +.page-item.disabled .page-link { + color: #6c757d; + pointer-events: none; + cursor: auto; + background-color: transparent; + border-color: #e3e3e3; +} + +.pagination-lg .page-link { + padding: 0.75rem 1.5rem; + font-size: 0.99925rem; + line-height: 1.625rem; +} + +.pagination-lg .page-item:first-child .page-link { + border-top-left-radius: 0.4285rem; + border-bottom-left-radius: 0.4285rem; +} + +.pagination-lg .page-item:last-child .page-link { + border-top-right-radius: 0.4285rem; + border-bottom-right-radius: 0.4285rem; +} + +.pagination-sm .page-link { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + line-height: 1.5; +} + +.pagination-sm .page-item:first-child .page-link { + border-top-left-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; +} + +.pagination-sm .page-item:last-child .page-link { + border-top-right-radius: 0.2857rem; + border-bottom-right-radius: 0.2857rem; +} + +.badge { + display: inline-block; + padding: 0.25rem 0.5rem; + font-size: 0.62475rem; + font-weight: 700; + line-height: 1; + text-align: center; + white-space: nowrap; + vertical-align: baseline; + border-radius: 0.25rem; +} + +.badge:empty { + display: none; +} + +.btn .badge { + position: relative; + top: -1px; +} + +.badge-pill { + padding-right: 0.875em; + padding-left: 0.875em; + border-radius: 0.875rem; +} + +.badge-primary { + color: #ffffff; + background-color: #e14eca; +} + +.badge-primary[href]:hover, +.badge-primary[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #d725bb; +} + +.badge-secondary { + color: #212529; + background-color: #f4f5f7; +} + +.badge-secondary[href]:hover, +.badge-secondary[href]:focus { + color: #212529; + text-decoration: none; + background-color: #d6dae2; +} + +.badge-success { + color: #ffffff; + background-color: #00f2c3; +} + +.badge-success[href]:hover, +.badge-success[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #00bf9a; +} + +.badge-info { + color: #ffffff; + background-color: #1d8cf8; +} + +.badge-info[href]:hover, +.badge-info[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #0772db; +} + +.badge-warning { + color: #ffffff; + background-color: #ff8d72; +} + +.badge-warning[href]:hover, +.badge-warning[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #ff643f; +} + +.badge-danger { + color: #ffffff; + background-color: #fd5d93; +} + +.badge-danger[href]:hover, +.badge-danger[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #fc2b71; +} + +.badge-light { + color: #ffffff; + background-color: #adb5bd; +} + +.badge-light[href]:hover, +.badge-light[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #919ca6; +} + +.badge-dark { + color: #ffffff; + background-color: #212529; +} + +.badge-dark[href]:hover, +.badge-dark[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #0a0c0d; +} + +.badge-default { + color: #ffffff; + background-color: #344675; +} + +.badge-default[href]:hover, +.badge-default[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #243152; +} + +.badge-white { + color: #212529; + background-color: #ffffff; +} + +.badge-white[href]:hover, +.badge-white[href]:focus { + color: #212529; + text-decoration: none; + background-color: #e6e6e6; +} + +.badge-neutral { + color: #212529; + background-color: #ffffff; +} + +.badge-neutral[href]:hover, +.badge-neutral[href]:focus { + color: #212529; + text-decoration: none; + background-color: #e6e6e6; +} + +.badge-darker { + color: #ffffff; + background-color: black; +} + +.badge-darker[href]:hover, +.badge-darker[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: black; +} + +.jumbotron { + padding: 2rem 1rem; + margin-bottom: 2rem; + background-color: #e9ecef; + border-radius: 0.4285rem; +} + +@media (min-width: 576px) { + .jumbotron { + padding: 4rem 2rem; + } +} + +.jumbotron-fluid { + padding-right: 0; + padding-left: 0; + border-radius: 0; +} + +.alert { + position: relative; + padding: 0.9rem 1.25rem; + margin-bottom: 1rem; + border: 0.0625rem solid transparent; + border-radius: 0.2857rem; +} + +.alert-heading { + color: inherit; +} + +.alert-link { + font-weight: 600; +} + +.alert-dismissible { + padding-right: 3.8125rem; +} + +.alert-dismissible .close { + position: absolute; + top: 0; + right: 0; + padding: 0.9rem 1.25rem; + color: inherit; +} + +.alert-primary { + color: #e14eca; + background-color: #e66ad2; + border-color: #e66ad2; +} + +.alert-primary hr { + border-top-color: #e254cb; +} + +.alert-primary .alert-link { + color: #d725bb; +} + +.alert-secondary { + color: #f4f5f7; + background-color: #f6f7f8; + border-color: #f6f7f8; +} + +.alert-secondary hr { + border-top-color: #e8eaed; +} + +.alert-secondary .alert-link { + color: #d6dae2; +} + +.alert-success { + color: #00f2c3; + background-color: #29f4cd; + border-color: #29f4cd; +} + +.alert-success hr { + border-top-color: #11f3c7; +} + +.alert-success .alert-link { + color: #00bf9a; +} + +.alert-info { + color: #1d8cf8; + background-color: #419ef9; + border-color: #419ef9; +} + +.alert-info hr { + border-top-color: #2891f8; +} + +.alert-info .alert-link { + color: #0772db; +} + +.alert-warning { + color: #ff8d72; + background-color: #ff9f89; + border-color: #ff9f89; +} + +.alert-warning hr { + border-top-color: #ff8a70; +} + +.alert-warning .alert-link { + color: #ff643f; +} + +.alert-danger { + color: #fd5d93; + background-color: #fd77a4; + border-color: #fd77a4; +} + +.alert-danger hr { + border-top-color: #fd5e93; +} + +.alert-danger .alert-link { + color: #fc2b71; +} + +.alert-light { + color: #adb5bd; + background-color: #bac1c8; + border-color: #bac1c8; +} + +.alert-light hr { + border-top-color: #acb4bd; +} + +.alert-light .alert-link { + color: #919ca6; +} + +.alert-dark { + color: #212529; + background-color: #45484b; + border-color: #45484b; +} + +.alert-dark hr { + border-top-color: #393b3e; +} + +.alert-dark .alert-link { + color: #0a0c0d; +} + +.alert-default { + color: #344675; + background-color: #54648b; + border-color: #54648b; +} + +.alert-default hr { + border-top-color: #4a597b; +} + +.alert-default .alert-link { + color: #243152; +} + +.alert-white { + color: white; + background-color: white; + border-color: white; +} + +.alert-white hr { + border-top-color: #f2f2f2; +} + +.alert-white .alert-link { + color: #e6e6e6; +} + +.alert-neutral { + color: white; + background-color: white; + border-color: white; +} + +.alert-neutral hr { + border-top-color: #f2f2f2; +} + +.alert-neutral .alert-link { + color: #e6e6e6; +} + +.alert-darker { + color: black; + background-color: #292929; + border-color: #292929; +} + +.alert-darker hr { + border-top-color: #1c1c1c; +} + +.alert-darker .alert-link { + color: black; +} + +@keyframes progress-bar-stripes { + from { + background-position: 0.5rem 0; + } + to { + background-position: 0 0; + } +} + +.progress { + display: flex; + height: 0.5rem; + overflow: hidden; + font-size: 0.65625rem; + background-color: rgba(0, 0, 0, 0.3); + border-radius: 0.875rem; + box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.3); +} + +.progress-bar { + display: flex; + flex-direction: column; + justify-content: center; + color: #ffffff; + text-align: center; + white-space: nowrap; + background-color: #e14eca; + transition: width 0.6s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .progress-bar { + transition: none; + } +} + +.progress-bar-striped { + background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); + background-size: 0.5rem 0.5rem; +} + +.progress-bar-animated { + animation: progress-bar-stripes 1s linear infinite; +} + +.media { + display: flex; + align-items: flex-start; +} + +.media-body { + flex: 1; +} + +.list-group { + display: flex; + flex-direction: column; + padding-left: 0; + margin-bottom: 0; +} + +.list-group-item-action { + width: 100%; + color: #525f7f; + text-align: inherit; +} + +.list-group-item-action:hover, +.list-group-item-action:focus { + color: #525f7f; + text-decoration: none; + background-color: #f6f9fc; +} + +.list-group-item-action:active { + color: #525f7f; + background-color: #e9ecef; +} + +.list-group-item { + position: relative; + display: block; + padding: 1rem 1rem; + margin-bottom: -0.0625rem; + background-color: #ffffff; + border: 0.0625rem solid #e9ecef; +} + +.list-group-item:first-child { + border-top-left-radius: 0.25rem; + border-top-right-radius: 0.25rem; +} + +.list-group-item:last-child { + margin-bottom: 0; + border-bottom-right-radius: 0.25rem; + border-bottom-left-radius: 0.25rem; +} + +.list-group-item:hover, +.list-group-item:focus { + z-index: 1; + text-decoration: none; +} + +.list-group-item.disabled, +.list-group-item:disabled { + color: #6c757d; + background-color: #ffffff; +} + +.list-group-item.active { + z-index: 2; + color: #ffffff; + background-color: #e14eca; + border-color: #e14eca; +} + +.list-group-flush .list-group-item { + border-right: 0; + border-left: 0; + border-radius: 0; +} + +.list-group-flush:first-child .list-group-item:first-child { + border-top: 0; +} + +.list-group-flush:last-child .list-group-item:last-child { + border-bottom: 0; +} + +.list-group-item-primary { + color: #853d89; + background-color: #f7cdf0; +} + +.list-group-item-primary.list-group-item-action:hover, +.list-group-item-primary.list-group-item-action:focus { + color: #853d89; + background-color: #f3b7e9; +} + +.list-group-item-primary.list-group-item-action.active { + color: #ffffff; + background-color: #853d89; + border-color: #853d89; +} + +.list-group-item-secondary { + color: #8f94a0; + background-color: #fcfcfd; +} + +.list-group-item-secondary.list-group-item-action:hover, +.list-group-item-secondary.list-group-item-action:focus { + color: #8f94a0; + background-color: #ededf3; +} + +.list-group-item-secondary.list-group-item-action.active { + color: #ffffff; + background-color: #8f94a0; + border-color: #8f94a0; +} + +.list-group-item-success { + color: #109285; + background-color: #b8fbee; +} + +.list-group-item-success.list-group-item-action:hover, +.list-group-item-success.list-group-item-action:focus { + color: #109285; + background-color: #a0fae8; +} + +.list-group-item-success.list-group-item-action.active { + color: #ffffff; + background-color: #109285; + border-color: #109285; +} + +.list-group-item-info { + color: #1f5da1; + background-color: #c0dffd; +} + +.list-group-item-info.list-group-item-action:hover, +.list-group-item-info.list-group-item-action:focus { + color: #1f5da1; + background-color: #a7d2fc; +} + +.list-group-item-info.list-group-item-action.active { + color: #ffffff; + background-color: #1f5da1; + border-color: #1f5da1; +} + +.list-group-item-warning { + color: #955d5b; + background-color: #ffdfd8; +} + +.list-group-item-warning.list-group-item-action:hover, +.list-group-item-warning.list-group-item-action:focus { + color: #955d5b; + background-color: #ffcabf; +} + +.list-group-item-warning.list-group-item-action.active { + color: #ffffff; + background-color: #955d5b; + border-color: #955d5b; +} + +.list-group-item-danger { + color: #94456c; + background-color: #fed2e1; +} + +.list-group-item-danger.list-group-item-action:hover, +.list-group-item-danger.list-group-item-action:focus { + color: #94456c; + background-color: #fdb9d0; +} + +.list-group-item-danger.list-group-item-action.active { + color: #ffffff; + background-color: #94456c; + border-color: #94456c; +} + +.list-group-item-light { + color: #6a7282; + background-color: #e8eaed; +} + +.list-group-item-light.list-group-item-action:hover, +.list-group-item-light.list-group-item-action:focus { + color: #6a7282; + background-color: #dadde2; +} + +.list-group-item-light.list-group-item-action.active { + color: #ffffff; + background-color: #6a7282; + border-color: #6a7282; +} + +.list-group-item-dark { + color: #212735; + background-color: #c1c2c3; +} + +.list-group-item-dark.list-group-item-action:hover, +.list-group-item-dark.list-group-item-action:focus { + color: #212735; + background-color: #b4b5b6; +} + +.list-group-item-dark.list-group-item-action.active { + color: #ffffff; + background-color: #212735; + border-color: #212735; +} + +.list-group-item-default { + color: #2b395d; + background-color: #c6cbd8; +} + +.list-group-item-default.list-group-item-action:hover, +.list-group-item-default.list-group-item-action:focus { + color: #2b395d; + background-color: #b7bdce; +} + +.list-group-item-default.list-group-item-action.active { + color: #ffffff; + background-color: #2b395d; + border-color: #2b395d; +} + +.list-group-item-white { + color: #9599a4; + background-color: white; +} + +.list-group-item-white.list-group-item-action:hover, +.list-group-item-white.list-group-item-action:focus { + color: #9599a4; + background-color: #f2f2f2; +} + +.list-group-item-white.list-group-item-action.active { + color: #ffffff; + background-color: #9599a4; + border-color: #9599a4; +} + +.list-group-item-neutral { + color: #9599a4; + background-color: white; +} + +.list-group-item-neutral.list-group-item-action:hover, +.list-group-item-neutral.list-group-item-action:focus { + color: #9599a4; + background-color: #f2f2f2; +} + +.list-group-item-neutral.list-group-item-action.active { + color: #ffffff; + background-color: #9599a4; + border-color: #9599a4; +} + +.list-group-item-darker { + color: #101420; + background-color: #b8b8b8; +} + +.list-group-item-darker.list-group-item-action:hover, +.list-group-item-darker.list-group-item-action:focus { + color: #101420; + background-color: #ababab; +} + +.list-group-item-darker.list-group-item-action.active { + color: #ffffff; + background-color: #101420; + border-color: #101420; +} + +.close { + float: right; + font-size: 1.3125rem; + font-weight: 600; + line-height: 1; + color: rgba(0, 0, 0, 0.6); + text-shadow: none; + opacity: .5; +} + +.close:not(:disabled):not(.disabled) { + cursor: pointer; +} + +.close:not(:disabled):not(.disabled):hover, +.close:not(:disabled):not(.disabled):focus { + color: rgba(0, 0, 0, 0.6); + text-decoration: none; + opacity: .75; +} + +button.close { + padding: 0; + background-color: transparent; + border: 0; + -webkit-appearance: none; +} + +.modal-open { + overflow: hidden; +} + +.modal-open .modal { + overflow-x: hidden; + overflow-y: auto; +} + +.modal { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1050; + display: none; + overflow: hidden; + outline: 0; +} + +.modal-dialog { + position: relative; + width: auto; + margin: 0.5rem; + pointer-events: none; +} + +.modal.fade .modal-dialog { + transition: transform 0.3s ease-out; + transform: translate(0, -25%); +} + +@media screen and (prefers-reduced-motion: reduce) { + .modal.fade .modal-dialog { + transition: none; + } +} + +.modal.show .modal-dialog { + transform: translate(0, 0); +} + +.modal-dialog-centered { + display: flex; + align-items: center; + min-height: calc(100% - (0.5rem * 2)); +} + +.modal-dialog-centered::before { + display: block; + height: calc(100vh - (0.5rem * 2)); + content: ""; +} + +.modal-content { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + pointer-events: auto; + background-color: #ffffff; + background-clip: padding-box; + border: 1px solid rgba(34, 42, 66, 0.2); + border-radius: 0.2857rem; + box-shadow: 0px 10px 50px 0px rgba(0, 0, 0, 0.5); + outline: 0; +} + +.modal-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1040; + background-color: #222a42; +} + +.modal-backdrop.fade { + opacity: 0; +} + +.modal-backdrop.show { + opacity: 0.16; +} + +.modal-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + padding: 24px 24px 0 24px; + border-bottom: 1px solid #e9ecef; + border-top-left-radius: 0.2857rem; + border-top-right-radius: 0.2857rem; +} + +.modal-header .close { + padding: 24px 24px 0 24px; + margin: -24px 24px 0 24px -24px 24px 0 24px -24px 24px 0 24px auto; +} + +.modal-title { + margin-bottom: 0; + line-height: 1.1; +} + +.modal-body { + position: relative; + flex: 1 1 auto; + padding: 24px 24px 16px 24px; +} + +.modal-footer { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 24px 24px 16px 24px; + border-top: 1px solid #e9ecef; +} + +.modal-footer> :not(:first-child) { + margin-left: .25rem; +} + +.modal-footer> :not(:last-child) { + margin-right: .25rem; +} + +.modal-scrollbar-measure { + position: absolute; + top: -9999px; + width: 50px; + height: 50px; + overflow: scroll; +} + +@media (min-width: 576px) { + .modal-dialog { + max-width: 500px; + margin: 1.75rem auto; + } + .modal-dialog-centered { + min-height: calc(100% - (1.75rem * 2)); + } + .modal-dialog-centered::before { + height: calc(100vh - (1.75rem * 2)); + } + .modal-content { + box-shadow: 0 15px 35px rgba(50, 50, 93, 0.2), 0 5px 15px rgba(0, 0, 0, 0.17); + } + .modal-sm { + max-width: 380px; + } +} + +@media (min-width: 992px) { + .modal-lg { + max-width: 800px; + } +} + +.tooltip { + position: absolute; + z-index: 1070; + display: block; + margin: 0; + font-family: "Poppins", sans-serif; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.75rem; + word-wrap: break-word; + opacity: 0; +} + +.tooltip.show { + opacity: 0.9; +} + +.tooltip .arrow { + position: absolute; + display: block; + width: 0.8rem; + height: 0.4rem; +} + +.tooltip .arrow::before { + position: absolute; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-tooltip-top, +.bs-tooltip-auto[x-placement^="top"] { + padding: 0.4rem 0; +} + +.bs-tooltip-top .arrow, +.bs-tooltip-auto[x-placement^="top"] .arrow { + bottom: 0; +} + +.bs-tooltip-top .arrow::before, +.bs-tooltip-auto[x-placement^="top"] .arrow::before { + top: 0; + border-width: 0.4rem 0.4rem 0; + border-top-color: #ffffff; +} + +.bs-tooltip-right, +.bs-tooltip-auto[x-placement^="right"] { + padding: 0 0.4rem; +} + +.bs-tooltip-right .arrow, +.bs-tooltip-auto[x-placement^="right"] .arrow { + left: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-right .arrow::before, +.bs-tooltip-auto[x-placement^="right"] .arrow::before { + right: 0; + border-width: 0.4rem 0.4rem 0.4rem 0; + border-right-color: #ffffff; +} + +.bs-tooltip-bottom, +.bs-tooltip-auto[x-placement^="bottom"] { + padding: 0.4rem 0; +} + +.bs-tooltip-bottom .arrow, +.bs-tooltip-auto[x-placement^="bottom"] .arrow { + top: 0; +} + +.bs-tooltip-bottom .arrow::before, +.bs-tooltip-auto[x-placement^="bottom"] .arrow::before { + bottom: 0; + border-width: 0 0.4rem 0.4rem; + border-bottom-color: #ffffff; +} + +.bs-tooltip-left, +.bs-tooltip-auto[x-placement^="left"] { + padding: 0 0.4rem; +} + +.bs-tooltip-left .arrow, +.bs-tooltip-auto[x-placement^="left"] .arrow { + right: 0; + width: 0.4rem; + height: 0.8rem; +} + +.bs-tooltip-left .arrow::before, +.bs-tooltip-auto[x-placement^="left"] .arrow::before { + left: 0; + border-width: 0.4rem 0 0.4rem 0.4rem; + border-left-color: #ffffff; +} + +.tooltip-inner { + max-width: 200px; + padding: 0.25rem 0.5rem; + color: #222a42; + text-align: center; + background-color: #ffffff; + border-radius: 0.25rem; +} + +.popover { + position: absolute; + top: 0; + left: 0; + z-index: 1060; + display: block; + max-width: 276px; + font-family: "Poppins", sans-serif; + font-style: normal; + font-weight: 400; + line-height: 1.5; + text-align: left; + text-align: start; + text-decoration: none; + text-shadow: none; + text-transform: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + font-size: 0.75rem; + word-wrap: break-word; + background-color: #ffffff; + background-clip: padding-box; + border: 1px solid rgba(34, 42, 66, 0.05); + border-radius: 0.4285rem; + box-shadow: 0px 0.5rem 2rem 0px rgba(34, 42, 66, 0.2); +} + +.popover .arrow { + position: absolute; + display: block; + width: 1.5rem; + height: 0.75rem; + margin: 0 0.4285rem; +} + +.popover .arrow::before, +.popover .arrow::after { + position: absolute; + display: block; + content: ""; + border-color: transparent; + border-style: solid; +} + +.bs-popover-top, +.bs-popover-auto[x-placement^="top"] { + margin-bottom: 0.75rem; +} + +.bs-popover-top .arrow, +.bs-popover-auto[x-placement^="top"] .arrow { + bottom: calc((0.75rem + 1px) * -1); +} + +.bs-popover-top .arrow::before, +.bs-popover-auto[x-placement^="top"] .arrow::before, +.bs-popover-top .arrow::after, +.bs-popover-auto[x-placement^="top"] .arrow::after { + border-width: 0.75rem 0.75rem 0; +} + +.bs-popover-top .arrow::before, +.bs-popover-auto[x-placement^="top"] .arrow::before { + bottom: 0; + border-top-color: transparent; +} + +.bs-popover-top .arrow::after, +.bs-popover-auto[x-placement^="top"] .arrow::after { + bottom: 1px; + border-top-color: #ffffff; +} + +.bs-popover-right, +.bs-popover-auto[x-placement^="right"] { + margin-left: 0.75rem; +} + +.bs-popover-right .arrow, +.bs-popover-auto[x-placement^="right"] .arrow { + left: calc((0.75rem + 1px) * -1); + width: 0.75rem; + height: 1.5rem; + margin: 0.4285rem 0; +} + +.bs-popover-right .arrow::before, +.bs-popover-auto[x-placement^="right"] .arrow::before, +.bs-popover-right .arrow::after, +.bs-popover-auto[x-placement^="right"] .arrow::after { + border-width: 0.75rem 0.75rem 0.75rem 0; +} + +.bs-popover-right .arrow::before, +.bs-popover-auto[x-placement^="right"] .arrow::before { + left: 0; + border-right-color: transparent; +} + +.bs-popover-right .arrow::after, +.bs-popover-auto[x-placement^="right"] .arrow::after { + left: 1px; + border-right-color: #ffffff; +} + +.bs-popover-bottom, +.bs-popover-auto[x-placement^="bottom"] { + margin-top: 0.75rem; +} + +.bs-popover-bottom .arrow, +.bs-popover-auto[x-placement^="bottom"] .arrow { + top: calc((0.75rem + 1px) * -1); +} + +.bs-popover-bottom .arrow::before, +.bs-popover-auto[x-placement^="bottom"] .arrow::before, +.bs-popover-bottom .arrow::after, +.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-width: 0 0.75rem 0.75rem 0.75rem; +} + +.bs-popover-bottom .arrow::before, +.bs-popover-auto[x-placement^="bottom"] .arrow::before { + top: 0; + border-bottom-color: transparent; +} + +.bs-popover-bottom .arrow::after, +.bs-popover-auto[x-placement^="bottom"] .arrow::after { + top: 1px; + border-bottom-color: #ffffff; +} + +.bs-popover-bottom .popover-header::before, +.bs-popover-auto[x-placement^="bottom"] .popover-header::before { + position: absolute; + top: 0; + left: 50%; + display: block; + width: 1.5rem; + margin-left: -0.75rem; + content: ""; + border-bottom: 1px solid #ffffff; +} + +.bs-popover-left, +.bs-popover-auto[x-placement^="left"] { + margin-right: 0.75rem; +} + +.bs-popover-left .arrow, +.bs-popover-auto[x-placement^="left"] .arrow { + right: calc((0.75rem + 1px) * -1); + width: 0.75rem; + height: 1.5rem; + margin: 0.4285rem 0; +} + +.bs-popover-left .arrow::before, +.bs-popover-auto[x-placement^="left"] .arrow::before, +.bs-popover-left .arrow::after, +.bs-popover-auto[x-placement^="left"] .arrow::after { + border-width: 0.75rem 0 0.75rem 0.75rem; +} + +.bs-popover-left .arrow::before, +.bs-popover-auto[x-placement^="left"] .arrow::before { + right: 0; + border-left-color: transparent; +} + +.bs-popover-left .arrow::after, +.bs-popover-auto[x-placement^="left"] .arrow::after { + right: 1px; + border-left-color: #ffffff; +} + +.popover-header { + padding: 0.75rem 0.75rem; + margin-bottom: 0; + font-size: 0.875rem; + color: #32325d; + background-color: #ffffff; + border-bottom: 1px solid #f2f2f2; + border-top-left-radius: calc(0.4285rem - 1px); + border-top-right-radius: calc(0.4285rem - 1px); +} + +.popover-header:empty { + display: none; +} + +.popover-body { + padding: 0.75rem 0.75rem; + color: #525f7f; +} + +.carousel { + position: relative; +} + +.carousel-inner { + position: relative; + width: 100%; + overflow: hidden; +} + +.carousel-item { + position: relative; + display: none; + align-items: center; + width: 100%; + backface-visibility: hidden; + perspective: 1000px; +} + +.carousel-item.active, +.carousel-item-next, +.carousel-item-prev { + display: block; + transition: transform 0.6s ease; +} + +@media screen and (prefers-reduced-motion: reduce) { + .carousel-item.active, + .carousel-item-next, + .carousel-item-prev { + transition: none; + } +} + +.carousel-item-next, +.carousel-item-prev { + position: absolute; + top: 0; +} + +.carousel-item-next.carousel-item-left, +.carousel-item-prev.carousel-item-right { + transform: translateX(0); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-next.carousel-item-left, + .carousel-item-prev.carousel-item-right { + transform: translate3d(0, 0, 0); + } +} + +.carousel-item-next, +.active.carousel-item-right { + transform: translateX(100%); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-next, + .active.carousel-item-right { + transform: translate3d(100%, 0, 0); + } +} + +.carousel-item-prev, +.active.carousel-item-left { + transform: translateX(-100%); +} + +@supports (transform-style: preserve-3d) { + .carousel-item-prev, + .active.carousel-item-left { + transform: translate3d(-100%, 0, 0); + } +} + +.carousel-fade .carousel-item { + opacity: 0; + transition-duration: .6s; + transition-property: opacity; +} + +.carousel-fade .carousel-item.active, +.carousel-fade .carousel-item-next.carousel-item-left, +.carousel-fade .carousel-item-prev.carousel-item-right { + opacity: 1; +} + +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-right { + opacity: 0; +} + +.carousel-fade .carousel-item-next, +.carousel-fade .carousel-item-prev, +.carousel-fade .carousel-item.active, +.carousel-fade .active.carousel-item-left, +.carousel-fade .active.carousel-item-prev { + transform: translateX(0); +} + +@supports (transform-style: preserve-3d) { + .carousel-fade .carousel-item-next, + .carousel-fade .carousel-item-prev, + .carousel-fade .carousel-item.active, + .carousel-fade .active.carousel-item-left, + .carousel-fade .active.carousel-item-prev { + transform: translate3d(0, 0, 0); + } +} + +.carousel-control-prev, +.carousel-control-next { + position: absolute; + top: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + width: 15%; + color: #ffffff; + text-align: center; + opacity: 0.5; +} + +.carousel-control-prev:hover, +.carousel-control-prev:focus, +.carousel-control-next:hover, +.carousel-control-next:focus { + color: #ffffff; + text-decoration: none; + outline: 0; + opacity: .9; +} + +.carousel-control-prev { + left: 0; +} + +.carousel-control-next { + right: 0; +} + +.carousel-control-prev-icon, +.carousel-control-next-icon { + display: inline-block; + width: 20px; + height: 20px; + background: transparent no-repeat center center; + background-size: 100% 100%; +} + +.carousel-control-prev-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23ffffff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E"); +} + +.carousel-control-next-icon { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23ffffff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E"); +} + +.carousel-indicators { + position: absolute; + right: 0; + bottom: 10px; + left: 0; + z-index: 15; + display: flex; + justify-content: center; + padding-left: 0; + margin-right: 15%; + margin-left: 15%; + list-style: none; +} + +.carousel-indicators li { + position: relative; + flex: 0 1 auto; + width: 30px; + height: 3px; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: rgba(255, 255, 255, 0.5); +} + +.carousel-indicators li::before { + position: absolute; + top: -10px; + left: 0; + display: inline-block; + width: 100%; + height: 10px; + content: ""; +} + +.carousel-indicators li::after { + position: absolute; + bottom: -10px; + left: 0; + display: inline-block; + width: 100%; + height: 10px; + content: ""; +} + +.carousel-indicators .active { + background-color: #ffffff; +} + +.carousel-caption { + position: absolute; + right: 15%; + bottom: 20px; + left: 15%; + z-index: 10; + padding-top: 20px; + padding-bottom: 20px; + color: #ffffff; + text-align: center; +} + +.align-baseline { + vertical-align: baseline !important; +} + +.align-top { + vertical-align: top !important; +} + +.align-middle { + vertical-align: middle !important; +} + +.align-bottom { + vertical-align: bottom !important; +} + +.align-text-bottom { + vertical-align: text-bottom !important; +} + +.align-text-top { + vertical-align: text-top !important; +} + +.bg-primary { + background-color: #e14eca !important; +} + +a.bg-primary:hover, +a.bg-primary:focus, +button.bg-primary:hover, +button.bg-primary:focus { + background-color: #d725bb !important; +} + +.bg-secondary { + background-color: #f4f5f7 !important; +} + +a.bg-secondary:hover, +a.bg-secondary:focus, +button.bg-secondary:hover, +button.bg-secondary:focus { + background-color: #d6dae2 !important; +} + +.bg-success { + background-color: #00f2c3 !important; +} + +a.bg-success:hover, +a.bg-success:focus, +button.bg-success:hover, +button.bg-success:focus { + background-color: #00bf9a !important; +} + +.bg-info { + background-color: #1d8cf8 !important; +} + +a.bg-info:hover, +a.bg-info:focus, +button.bg-info:hover, +button.bg-info:focus { + background-color: #0772db !important; +} + +.bg-warning { + background-color: #ff8d72 !important; +} + +a.bg-warning:hover, +a.bg-warning:focus, +button.bg-warning:hover, +button.bg-warning:focus { + background-color: #ff643f !important; +} + +.bg-danger { + background-color: #fd5d93 !important; +} + +a.bg-danger:hover, +a.bg-danger:focus, +button.bg-danger:hover, +button.bg-danger:focus { + background-color: #fc2b71 !important; +} + +.bg-light { + background-color: #adb5bd !important; +} + +a.bg-light:hover, +a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #919ca6 !important; +} + +.bg-dark { + background-color: #212529 !important; +} + +a.bg-dark:hover, +a.bg-dark:focus, +button.bg-dark:hover, +button.bg-dark:focus { + background-color: #0a0c0d !important; +} + +.bg-default { + background-color: #344675 !important; +} + +a.bg-default:hover, +a.bg-default:focus, +button.bg-default:hover, +button.bg-default:focus { + background-color: #243152 !important; +} + +.bg-white { + background-color: #ffffff !important; +} + +a.bg-white:hover, +a.bg-white:focus, +button.bg-white:hover, +button.bg-white:focus { + background-color: #e6e6e6 !important; +} + +.bg-neutral { + background-color: #ffffff !important; +} + +a.bg-neutral:hover, +a.bg-neutral:focus, +button.bg-neutral:hover, +button.bg-neutral:focus { + background-color: #e6e6e6 !important; +} + +.bg-darker { + background-color: black !important; +} + +a.bg-darker:hover, +a.bg-darker:focus, +button.bg-darker:hover, +button.bg-darker:focus { + background-color: black !important; +} + +.bg-white { + background-color: #ffffff !important; +} + +.bg-transparent { + background-color: transparent !important; +} + +.border { + border: 0.0625rem solid #e9ecef !important; +} + +.border-top { + border-top: 0.0625rem solid #e9ecef !important; +} + +.border-right { + border-right: 0.0625rem solid #e9ecef !important; +} + +.border-bottom { + border-bottom: 0.0625rem solid #e9ecef !important; +} + +.border-left { + border-left: 0.0625rem solid #e9ecef !important; +} + +.border-0 { + border: 0 !important; +} + +.border-top-0 { + border-top: 0 !important; +} + +.border-right-0 { + border-right: 0 !important; +} + +.border-bottom-0 { + border-bottom: 0 !important; +} + +.border-left-0 { + border-left: 0 !important; +} + +.border-primary { + border-color: #e14eca !important; +} + +.border-secondary { + border-color: #f4f5f7 !important; +} + +.border-success { + border-color: #00f2c3 !important; +} + +.border-info { + border-color: #1d8cf8 !important; +} + +.border-warning { + border-color: #ff8d72 !important; +} + +.border-danger { + border-color: #fd5d93 !important; +} + +.border-light { + border-color: #adb5bd !important; +} + +.border-dark { + border-color: #212529 !important; +} + +.border-default { + border-color: #344675 !important; +} + +.border-white { + border-color: #ffffff !important; +} + +.border-neutral { + border-color: #ffffff !important; +} + +.border-darker { + border-color: black !important; +} + +.border-white { + border-color: #ffffff !important; +} + +.rounded { + border-radius: 0.25rem !important; +} + +.rounded-top { + border-top-left-radius: 0.25rem !important; + border-top-right-radius: 0.25rem !important; +} + +.rounded-right { + border-top-right-radius: 0.25rem !important; + border-bottom-right-radius: 0.25rem !important; +} + +.rounded-bottom { + border-bottom-right-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-left { + border-top-left-radius: 0.25rem !important; + border-bottom-left-radius: 0.25rem !important; +} + +.rounded-circle { + border-radius: 50% !important; +} + +.rounded-0 { + border-radius: 0 !important; +} + +.clearfix::after { + display: block; + clear: both; + content: ""; +} + +.d-none { + display: none !important; +} + +.d-inline { + display: inline !important; +} + +.d-inline-block { + display: inline-block !important; +} + +.d-block { + display: block !important; +} + +.d-table { + display: table !important; +} + +.d-table-row { + display: table-row !important; +} + +.d-table-cell { + display: table-cell !important; +} + +.d-flex { + display: flex !important; +} + +.d-inline-flex { + display: inline-flex !important; +} + +@media (min-width: 576px) { + .d-sm-none { + display: none !important; + } + .d-sm-inline { + display: inline !important; + } + .d-sm-inline-block { + display: inline-block !important; + } + .d-sm-block { + display: block !important; + } + .d-sm-table { + display: table !important; + } + .d-sm-table-row { + display: table-row !important; + } + .d-sm-table-cell { + display: table-cell !important; + } + .d-sm-flex { + display: flex !important; + } + .d-sm-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 768px) { + .d-md-none { + display: none !important; + } + .d-md-inline { + display: inline !important; + } + .d-md-inline-block { + display: inline-block !important; + } + .d-md-block { + display: block !important; + } + .d-md-table { + display: table !important; + } + .d-md-table-row { + display: table-row !important; + } + .d-md-table-cell { + display: table-cell !important; + } + .d-md-flex { + display: flex !important; + } + .d-md-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 992px) { + .d-lg-none { + display: none !important; + } + .d-lg-inline { + display: inline !important; + } + .d-lg-inline-block { + display: inline-block !important; + } + .d-lg-block { + display: block !important; + } + .d-lg-table { + display: table !important; + } + .d-lg-table-row { + display: table-row !important; + } + .d-lg-table-cell { + display: table-cell !important; + } + .d-lg-flex { + display: flex !important; + } + .d-lg-inline-flex { + display: inline-flex !important; + } +} + +@media (min-width: 1200px) { + .d-xl-none { + display: none !important; + } + .d-xl-inline { + display: inline !important; + } + .d-xl-inline-block { + display: inline-block !important; + } + .d-xl-block { + display: block !important; + } + .d-xl-table { + display: table !important; + } + .d-xl-table-row { + display: table-row !important; + } + .d-xl-table-cell { + display: table-cell !important; + } + .d-xl-flex { + display: flex !important; + } + .d-xl-inline-flex { + display: inline-flex !important; + } +} + +@media print { + .d-print-none { + display: none !important; + } + .d-print-inline { + display: inline !important; + } + .d-print-inline-block { + display: inline-block !important; + } + .d-print-block { + display: block !important; + } + .d-print-table { + display: table !important; + } + .d-print-table-row { + display: table-row !important; + } + .d-print-table-cell { + display: table-cell !important; + } + .d-print-flex { + display: flex !important; + } + .d-print-inline-flex { + display: inline-flex !important; + } +} + +.embed-responsive { + position: relative; + display: block; + width: 100%; + padding: 0; + overflow: hidden; +} + +.embed-responsive::before { + display: block; + content: ""; +} + +.embed-responsive .embed-responsive-item, +.embed-responsive iframe, +.embed-responsive embed, +.embed-responsive object, +.embed-responsive video { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + border: 0; +} + +.embed-responsive-21by9::before { + padding-top: 42.857143%; +} + +.embed-responsive-16by9::before { + padding-top: 56.25%; +} + +.embed-responsive-4by3::before { + padding-top: 75%; +} + +.embed-responsive-1by1::before { + padding-top: 100%; +} + +.flex-row { + flex-direction: row !important; +} + +.flex-column { + flex-direction: column !important; +} + +.flex-row-reverse { + flex-direction: row-reverse !important; +} + +.flex-column-reverse { + flex-direction: column-reverse !important; +} + +.flex-wrap { + flex-wrap: wrap !important; +} + +.flex-nowrap { + flex-wrap: nowrap !important; +} + +.flex-wrap-reverse { + flex-wrap: wrap-reverse !important; +} + +.flex-fill { + flex: 1 1 auto !important; +} + +.flex-grow-0 { + flex-grow: 0 !important; +} + +.flex-grow-1 { + flex-grow: 1 !important; +} + +.flex-shrink-0 { + flex-shrink: 0 !important; +} + +.flex-shrink-1 { + flex-shrink: 1 !important; +} + +.justify-content-start { + justify-content: flex-start !important; +} + +.justify-content-end { + justify-content: flex-end !important; +} + +.justify-content-center { + justify-content: center !important; +} + +.justify-content-between { + justify-content: space-between !important; +} + +.justify-content-around { + justify-content: space-around !important; +} + +.align-items-start { + align-items: flex-start !important; +} + +.align-items-end { + align-items: flex-end !important; +} + +.align-items-center { + align-items: center !important; +} + +.align-items-baseline { + align-items: baseline !important; +} + +.align-items-stretch { + align-items: stretch !important; +} + +.align-content-start { + align-content: flex-start !important; +} + +.align-content-end { + align-content: flex-end !important; +} + +.align-content-center { + align-content: center !important; +} + +.align-content-between { + align-content: space-between !important; +} + +.align-content-around { + align-content: space-around !important; +} + +.align-content-stretch { + align-content: stretch !important; +} + +.align-self-auto { + align-self: auto !important; +} + +.align-self-start { + align-self: flex-start !important; +} + +.align-self-end { + align-self: flex-end !important; +} + +.align-self-center { + align-self: center !important; +} + +.align-self-baseline { + align-self: baseline !important; +} + +.align-self-stretch { + align-self: stretch !important; +} + +@media (min-width: 576px) { + .flex-sm-row { + flex-direction: row !important; + } + .flex-sm-column { + flex-direction: column !important; + } + .flex-sm-row-reverse { + flex-direction: row-reverse !important; + } + .flex-sm-column-reverse { + flex-direction: column-reverse !important; + } + .flex-sm-wrap { + flex-wrap: wrap !important; + } + .flex-sm-nowrap { + flex-wrap: nowrap !important; + } + .flex-sm-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .flex-sm-fill { + flex: 1 1 auto !important; + } + .flex-sm-grow-0 { + flex-grow: 0 !important; + } + .flex-sm-grow-1 { + flex-grow: 1 !important; + } + .flex-sm-shrink-0 { + flex-shrink: 0 !important; + } + .flex-sm-shrink-1 { + flex-shrink: 1 !important; + } + .justify-content-sm-start { + justify-content: flex-start !important; + } + .justify-content-sm-end { + justify-content: flex-end !important; + } + .justify-content-sm-center { + justify-content: center !important; + } + .justify-content-sm-between { + justify-content: space-between !important; + } + .justify-content-sm-around { + justify-content: space-around !important; + } + .align-items-sm-start { + align-items: flex-start !important; + } + .align-items-sm-end { + align-items: flex-end !important; + } + .align-items-sm-center { + align-items: center !important; + } + .align-items-sm-baseline { + align-items: baseline !important; + } + .align-items-sm-stretch { + align-items: stretch !important; + } + .align-content-sm-start { + align-content: flex-start !important; + } + .align-content-sm-end { + align-content: flex-end !important; + } + .align-content-sm-center { + align-content: center !important; + } + .align-content-sm-between { + align-content: space-between !important; + } + .align-content-sm-around { + align-content: space-around !important; + } + .align-content-sm-stretch { + align-content: stretch !important; + } + .align-self-sm-auto { + align-self: auto !important; + } + .align-self-sm-start { + align-self: flex-start !important; + } + .align-self-sm-end { + align-self: flex-end !important; + } + .align-self-sm-center { + align-self: center !important; + } + .align-self-sm-baseline { + align-self: baseline !important; + } + .align-self-sm-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 768px) { + .flex-md-row { + flex-direction: row !important; + } + .flex-md-column { + flex-direction: column !important; + } + .flex-md-row-reverse { + flex-direction: row-reverse !important; + } + .flex-md-column-reverse { + flex-direction: column-reverse !important; + } + .flex-md-wrap { + flex-wrap: wrap !important; + } + .flex-md-nowrap { + flex-wrap: nowrap !important; + } + .flex-md-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .flex-md-fill { + flex: 1 1 auto !important; + } + .flex-md-grow-0 { + flex-grow: 0 !important; + } + .flex-md-grow-1 { + flex-grow: 1 !important; + } + .flex-md-shrink-0 { + flex-shrink: 0 !important; + } + .flex-md-shrink-1 { + flex-shrink: 1 !important; + } + .justify-content-md-start { + justify-content: flex-start !important; + } + .justify-content-md-end { + justify-content: flex-end !important; + } + .justify-content-md-center { + justify-content: center !important; + } + .justify-content-md-between { + justify-content: space-between !important; + } + .justify-content-md-around { + justify-content: space-around !important; + } + .align-items-md-start { + align-items: flex-start !important; + } + .align-items-md-end { + align-items: flex-end !important; + } + .align-items-md-center { + align-items: center !important; + } + .align-items-md-baseline { + align-items: baseline !important; + } + .align-items-md-stretch { + align-items: stretch !important; + } + .align-content-md-start { + align-content: flex-start !important; + } + .align-content-md-end { + align-content: flex-end !important; + } + .align-content-md-center { + align-content: center !important; + } + .align-content-md-between { + align-content: space-between !important; + } + .align-content-md-around { + align-content: space-around !important; + } + .align-content-md-stretch { + align-content: stretch !important; + } + .align-self-md-auto { + align-self: auto !important; + } + .align-self-md-start { + align-self: flex-start !important; + } + .align-self-md-end { + align-self: flex-end !important; + } + .align-self-md-center { + align-self: center !important; + } + .align-self-md-baseline { + align-self: baseline !important; + } + .align-self-md-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 992px) { + .flex-lg-row { + flex-direction: row !important; + } + .flex-lg-column { + flex-direction: column !important; + } + .flex-lg-row-reverse { + flex-direction: row-reverse !important; + } + .flex-lg-column-reverse { + flex-direction: column-reverse !important; + } + .flex-lg-wrap { + flex-wrap: wrap !important; + } + .flex-lg-nowrap { + flex-wrap: nowrap !important; + } + .flex-lg-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .flex-lg-fill { + flex: 1 1 auto !important; + } + .flex-lg-grow-0 { + flex-grow: 0 !important; + } + .flex-lg-grow-1 { + flex-grow: 1 !important; + } + .flex-lg-shrink-0 { + flex-shrink: 0 !important; + } + .flex-lg-shrink-1 { + flex-shrink: 1 !important; + } + .justify-content-lg-start { + justify-content: flex-start !important; + } + .justify-content-lg-end { + justify-content: flex-end !important; + } + .justify-content-lg-center { + justify-content: center !important; + } + .justify-content-lg-between { + justify-content: space-between !important; + } + .justify-content-lg-around { + justify-content: space-around !important; + } + .align-items-lg-start { + align-items: flex-start !important; + } + .align-items-lg-end { + align-items: flex-end !important; + } + .align-items-lg-center { + align-items: center !important; + } + .align-items-lg-baseline { + align-items: baseline !important; + } + .align-items-lg-stretch { + align-items: stretch !important; + } + .align-content-lg-start { + align-content: flex-start !important; + } + .align-content-lg-end { + align-content: flex-end !important; + } + .align-content-lg-center { + align-content: center !important; + } + .align-content-lg-between { + align-content: space-between !important; + } + .align-content-lg-around { + align-content: space-around !important; + } + .align-content-lg-stretch { + align-content: stretch !important; + } + .align-self-lg-auto { + align-self: auto !important; + } + .align-self-lg-start { + align-self: flex-start !important; + } + .align-self-lg-end { + align-self: flex-end !important; + } + .align-self-lg-center { + align-self: center !important; + } + .align-self-lg-baseline { + align-self: baseline !important; + } + .align-self-lg-stretch { + align-self: stretch !important; + } +} + +@media (min-width: 1200px) { + .flex-xl-row { + flex-direction: row !important; + } + .flex-xl-column { + flex-direction: column !important; + } + .flex-xl-row-reverse { + flex-direction: row-reverse !important; + } + .flex-xl-column-reverse { + flex-direction: column-reverse !important; + } + .flex-xl-wrap { + flex-wrap: wrap !important; + } + .flex-xl-nowrap { + flex-wrap: nowrap !important; + } + .flex-xl-wrap-reverse { + flex-wrap: wrap-reverse !important; + } + .flex-xl-fill { + flex: 1 1 auto !important; + } + .flex-xl-grow-0 { + flex-grow: 0 !important; + } + .flex-xl-grow-1 { + flex-grow: 1 !important; + } + .flex-xl-shrink-0 { + flex-shrink: 0 !important; + } + .flex-xl-shrink-1 { + flex-shrink: 1 !important; + } + .justify-content-xl-start { + justify-content: flex-start !important; + } + .justify-content-xl-end { + justify-content: flex-end !important; + } + .justify-content-xl-center { + justify-content: center !important; + } + .justify-content-xl-between { + justify-content: space-between !important; + } + .justify-content-xl-around { + justify-content: space-around !important; + } + .align-items-xl-start { + align-items: flex-start !important; + } + .align-items-xl-end { + align-items: flex-end !important; + } + .align-items-xl-center { + align-items: center !important; + } + .align-items-xl-baseline { + align-items: baseline !important; + } + .align-items-xl-stretch { + align-items: stretch !important; + } + .align-content-xl-start { + align-content: flex-start !important; + } + .align-content-xl-end { + align-content: flex-end !important; + } + .align-content-xl-center { + align-content: center !important; + } + .align-content-xl-between { + align-content: space-between !important; + } + .align-content-xl-around { + align-content: space-around !important; + } + .align-content-xl-stretch { + align-content: stretch !important; + } + .align-self-xl-auto { + align-self: auto !important; + } + .align-self-xl-start { + align-self: flex-start !important; + } + .align-self-xl-end { + align-self: flex-end !important; + } + .align-self-xl-center { + align-self: center !important; + } + .align-self-xl-baseline { + align-self: baseline !important; + } + .align-self-xl-stretch { + align-self: stretch !important; + } +} + +.float-left { + float: left !important; +} + +.float-right { + float: right !important; +} + +.float-none { + float: none !important; +} + +@media (min-width: 576px) { + .float-sm-left { + float: left !important; + } + .float-sm-right { + float: right !important; + } + .float-sm-none { + float: none !important; + } +} + +@media (min-width: 768px) { + .float-md-left { + float: left !important; + } + .float-md-right { + float: right !important; + } + .float-md-none { + float: none !important; + } +} + +@media (min-width: 992px) { + .float-lg-left { + float: left !important; + } + .float-lg-right { + float: right !important; + } + .float-lg-none { + float: none !important; + } +} + +@media (min-width: 1200px) { + .float-xl-left { + float: left !important; + } + .float-xl-right { + float: right !important; + } + .float-xl-none { + float: none !important; + } +} + +.position-static { + position: static !important; +} + +.position-relative { + position: relative !important; +} + +.position-absolute { + position: absolute !important; +} + +.position-fixed { + position: fixed !important; +} + +.position-sticky { + position: sticky !important; +} + +.fixed-top { + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 1030; +} + +.fixed-bottom { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 1030; +} + +@supports (position: sticky) { + .sticky-top { + position: sticky; + top: 0; + z-index: 1020; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +.sr-only-focusable:active, +.sr-only-focusable:focus { + position: static; + width: auto; + height: auto; + overflow: visible; + clip: auto; + white-space: normal; +} + +.shadow-sm { + box-shadow: 0 0.125rem 0.25rem rgba(34, 42, 66, 0.075) !important; +} + +.shadow { + box-shadow: 0 1px 20px 0px rgba(0, 0, 0, 0.1) !important; +} + +.shadow-lg { + box-shadow: 0 1rem 3rem rgba(34, 42, 66, 0.175) !important; +} + +.shadow-none { + box-shadow: none !important; +} + +.w-25 { + width: 25% !important; +} + +.w-50 { + width: 50% !important; +} + +.w-75 { + width: 75% !important; +} + +.w-100 { + width: 100% !important; +} + +.w-auto { + width: auto !important; +} + +.h-25 { + height: 25% !important; +} + +.h-50 { + height: 50% !important; +} + +.h-75 { + height: 75% !important; +} + +.h-100 { + height: 100% !important; +} + +.h-auto { + height: auto !important; +} + +.mw-100 { + max-width: 100% !important; +} + +.mh-100 { + max-height: 100% !important; +} + +.m-0 { + margin: 0 !important; +} + +.mt-0, +.my-0 { + margin-top: 0 !important; +} + +.mr-0, +.mx-0 { + margin-right: 0 !important; +} + +.mb-0, +.my-0 { + margin-bottom: 0 !important; +} + +.ml-0, +.mx-0 { + margin-left: 0 !important; +} + +.m-1 { + margin: 0.25rem !important; +} + +.mt-1, +.my-1 { + margin-top: 0.25rem !important; +} + +.mr-1, +.mx-1 { + margin-right: 0.25rem !important; +} + +.mb-1, +.my-1 { + margin-bottom: 0.25rem !important; +} + +.ml-1, +.mx-1 { + margin-left: 0.25rem !important; +} + +.m-2 { + margin: 0.5rem !important; +} + +.mt-2, +.my-2 { + margin-top: 0.5rem !important; +} + +.mr-2, +.mx-2 { + margin-right: 0.5rem !important; +} + +.mb-2, +.my-2 { + margin-bottom: 0.5rem !important; +} + +.ml-2, +.mx-2 { + margin-left: 0.5rem !important; +} + +.m-3 { + margin: 1rem !important; +} + +.mt-3, +.my-3 { + margin-top: 1rem !important; +} + +.mr-3, +.mx-3 { + margin-right: 1rem !important; +} + +.mb-3, +.my-3 { + margin-bottom: 1rem !important; +} + +.ml-3, +.mx-3 { + margin-left: 1rem !important; +} + +.m-4 { + margin: 1.5rem !important; +} + +.mt-4, +.my-4 { + margin-top: 1.5rem !important; +} + +.mr-4, +.mx-4 { + margin-right: 1.5rem !important; +} + +.mb-4, +.my-4 { + margin-bottom: 1.5rem !important; +} + +.ml-4, +.mx-4 { + margin-left: 1.5rem !important; +} + +.m-5 { + margin: 3rem !important; +} + +.mt-5, +.my-5 { + margin-top: 3rem !important; +} + +.mr-5, +.mx-5 { + margin-right: 3rem !important; +} + +.mb-5, +.my-5 { + margin-bottom: 3rem !important; +} + +.ml-5, +.mx-5 { + margin-left: 3rem !important; +} + +.m-sm { + margin: 2rem !important; +} + +.mt-sm, +.my-sm { + margin-top: 2rem !important; +} + +.mr-sm, +.mx-sm { + margin-right: 2rem !important; +} + +.mb-sm, +.my-sm { + margin-bottom: 2rem !important; +} + +.ml-sm, +.mx-sm { + margin-left: 2rem !important; +} + +.m-md { + margin: 4rem !important; +} + +.mt-md, +.my-md { + margin-top: 4rem !important; +} + +.mr-md, +.mx-md { + margin-right: 4rem !important; +} + +.mb-md, +.my-md { + margin-bottom: 4rem !important; +} + +.ml-md, +.mx-md { + margin-left: 4rem !important; +} + +.m-lg { + margin: 6rem !important; +} + +.mt-lg, +.my-lg { + margin-top: 6rem !important; +} + +.mr-lg, +.mx-lg { + margin-right: 6rem !important; +} + +.mb-lg, +.my-lg { + margin-bottom: 6rem !important; +} + +.ml-lg, +.mx-lg { + margin-left: 6rem !important; +} + +.m-xl { + margin: 8rem !important; +} + +.mt-xl, +.my-xl { + margin-top: 8rem !important; +} + +.mr-xl, +.mx-xl { + margin-right: 8rem !important; +} + +.mb-xl, +.my-xl { + margin-bottom: 8rem !important; +} + +.ml-xl, +.mx-xl { + margin-left: 8rem !important; +} + +.p-0 { + padding: 0 !important; +} + +.pt-0, +.py-0 { + padding-top: 0 !important; +} + +.pr-0, +.px-0 { + padding-right: 0 !important; +} + +.pb-0, +.py-0 { + padding-bottom: 0 !important; +} + +.pl-0, +.px-0 { + padding-left: 0 !important; +} + +.p-1 { + padding: 0.25rem !important; +} + +.pt-1, +.py-1 { + padding-top: 0.25rem !important; +} + +.pr-1, +.px-1 { + padding-right: 0.25rem !important; +} + +.pb-1, +.py-1 { + padding-bottom: 0.25rem !important; +} + +.pl-1, +.px-1 { + padding-left: 0.25rem !important; +} + +.p-2 { + padding: 0.5rem !important; +} + +.pt-2, +.py-2 { + padding-top: 0.5rem !important; +} + +.pr-2, +.px-2 { + padding-right: 0.5rem !important; +} + +.pb-2, +.py-2 { + padding-bottom: 0.5rem !important; +} + +.pl-2, +.px-2 { + padding-left: 0.5rem !important; +} + +.p-3 { + padding: 1rem !important; +} + +.pt-3, +.py-3 { + padding-top: 1rem !important; +} + +.pr-3, +.px-3 { + padding-right: 1rem !important; +} + +.pb-3, +.py-3 { + padding-bottom: 1rem !important; +} + +.pl-3, +.px-3 { + padding-left: 1rem !important; +} + +.p-4 { + padding: 1.5rem !important; +} + +.pt-4, +.py-4 { + padding-top: 1.5rem !important; +} + +.pr-4, +.px-4 { + padding-right: 1.5rem !important; +} + +.pb-4, +.py-4 { + padding-bottom: 1.5rem !important; +} + +.pl-4, +.px-4 { + padding-left: 1.5rem !important; +} + +.p-5 { + padding: 3rem !important; +} + +.pt-5, +.py-5 { + padding-top: 3rem !important; +} + +.pr-5, +.px-5 { + padding-right: 3rem !important; +} + +.pb-5, +.py-5 { + padding-bottom: 3rem !important; +} + +.pl-5, +.px-5 { + padding-left: 3rem !important; +} + +.p-sm { + padding: 2rem !important; +} + +.pt-sm, +.py-sm { + padding-top: 2rem !important; +} + +.pr-sm, +.px-sm { + padding-right: 2rem !important; +} + +.pb-sm, +.py-sm { + padding-bottom: 2rem !important; +} + +.pl-sm, +.px-sm { + padding-left: 2rem !important; +} + +.p-md { + padding: 4rem !important; +} + +.pt-md, +.py-md { + padding-top: 4rem !important; +} + +.pr-md, +.px-md { + padding-right: 4rem !important; +} + +.pb-md, +.py-md { + padding-bottom: 4rem !important; +} + +.pl-md, +.px-md { + padding-left: 4rem !important; +} + +.p-lg { + padding: 6rem !important; +} + +.pt-lg, +.py-lg { + padding-top: 6rem !important; +} + +.pr-lg, +.px-lg { + padding-right: 6rem !important; +} + +.pb-lg, +.py-lg { + padding-bottom: 6rem !important; +} + +.pl-lg, +.px-lg { + padding-left: 6rem !important; +} + +.p-xl { + padding: 8rem !important; +} + +.pt-xl, +.py-xl { + padding-top: 8rem !important; +} + +.pr-xl, +.px-xl { + padding-right: 8rem !important; +} + +.pb-xl, +.py-xl { + padding-bottom: 8rem !important; +} + +.pl-xl, +.px-xl { + padding-left: 8rem !important; +} + +.m-auto { + margin: auto !important; +} + +.mt-auto, +.my-auto { + margin-top: auto !important; +} + +.mr-auto, +.mx-auto { + margin-right: auto !important; +} + +.mb-auto, +.my-auto { + margin-bottom: auto !important; +} + +.ml-auto, +.mx-auto { + margin-left: auto !important; +} + +@media (min-width: 576px) { + .m-sm-0 { + margin: 0 !important; + } + .mt-sm-0, + .my-sm-0 { + margin-top: 0 !important; + } + .mr-sm-0, + .mx-sm-0 { + margin-right: 0 !important; + } + .mb-sm-0, + .my-sm-0 { + margin-bottom: 0 !important; + } + .ml-sm-0, + .mx-sm-0 { + margin-left: 0 !important; + } + .m-sm-1 { + margin: 0.25rem !important; + } + .mt-sm-1, + .my-sm-1 { + margin-top: 0.25rem !important; + } + .mr-sm-1, + .mx-sm-1 { + margin-right: 0.25rem !important; + } + .mb-sm-1, + .my-sm-1 { + margin-bottom: 0.25rem !important; + } + .ml-sm-1, + .mx-sm-1 { + margin-left: 0.25rem !important; + } + .m-sm-2 { + margin: 0.5rem !important; + } + .mt-sm-2, + .my-sm-2 { + margin-top: 0.5rem !important; + } + .mr-sm-2, + .mx-sm-2 { + margin-right: 0.5rem !important; + } + .mb-sm-2, + .my-sm-2 { + margin-bottom: 0.5rem !important; + } + .ml-sm-2, + .mx-sm-2 { + margin-left: 0.5rem !important; + } + .m-sm-3 { + margin: 1rem !important; + } + .mt-sm-3, + .my-sm-3 { + margin-top: 1rem !important; + } + .mr-sm-3, + .mx-sm-3 { + margin-right: 1rem !important; + } + .mb-sm-3, + .my-sm-3 { + margin-bottom: 1rem !important; + } + .ml-sm-3, + .mx-sm-3 { + margin-left: 1rem !important; + } + .m-sm-4 { + margin: 1.5rem !important; + } + .mt-sm-4, + .my-sm-4 { + margin-top: 1.5rem !important; + } + .mr-sm-4, + .mx-sm-4 { + margin-right: 1.5rem !important; + } + .mb-sm-4, + .my-sm-4 { + margin-bottom: 1.5rem !important; + } + .ml-sm-4, + .mx-sm-4 { + margin-left: 1.5rem !important; + } + .m-sm-5 { + margin: 3rem !important; + } + .mt-sm-5, + .my-sm-5 { + margin-top: 3rem !important; + } + .mr-sm-5, + .mx-sm-5 { + margin-right: 3rem !important; + } + .mb-sm-5, + .my-sm-5 { + margin-bottom: 3rem !important; + } + .ml-sm-5, + .mx-sm-5 { + margin-left: 3rem !important; + } + .m-sm-sm { + margin: 2rem !important; + } + .mt-sm-sm, + .my-sm-sm { + margin-top: 2rem !important; + } + .mr-sm-sm, + .mx-sm-sm { + margin-right: 2rem !important; + } + .mb-sm-sm, + .my-sm-sm { + margin-bottom: 2rem !important; + } + .ml-sm-sm, + .mx-sm-sm { + margin-left: 2rem !important; + } + .m-sm-md { + margin: 4rem !important; + } + .mt-sm-md, + .my-sm-md { + margin-top: 4rem !important; + } + .mr-sm-md, + .mx-sm-md { + margin-right: 4rem !important; + } + .mb-sm-md, + .my-sm-md { + margin-bottom: 4rem !important; + } + .ml-sm-md, + .mx-sm-md { + margin-left: 4rem !important; + } + .m-sm-lg { + margin: 6rem !important; + } + .mt-sm-lg, + .my-sm-lg { + margin-top: 6rem !important; + } + .mr-sm-lg, + .mx-sm-lg { + margin-right: 6rem !important; + } + .mb-sm-lg, + .my-sm-lg { + margin-bottom: 6rem !important; + } + .ml-sm-lg, + .mx-sm-lg { + margin-left: 6rem !important; + } + .m-sm-xl { + margin: 8rem !important; + } + .mt-sm-xl, + .my-sm-xl { + margin-top: 8rem !important; + } + .mr-sm-xl, + .mx-sm-xl { + margin-right: 8rem !important; + } + .mb-sm-xl, + .my-sm-xl { + margin-bottom: 8rem !important; + } + .ml-sm-xl, + .mx-sm-xl { + margin-left: 8rem !important; + } + .p-sm-0 { + padding: 0 !important; + } + .pt-sm-0, + .py-sm-0 { + padding-top: 0 !important; + } + .pr-sm-0, + .px-sm-0 { + padding-right: 0 !important; + } + .pb-sm-0, + .py-sm-0 { + padding-bottom: 0 !important; + } + .pl-sm-0, + .px-sm-0 { + padding-left: 0 !important; + } + .p-sm-1 { + padding: 0.25rem !important; + } + .pt-sm-1, + .py-sm-1 { + padding-top: 0.25rem !important; + } + .pr-sm-1, + .px-sm-1 { + padding-right: 0.25rem !important; + } + .pb-sm-1, + .py-sm-1 { + padding-bottom: 0.25rem !important; + } + .pl-sm-1, + .px-sm-1 { + padding-left: 0.25rem !important; + } + .p-sm-2 { + padding: 0.5rem !important; + } + .pt-sm-2, + .py-sm-2 { + padding-top: 0.5rem !important; + } + .pr-sm-2, + .px-sm-2 { + padding-right: 0.5rem !important; + } + .pb-sm-2, + .py-sm-2 { + padding-bottom: 0.5rem !important; + } + .pl-sm-2, + .px-sm-2 { + padding-left: 0.5rem !important; + } + .p-sm-3 { + padding: 1rem !important; + } + .pt-sm-3, + .py-sm-3 { + padding-top: 1rem !important; + } + .pr-sm-3, + .px-sm-3 { + padding-right: 1rem !important; + } + .pb-sm-3, + .py-sm-3 { + padding-bottom: 1rem !important; + } + .pl-sm-3, + .px-sm-3 { + padding-left: 1rem !important; + } + .p-sm-4 { + padding: 1.5rem !important; + } + .pt-sm-4, + .py-sm-4 { + padding-top: 1.5rem !important; + } + .pr-sm-4, + .px-sm-4 { + padding-right: 1.5rem !important; + } + .pb-sm-4, + .py-sm-4 { + padding-bottom: 1.5rem !important; + } + .pl-sm-4, + .px-sm-4 { + padding-left: 1.5rem !important; + } + .p-sm-5 { + padding: 3rem !important; + } + .pt-sm-5, + .py-sm-5 { + padding-top: 3rem !important; + } + .pr-sm-5, + .px-sm-5 { + padding-right: 3rem !important; + } + .pb-sm-5, + .py-sm-5 { + padding-bottom: 3rem !important; + } + .pl-sm-5, + .px-sm-5 { + padding-left: 3rem !important; + } + .p-sm-sm { + padding: 2rem !important; + } + .pt-sm-sm, + .py-sm-sm { + padding-top: 2rem !important; + } + .pr-sm-sm, + .px-sm-sm { + padding-right: 2rem !important; + } + .pb-sm-sm, + .py-sm-sm { + padding-bottom: 2rem !important; + } + .pl-sm-sm, + .px-sm-sm { + padding-left: 2rem !important; + } + .p-sm-md { + padding: 4rem !important; + } + .pt-sm-md, + .py-sm-md { + padding-top: 4rem !important; + } + .pr-sm-md, + .px-sm-md { + padding-right: 4rem !important; + } + .pb-sm-md, + .py-sm-md { + padding-bottom: 4rem !important; + } + .pl-sm-md, + .px-sm-md { + padding-left: 4rem !important; + } + .p-sm-lg { + padding: 6rem !important; + } + .pt-sm-lg, + .py-sm-lg { + padding-top: 6rem !important; + } + .pr-sm-lg, + .px-sm-lg { + padding-right: 6rem !important; + } + .pb-sm-lg, + .py-sm-lg { + padding-bottom: 6rem !important; + } + .pl-sm-lg, + .px-sm-lg { + padding-left: 6rem !important; + } + .p-sm-xl { + padding: 8rem !important; + } + .pt-sm-xl, + .py-sm-xl { + padding-top: 8rem !important; + } + .pr-sm-xl, + .px-sm-xl { + padding-right: 8rem !important; + } + .pb-sm-xl, + .py-sm-xl { + padding-bottom: 8rem !important; + } + .pl-sm-xl, + .px-sm-xl { + padding-left: 8rem !important; + } + .m-sm-auto { + margin: auto !important; + } + .mt-sm-auto, + .my-sm-auto { + margin-top: auto !important; + } + .mr-sm-auto, + .mx-sm-auto { + margin-right: auto !important; + } + .mb-sm-auto, + .my-sm-auto { + margin-bottom: auto !important; + } + .ml-sm-auto, + .mx-sm-auto { + margin-left: auto !important; + } +} + +@media (min-width: 768px) { + .m-md-0 { + margin: 0 !important; + } + .mt-md-0, + .my-md-0 { + margin-top: 0 !important; + } + .mr-md-0, + .mx-md-0 { + margin-right: 0 !important; + } + .mb-md-0, + .my-md-0 { + margin-bottom: 0 !important; + } + .ml-md-0, + .mx-md-0 { + margin-left: 0 !important; + } + .m-md-1 { + margin: 0.25rem !important; + } + .mt-md-1, + .my-md-1 { + margin-top: 0.25rem !important; + } + .mr-md-1, + .mx-md-1 { + margin-right: 0.25rem !important; + } + .mb-md-1, + .my-md-1 { + margin-bottom: 0.25rem !important; + } + .ml-md-1, + .mx-md-1 { + margin-left: 0.25rem !important; + } + .m-md-2 { + margin: 0.5rem !important; + } + .mt-md-2, + .my-md-2 { + margin-top: 0.5rem !important; + } + .mr-md-2, + .mx-md-2 { + margin-right: 0.5rem !important; + } + .mb-md-2, + .my-md-2 { + margin-bottom: 0.5rem !important; + } + .ml-md-2, + .mx-md-2 { + margin-left: 0.5rem !important; + } + .m-md-3 { + margin: 1rem !important; + } + .mt-md-3, + .my-md-3 { + margin-top: 1rem !important; + } + .mr-md-3, + .mx-md-3 { + margin-right: 1rem !important; + } + .mb-md-3, + .my-md-3 { + margin-bottom: 1rem !important; + } + .ml-md-3, + .mx-md-3 { + margin-left: 1rem !important; + } + .m-md-4 { + margin: 1.5rem !important; + } + .mt-md-4, + .my-md-4 { + margin-top: 1.5rem !important; + } + .mr-md-4, + .mx-md-4 { + margin-right: 1.5rem !important; + } + .mb-md-4, + .my-md-4 { + margin-bottom: 1.5rem !important; + } + .ml-md-4, + .mx-md-4 { + margin-left: 1.5rem !important; + } + .m-md-5 { + margin: 3rem !important; + } + .mt-md-5, + .my-md-5 { + margin-top: 3rem !important; + } + .mr-md-5, + .mx-md-5 { + margin-right: 3rem !important; + } + .mb-md-5, + .my-md-5 { + margin-bottom: 3rem !important; + } + .ml-md-5, + .mx-md-5 { + margin-left: 3rem !important; + } + .m-md-sm { + margin: 2rem !important; + } + .mt-md-sm, + .my-md-sm { + margin-top: 2rem !important; + } + .mr-md-sm, + .mx-md-sm { + margin-right: 2rem !important; + } + .mb-md-sm, + .my-md-sm { + margin-bottom: 2rem !important; + } + .ml-md-sm, + .mx-md-sm { + margin-left: 2rem !important; + } + .m-md-md { + margin: 4rem !important; + } + .mt-md-md, + .my-md-md { + margin-top: 4rem !important; + } + .mr-md-md, + .mx-md-md { + margin-right: 4rem !important; + } + .mb-md-md, + .my-md-md { + margin-bottom: 4rem !important; + } + .ml-md-md, + .mx-md-md { + margin-left: 4rem !important; + } + .m-md-lg { + margin: 6rem !important; + } + .mt-md-lg, + .my-md-lg { + margin-top: 6rem !important; + } + .mr-md-lg, + .mx-md-lg { + margin-right: 6rem !important; + } + .mb-md-lg, + .my-md-lg { + margin-bottom: 6rem !important; + } + .ml-md-lg, + .mx-md-lg { + margin-left: 6rem !important; + } + .m-md-xl { + margin: 8rem !important; + } + .mt-md-xl, + .my-md-xl { + margin-top: 8rem !important; + } + .mr-md-xl, + .mx-md-xl { + margin-right: 8rem !important; + } + .mb-md-xl, + .my-md-xl { + margin-bottom: 8rem !important; + } + .ml-md-xl, + .mx-md-xl { + margin-left: 8rem !important; + } + .p-md-0 { + padding: 0 !important; + } + .pt-md-0, + .py-md-0 { + padding-top: 0 !important; + } + .pr-md-0, + .px-md-0 { + padding-right: 0 !important; + } + .pb-md-0, + .py-md-0 { + padding-bottom: 0 !important; + } + .pl-md-0, + .px-md-0 { + padding-left: 0 !important; + } + .p-md-1 { + padding: 0.25rem !important; + } + .pt-md-1, + .py-md-1 { + padding-top: 0.25rem !important; + } + .pr-md-1, + .px-md-1 { + padding-right: 0.25rem !important; + } + .pb-md-1, + .py-md-1 { + padding-bottom: 0.25rem !important; + } + .pl-md-1, + .px-md-1 { + padding-left: 0.25rem !important; + } + .p-md-2 { + padding: 0.5rem !important; + } + .pt-md-2, + .py-md-2 { + padding-top: 0.5rem !important; + } + .pr-md-2, + .px-md-2 { + padding-right: 0.5rem !important; + } + .pb-md-2, + .py-md-2 { + padding-bottom: 0.5rem !important; + } + .pl-md-2, + .px-md-2 { + padding-left: 0.5rem !important; + } + .p-md-3 { + padding: 1rem !important; + } + .pt-md-3, + .py-md-3 { + padding-top: 1rem !important; + } + .pr-md-3, + .px-md-3 { + padding-right: 1rem !important; + } + .pb-md-3, + .py-md-3 { + padding-bottom: 1rem !important; + } + .pl-md-3, + .px-md-3 { + padding-left: 1rem !important; + } + .p-md-4 { + padding: 1.5rem !important; + } + .pt-md-4, + .py-md-4 { + padding-top: 1.5rem !important; + } + .pr-md-4, + .px-md-4 { + padding-right: 1.5rem !important; + } + .pb-md-4, + .py-md-4 { + padding-bottom: 1.5rem !important; + } + .pl-md-4, + .px-md-4 { + padding-left: 1.5rem !important; + } + .p-md-5 { + padding: 3rem !important; + } + .pt-md-5, + .py-md-5 { + padding-top: 3rem !important; + } + .pr-md-5, + .px-md-5 { + padding-right: 3rem !important; + } + .pb-md-5, + .py-md-5 { + padding-bottom: 3rem !important; + } + .pl-md-5, + .px-md-5 { + padding-left: 3rem !important; + } + .p-md-sm { + padding: 2rem !important; + } + .pt-md-sm, + .py-md-sm { + padding-top: 2rem !important; + } + .pr-md-sm, + .px-md-sm { + padding-right: 2rem !important; + } + .pb-md-sm, + .py-md-sm { + padding-bottom: 2rem !important; + } + .pl-md-sm, + .px-md-sm { + padding-left: 2rem !important; + } + .p-md-md { + padding: 4rem !important; + } + .pt-md-md, + .py-md-md { + padding-top: 4rem !important; + } + .pr-md-md, + .px-md-md { + padding-right: 4rem !important; + } + .pb-md-md, + .py-md-md { + padding-bottom: 4rem !important; + } + .pl-md-md, + .px-md-md { + padding-left: 4rem !important; + } + .p-md-lg { + padding: 6rem !important; + } + .pt-md-lg, + .py-md-lg { + padding-top: 6rem !important; + } + .pr-md-lg, + .px-md-lg { + padding-right: 6rem !important; + } + .pb-md-lg, + .py-md-lg { + padding-bottom: 6rem !important; + } + .pl-md-lg, + .px-md-lg { + padding-left: 6rem !important; + } + .p-md-xl { + padding: 8rem !important; + } + .pt-md-xl, + .py-md-xl { + padding-top: 8rem !important; + } + .pr-md-xl, + .px-md-xl { + padding-right: 8rem !important; + } + .pb-md-xl, + .py-md-xl { + padding-bottom: 8rem !important; + } + .pl-md-xl, + .px-md-xl { + padding-left: 8rem !important; + } + .m-md-auto { + margin: auto !important; + } + .mt-md-auto, + .my-md-auto { + margin-top: auto !important; + } + .mr-md-auto, + .mx-md-auto { + margin-right: auto !important; + } + .mb-md-auto, + .my-md-auto { + margin-bottom: auto !important; + } + .ml-md-auto, + .mx-md-auto { + margin-left: auto !important; + } +} + +@media (min-width: 992px) { + .m-lg-0 { + margin: 0 !important; + } + .mt-lg-0, + .my-lg-0 { + margin-top: 0 !important; + } + .mr-lg-0, + .mx-lg-0 { + margin-right: 0 !important; + } + .mb-lg-0, + .my-lg-0 { + margin-bottom: 0 !important; + } + .ml-lg-0, + .mx-lg-0 { + margin-left: 0 !important; + } + .m-lg-1 { + margin: 0.25rem !important; + } + .mt-lg-1, + .my-lg-1 { + margin-top: 0.25rem !important; + } + .mr-lg-1, + .mx-lg-1 { + margin-right: 0.25rem !important; + } + .mb-lg-1, + .my-lg-1 { + margin-bottom: 0.25rem !important; + } + .ml-lg-1, + .mx-lg-1 { + margin-left: 0.25rem !important; + } + .m-lg-2 { + margin: 0.5rem !important; + } + .mt-lg-2, + .my-lg-2 { + margin-top: 0.5rem !important; + } + .mr-lg-2, + .mx-lg-2 { + margin-right: 0.5rem !important; + } + .mb-lg-2, + .my-lg-2 { + margin-bottom: 0.5rem !important; + } + .ml-lg-2, + .mx-lg-2 { + margin-left: 0.5rem !important; + } + .m-lg-3 { + margin: 1rem !important; + } + .mt-lg-3, + .my-lg-3 { + margin-top: 1rem !important; + } + .mr-lg-3, + .mx-lg-3 { + margin-right: 1rem !important; + } + .mb-lg-3, + .my-lg-3 { + margin-bottom: 1rem !important; + } + .ml-lg-3, + .mx-lg-3 { + margin-left: 1rem !important; + } + .m-lg-4 { + margin: 1.5rem !important; + } + .mt-lg-4, + .my-lg-4 { + margin-top: 1.5rem !important; + } + .mr-lg-4, + .mx-lg-4 { + margin-right: 1.5rem !important; + } + .mb-lg-4, + .my-lg-4 { + margin-bottom: 1.5rem !important; + } + .ml-lg-4, + .mx-lg-4 { + margin-left: 1.5rem !important; + } + .m-lg-5 { + margin: 3rem !important; + } + .mt-lg-5, + .my-lg-5 { + margin-top: 3rem !important; + } + .mr-lg-5, + .mx-lg-5 { + margin-right: 3rem !important; + } + .mb-lg-5, + .my-lg-5 { + margin-bottom: 3rem !important; + } + .ml-lg-5, + .mx-lg-5 { + margin-left: 3rem !important; + } + .m-lg-sm { + margin: 2rem !important; + } + .mt-lg-sm, + .my-lg-sm { + margin-top: 2rem !important; + } + .mr-lg-sm, + .mx-lg-sm { + margin-right: 2rem !important; + } + .mb-lg-sm, + .my-lg-sm { + margin-bottom: 2rem !important; + } + .ml-lg-sm, + .mx-lg-sm { + margin-left: 2rem !important; + } + .m-lg-md { + margin: 4rem !important; + } + .mt-lg-md, + .my-lg-md { + margin-top: 4rem !important; + } + .mr-lg-md, + .mx-lg-md { + margin-right: 4rem !important; + } + .mb-lg-md, + .my-lg-md { + margin-bottom: 4rem !important; + } + .ml-lg-md, + .mx-lg-md { + margin-left: 4rem !important; + } + .m-lg-lg { + margin: 6rem !important; + } + .mt-lg-lg, + .my-lg-lg { + margin-top: 6rem !important; + } + .mr-lg-lg, + .mx-lg-lg { + margin-right: 6rem !important; + } + .mb-lg-lg, + .my-lg-lg { + margin-bottom: 6rem !important; + } + .ml-lg-lg, + .mx-lg-lg { + margin-left: 6rem !important; + } + .m-lg-xl { + margin: 8rem !important; + } + .mt-lg-xl, + .my-lg-xl { + margin-top: 8rem !important; + } + .mr-lg-xl, + .mx-lg-xl { + margin-right: 8rem !important; + } + .mb-lg-xl, + .my-lg-xl { + margin-bottom: 8rem !important; + } + .ml-lg-xl, + .mx-lg-xl { + margin-left: 8rem !important; + } + .p-lg-0 { + padding: 0 !important; + } + .pt-lg-0, + .py-lg-0 { + padding-top: 0 !important; + } + .pr-lg-0, + .px-lg-0 { + padding-right: 0 !important; + } + .pb-lg-0, + .py-lg-0 { + padding-bottom: 0 !important; + } + .pl-lg-0, + .px-lg-0 { + padding-left: 0 !important; + } + .p-lg-1 { + padding: 0.25rem !important; + } + .pt-lg-1, + .py-lg-1 { + padding-top: 0.25rem !important; + } + .pr-lg-1, + .px-lg-1 { + padding-right: 0.25rem !important; + } + .pb-lg-1, + .py-lg-1 { + padding-bottom: 0.25rem !important; + } + .pl-lg-1, + .px-lg-1 { + padding-left: 0.25rem !important; + } + .p-lg-2 { + padding: 0.5rem !important; + } + .pt-lg-2, + .py-lg-2 { + padding-top: 0.5rem !important; + } + .pr-lg-2, + .px-lg-2 { + padding-right: 0.5rem !important; + } + .pb-lg-2, + .py-lg-2 { + padding-bottom: 0.5rem !important; + } + .pl-lg-2, + .px-lg-2 { + padding-left: 0.5rem !important; + } + .p-lg-3 { + padding: 1rem !important; + } + .pt-lg-3, + .py-lg-3 { + padding-top: 1rem !important; + } + .pr-lg-3, + .px-lg-3 { + padding-right: 1rem !important; + } + .pb-lg-3, + .py-lg-3 { + padding-bottom: 1rem !important; + } + .pl-lg-3, + .px-lg-3 { + padding-left: 1rem !important; + } + .p-lg-4 { + padding: 1.5rem !important; + } + .pt-lg-4, + .py-lg-4 { + padding-top: 1.5rem !important; + } + .pr-lg-4, + .px-lg-4 { + padding-right: 1.5rem !important; + } + .pb-lg-4, + .py-lg-4 { + padding-bottom: 1.5rem !important; + } + .pl-lg-4, + .px-lg-4 { + padding-left: 1.5rem !important; + } + .p-lg-5 { + padding: 3rem !important; + } + .pt-lg-5, + .py-lg-5 { + padding-top: 3rem !important; + } + .pr-lg-5, + .px-lg-5 { + padding-right: 3rem !important; + } + .pb-lg-5, + .py-lg-5 { + padding-bottom: 3rem !important; + } + .pl-lg-5, + .px-lg-5 { + padding-left: 3rem !important; + } + .p-lg-sm { + padding: 2rem !important; + } + .pt-lg-sm, + .py-lg-sm { + padding-top: 2rem !important; + } + .pr-lg-sm, + .px-lg-sm { + padding-right: 2rem !important; + } + .pb-lg-sm, + .py-lg-sm { + padding-bottom: 2rem !important; + } + .pl-lg-sm, + .px-lg-sm { + padding-left: 2rem !important; + } + .p-lg-md { + padding: 4rem !important; + } + .pt-lg-md, + .py-lg-md { + padding-top: 4rem !important; + } + .pr-lg-md, + .px-lg-md { + padding-right: 4rem !important; + } + .pb-lg-md, + .py-lg-md { + padding-bottom: 4rem !important; + } + .pl-lg-md, + .px-lg-md { + padding-left: 4rem !important; + } + .p-lg-lg { + padding: 6rem !important; + } + .pt-lg-lg, + .py-lg-lg { + padding-top: 6rem !important; + } + .pr-lg-lg, + .px-lg-lg { + padding-right: 6rem !important; + } + .pb-lg-lg, + .py-lg-lg { + padding-bottom: 6rem !important; + } + .pl-lg-lg, + .px-lg-lg { + padding-left: 6rem !important; + } + .p-lg-xl { + padding: 8rem !important; + } + .pt-lg-xl, + .py-lg-xl { + padding-top: 8rem !important; + } + .pr-lg-xl, + .px-lg-xl { + padding-right: 8rem !important; + } + .pb-lg-xl, + .py-lg-xl { + padding-bottom: 8rem !important; + } + .pl-lg-xl, + .px-lg-xl { + padding-left: 8rem !important; + } + .m-lg-auto { + margin: auto !important; + } + .mt-lg-auto, + .my-lg-auto { + margin-top: auto !important; + } + .mr-lg-auto, + .mx-lg-auto { + margin-right: auto !important; + } + .mb-lg-auto, + .my-lg-auto { + margin-bottom: auto !important; + } + .ml-lg-auto, + .mx-lg-auto { + margin-left: auto !important; + } +} + +@media (min-width: 1200px) { + .m-xl-0 { + margin: 0 !important; + } + .mt-xl-0, + .my-xl-0 { + margin-top: 0 !important; + } + .mr-xl-0, + .mx-xl-0 { + margin-right: 0 !important; + } + .mb-xl-0, + .my-xl-0 { + margin-bottom: 0 !important; + } + .ml-xl-0, + .mx-xl-0 { + margin-left: 0 !important; + } + .m-xl-1 { + margin: 0.25rem !important; + } + .mt-xl-1, + .my-xl-1 { + margin-top: 0.25rem !important; + } + .mr-xl-1, + .mx-xl-1 { + margin-right: 0.25rem !important; + } + .mb-xl-1, + .my-xl-1 { + margin-bottom: 0.25rem !important; + } + .ml-xl-1, + .mx-xl-1 { + margin-left: 0.25rem !important; + } + .m-xl-2 { + margin: 0.5rem !important; + } + .mt-xl-2, + .my-xl-2 { + margin-top: 0.5rem !important; + } + .mr-xl-2, + .mx-xl-2 { + margin-right: 0.5rem !important; + } + .mb-xl-2, + .my-xl-2 { + margin-bottom: 0.5rem !important; + } + .ml-xl-2, + .mx-xl-2 { + margin-left: 0.5rem !important; + } + .m-xl-3 { + margin: 1rem !important; + } + .mt-xl-3, + .my-xl-3 { + margin-top: 1rem !important; + } + .mr-xl-3, + .mx-xl-3 { + margin-right: 1rem !important; + } + .mb-xl-3, + .my-xl-3 { + margin-bottom: 1rem !important; + } + .ml-xl-3, + .mx-xl-3 { + margin-left: 1rem !important; + } + .m-xl-4 { + margin: 1.5rem !important; + } + .mt-xl-4, + .my-xl-4 { + margin-top: 1.5rem !important; + } + .mr-xl-4, + .mx-xl-4 { + margin-right: 1.5rem !important; + } + .mb-xl-4, + .my-xl-4 { + margin-bottom: 1.5rem !important; + } + .ml-xl-4, + .mx-xl-4 { + margin-left: 1.5rem !important; + } + .m-xl-5 { + margin: 3rem !important; + } + .mt-xl-5, + .my-xl-5 { + margin-top: 3rem !important; + } + .mr-xl-5, + .mx-xl-5 { + margin-right: 3rem !important; + } + .mb-xl-5, + .my-xl-5 { + margin-bottom: 3rem !important; + } + .ml-xl-5, + .mx-xl-5 { + margin-left: 3rem !important; + } + .m-xl-sm { + margin: 2rem !important; + } + .mt-xl-sm, + .my-xl-sm { + margin-top: 2rem !important; + } + .mr-xl-sm, + .mx-xl-sm { + margin-right: 2rem !important; + } + .mb-xl-sm, + .my-xl-sm { + margin-bottom: 2rem !important; + } + .ml-xl-sm, + .mx-xl-sm { + margin-left: 2rem !important; + } + .m-xl-md { + margin: 4rem !important; + } + .mt-xl-md, + .my-xl-md { + margin-top: 4rem !important; + } + .mr-xl-md, + .mx-xl-md { + margin-right: 4rem !important; + } + .mb-xl-md, + .my-xl-md { + margin-bottom: 4rem !important; + } + .ml-xl-md, + .mx-xl-md { + margin-left: 4rem !important; + } + .m-xl-lg { + margin: 6rem !important; + } + .mt-xl-lg, + .my-xl-lg { + margin-top: 6rem !important; + } + .mr-xl-lg, + .mx-xl-lg { + margin-right: 6rem !important; + } + .mb-xl-lg, + .my-xl-lg { + margin-bottom: 6rem !important; + } + .ml-xl-lg, + .mx-xl-lg { + margin-left: 6rem !important; + } + .m-xl-xl { + margin: 8rem !important; + } + .mt-xl-xl, + .my-xl-xl { + margin-top: 8rem !important; + } + .mr-xl-xl, + .mx-xl-xl { + margin-right: 8rem !important; + } + .mb-xl-xl, + .my-xl-xl { + margin-bottom: 8rem !important; + } + .ml-xl-xl, + .mx-xl-xl { + margin-left: 8rem !important; + } + .p-xl-0 { + padding: 0 !important; + } + .pt-xl-0, + .py-xl-0 { + padding-top: 0 !important; + } + .pr-xl-0, + .px-xl-0 { + padding-right: 0 !important; + } + .pb-xl-0, + .py-xl-0 { + padding-bottom: 0 !important; + } + .pl-xl-0, + .px-xl-0 { + padding-left: 0 !important; + } + .p-xl-1 { + padding: 0.25rem !important; + } + .pt-xl-1, + .py-xl-1 { + padding-top: 0.25rem !important; + } + .pr-xl-1, + .px-xl-1 { + padding-right: 0.25rem !important; + } + .pb-xl-1, + .py-xl-1 { + padding-bottom: 0.25rem !important; + } + .pl-xl-1, + .px-xl-1 { + padding-left: 0.25rem !important; + } + .p-xl-2 { + padding: 0.5rem !important; + } + .pt-xl-2, + .py-xl-2 { + padding-top: 0.5rem !important; + } + .pr-xl-2, + .px-xl-2 { + padding-right: 0.5rem !important; + } + .pb-xl-2, + .py-xl-2 { + padding-bottom: 0.5rem !important; + } + .pl-xl-2, + .px-xl-2 { + padding-left: 0.5rem !important; + } + .p-xl-3 { + padding: 1rem !important; + } + .pt-xl-3, + .py-xl-3 { + padding-top: 1rem !important; + } + .pr-xl-3, + .px-xl-3 { + padding-right: 1rem !important; + } + .pb-xl-3, + .py-xl-3 { + padding-bottom: 1rem !important; + } + .pl-xl-3, + .px-xl-3 { + padding-left: 1rem !important; + } + .p-xl-4 { + padding: 1.5rem !important; + } + .pt-xl-4, + .py-xl-4 { + padding-top: 1.5rem !important; + } + .pr-xl-4, + .px-xl-4 { + padding-right: 1.5rem !important; + } + .pb-xl-4, + .py-xl-4 { + padding-bottom: 1.5rem !important; + } + .pl-xl-4, + .px-xl-4 { + padding-left: 1.5rem !important; + } + .p-xl-5 { + padding: 3rem !important; + } + .pt-xl-5, + .py-xl-5 { + padding-top: 3rem !important; + } + .pr-xl-5, + .px-xl-5 { + padding-right: 3rem !important; + } + .pb-xl-5, + .py-xl-5 { + padding-bottom: 3rem !important; + } + .pl-xl-5, + .px-xl-5 { + padding-left: 3rem !important; + } + .p-xl-sm { + padding: 2rem !important; + } + .pt-xl-sm, + .py-xl-sm { + padding-top: 2rem !important; + } + .pr-xl-sm, + .px-xl-sm { + padding-right: 2rem !important; + } + .pb-xl-sm, + .py-xl-sm { + padding-bottom: 2rem !important; + } + .pl-xl-sm, + .px-xl-sm { + padding-left: 2rem !important; + } + .p-xl-md { + padding: 4rem !important; + } + .pt-xl-md, + .py-xl-md { + padding-top: 4rem !important; + } + .pr-xl-md, + .px-xl-md { + padding-right: 4rem !important; + } + .pb-xl-md, + .py-xl-md { + padding-bottom: 4rem !important; + } + .pl-xl-md, + .px-xl-md { + padding-left: 4rem !important; + } + .p-xl-lg { + padding: 6rem !important; + } + .pt-xl-lg, + .py-xl-lg { + padding-top: 6rem !important; + } + .pr-xl-lg, + .px-xl-lg { + padding-right: 6rem !important; + } + .pb-xl-lg, + .py-xl-lg { + padding-bottom: 6rem !important; + } + .pl-xl-lg, + .px-xl-lg { + padding-left: 6rem !important; + } + .p-xl-xl { + padding: 8rem !important; + } + .pt-xl-xl, + .py-xl-xl { + padding-top: 8rem !important; + } + .pr-xl-xl, + .px-xl-xl { + padding-right: 8rem !important; + } + .pb-xl-xl, + .py-xl-xl { + padding-bottom: 8rem !important; + } + .pl-xl-xl, + .px-xl-xl { + padding-left: 8rem !important; + } + .m-xl-auto { + margin: auto !important; + } + .mt-xl-auto, + .my-xl-auto { + margin-top: auto !important; + } + .mr-xl-auto, + .mx-xl-auto { + margin-right: auto !important; + } + .mb-xl-auto, + .my-xl-auto { + margin-bottom: auto !important; + } + .ml-xl-auto, + .mx-xl-auto { + margin-left: auto !important; + } +} + +.text-monospace { + font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; +} + +.text-justify { + text-align: justify !important; +} + +.text-nowrap { + white-space: nowrap !important; +} + +.text-truncate { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +@media (min-width: 576px) { + .text-sm-left { + text-align: left !important; + } + .text-sm-right { + text-align: right !important; + } + .text-sm-center { + text-align: center !important; + } +} + +@media (min-width: 768px) { + .text-md-left { + text-align: left !important; + } + .text-md-right { + text-align: right !important; + } + .text-md-center { + text-align: center !important; + } +} + +@media (min-width: 992px) { + .text-lg-left { + text-align: left !important; + } + .text-lg-right { + text-align: right !important; + } + .text-lg-center { + text-align: center !important; + } +} + +@media (min-width: 1200px) { + .text-xl-left { + text-align: left !important; + } + .text-xl-right { + text-align: right !important; + } + .text-xl-center { + text-align: center !important; + } +} + +.text-lowercase { + text-transform: lowercase !important; +} + +.text-uppercase { + text-transform: uppercase !important; +} + +.text-capitalize { + text-transform: capitalize !important; +} + +.font-weight-light { + font-weight: 300 !important; +} + +.font-weight-normal { + font-weight: 400 !important; +} + +.font-weight-bold { + font-weight: 600 !important; +} + +.font-italic { + font-style: italic !important; +} + +.text-white { + color: #ffffff !important; +} + +.text-primary { + color: #e14eca !important; +} + +a.text-primary:hover, +a.text-primary:focus { + color: #d725bb !important; +} + +.text-secondary { + color: #f4f5f7 !important; +} + +a.text-secondary:hover, +a.text-secondary:focus { + color: #d6dae2 !important; +} + +.text-success { + color: #00f2c3 !important; +} + +a.text-success:hover, +a.text-success:focus { + color: #00bf9a !important; +} + +.text-info { + color: #1d8cf8 !important; +} + +a.text-info:hover, +a.text-info:focus { + color: #0772db !important; +} + +.text-warning { + color: #ff8d72 !important; +} + +a.text-warning:hover, +a.text-warning:focus { + color: #ff643f !important; +} + +.text-danger { + color: #fd5d93 !important; +} + +a.text-danger:hover, +a.text-danger:focus { + color: #fc2b71 !important; +} + +.text-light { + color: #adb5bd !important; +} + +a.text-light:hover, +a.text-light:focus { + color: #919ca6 !important; +} + +.text-dark { + color: #212529 !important; +} + +a.text-dark:hover, +a.text-dark:focus { + color: #0a0c0d !important; +} + +.text-default { + color: #344675 !important; +} + +a.text-default:hover, +a.text-default:focus { + color: #243152 !important; +} + +.text-white { + color: #ffffff !important; +} + +a.text-white:hover, +a.text-white:focus { + color: #e6e6e6 !important; +} + +.text-neutral { + color: #ffffff !important; +} + +a.text-neutral:hover, +a.text-neutral:focus { + color: #e6e6e6 !important; +} + +.text-darker { + color: black !important; +} + +a.text-darker:hover, +a.text-darker:focus { + color: black !important; +} + +.text-body { + color: #525f7f !important; +} + +.text-muted { + color: #6c757d !important; +} + +.text-black-50 { + color: rgba(34, 42, 66, 0.5) !important; +} + +.text-white-50 { + color: rgba(255, 255, 255, 0.5) !important; +} + +.text-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; +} + +.visible { + visibility: visible !important; +} + +.invisible { + visibility: hidden !important; +} + +@media print { + *, + *::before, + *::after { + text-shadow: none !important; + box-shadow: none !important; + } + a:not(.btn) { + text-decoration: underline; + } + abbr[title]::after { + content: " (" attr(title) ")"; + } + pre { + white-space: pre-wrap !important; + } + pre, + blockquote { + border: 0.0625rem solid #adb5bd; + page-break-inside: avoid; + } + thead { + display: table-header-group; + } + tr, + img { + page-break-inside: avoid; + } + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + h2, + h3 { + page-break-after: avoid; + } + @page { + size: a3; + } + body { + min-width: 992px !important; + } + .container { + min-width: 992px !important; + } + .navbar { + display: none; + } + .badge { + border: 0.0625rem solid #222a42; + } + .table { + border-collapse: collapse !important; + } + .table td, + .table th { + background-color: #ffffff !important; + } + .table-bordered th, + .table-bordered td { + border: 1px solid #e3e3e3 !important; + } + .table-dark { + color: inherit; + } + .table-dark th, + .table-dark td, + .table-dark thead th, + .table-dark tbody+tbody { + border-color: #e3e3e3; + } + .table .thead-dark th { + color: inherit; + border-color: #e3e3e3; + } +} + +body { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +iframe { + border: 0; +} + +figcaption, +figure, +main { + display: block; +} + +main { + overflow: hidden; +} + +.section-nucleo-icons .icons-container { + position: relative; + max-width: 100%; + height: 360px; + margin: 0 auto; + z-index: 1; +} + +.section-nucleo-icons { + --icon-size: 5rem; + --icon-sm-size: 3.75rem; + --gutter: 7rem; +} + +.section-nucleo-icons .icons-container i { + position: absolute; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: #ffffff; + z-index: 1; + transform: translate(-50%, -50%); + box-shadow: 0 1px 20px 0px rgba(0, 0, 0, 0.1); + transition: all 0.2s cubic-bezier(0.25, 0.65, 0.9, 0.75); +} + +.section-nucleo-icons .icons-container i.icon { + width: var(--icon-size); + height: var(--icon-size); + font-size: 1.7em; +} + +.section-nucleo-icons .icons-container i.icon-sm { + width: var(--icon-sm-size); + height: var(--icon-sm-size); + font-size: 1.5em; +} + +.section-nucleo-icons .icons-container i:nth-child(1) { + font-size: 42px; + color: #ff8d72; + z-index: 2; +} + +.section-nucleo-icons .icons-container:not(.on-screen) i { + transform: translate(-50%, -50%); + left: 50%; + top: 50%; +} + +.section-nucleo-icons .icons-container:not(.on-screen) i:not(:nth-child(1)) { + opacity: 0; +} + +.section-nucleo-icons .icons-container.on-screen i { + opacity: 1; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(1) { + left: 50%; + top: 50%; + font-size: 42px; + color: #ff8d72; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(2) { + left: calc(50% + (var(--gutter) * 1.7)); + top: 50%; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(3) { + left: calc(50% + var(--gutter)); + top: calc(50% + var(--gutter)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(4) { + left: calc(50% + var(--gutter)); + top: calc(50% - var(--gutter)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(5) { + left: calc(50% + (var(--gutter) * 4)); + top: 50%; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(6) { + left: calc(50% + (var(--gutter) * 2.7)); + top: calc(50% + (var(--gutter) * 1.5)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(7) { + left: calc(50% + (var(--gutter) * 2.7)); + top: calc(50% - (var(--gutter) * 1.5)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(8) { + left: calc(50% - (var(--gutter) * 1.7)); + top: 50%; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(9) { + left: calc(50% - var(--gutter)); + top: calc(50% + var(--gutter)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(10) { + left: calc(50% - var(--gutter)); + top: calc(50% - var(--gutter)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(11) { + left: calc(50% - (var(--gutter) * 4)); + top: 50%; +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(12) { + left: calc(50% - (var(--gutter) * 2.7)); + top: calc(50% + (var(--gutter) * 1.5)); +} + +.section-nucleo-icons .icons-container.on-screen i:nth-child(13) { + left: calc(50% - (var(--gutter) * 2.7)); + top: calc(50% - (var(--gutter) * 1.5)); +} + +.blur--hover { + position: relative; +} + +.blur--hover .blur-item { + transition: 1s cubic-bezier(0.19, 1, 0.22, 1); + will-change: transform; + filter: blur(0); + opacity: 1; +} + +.blur--hover .blur-hidden { + position: absolute; + top: calc(50% + 7px); + left: 50%; + transform: translate(-50%, -50%); + opacity: 0; + transition: all 0.15s ease; + z-index: 100; +} + +.blur--hover:hover .blur-item { + opacity: .8; + filter: blur(10px); + transform: scale(0.95); + z-index: 1; +} + +.blur--hover:hover .blur-hidden { + opacity: 1; + top: 50%; +} + +.bg-blue { + background-color: #5e72e4 !important; +} + +a.bg-blue:hover, +a.bg-blue:focus, +button.bg-blue:hover, +button.bg-blue:focus { + background-color: #324cdd !important; +} + +.bg-indigo { + background-color: #5603ad !important; +} + +a.bg-indigo:hover, +a.bg-indigo:focus, +button.bg-indigo:hover, +button.bg-indigo:focus { + background-color: #3d027b !important; +} + +.bg-purple { + background-color: #8965e0 !important; +} + +a.bg-purple:hover, +a.bg-purple:focus, +button.bg-purple:hover, +button.bg-purple:focus { + background-color: #683bd7 !important; +} + +.bg-pink { + background-color: #f3a4b5 !important; +} + +a.bg-pink:hover, +a.bg-pink:focus, +button.bg-pink:hover, +button.bg-pink:focus { + background-color: #ed7790 !important; +} + +.bg-red { + background-color: #f5365c !important; +} + +a.bg-red:hover, +a.bg-red:focus, +button.bg-red:hover, +button.bg-red:focus { + background-color: #ec0c38 !important; +} + +.bg-orange { + background-color: #fb6340 !important; +} + +a.bg-orange:hover, +a.bg-orange:focus, +button.bg-orange:hover, +button.bg-orange:focus { + background-color: #fa3a0e !important; +} + +.bg-yellow { + background-color: #ffd600 !important; +} + +a.bg-yellow:hover, +a.bg-yellow:focus, +button.bg-yellow:hover, +button.bg-yellow:focus { + background-color: #ccab00 !important; +} + +.bg-green { + background-color: #2dce89 !important; +} + +a.bg-green:hover, +a.bg-green:focus, +button.bg-green:hover, +button.bg-green:focus { + background-color: #24a46d !important; +} + +.bg-teal { + background-color: #11cdef !important; +} + +a.bg-teal:hover, +a.bg-teal:focus, +button.bg-teal:hover, +button.bg-teal:focus { + background-color: #0da5c0 !important; +} + +.bg-cyan { + background-color: #2bffc6 !important; +} + +a.bg-cyan:hover, +a.bg-cyan:focus, +button.bg-cyan:hover, +button.bg-cyan:focus { + background-color: #00f7b5 !important; +} + +.bg-white { + background-color: #ffffff !important; +} + +a.bg-white:hover, +a.bg-white:focus, +button.bg-white:hover, +button.bg-white:focus { + background-color: #e6e6e6 !important; +} + +.bg-gray { + background-color: #6c757d !important; +} + +a.bg-gray:hover, +a.bg-gray:focus, +button.bg-gray:hover, +button.bg-gray:focus { + background-color: #545b62 !important; +} + +.bg-gray-dark { + background-color: #32325d !important; +} + +a.bg-gray-dark:hover, +a.bg-gray-dark:focus, +button.bg-gray-dark:hover, +button.bg-gray-dark:focus { + background-color: #20203c !important; +} + +.bg-light { + background-color: #ced4da !important; +} + +a.bg-light:hover, +a.bg-light:focus, +button.bg-light:hover, +button.bg-light:focus { + background-color: #b1bbc4 !important; +} + +.bg-lighter { + background-color: #e9ecef !important; +} + +a.bg-lighter:hover, +a.bg-lighter:focus, +button.bg-lighter:hover, +button.bg-lighter:focus { + background-color: #cbd3da !important; +} + +.bg-gradient-primary { + background: #e14eca linear-gradient(180deg, #c447b3, #e14eca) repeat-x !important; +} + +.bg-gradient-secondary { + background: #f4f5f7 linear-gradient(180deg, #d4d5d9, #f4f5f7) repeat-x !important; +} + +.bg-gradient-success { + background: #00f2c3 linear-gradient(180deg, #05d2ad, #00f2c3) repeat-x !important; +} + +.bg-gradient-info { + background: #1d8cf8 linear-gradient(180deg, #1d7cda, #1d8cf8) repeat-x !important; +} + +.bg-gradient-warning { + background: #ff8d72 linear-gradient(180deg, #dd7c68, #ff8d72) repeat-x !important; +} + +.bg-gradient-danger { + background: #fd5d93 linear-gradient(180deg, #dc5484, #fd5d93) repeat-x !important; +} + +.bg-gradient-light { + background: #adb5bd linear-gradient(180deg, #989ea8, #adb5bd) repeat-x !important; +} + +.bg-gradient-dark { + background: #212529 linear-gradient(180deg, #21242a, #212529) repeat-x !important; +} + +.bg-gradient-default { + background: #344675 linear-gradient(180deg, #31406b, #344675) repeat-x !important; +} + +.bg-gradient-white { + background: #ffffff linear-gradient(180deg, #dddde0, #ffffff) repeat-x !important; +} + +.bg-gradient-neutral { + background: #ffffff linear-gradient(180deg, #dddde0, #ffffff) repeat-x !important; +} + +.bg-gradient-darker { + background: black linear-gradient(180deg, #050507, black) repeat-x !important; +} + +.bg-gradient-blue { + background: #5e72e4 linear-gradient(180deg, #5465c9, #5e72e4) repeat-x !important; +} + +.bg-gradient-indigo { + background: #5603ad linear-gradient(180deg, #4e079a, #5603ad) repeat-x !important; +} + +.bg-gradient-purple { + background: #8965e0 linear-gradient(180deg, #795ac5, #8965e0) repeat-x !important; +} + +.bg-gradient-pink { + background: #f3a4b5 linear-gradient(180deg, #d390a1, #f3a4b5) repeat-x !important; +} + +.bg-gradient-red { + background: #f5365c linear-gradient(180deg, #d53255, #f5365c) repeat-x !important; +} + +.bg-gradient-orange { + background: #fb6340 linear-gradient(180deg, #da593d, #fb6340) repeat-x !important; +} + +.bg-gradient-yellow { + background: #ffd600 linear-gradient(180deg, #ddba07, #ffd600) repeat-x !important; +} + +.bg-gradient-green { + background: #2dce89 linear-gradient(180deg, #2bb47c, #2dce89) repeat-x !important; +} + +.bg-gradient-teal { + background: #11cdef linear-gradient(180deg, #13b3d2, #11cdef) repeat-x !important; +} + +.bg-gradient-cyan { + background: #2bffc6 linear-gradient(180deg, #29ddaf, #2bffc6) repeat-x !important; +} + +.bg-gradient-white { + background: #ffffff linear-gradient(180deg, #dddde0, #ffffff) repeat-x !important; +} + +.bg-gradient-gray { + background: #6c757d linear-gradient(180deg, #606871, #6c757d) repeat-x !important; +} + +.bg-gradient-gray-dark { + background: #32325d linear-gradient(180deg, #2f2f56, #32325d) repeat-x !important; +} + +.bg-gradient-light { + background: #ced4da linear-gradient(180deg, #b4b9c0, #ced4da) repeat-x !important; +} + +.bg-gradient-lighter { + background: #e9ecef linear-gradient(180deg, #cbcdd2, #e9ecef) repeat-x !important; +} + +.section-primary { + background-color: #1e1e2f !important; +} + +a.section-primary:hover, +a.section-primary:focus, +button.section-primary:hover, +button.section-primary:focus { + background-color: #0a0a10 !important; +} + +.section-secondary { + background-color: #f4f5f7 !important; +} + +a.section-secondary:hover, +a.section-secondary:focus, +button.section-secondary:hover, +button.section-secondary:focus { + background-color: #d6dae2 !important; +} + +.section-light { + background-color: #ced4da !important; +} + +a.section-light:hover, +a.section-light:focus, +button.section-light:hover, +button.section-light:focus { + background-color: #b1bbc4 !important; +} + +.section-dark { + background-color: #212529 !important; +} + +a.section-dark:hover, +a.section-dark:focus, +button.section-dark:hover, +button.section-dark:focus { + background-color: #0a0c0d !important; +} + +.section-darker { + background-color: black !important; +} + +a.section-darker:hover, +a.section-darker:focus, +button.section-darker:hover, +button.section-darker:focus { + background-color: black !important; +} + +.bg-gradient-primary { + background: #e14eca linear-gradient(180deg, #c447b3, #e14eca) repeat-x !important; +} + +.bg-gradient-secondary { + background: #f4f5f7 linear-gradient(180deg, #d4d5d9, #f4f5f7) repeat-x !important; +} + +.bg-gradient-success { + background: #00f2c3 linear-gradient(180deg, #05d2ad, #00f2c3) repeat-x !important; +} + +.bg-gradient-info { + background: #1d8cf8 linear-gradient(180deg, #1d7cda, #1d8cf8) repeat-x !important; +} + +.bg-gradient-warning { + background: #ff8d72 linear-gradient(180deg, #dd7c68, #ff8d72) repeat-x !important; +} + +.bg-gradient-danger { + background: #fd5d93 linear-gradient(180deg, #dc5484, #fd5d93) repeat-x !important; +} + +.bg-gradient-light { + background: #adb5bd linear-gradient(180deg, #989ea8, #adb5bd) repeat-x !important; +} + +.bg-gradient-dark { + background: #212529 linear-gradient(180deg, #21242a, #212529) repeat-x !important; +} + +.bg-gradient-default { + background: #344675 linear-gradient(180deg, #31406b, #344675) repeat-x !important; +} + +.bg-gradient-white { + background: #ffffff linear-gradient(180deg, #dddde0, #ffffff) repeat-x !important; +} + +.bg-gradient-neutral { + background: #ffffff linear-gradient(180deg, #dddde0, #ffffff) repeat-x !important; +} + +.bg-gradient-darker { + background: black linear-gradient(180deg, #050507, black) repeat-x !important; +} + +.fill-primary { + fill: #e14eca; +} + +.stroke-primary { + stroke: #e14eca; +} + +.fill-secondary { + fill: #f4f5f7; +} + +.stroke-secondary { + stroke: #f4f5f7; +} + +.fill-success { + fill: #00f2c3; +} + +.stroke-success { + stroke: #00f2c3; +} + +.fill-info { + fill: #1d8cf8; +} + +.stroke-info { + stroke: #1d8cf8; +} + +.fill-warning { + fill: #ff8d72; +} + +.stroke-warning { + stroke: #ff8d72; +} + +.fill-danger { + fill: #fd5d93; +} + +.stroke-danger { + stroke: #fd5d93; +} + +.fill-light { + fill: #adb5bd; +} + +.stroke-light { + stroke: #adb5bd; +} + +.fill-dark { + fill: #212529; +} + +.stroke-dark { + stroke: #212529; +} + +.fill-default { + fill: #344675; +} + +.stroke-default { + stroke: #344675; +} + +.fill-white { + fill: #ffffff; +} + +.stroke-white { + stroke: #ffffff; +} + +.fill-neutral { + fill: #ffffff; +} + +.stroke-neutral { + stroke: #ffffff; +} + +.fill-darker { + fill: black; +} + +.stroke-darker { + stroke: black; +} + +.fill-opacity-8 { + fill-opacity: .8; +} + +.floating { + animation: floating 3s ease infinite; + will-change: transform; +} + +.floating:hover { + animation-play-state: paused; +} + +.floating-lg { + animation: floating-lg 3s ease infinite; +} + +.floating-sm { + animation: floating-sm 3s ease infinite; +} + +@keyframes floating-lg { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(15px); + } + 100% { + transform: translateY(0px); + } +} + +@keyframes floating { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(10px); + } + 100% { + transform: translateY(0px); + } +} + +@keyframes floating-sm { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(5px); + } + 100% { + transform: translateY(0px); + } +} + +.img-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +.floatfix:before, +.floatfix:after { + content: ''; + display: table; +} + +.floatfix:after { + clear: both; +} + +.overflow-visible { + overflow: visible !important; +} + +.overflow-hidden { + overflow: hidden !important; +} + +.opacity-1 { + opacity: .1 !important; +} + +.opacity-2 { + opacity: .2 !important; +} + +.opacity-3 { + opacity: .3 !important; +} + +.opacity-4 { + opacity: .4 !important; +} + +.opacity-5 { + opacity: .5 !important; +} + +.opacity-6 { + opacity: .6 !important; +} + +.opacity-7 { + opacity: .7 !important; +} + +.opacity-8 { + opacity: .8 !important; +} + +.opacity-8 { + opacity: .9 !important; +} + +.opacity-10 { + opacity: 1 !important; +} + +.top-0 { + top: 0; +} + +.right-0 { + right: 0; +} + +.bottom-0 { + bottom: 0; +} + +.left-0 { + left: 0; +} + +.top-1 { + top: 0.25rem; +} + +.right-1 { + right: 0.25rem; +} + +.bottom-1 { + bottom: 0.25rem; +} + +.left-1 { + left: 0.25rem; +} + +.top-2 { + top: 0.5rem; +} + +.right-2 { + right: 0.5rem; +} + +.bottom-2 { + bottom: 0.5rem; +} + +.left-2 { + left: 0.5rem; +} + +.top-3 { + top: 1rem; +} + +.right-3 { + right: 1rem; +} + +.bottom-3 { + bottom: 1rem; +} + +.left-3 { + left: 1rem; +} + +.top-4 { + top: 1.5rem; +} + +.right-4 { + right: 1.5rem; +} + +.bottom-4 { + bottom: 1.5rem; +} + +.left-4 { + left: 1.5rem; +} + +.top-5 { + top: 3rem; +} + +.right-5 { + right: 3rem; +} + +.bottom-5 { + bottom: 3rem; +} + +.left-5 { + left: 3rem; +} + +.top-sm { + top: 2rem; +} + +.right-sm { + right: 2rem; +} + +.bottom-sm { + bottom: 2rem; +} + +.left-sm { + left: 2rem; +} + +.top-md { + top: 4rem; +} + +.right-md { + right: 4rem; +} + +.bottom-md { + bottom: 4rem; +} + +.left-md { + left: 4rem; +} + +.top-lg { + top: 6rem; +} + +.right-lg { + right: 6rem; +} + +.bottom-lg { + bottom: 6rem; +} + +.left-lg { + left: 6rem; +} + +.top-xl { + top: 8rem; +} + +.right-xl { + right: 8rem; +} + +.bottom-xl { + bottom: 8rem; +} + +.left-xl { + left: 8rem; +} + +.center { + left: 50%; + transform: translateX(-50%); +} + +.h-100vh { + height: 100vh !important; +} + +.row.row-grid>[class*="col-"]+[class*="col-"] { + margin-top: 3rem; +} + +@media (min-width: 1200px) { + .row.row-grid>[class*="col-lg-"]+[class*="col-lg-"] { + margin-top: 0; + } +} + +@media (min-width: 992px) { + .row.row-grid>[class*="col-md-"]+[class*="col-md-"] { + margin-top: 0; + } +} + +@media (min-width: 768px) { + .row.row-grid>[class*="col-sm-"]+[class*="col-sm-"] { + margin-top: 0; + } +} + +.row-grid+.row-grid { + margin-top: 3rem; +} + +@media (min-width: 992px) { + [class*="mt--"], + [class*="mr--"], + [class*="mb--"], + [class*="ml--"] { + position: relative; + z-index: 5; + } + .mt--100 { + margin-top: -100px !important; + } + .mr--100 { + margin-right: -100px !important; + } + .mb--100 { + margin-bottom: -100px !important; + } + .ml--100 { + margin-left: -100px !important; + } + .mt--150 { + margin-top: -150px !important; + } + .mb--150 { + margin-bottom: -150px !important; + } + .mt--200 { + margin-top: -200px !important; + } + .mb--200 { + margin-bottom: -200px !important; + } + .mt--300 { + margin-top: -300px !important; + } + .mb--300 { + margin-bottom: -300px !important; + } + .pt-100 { + padding-top: 100px !important; + } + .pb-100 { + padding-bottom: 100px !important; + } + .pt-150 { + padding-top: 150px !important; + } + .pb-150 { + padding-bottom: 150px !important; + } + .pt-200 { + padding-top: 200px !important; + } + .pb-200 { + padding-bottom: 200px !important; + } + .pt-250 { + padding-top: 250px !important; + } + .pb-250 { + padding-bottom: 250px !important; + } + .pt-300 { + padding-top: 300px !important; + } + .pb-300 { + padding-bottom: 300px !important; + } +} + +[class*="shadow"] { + transition: all 0.15s ease; +} + +.shadow-sm--hover:hover { + box-shadow: 0 0.125rem 0.25rem rgba(34, 42, 66, 0.075) !important; +} + +.shadow--hover:hover { + box-shadow: 0 1px 20px 0px rgba(0, 0, 0, 0.1) !important; +} + +.shadow-lg--hover:hover { + box-shadow: 0 1rem 3rem rgba(34, 42, 66, 0.175) !important; +} + +.shadow-none--hover:hover { + box-shadow: none !important; +} + +.font-weight-300 { + font-weight: 300 !important; +} + +.font-weight-400 { + font-weight: 400 !important; +} + +.font-weight-500 { + font-weight: 500 !important; +} + +.font-weight-600 { + font-weight: 600 !important; +} + +.font-weight-700 { + font-weight: 700 !important; +} + +.font-weight-800 { + font-weight: 800 !important; +} + +.font-weight-900 { + font-weight: 900 !important; +} + +.text-underline { + text-decoration: underline; +} + +.text-through { + text-decoration: line-through; +} + +.lh-100 { + line-height: 1; +} + +.lh-110 { + line-height: 1.1; +} + +.lh-120 { + line-height: 1.2; +} + +.lh-130 { + line-height: 1.3; +} + +.lh-140 { + line-height: 1.4; +} + +.lh-150 { + line-height: 1.5; +} + +.lh-160 { + line-height: 1.6; +} + +.lh-170 { + line-height: 1.7; +} + +.lh-180 { + line-height: 1.8; +} + +.text-muted { + color: #6c757d !important; +} + +.ls-1 { + letter-spacing: .0625rem; +} + +.ls-15 { + letter-spacing: .09375rem; +} + +.ls-2 { + letter-spacing: 0.125rem; +} + +@media (min-width: 1200px) { + .transform-perspective-right { + transform: scale(1) perspective(1040px) rotateY(-11deg) rotateX(2deg) rotate(2deg); + } + .transform-perspective-left { + transform: scale(1) perspective(2000px) rotateY(11deg) rotateX(2deg) rotate(-2deg); + } +} + +.alert { + border: 0; + color: #ffffff; +} + +.alert .alert-link { + color: #ffffff; +} + +.alert.alert-success { + background-color: #00bf9a; +} + +.alert i.fa, +.alert i.tim-icons { + font-size: 1rem; +} + +.alert .close { + color: #ffffff; + opacity: .9; + text-shadow: none; + line-height: 0; + outline: 0; +} + +.alert span[data-notify="icon"] { + font-size: 22px; + display: block; + left: 19px; + position: absolute; + top: 50%; + margin-top: -11px; +} + +.alert button.close { + position: absolute; + right: 15px; + top: 50%; + margin-top: -13px; + width: 25px; + height: 25px; + padding: 3px; +} + +.alert .close~span { + display: block; + max-width: 89%; +} + +.alert.alert-with-icon { + padding-left: 65px; +} + +.alert-dismissible .close { + top: 50%; + right: 1.25rem; + padding: 0; + transform: translateY(-50%); + color: rgba(255, 255, 255, 0.6); + opacity: 1; +} + +.alert-dismissible .close:hover, +.alert-dismissible .close:focus { + color: rgba(255, 255, 255, 0.9); + opacity: 1 !important; +} + +@media (max-width: 575.98px) { + .alert-dismissible .close { + top: 1rem; + right: .5rem; + } +} + +.alert-dismissible .close>span:not(.sr-only) { + font-size: 1.5rem; + background-color: transparent; + color: rgba(255, 255, 255, 0.6); +} + +.alert-dismissible .close:hover>span:not(.sr-only), +.alert-dismissible .close:focus>span:not(.sr-only) { + background-color: transparent; + color: rgba(255, 255, 255, 0.9); +} + +.avatar { + color: #ffffff; + background-color: #adb5bd; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1rem; + border-radius: 50%; + height: 48px; + width: 48px; +} + +.avatar img { + width: 100%; + border-radius: 50%; +} + +.avatar+.avatar-content { + display: inline-block; + margin-left: .75rem; +} + +.avatar-lg { + width: 58px; + height: 58px; + font-size: 0.75rem; +} + +.avatar-sm { + width: 38px; + height: 38px; + font-size: 0.75rem; +} + +.avatar-group .avatar { + position: relative; + z-index: 2; + border: 2px solid #ffffff; +} + +.avatar-group .avatar:hover { + z-index: 3; +} + +.avatar-group .avatar+.avatar { + margin-left: -1rem; +} + +/* badges */ + +.badge { + text-transform: uppercase; + line-height: 12px; + border: none; + text-decoration: none; + margin-bottom: 5px; +} + +.badge:hover, +.badge:focus { + text-decoration: none; +} + +.badge-icon { + padding: 0.4em 0.55em; +} + +.badge-icon i { + font-size: 0.8em; +} + +.badge-success { + color: #ffffff; + background-color: #00bf9a; +} + +.badge-success[href]:hover, +.badge-success[href]:focus { + color: #ffffff; + text-decoration: none; + background-color: #008c71; +} + +.btn, +.navbar .navbar-nav>a.btn { + border-width: 2px; + border: none; + position: relative; + overflow: hidden; + margin: 4px 1px; + border-radius: 0.4285rem; + cursor: pointer; + background: #344675; + background-image: -webkit-linear-gradient(to bottom left, #344675, #263148, #344675); + background-image: -o-linear-gradient(to bottom left, #344675, #263148, #344675); + background-image: -moz-linear-gradient(to bottom left, #344675, #263148, #344675); + background-image: linear-gradient(to bottom left, #344675, #263148, #344675); + background-size: 210% 210%; + background-position: top right; + background-color: #344675; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn.animation-on-hover:hover, +.navbar .navbar-nav>a.btn.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn:hover, +.btn:focus, +.btn:active, +.btn.active, +.btn:active:focus, +.btn:active:hover, +.btn.active:focus, +.btn.active:hover, +.navbar .navbar-nav>a.btn:hover, +.navbar .navbar-nav>a.btn:focus, +.navbar .navbar-nav>a.btn:active, +.navbar .navbar-nav>a.btn.active, +.navbar .navbar-nav>a.btn:active:focus, +.navbar .navbar-nav>a.btn:active:hover, +.navbar .navbar-nav>a.btn.active:focus, +.navbar .navbar-nav>a.btn.active:hover { + background-color: #263148 !important; + background-image: linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -webkit-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -o-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -moz-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + color: #ffffff; + box-shadow: none; +} + +.btn:active, +.navbar .navbar-nav>a.btn:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn:not([data-action]):hover, +.navbar .navbar-nav>a.btn:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn.disabled, +.btn.disabled:hover, +.btn.disabled:focus, +.btn.disabled.focus, +.btn.disabled:active, +.btn.disabled.active, +.btn:disabled, +.btn:disabled:hover, +.btn:disabled:focus, +.btn:disabled.focus, +.btn:disabled:active, +.btn:disabled.active, +.btn[disabled], +.btn[disabled]:hover, +.btn[disabled]:focus, +.btn[disabled].focus, +.btn[disabled]:active, +.btn[disabled].active, +fieldset[disabled] .btn, +fieldset[disabled] .btn:hover, +fieldset[disabled] .btn:focus, +fieldset[disabled] .btn.focus, +fieldset[disabled] .btn:active, +fieldset[disabled] .btn.active, +.navbar .navbar-nav>a.btn.disabled, +.navbar .navbar-nav>a.btn.disabled:hover, +.navbar .navbar-nav>a.btn.disabled:focus, +.navbar .navbar-nav>a.btn.disabled.focus, +.navbar .navbar-nav>a.btn.disabled:active, +.navbar .navbar-nav>a.btn.disabled.active, +.navbar .navbar-nav>a.btn:disabled, +.navbar .navbar-nav>a.btn:disabled:hover, +.navbar .navbar-nav>a.btn:disabled:focus, +.navbar .navbar-nav>a.btn:disabled.focus, +.navbar .navbar-nav>a.btn:disabled:active, +.navbar .navbar-nav>a.btn:disabled.active, +.navbar .navbar-nav>a.btn[disabled], +.navbar .navbar-nav>a.btn[disabled]:hover, +.navbar .navbar-nav>a.btn[disabled]:focus, +.navbar .navbar-nav>a.btn[disabled].focus, +.navbar .navbar-nav>a.btn[disabled]:active, +.navbar .navbar-nav>a.btn[disabled].active, +fieldset[disabled] .navbar .navbar-nav>a.btn, +fieldset[disabled] .navbar .navbar-nav>a.btn:hover, +fieldset[disabled] .navbar .navbar-nav>a.btn:focus, +fieldset[disabled] .navbar .navbar-nav>a.btn.focus, +fieldset[disabled] .navbar .navbar-nav>a.btn:active, +fieldset[disabled] .navbar .navbar-nav>a.btn.active { + background-color: #344675; + border-color: #344675; +} + +.btn.btn-simple, +.navbar .navbar-nav>a.btn.btn-simple { + color: #344675; + border-color: #344675; + background: transparent; +} + +.btn.btn-simple:hover, +.btn.btn-simple:focus, +.btn.btn-simple:active, +.btn.btn-simple:not(:disabled):not(.disabled):active, +.navbar .navbar-nav>a.btn.btn-simple:hover, +.navbar .navbar-nav>a.btn.btn-simple:focus, +.navbar .navbar-nav>a.btn.btn-simple:active, +.navbar .navbar-nav>a.btn.btn-simple:not(:disabled):not(.disabled):active { + color: #344675; + border-color: #344675; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn.btn-simple.active, +.navbar .navbar-nav>a.btn.btn-simple.active { + border-color: #344675 !important; +} + +.btn.btn-simple.active:hover, +.btn.btn-simple.active:focus, +.btn.btn-simple.active:active, +.btn.btn-simple.active:not(:disabled):not(.disabled):active, +.navbar .navbar-nav>a.btn.btn-simple.active:hover, +.navbar .navbar-nav>a.btn.btn-simple.active:focus, +.navbar .navbar-nav>a.btn.btn-simple.active:active, +.navbar .navbar-nav>a.btn.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #344675; + background-image: linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -webkit-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -o-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-image: -moz-linear-gradient(to bottom left, #344675, #263148, #344675) !important; + background-color: #263148 !important; + box-shadow: none; +} + +.btn.btn-link, +.navbar .navbar-nav>a.btn.btn-link { + color: #344675; +} + +.btn.btn-link:hover, +.btn.btn-link:focus, +.btn.btn-link:active, +.navbar .navbar-nav>a.btn.btn-link:hover, +.navbar .navbar-nav>a.btn.btn-link:focus, +.navbar .navbar-nav>a.btn.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn:hover, +.btn:focus, +.navbar .navbar-nav>a.btn:hover, +.navbar .navbar-nav>a.btn:focus { + opacity: 1; + filter: alpha(opacity=100); + outline: 0 !important; +} + +.btn:active, +.btn.active, +.open>.btn.dropdown-toggle, +.navbar .navbar-nav>a.btn:active, +.navbar .navbar-nav>a.btn.active, +.open>.navbar .navbar-nav>a.btn.dropdown-toggle { + box-shadow: none; + outline: 0 !important; +} + +.btn .badge, +.navbar .navbar-nav>a.btn .badge { + margin: 0; +} + +.btn.btn-icon, +.navbar .navbar-nav>a.btn.btn-icon { + height: 2.375rem; + min-width: 2.375rem; + width: 2.375rem; + padding: 0; + font-size: 0.9375rem; + overflow: hidden; + position: relative; + line-height: normal; +} + +.btn.btn-icon.btn-simple, +.navbar .navbar-nav>a.btn.btn-icon.btn-simple { + padding: 0; +} + +.btn.btn-icon.btn-sm, +.btn-group-sm>.btn.btn-icon, +.navbar .navbar-nav>a.btn.btn-icon.btn-sm, +.navbar .btn-group-sm.navbar-nav>a.btn.btn-icon { + height: 1.875rem; + min-width: 1.875rem; + width: 1.875rem; +} + +.btn.btn-icon.btn-sm .fa, +.btn-group-sm>.btn.btn-icon .fa, +.btn.btn-icon.btn-sm .far, +.btn-group-sm>.btn.btn-icon .far, +.btn.btn-icon.btn-sm .fas, +.btn-group-sm>.btn.btn-icon .fas, +.btn.btn-icon.btn-sm .tim-icons, +.btn-group-sm>.btn.btn-icon .tim-icons, +.navbar .navbar-nav>a.btn.btn-icon.btn-sm .fa, +.navbar .btn-group-sm.navbar-nav>a.btn.btn-icon .fa, +.navbar .navbar-nav>a.btn.btn-icon.btn-sm .far, +.navbar .btn-group-sm.navbar-nav>a.btn.btn-icon .far, +.navbar .navbar-nav>a.btn.btn-icon.btn-sm .fas, +.navbar .btn-group-sm.navbar-nav>a.btn.btn-icon .fas, +.navbar .navbar-nav>a.btn.btn-icon.btn-sm .tim-icons, +.navbar .btn-group-sm.navbar-nav>a.btn.btn-icon .tim-icons { + font-size: 0.6875rem; +} + +.btn.btn-icon.btn-lg, +.btn-group-lg>.btn.btn-icon, +.navbar .navbar-nav>a.btn.btn-icon.btn-lg, +.navbar .btn-group-lg.navbar-nav>a.btn.btn-icon { + height: 3.6rem; + min-width: 3.6rem; + width: 3.6rem; +} + +.btn.btn-icon.btn-lg .fa, +.btn-group-lg>.btn.btn-icon .fa, +.btn.btn-icon.btn-lg .far, +.btn-group-lg>.btn.btn-icon .far, +.btn.btn-icon.btn-lg .fas, +.btn-group-lg>.btn.btn-icon .fas, +.btn.btn-icon.btn-lg .tim-icons, +.btn-group-lg>.btn.btn-icon .tim-icons, +.navbar .navbar-nav>a.btn.btn-icon.btn-lg .fa, +.navbar .btn-group-lg.navbar-nav>a.btn.btn-icon .fa, +.navbar .navbar-nav>a.btn.btn-icon.btn-lg .far, +.navbar .btn-group-lg.navbar-nav>a.btn.btn-icon .far, +.navbar .navbar-nav>a.btn.btn-icon.btn-lg .fas, +.navbar .btn-group-lg.navbar-nav>a.btn.btn-icon .fas, +.navbar .navbar-nav>a.btn.btn-icon.btn-lg .tim-icons, +.navbar .btn-group-lg.navbar-nav>a.btn.btn-icon .tim-icons { + font-size: 1.325rem; +} + +.btn.btn-icon:not(.btn-footer) .tim-icons, +.btn.btn-icon:not(.btn-footer) .fa, +.btn.btn-icon:not(.btn-footer) .far, +.btn.btn-icon:not(.btn-footer) .fas, +.navbar .navbar-nav>a.btn.btn-icon:not(.btn-footer) .tim-icons, +.navbar .navbar-nav>a.btn.btn-icon:not(.btn-footer) .fa, +.navbar .navbar-nav>a.btn.btn-icon:not(.btn-footer) .far, +.navbar .navbar-nav>a.btn.btn-icon:not(.btn-footer) .fas { + position: absolute; + font-size: 1em; + top: 50%; + left: 50%; + transform: translate(-12px, -12px); + line-height: 1.5626rem; + width: 24px; +} + +.btn:not(.btn-icon) .tim-icons, +.navbar .navbar-nav>a.btn:not(.btn-icon) .tim-icons { + position: relative; + top: 1px; +} + +.btn span, +.navbar .navbar-nav>a.btn span { + position: relative; + display: block; +} + +.btn.btn-link.dropdown-toggle, +.navbar .navbar-nav>a.btn.btn-link.dropdown-toggle { + color: #9A9A9A; +} + +.btn.dropdown-toggle:after, +.navbar .navbar-nav>a.btn.dropdown-toggle:after { + margin-left: 30px !important; +} + +.btn-primary { + background: #e14eca; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-size: 210% 210%; + background-position: top right; + background-color: #e14eca; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn-primary.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-primary:hover, +.btn-primary:focus, +.btn-primary:active, +.btn-primary.active, +.btn-primary:active:focus, +.btn-primary:active:hover, +.btn-primary.active:focus, +.btn-primary.active:hover { + background-color: #ba54f5 !important; + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-primary:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-primary:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-primary.disabled, +.btn-primary.disabled:hover, +.btn-primary.disabled:focus, +.btn-primary.disabled.focus, +.btn-primary.disabled:active, +.btn-primary.disabled.active, +.btn-primary:disabled, +.btn-primary:disabled:hover, +.btn-primary:disabled:focus, +.btn-primary:disabled.focus, +.btn-primary:disabled:active, +.btn-primary:disabled.active, +.btn-primary[disabled], +.btn-primary[disabled]:hover, +.btn-primary[disabled]:focus, +.btn-primary[disabled].focus, +.btn-primary[disabled]:active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary, +fieldset[disabled] .btn-primary:hover, +fieldset[disabled] .btn-primary:focus, +fieldset[disabled] .btn-primary.focus, +fieldset[disabled] .btn-primary:active, +fieldset[disabled] .btn-primary.active { + background-color: #e14eca; + border-color: #e14eca; +} + +.btn-primary.btn-simple { + color: #e14eca; + border-color: #e14eca; + background: transparent; +} + +.btn-primary.btn-simple:hover, +.btn-primary.btn-simple:focus, +.btn-primary.btn-simple:active, +.btn-primary.btn-simple:not(:disabled):not(.disabled):active { + color: #e14eca; + border-color: #e14eca; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-primary.btn-simple.active { + border-color: #e14eca !important; +} + +.btn-primary.btn-simple.active:hover, +.btn-primary.btn-simple.active:focus, +.btn-primary.btn-simple.active:active, +.btn-primary.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #e14eca; + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca) !important; + background-color: #ba54f5 !important; + box-shadow: none; +} + +.btn-primary.btn-link { + color: #e14eca; +} + +.btn-primary.btn-link:hover, +.btn-primary.btn-link:focus, +.btn-primary.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn-success { + background: #00f2c3; + background-image: -webkit-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -o-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -moz-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-size: 210% 210%; + background-position: top right; + background-color: #00f2c3; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn-success.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-success:hover, +.btn-success:focus, +.btn-success:active, +.btn-success.active, +.btn-success:active:focus, +.btn-success:active:hover, +.btn-success.active:focus, +.btn-success.active:hover { + background-color: #0098f0 !important; + background-image: linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -webkit-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -o-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -moz-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-success:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-success:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-success.disabled, +.btn-success.disabled:hover, +.btn-success.disabled:focus, +.btn-success.disabled.focus, +.btn-success.disabled:active, +.btn-success.disabled.active, +.btn-success:disabled, +.btn-success:disabled:hover, +.btn-success:disabled:focus, +.btn-success:disabled.focus, +.btn-success:disabled:active, +.btn-success:disabled.active, +.btn-success[disabled], +.btn-success[disabled]:hover, +.btn-success[disabled]:focus, +.btn-success[disabled].focus, +.btn-success[disabled]:active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success, +fieldset[disabled] .btn-success:hover, +fieldset[disabled] .btn-success:focus, +fieldset[disabled] .btn-success.focus, +fieldset[disabled] .btn-success:active, +fieldset[disabled] .btn-success.active { + background-color: #00f2c3; + border-color: #00f2c3; +} + +.btn-success.btn-simple { + color: #00f2c3; + border-color: #00f2c3; + background: transparent; +} + +.btn-success.btn-simple:hover, +.btn-success.btn-simple:focus, +.btn-success.btn-simple:active, +.btn-success.btn-simple:not(:disabled):not(.disabled):active { + color: #00f2c3; + border-color: #00f2c3; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-success.btn-simple.active { + border-color: #00f2c3 !important; +} + +.btn-success.btn-simple.active:hover, +.btn-success.btn-simple.active:focus, +.btn-success.btn-simple.active:active, +.btn-success.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #00f2c3; + background-image: linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -webkit-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -o-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-image: -moz-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3) !important; + background-color: #0098f0 !important; + box-shadow: none; +} + +.btn-success.btn-link { + color: #00f2c3; +} + +.btn-success.btn-link:hover, +.btn-success.btn-link:focus, +.btn-success.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn-info { + background: #1d8cf8; + background-image: -webkit-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -o-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -moz-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-size: 210% 210%; + background-position: top right; + background-color: #1d8cf8; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn-info.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-info:hover, +.btn-info:focus, +.btn-info:active, +.btn-info.active, +.btn-info:active:focus, +.btn-info:active:hover, +.btn-info.active:focus, +.btn-info.active:hover { + background-color: #3358f4 !important; + background-image: linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -webkit-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -o-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -moz-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-info:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-info:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-info.disabled, +.btn-info.disabled:hover, +.btn-info.disabled:focus, +.btn-info.disabled.focus, +.btn-info.disabled:active, +.btn-info.disabled.active, +.btn-info:disabled, +.btn-info:disabled:hover, +.btn-info:disabled:focus, +.btn-info:disabled.focus, +.btn-info:disabled:active, +.btn-info:disabled.active, +.btn-info[disabled], +.btn-info[disabled]:hover, +.btn-info[disabled]:focus, +.btn-info[disabled].focus, +.btn-info[disabled]:active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info, +fieldset[disabled] .btn-info:hover, +fieldset[disabled] .btn-info:focus, +fieldset[disabled] .btn-info.focus, +fieldset[disabled] .btn-info:active, +fieldset[disabled] .btn-info.active { + background-color: #1d8cf8; + border-color: #1d8cf8; +} + +.btn-info.btn-simple { + color: #1d8cf8; + border-color: #1d8cf8; + background: transparent; +} + +.btn-info.btn-simple:hover, +.btn-info.btn-simple:focus, +.btn-info.btn-simple:active, +.btn-info.btn-simple:not(:disabled):not(.disabled):active { + color: #1d8cf8; + border-color: #1d8cf8; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-info.btn-simple.active { + border-color: #1d8cf8 !important; +} + +.btn-info.btn-simple.active:hover, +.btn-info.btn-simple.active:focus, +.btn-info.btn-simple.active:active, +.btn-info.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #1d8cf8; + background-image: linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -webkit-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -o-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-image: -moz-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8) !important; + background-color: #3358f4 !important; + box-shadow: none; +} + +.btn-info.btn-link { + color: #1d8cf8; +} + +.btn-info.btn-link:hover, +.btn-info.btn-link:focus, +.btn-info.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn-warning { + background: #ff8d72; + background-image: -webkit-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -o-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -moz-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-size: 210% 210%; + background-position: top right; + background-color: #ff8d72; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn-warning.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-warning:hover, +.btn-warning:focus, +.btn-warning:active, +.btn-warning.active, +.btn-warning:active:focus, +.btn-warning:active:hover, +.btn-warning.active:focus, +.btn-warning.active:hover { + background-color: #ff6491 !important; + background-image: linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -webkit-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -o-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -moz-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-warning:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-warning:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-warning.disabled, +.btn-warning.disabled:hover, +.btn-warning.disabled:focus, +.btn-warning.disabled.focus, +.btn-warning.disabled:active, +.btn-warning.disabled.active, +.btn-warning:disabled, +.btn-warning:disabled:hover, +.btn-warning:disabled:focus, +.btn-warning:disabled.focus, +.btn-warning:disabled:active, +.btn-warning:disabled.active, +.btn-warning[disabled], +.btn-warning[disabled]:hover, +.btn-warning[disabled]:focus, +.btn-warning[disabled].focus, +.btn-warning[disabled]:active, +.btn-warning[disabled].active, +fieldset[disabled] .btn-warning, +fieldset[disabled] .btn-warning:hover, +fieldset[disabled] .btn-warning:focus, +fieldset[disabled] .btn-warning.focus, +fieldset[disabled] .btn-warning:active, +fieldset[disabled] .btn-warning.active { + background-color: #ff8d72; + border-color: #ff8d72; +} + +.btn-warning.btn-simple { + color: #ff8d72; + border-color: #ff8d72; + background: transparent; +} + +.btn-warning.btn-simple:hover, +.btn-warning.btn-simple:focus, +.btn-warning.btn-simple:active, +.btn-warning.btn-simple:not(:disabled):not(.disabled):active { + color: #ff8d72; + border-color: #ff8d72; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-warning.btn-simple.active { + border-color: #ff8d72 !important; +} + +.btn-warning.btn-simple.active:hover, +.btn-warning.btn-simple.active:focus, +.btn-warning.btn-simple.active:active, +.btn-warning.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #ff8d72; + background-image: linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -webkit-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -o-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-image: -moz-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72) !important; + background-color: #ff6491 !important; + box-shadow: none; +} + +.btn-warning.btn-link { + color: #ff8d72; +} + +.btn-warning.btn-link:hover, +.btn-warning.btn-link:focus, +.btn-warning.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn-warning:not(:disabled):not(.disabled):active { + color: #ffffff; +} + +.btn-danger { + background: #fd5d93; + background-image: -webkit-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -o-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -moz-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-size: 210% 210%; + background-position: top right; + background-color: #fd5d93; + transition: all 0.15s ease; + box-shadow: none; + color: #ffffff; +} + +.btn-danger.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-danger:hover, +.btn-danger:focus, +.btn-danger:active, +.btn-danger.active, +.btn-danger:active:focus, +.btn-danger:active:hover, +.btn-danger.active:focus, +.btn-danger.active:hover { + background-color: #ec250d !important; + background-image: linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -webkit-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -o-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -moz-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-danger:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-danger:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-danger.disabled, +.btn-danger.disabled:hover, +.btn-danger.disabled:focus, +.btn-danger.disabled.focus, +.btn-danger.disabled:active, +.btn-danger.disabled.active, +.btn-danger:disabled, +.btn-danger:disabled:hover, +.btn-danger:disabled:focus, +.btn-danger:disabled.focus, +.btn-danger:disabled:active, +.btn-danger:disabled.active, +.btn-danger[disabled], +.btn-danger[disabled]:hover, +.btn-danger[disabled]:focus, +.btn-danger[disabled].focus, +.btn-danger[disabled]:active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger, +fieldset[disabled] .btn-danger:hover, +fieldset[disabled] .btn-danger:focus, +fieldset[disabled] .btn-danger.focus, +fieldset[disabled] .btn-danger:active, +fieldset[disabled] .btn-danger.active { + background-color: #fd5d93; + border-color: #fd5d93; +} + +.btn-danger.btn-simple { + color: #fd5d93; + border-color: #fd5d93; + background: transparent; +} + +.btn-danger.btn-simple:hover, +.btn-danger.btn-simple:focus, +.btn-danger.btn-simple:active, +.btn-danger.btn-simple:not(:disabled):not(.disabled):active { + color: #fd5d93; + border-color: #fd5d93; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-danger.btn-simple.active { + border-color: #fd5d93 !important; +} + +.btn-danger.btn-simple.active:hover, +.btn-danger.btn-simple.active:focus, +.btn-danger.btn-simple.active:active, +.btn-danger.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #fd5d93; + background-image: linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -webkit-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -o-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-image: -moz-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93) !important; + background-color: #ec250d !important; + box-shadow: none; +} + +.btn-danger.btn-link { + color: #fd5d93; +} + +.btn-danger.btn-link:hover, +.btn-danger.btn-link:focus, +.btn-danger.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn-neutral { + background: #ffffff; + background-image: -webkit-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff); + background-image: -o-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff); + background-image: -moz-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff); + background-image: linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff); + background-size: 210% 210%; + background-position: top right; + background-color: #ffffff; + transition: all 0.15s ease; + box-shadow: none; + color: #e14eca; +} + +.btn-neutral.animation-on-hover:hover { + background-position: bottom left; + transition: 0.3s ease-in-out; +} + +.btn-neutral:hover, +.btn-neutral:focus, +.btn-neutral:active, +.btn-neutral.active, +.btn-neutral:active:focus, +.btn-neutral:active:hover, +.btn-neutral.active:focus, +.btn-neutral.active:hover { + background-color: #ffffff !important; + background-image: linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -webkit-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -o-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -moz-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + color: #ffffff; + box-shadow: none; +} + +.btn-neutral:active { + box-shadow: none !important; + transform: translateY(1px) !important; + transition: all .15s ease; +} + +.btn-neutral:not([data-action]):hover { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-neutral.disabled, +.btn-neutral.disabled:hover, +.btn-neutral.disabled:focus, +.btn-neutral.disabled.focus, +.btn-neutral.disabled:active, +.btn-neutral.disabled.active, +.btn-neutral:disabled, +.btn-neutral:disabled:hover, +.btn-neutral:disabled:focus, +.btn-neutral:disabled.focus, +.btn-neutral:disabled:active, +.btn-neutral:disabled.active, +.btn-neutral[disabled], +.btn-neutral[disabled]:hover, +.btn-neutral[disabled]:focus, +.btn-neutral[disabled].focus, +.btn-neutral[disabled]:active, +.btn-neutral[disabled].active, +fieldset[disabled] .btn-neutral, +fieldset[disabled] .btn-neutral:hover, +fieldset[disabled] .btn-neutral:focus, +fieldset[disabled] .btn-neutral.focus, +fieldset[disabled] .btn-neutral:active, +fieldset[disabled] .btn-neutral.active { + background-color: #ffffff; + border-color: #ffffff; +} + +.btn-neutral.btn-danger { + color: #fd5d93; +} + +.btn-neutral.btn-danger:hover, +.btn-neutral.btn-danger:focus, +.btn-neutral.btn-danger:active, +.btn-neutral.btn-danger:active:focus { + color: #ec250d; +} + +.btn-neutral.btn-info { + color: #1d8cf8; +} + +.btn-neutral.btn-info:hover, +.btn-neutral.btn-info:focus, +.btn-neutral.btn-info:active, +.btn-neutral.btn-info:active:focus { + color: #3358f4; +} + +.btn-neutral.btn-warning { + color: #ff8d72; +} + +.btn-neutral.btn-warning:hover, +.btn-neutral.btn-warning:focus, +.btn-neutral.btn-warning:active, +.btn-neutral.btn-warning:active:focus { + color: #ff6491; +} + +.btn-neutral.btn-success { + color: #00f2c3; +} + +.btn-neutral.btn-success:hover, +.btn-neutral.btn-success:focus, +.btn-neutral.btn-success:active, +.btn-neutral.btn-success:active:focus { + color: #0098f0; +} + +.btn-neutral.btn-default { + color: #344675; +} + +.btn-neutral.btn-default:hover, +.btn-neutral.btn-default:focus, +.btn-neutral.btn-default:active, +.btn-neutral.btn-default:active:focus { + color: #263148; +} + +.btn-neutral.active, +.btn-neutral:active, +.btn-neutral:active:focus, +.btn-neutral:active:hover, +.btn-neutral.active:focus, +.btn-neutral.active:hover, +.show>.btn-neutral.dropdown-toggle, +.show>.btn-neutral.dropdown-toggle:focus, +.show>.btn-neutral.dropdown-toggle:hover { + background-color: #ffffff; + color: #ba54f5; + box-shadow: none; +} + +.btn-neutral:hover, +.btn-neutral:focus { + color: #ba54f5; +} + +.btn-neutral:hover:not(.nav-link), +.btn-neutral:focus:not(.nav-link) { + box-shadow: none; +} + +.btn-neutral.btn-simple { + color: #ffffff; + border-color: #ffffff; + background: transparent; +} + +.btn-neutral.btn-simple:hover, +.btn-neutral.btn-simple:focus, +.btn-neutral.btn-simple:active, +.btn-neutral.btn-simple:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #ffffff; + background-color: transparent !important; + background-image: none !important; + box-shadow: none; +} + +.btn-neutral.btn-simple.active { + border-color: #ffffff !important; +} + +.btn-neutral.btn-simple.active:hover, +.btn-neutral.btn-simple.active:focus, +.btn-neutral.btn-simple.active:active, +.btn-neutral.btn-simple.active:not(:disabled):not(.disabled):active { + color: #ffffff; + border-color: #ffffff; + background-image: linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -webkit-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -o-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-image: -moz-linear-gradient(to bottom left, #ffffff, #ffffff, #ffffff) !important; + background-color: #ffffff !important; + box-shadow: none; +} + +.btn-neutral.btn-link { + color: #ffffff; +} + +.btn-neutral.btn-link:hover, +.btn-neutral.btn-link:focus, +.btn-neutral.btn-link:active { + background-color: transparent !important; + background-image: none !important; + color: #ffffff !important; + text-decoration: none; + box-shadow: none; +} + +.btn:disabled, +.btn[disabled], +.btn.disabled { + opacity: 0.5; + filter: alpha(opacity=50); + pointer-events: none; +} + +.btn-simple { + border: 1px solid; + border-color: #344675; + box-shadow: none; + padding: 10px 22px; + background-color: transparent; +} + +.btn-simple.disabled, +.btn-simple.disabled:hover, +.btn-simple.disabled:focus, +.btn-simple.disabled.focus, +.btn-simple.disabled:active, +.btn-simple.disabled.active, +.btn-simple:disabled, +.btn-simple:disabled:hover, +.btn-simple:disabled:focus, +.btn-simple:disabled.focus, +.btn-simple:disabled:active, +.btn-simple:disabled.active, +.btn-simple[disabled], +.btn-simple[disabled]:hover, +.btn-simple[disabled]:focus, +.btn-simple[disabled].focus, +.btn-simple[disabled]:active, +.btn-simple[disabled].active, +fieldset[disabled] .btn-simple, +fieldset[disabled] .btn-simple:hover, +fieldset[disabled] .btn-simple:focus, +fieldset[disabled] .btn-simple.focus, +fieldset[disabled] .btn-simple:active, +fieldset[disabled] .btn-simple.active, +.btn-link.disabled, +.btn-link.disabled:hover, +.btn-link.disabled:focus, +.btn-link.disabled.focus, +.btn-link.disabled:active, +.btn-link.disabled.active, +.btn-link:disabled, +.btn-link:disabled:hover, +.btn-link:disabled:focus, +.btn-link:disabled.focus, +.btn-link:disabled:active, +.btn-link:disabled.active, +.btn-link[disabled], +.btn-link[disabled]:hover, +.btn-link[disabled]:focus, +.btn-link[disabled].focus, +.btn-link[disabled]:active, +.btn-link[disabled].active, +fieldset[disabled] .btn-link, +fieldset[disabled] .btn-link:hover, +fieldset[disabled] .btn-link:focus, +fieldset[disabled] .btn-link.focus, +fieldset[disabled] .btn-link:active, +fieldset[disabled] .btn-link.active { + background: transparent; +} + +.btn:not(:disabled):not(.disabled):active, +.btn:not(:disabled):not(.disabled).active { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); +} + +.btn-link { + border: 0; + box-shadow: none; + padding: 0.5rem 0.7rem; + background: transparent; + color: #e3e3e3; + font-weight: 600; +} + +.btn-link:hover { + box-shadow: none !important; + transform: none !important; +} + +.btn-lg, +.btn-group-lg>.btn { + font-size: 0.875rem; + border-radius: 0.4285rem; + padding: 15px 48px; +} + +.btn-lg.btn-simple, +.btn-group-lg>.btn-simple.btn { + padding: 14px 47px; +} + +.btn-sm, +.btn-group-sm>.btn { + font-size: 0.875rem; + border-radius: 0.2857rem; + padding: 5px 15px; +} + +.btn-sm.btn-simple, +.btn-group-sm>.btn-simple.btn { + padding: 4px 14px; +} + +.btn-wd { + min-width: 140px; +} + +.btn-group.select { + width: 100%; +} + +.btn-group .btn.active { + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); + transform: translateY(-1px); + -webkit-transform: translateY(-1px); +} + +.btn-group label.btn.active { + transform: translateY(0); + -webkit-transform: translateY(0); +} + +.btn-group.select .btn { + text-align: left; +} + +.btn-group.select .caret { + position: absolute; + top: 50%; + margin-top: -1px; + right: 8px; +} + +.btn-round { + border-width: 1px; + border-radius: 30px; +} + +.btn-round.btn-simple { + padding: 10px 22px; +} + +.no-caret.dropdown-toggle::after { + display: none; +} + +.btn.btn-facebook { + background: #3b5998; + background-image: -webkit-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998); + background-image: -o-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998); + background-image: -moz-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998); + background-image: linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-facebook:focus, +.btn.btn-facebook:active, +.btn.btn-facebook:hover { + background-color: #344e86; + background-image: linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998) !important; + background-image: -webkit-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998) !important; + background-image: -o-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998) !important; + background-image: -moz-linear-gradient(to bottom left, #3b5998, #1e2e4f, #3b5998) !important; + color: #ffffff; +} + +.btn.btn-facebook.btn-simple { + color: #344e86; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #344e86; +} + +.btn.btn-facebook.btn-simple:hover, +.btn.btn-facebook.btn-simple:focus, +.btn.btn-facebook.btn-simple:active { + color: #344e86; + border-color: #344e86; +} + +.btn.btn-facebook.btn-neutral { + color: #3b5998; + background-color: #ffffff; +} + +.btn.btn-facebook.btn-neutral:hover, +.btn.btn-facebook.btn-neutral:focus, +.btn.btn-facebook.btn-neutral:active { + color: #344e86; +} + +.btn.btn-twitter { + background: #55acee; + background-image: -webkit-linear-gradient(to bottom left, #55acee, #147bc9, #55acee); + background-image: -o-linear-gradient(to bottom left, #55acee, #147bc9, #55acee); + background-image: -moz-linear-gradient(to bottom left, #55acee, #147bc9, #55acee); + background-image: linear-gradient(to bottom left, #55acee, #147bc9, #55acee); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-twitter:focus, +.btn.btn-twitter:active, +.btn.btn-twitter:hover { + background-color: #3ea1ec; + background-image: linear-gradient(to bottom left, #55acee, #147bc9, #55acee) !important; + background-image: -webkit-linear-gradient(to bottom left, #55acee, #147bc9, #55acee) !important; + background-image: -o-linear-gradient(to bottom left, #55acee, #147bc9, #55acee) !important; + background-image: -moz-linear-gradient(to bottom left, #55acee, #147bc9, #55acee) !important; + color: #ffffff; +} + +.btn.btn-twitter.btn-simple { + color: #3ea1ec; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #3ea1ec; +} + +.btn.btn-twitter.btn-simple:hover, +.btn.btn-twitter.btn-simple:focus, +.btn.btn-twitter.btn-simple:active { + color: #3ea1ec; + border-color: #3ea1ec; +} + +.btn.btn-twitter.btn-neutral { + color: #55acee; + background-color: #ffffff; +} + +.btn.btn-twitter.btn-neutral:hover, +.btn.btn-twitter.btn-neutral:focus, +.btn.btn-twitter.btn-neutral:active { + color: #3ea1ec; +} + +.btn.btn-pinterest { + background: #cc2127; + background-image: -webkit-linear-gradient(to bottom left, #cc2127, #741316, #cc2127); + background-image: -o-linear-gradient(to bottom left, #cc2127, #741316, #cc2127); + background-image: -moz-linear-gradient(to bottom left, #cc2127, #741316, #cc2127); + background-image: linear-gradient(to bottom left, #cc2127, #741316, #cc2127); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-pinterest:focus, +.btn.btn-pinterest:active, +.btn.btn-pinterest:hover { + background-color: #dd2e34; + background-image: linear-gradient(to bottom left, #cc2127, #741316, #cc2127) !important; + background-image: -webkit-linear-gradient(to bottom left, #cc2127, #741316, #cc2127) !important; + background-image: -o-linear-gradient(to bottom left, #cc2127, #741316, #cc2127) !important; + background-image: -moz-linear-gradient(to bottom left, #cc2127, #741316, #cc2127) !important; + color: #ffffff; +} + +.btn.btn-pinterest.btn-simple { + color: #dd2e34; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #dd2e34; +} + +.btn.btn-pinterest.btn-simple:hover, +.btn.btn-pinterest.btn-simple:focus, +.btn.btn-pinterest.btn-simple:active { + color: #dd2e34; + border-color: #dd2e34; +} + +.btn.btn-pinterest.btn-neutral { + color: #cc2127; + background-color: #ffffff; +} + +.btn.btn-pinterest.btn-neutral:hover, +.btn.btn-pinterest.btn-neutral:focus, +.btn.btn-pinterest.btn-neutral:active { + color: #dd2e34; +} + +.btn.btn-google { + background: #dd4b39; + background-image: -webkit-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39); + background-image: -o-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39); + background-image: -moz-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39); + background-image: linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-google:focus, +.btn.btn-google:active, +.btn.btn-google:hover { + background-color: #d73925; + background-image: linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39) !important; + background-image: -webkit-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39) !important; + background-image: -o-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39) !important; + background-image: -moz-linear-gradient(to bottom left, #dd4b39, #96271a, #dd4b39) !important; + color: #ffffff; +} + +.btn.btn-google.btn-simple { + color: #d73925; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #d73925; +} + +.btn.btn-google.btn-simple:hover, +.btn.btn-google.btn-simple:focus, +.btn.btn-google.btn-simple:active { + color: #d73925; + border-color: #d73925; +} + +.btn.btn-google.btn-neutral { + color: #dd4b39; + background-color: #ffffff; +} + +.btn.btn-google.btn-neutral:hover, +.btn.btn-google.btn-neutral:focus, +.btn.btn-google.btn-neutral:active { + color: #d73925; +} + +.btn.btn-linkedin { + background: #0077B5; + background-image: -webkit-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5); + background-image: -o-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5); + background-image: -moz-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5); + background-image: linear-gradient(to bottom left, #0077B5, #00344f, #0077B5); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-linkedin:focus, +.btn.btn-linkedin:active, +.btn.btn-linkedin:hover { + background-color: #00669c; + background-image: linear-gradient(to bottom left, #0077B5, #00344f, #0077B5) !important; + background-image: -webkit-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5) !important; + background-image: -o-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5) !important; + background-image: -moz-linear-gradient(to bottom left, #0077B5, #00344f, #0077B5) !important; + color: #ffffff; +} + +.btn.btn-linkedin.btn-simple { + color: #00669c; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #00669c; +} + +.btn.btn-linkedin.btn-simple:hover, +.btn.btn-linkedin.btn-simple:focus, +.btn.btn-linkedin.btn-simple:active { + color: #00669c; + border-color: #00669c; +} + +.btn.btn-linkedin.btn-neutral { + color: #0077B5; + background-color: #ffffff; +} + +.btn.btn-linkedin.btn-neutral:hover, +.btn.btn-linkedin.btn-neutral:focus, +.btn.btn-linkedin.btn-neutral:active { + color: #00669c; +} + +.btn.btn-dribbble { + background: #ea4c89; + background-image: -webkit-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89); + background-image: -o-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89); + background-image: -moz-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89); + background-image: linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-dribbble:focus, +.btn.btn-dribbble:active, +.btn.btn-dribbble:hover { + background-color: #ed679b; + background-image: linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89) !important; + background-image: -webkit-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89) !important; + background-image: -o-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89) !important; + background-image: -moz-linear-gradient(to bottom left, #ea4c89, #ba1655, #ea4c89) !important; + color: #ffffff; +} + +.btn.btn-dribbble.btn-simple { + color: #ed679b; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #ed679b; +} + +.btn.btn-dribbble.btn-simple:hover, +.btn.btn-dribbble.btn-simple:focus, +.btn.btn-dribbble.btn-simple:active { + color: #ed679b; + border-color: #ed679b; +} + +.btn.btn-dribbble.btn-neutral { + color: #ea4c89; + background-color: #ffffff; +} + +.btn.btn-dribbble.btn-neutral:hover, +.btn.btn-dribbble.btn-neutral:focus, +.btn.btn-dribbble.btn-neutral:active { + color: #ed679b; +} + +.btn.btn-github { + background: #333333; + background-image: -webkit-linear-gradient(to bottom left, #333333, black, #333333); + background-image: -o-linear-gradient(to bottom left, #333333, black, #333333); + background-image: -moz-linear-gradient(to bottom left, #333333, black, #333333); + background-image: linear-gradient(to bottom left, #333333, black, #333333); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-github:focus, +.btn.btn-github:active, +.btn.btn-github:hover { + background-color: #ccc; + background-image: linear-gradient(to bottom left, #333333, black, #333333) !important; + background-image: -webkit-linear-gradient(to bottom left, #333333, black, #333333) !important; + background-image: -o-linear-gradient(to bottom left, #333333, black, #333333) !important; + background-image: -moz-linear-gradient(to bottom left, #333333, black, #333333) !important; + color: #ffffff; +} + +.btn.btn-github.btn-simple { + color: #ccc; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #ccc; +} + +.btn.btn-github.btn-simple:hover, +.btn.btn-github.btn-simple:focus, +.btn.btn-github.btn-simple:active { + color: #ccc; + border-color: #ccc; +} + +.btn.btn-github.btn-neutral { + color: #333333; + background-color: #ffffff; +} + +.btn.btn-github.btn-neutral:hover, +.btn.btn-github.btn-neutral:focus, +.btn.btn-github.btn-neutral:active { + color: #ccc; +} + +.btn.btn-youtube { + background: #e52d27; + background-image: -webkit-linear-gradient(to bottom left, #e52d27, #941612, #e52d27); + background-image: -o-linear-gradient(to bottom left, #e52d27, #941612, #e52d27); + background-image: -moz-linear-gradient(to bottom left, #e52d27, #941612, #e52d27); + background-image: linear-gradient(to bottom left, #e52d27, #941612, #e52d27); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-youtube:focus, +.btn.btn-youtube:active, +.btn.btn-youtube:hover { + background-color: #e84842; + background-image: linear-gradient(to bottom left, #e52d27, #941612, #e52d27) !important; + background-image: -webkit-linear-gradient(to bottom left, #e52d27, #941612, #e52d27) !important; + background-image: -o-linear-gradient(to bottom left, #e52d27, #941612, #e52d27) !important; + background-image: -moz-linear-gradient(to bottom left, #e52d27, #941612, #e52d27) !important; + color: #ffffff; +} + +.btn.btn-youtube.btn-simple { + color: #e84842; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #e84842; +} + +.btn.btn-youtube.btn-simple:hover, +.btn.btn-youtube.btn-simple:focus, +.btn.btn-youtube.btn-simple:active { + color: #e84842; + border-color: #e84842; +} + +.btn.btn-youtube.btn-neutral { + color: #e52d27; + background-color: #ffffff; +} + +.btn.btn-youtube.btn-neutral:hover, +.btn.btn-youtube.btn-neutral:focus, +.btn.btn-youtube.btn-neutral:active { + color: #e84842; +} + +.btn.btn-instagram { + background: #125688; + background-image: -webkit-linear-gradient(to bottom left, #125688, #061d2e, #125688); + background-image: -o-linear-gradient(to bottom left, #125688, #061d2e, #125688); + background-image: -moz-linear-gradient(to bottom left, #125688, #061d2e, #125688); + background-image: linear-gradient(to bottom left, #125688, #061d2e, #125688); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-instagram:focus, +.btn.btn-instagram:active, +.btn.btn-instagram:hover { + background-color: #1667a3; + background-image: linear-gradient(to bottom left, #125688, #061d2e, #125688) !important; + background-image: -webkit-linear-gradient(to bottom left, #125688, #061d2e, #125688) !important; + background-image: -o-linear-gradient(to bottom left, #125688, #061d2e, #125688) !important; + background-image: -moz-linear-gradient(to bottom left, #125688, #061d2e, #125688) !important; + color: #ffffff; +} + +.btn.btn-instagram.btn-simple { + color: #1667a3; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #1667a3; +} + +.btn.btn-instagram.btn-simple:hover, +.btn.btn-instagram.btn-simple:focus, +.btn.btn-instagram.btn-simple:active { + color: #1667a3; + border-color: #1667a3; +} + +.btn.btn-instagram.btn-neutral { + color: #125688; + background-color: #ffffff; +} + +.btn.btn-instagram.btn-neutral:hover, +.btn.btn-instagram.btn-neutral:focus, +.btn.btn-instagram.btn-neutral:active { + color: #1667a3; +} + +.btn.btn-reddit { + background: #ff4500; + background-image: -webkit-linear-gradient(to bottom left, #ff4500, #992900, #ff4500); + background-image: -o-linear-gradient(to bottom left, #ff4500, #992900, #ff4500); + background-image: -moz-linear-gradient(to bottom left, #ff4500, #992900, #ff4500); + background-image: linear-gradient(to bottom left, #ff4500, #992900, #ff4500); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-reddit:focus, +.btn.btn-reddit:active, +.btn.btn-reddit:hover { + background-color: #ff5b1f; + background-image: linear-gradient(to bottom left, #ff4500, #992900, #ff4500) !important; + background-image: -webkit-linear-gradient(to bottom left, #ff4500, #992900, #ff4500) !important; + background-image: -o-linear-gradient(to bottom left, #ff4500, #992900, #ff4500) !important; + background-image: -moz-linear-gradient(to bottom left, #ff4500, #992900, #ff4500) !important; + color: #ffffff; +} + +.btn.btn-reddit.btn-simple { + color: #ff5b1f; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #ff5b1f; +} + +.btn.btn-reddit.btn-simple:hover, +.btn.btn-reddit.btn-simple:focus, +.btn.btn-reddit.btn-simple:active { + color: #ff5b1f; + border-color: #ff5b1f; +} + +.btn.btn-reddit.btn-neutral { + color: #ff4500; + background-color: #ffffff; +} + +.btn.btn-reddit.btn-neutral:hover, +.btn.btn-reddit.btn-neutral:focus, +.btn.btn-reddit.btn-neutral:active { + color: #ff5b1f; +} + +.btn.btn-tumblr { + background: #35465c; + background-image: -webkit-linear-gradient(to bottom left, #35465c, #10151b, #35465c); + background-image: -o-linear-gradient(to bottom left, #35465c, #10151b, #35465c); + background-image: -moz-linear-gradient(to bottom left, #35465c, #10151b, #35465c); + background-image: linear-gradient(to bottom left, #35465c, #10151b, #35465c); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-tumblr:focus, +.btn.btn-tumblr:active, +.btn.btn-tumblr:hover { + background-color: #40556f; + background-image: linear-gradient(to bottom left, #35465c, #10151b, #35465c) !important; + background-image: -webkit-linear-gradient(to bottom left, #35465c, #10151b, #35465c) !important; + background-image: -o-linear-gradient(to bottom left, #35465c, #10151b, #35465c) !important; + background-image: -moz-linear-gradient(to bottom left, #35465c, #10151b, #35465c) !important; + color: #ffffff; +} + +.btn.btn-tumblr.btn-simple { + color: #40556f; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #40556f; +} + +.btn.btn-tumblr.btn-simple:hover, +.btn.btn-tumblr.btn-simple:focus, +.btn.btn-tumblr.btn-simple:active { + color: #40556f; + border-color: #40556f; +} + +.btn.btn-tumblr.btn-neutral { + color: #35465c; + background-color: #ffffff; +} + +.btn.btn-tumblr.btn-neutral:hover, +.btn.btn-tumblr.btn-neutral:focus, +.btn.btn-tumblr.btn-neutral:active { + color: #40556f; +} + +.btn.btn-behance { + background: #1769ff; + background-image: -webkit-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff); + background-image: -o-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff); + background-image: -moz-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff); + background-image: linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; + background-size: 210% 210%; + background-position: top right; + background-repeat: space; +} + +.btn.btn-behance:focus, +.btn.btn-behance:active, +.btn.btn-behance:hover { + background-color: #367dff; + background-image: linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff) !important; + background-image: -webkit-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff) !important; + background-image: -o-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff) !important; + background-image: -moz-linear-gradient(to bottom left, #1769ff, #003eb0, #1769ff) !important; + color: #ffffff; +} + +.btn.btn-behance.btn-simple { + color: #367dff; + background-color: transparent; + background-image: none !important; + box-shadow: none; + border-color: #367dff; +} + +.btn.btn-behance.btn-simple:hover, +.btn.btn-behance.btn-simple:focus, +.btn.btn-behance.btn-simple:active { + color: #367dff; + border-color: #367dff; +} + +.btn.btn-behance.btn-neutral { + color: #1769ff; + background-color: #ffffff; +} + +.btn.btn-behance.btn-neutral:hover, +.btn.btn-behance.btn-neutral:focus, +.btn.btn-behance.btn-neutral:active { + color: #367dff; +} + +.btn-secondary:not(:disabled):not(.disabled):active, +.btn-secondary:not(:disabled):not(.disabled).active, +.show>.btn-secondary.dropdown-toggle { + color: #ffffff; +} + +@media (max-width: 991.98px) { + .btn-group .btn { + padding-left: 23px; + padding-right: 23px; + } +} + +.close { + transition: all 0.15s ease; +} + +.close>span:not(.sr-only) { + background-color: transparent; + color: rgba(0, 0, 0, 0.6); + line-height: 17px; + height: 1.25rem; + width: 1.25rem; + border-radius: 50%; + font-size: 1.25rem; + display: block; + transition: all 0.15s ease; +} + +.close:hover, +.close:focus { + background-color: transparent; + color: rgba(0, 0, 0, 0.9); + outline: none; +} + +.close:hover span:not(.sr-only), +.close:focus span:not(.sr-only) { + background-color: transparent; +} + +.custom-control-label::before { + border: 1px solid #cad1d7; + transition: all 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +.custom-control-label span { + position: relative; + top: 2px; +} + +.custom-control-label { + margin-bottom: 0; +} + +.custom-control-input:active~.custom-control-label::before { + border-color: #e14eca; +} + +.custom-control-alternative .custom-control-label::before { + border: 0; + box-shadow: 0 1px 3px rgba(50, 50, 93, 0.15), 0 1px 0 rgba(0, 0, 0, 0.02); +} + +.custom-control-alternative .custom-control-input:checked~.custom-control-label::before { + box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08); +} + +.custom-control-alternative .custom-control-input:active~.custom-control-label::before, +.custom-control-alternative .custom-control-input:focus~.custom-control-label::before { + box-shadow: 0 1px 3px rgba(50, 50, 93, 0.15), 0 1px 0 rgba(0, 0, 0, 0.02); +} + +.custom-checkbox .custom-control-input~.custom-control-label { + cursor: pointer; + font-size: 0.75rem; +} + +.custom-checkbox .custom-control-input:checked~.custom-control-label::before { + border-color: #e14eca; +} + +.custom-checkbox .custom-control-input:checked~.custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23ffffff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E"); +} + +.custom-checkbox .custom-control-input:disabled~.custom-control-label::before { + border-color: #e9ecef; +} + +.custom-checkbox .custom-control-input:disabled:checked::before { + border-color: rgba(225, 78, 202, 0.5); +} + +.custom-radio .custom-control-input~.custom-control-label { + cursor: pointer; + font-size: 0.75rem; +} + +.custom-radio .custom-control-input:checked~.custom-control-label::before { + border-color: #e14eca; +} + +.custom-radio .custom-control-input:checked~.custom-control-label::after { + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23ffffff'/%3E%3C/svg%3E"); +} + +.custom-radio .custom-control-input:disabled~.custom-control-label::before { + border-color: #e9ecef; +} + +.custom-radio .custom-control-input:disabled:checked::before { + border-color: rgba(225, 78, 202, 0.5); +} + +.custom-toggle { + position: relative; + display: inline-block; + width: 50px; + height: 1.5rem; +} + +.custom-toggle input { + display: none; +} + +.custom-toggle input:checked+.custom-toggle-slider { + border: 1px solid #e14eca; +} + +.custom-toggle input:checked+.custom-toggle-slider:before { + background: #e14eca; + transform: translateX(1.625rem); +} + +.custom-toggle input:disabled+.custom-toggle-slider { + border: 1px solid #e9ecef; +} + +.custom-toggle input:disabled:checked+.custom-toggle-slider { + border: 1px solid #e9ecef; +} + +.custom-toggle input:disabled:checked+.custom-toggle-slider:before { + background-color: #e87ad7; +} + +.custom-toggle-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 1px solid #cad1d7; + border-radius: 34px !important; + background-color: transparent; +} + +.custom-toggle-slider:before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 2px; + bottom: 2px; + border-radius: 50% !important; + background-color: #ddd; + transition: all 0.2s cubic-bezier(0.68, -0.55, 0.265, 1.55); +} + +.dropdown-menu { + border: 0; + box-shadow: 0px 10px 50px 0px rgba(0, 0, 0, 0.2); + border-radius: 0.1428rem; + -webkit-transition: all 150ms linear; + -moz-transition: all 150ms linear; + -o-transition: all 150ms linear; + -ms-transition: all 150ms linear; + transition: all 150ms linear; +} + +.dropdown-menu.dropdown-menu-right:before, +.dropdown-menu.dropdown-menu-right:after { + left: auto; + right: 10px; +} + +.dropdown-menu.dropdown-black { + background: linear-gradient(to bottom, #222a42 0%, #1d253b 100%); + border: 1px solid #344675; +} + +.dropdown-menu.dropdown-black .dropdown-item { + color: rgba(255, 255, 255, 0.7); +} + +.dropdown-menu.dropdown-black .dropdown-divider { + border-color: #344675; +} + +.dropdown-menu.dropdown-black:before { + color: #222a42; + z-index: 2; +} + +.dropdown-menu.dropdown-black:after { + display: inline-block; + position: absolute; + width: 0; + height: 0; + z-index: 1; + vertical-align: middle; + content: ""; + top: -6px; + left: 10px; + right: auto; + color: #344675; + border-bottom: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; +} + +.dropdown-menu.dropdown-black.dropdown-menu-right:after { + left: auto; + right: 10px; +} + +.dropup .dropdown-menu.dropdown-black:after { + color: #1d253b; + z-index: 2; +} + +.dropup .dropdown-menu.dropdown-black:before { + display: inline-block; + position: absolute; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + top: auto; + bottom: -6px; + right: auto; + left: 10px; + color: #555555; + border-top: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; + border-bottom: none; + z-index: 1; +} + +.dropdown-menu i { + margin-right: 5px; + position: relative; + top: 1px; +} + +.dropdown-menu .tim-icons { + margin-right: 10px; + position: relative; + top: 4px; + font-size: 18px; + margin-top: -5px; + opacity: .5; +} + +.dropdown-menu .dropdown-item.active, +.dropdown-menu .dropdown-item:active { + color: inherit; +} + +.dropup .dropdown-menu:before { + display: none; +} + +.dropup .dropdown-menu:after { + display: inline-block; + position: absolute; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + top: auto; + bottom: -5px; + right: auto; + left: 10px; + color: #ffffff; + border-top: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; + border-bottom: none; +} + +.dropup .dropdown-menu.dropdown-menu-right:after, +.dropup .dropdown-menu.dropdown-menu-right:before { + right: 10px; + left: auto; +} + +.dropdown-menu:before { + display: inline-block; + position: absolute; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + top: -5px; + left: 10px; + right: auto; + color: #ffffff; + border-bottom: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; +} + +.dropdown-menu.dropdown-menu-right { + right: 0 !important; + left: auto !important; +} + +.dropdown-menu .dropdown-item, +.bootstrap-select .dropdown-menu.inner li a { + font-size: 0.75rem; + padding-top: .6rem; + padding-bottom: .6rem; + margin-top: 5px; + -webkit-transition: all 150ms linear; + -moz-transition: all 150ms linear; + -o-transition: all 150ms linear; + -ms-transition: all 150ms linear; + transition: all 150ms linear; +} + +.dropdown-menu .dropdown-item:hover, +.dropdown-menu .dropdown-item:focus, +.bootstrap-select .dropdown-menu.inner li a:hover, +.bootstrap-select .dropdown-menu.inner li a:focus { + background-color: rgba(222, 222, 222, 0.3); +} + +.dropdown-menu .dropdown-item.disabled, +.dropdown-menu .dropdown-item:disabled, +.bootstrap-select .dropdown-menu.inner li a.disabled, +.bootstrap-select .dropdown-menu.inner li a:disabled { + color: rgba(182, 182, 182, 0.6); +} + +.dropdown-menu .dropdown-item.disabled:hover, +.dropdown-menu .dropdown-item.disabled:focus, +.dropdown-menu .dropdown-item:disabled:hover, +.dropdown-menu .dropdown-item:disabled:focus, +.bootstrap-select .dropdown-menu.inner li a.disabled:hover, +.bootstrap-select .dropdown-menu.inner li a.disabled:focus, +.bootstrap-select .dropdown-menu.inner li a:disabled:hover, +.bootstrap-select .dropdown-menu.inner li a:disabled:focus { + background-color: transparent; + box-shadow: none; +} + +.dropdown-menu .dropdown-divider { + background-color: rgba(222, 222, 222, 0.5); +} + +.dropdown-menu .dropdown-header:not([href]):not([tabindex]) { + color: rgba(182, 182, 182, 0.6); + font-size: 0.62475rem; + text-transform: uppercase; + font-weight: 600; +} + +.dropdown-menu.dropdown-primary { + background-color: #df41c6; +} + +.dropdown-menu.dropdown-primary:before { + color: #df41c6; +} + +.dropdown-menu.dropdown-primary .dropdown-header:not([href]):not([tabindex]) { + color: rgba(255, 255, 255, 0.8); +} + +.dropdown-menu.dropdown-primary .dropdown-item { + color: #ffffff; +} + +.dropdown-menu.dropdown-primary .dropdown-item:hover, +.dropdown-menu.dropdown-primary .dropdown-item:focus { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-primary .dropdown-divider { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-info { + background-color: #0e84f8; +} + +.dropdown-menu.dropdown-info:before { + color: #0e84f8; +} + +.dropdown-menu.dropdown-info .dropdown-header:not([href]):not([tabindex]) { + color: rgba(255, 255, 255, 0.8); +} + +.dropdown-menu.dropdown-info .dropdown-item { + color: #ffffff; +} + +.dropdown-menu.dropdown-info .dropdown-item:hover, +.dropdown-menu.dropdown-info .dropdown-item:focus { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-info .dropdown-divider { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-danger { + background-color: #fd4e89; +} + +.dropdown-menu.dropdown-danger:before { + color: #fd4e89; +} + +.dropdown-menu.dropdown-danger .dropdown-header:not([href]):not([tabindex]) { + color: rgba(255, 255, 255, 0.8); +} + +.dropdown-menu.dropdown-danger .dropdown-item { + color: #ffffff; +} + +.dropdown-menu.dropdown-danger .dropdown-item:hover, +.dropdown-menu.dropdown-danger .dropdown-item:focus { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-danger .dropdown-divider { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-success { + background-color: #00e3b7; +} + +.dropdown-menu.dropdown-success:before { + color: #00e3b7; +} + +.dropdown-menu.dropdown-success .dropdown-header:not([href]):not([tabindex]) { + color: rgba(255, 255, 255, 0.8); +} + +.dropdown-menu.dropdown-success .dropdown-item { + color: #ffffff; +} + +.dropdown-menu.dropdown-success .dropdown-item:hover, +.dropdown-menu.dropdown-success .dropdown-item:focus { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-success .dropdown-divider { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-warning { + background-color: #ff8163; +} + +.dropdown-menu.dropdown-warning:before { + color: #ff8163; +} + +.dropdown-menu.dropdown-warning .dropdown-header:not([href]):not([tabindex]) { + color: rgba(255, 255, 255, 0.8); +} + +.dropdown-menu.dropdown-warning .dropdown-item { + color: #ffffff; +} + +.dropdown-menu.dropdown-warning .dropdown-item:hover, +.dropdown-menu.dropdown-warning .dropdown-item:focus { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown-menu.dropdown-warning .dropdown-divider { + background-color: rgba(255, 255, 255, 0.2); +} + +.dropdown .dropdown-menu, +.dropup:not(.bootstrap-select) .dropdown-menu, +.bootstrap-select .dropdown-menu:not(.inner), +.dropdown-menu.bootstrap-datetimepicker-widget.bottom { + -webkit-transform: translate3d(0, -20px, 0) !important; + -moz-transform: translate3d(0, -20px, 0) !important; + -o-transform: translate3d(0, -20px, 0) !important; + -ms-transform: translate3d(0, -20px, 0) !important; + transform: translate3d(0, -20px, 0) !important; + visibility: hidden; + display: block; + opacity: 0; + filter: alpha(opacity=0); + top: 100% !important; +} + +.dropdown-menu.bootstrap-datetimepicker-widget.top { + -webkit-transform: translate3d(0, -20px, 0) !important; + -moz-transform: translate3d(0, -20px, 0) !important; + -o-transform: translate3d(0, -20px, 0) !important; + -ms-transform: translate3d(0, -20px, 0) !important; + transform: translate3d(0, -20px, 0) !important; + visibility: hidden; + display: block; + opacity: 0; + filter: alpha(opacity=0); +} + +.dropdown-menu.bootstrap-datetimepicker-widget.top, +.dropdown-menu.bootstrap-datetimepicker-widget.bottom { + -webkit-transform: translate3d(0, -20px, 0) !important; + -moz-transform: translate3d(0, -20px, 0) !important; + -o-transform: translate3d(0, -20px, 0) !important; + -ms-transform: translate3d(0, -20px, 0) !important; + transform: translate3d(0, -20px, 0) !important; +} + +.bootstrap-select.dropup .dropdown-menu:not(.inner) { + -webkit-transform: translate3d(0, 25px, 0) !important; + -moz-transform: translate3d(0, 25px, 0) !important; + -o-transform: translate3d(0, 25px, 0) !important; + -ms-transform: translate3d(0, 25px, 0) !important; + transform: translate3d(0, 25px, 0) !important; +} + +.dropup:not(.bootstrap-select) .dropdown-menu { + -webkit-transform: translate3d(0, 20px, 0) !important; + -moz-transform: translate3d(0, 20px, 0) !important; + -o-transform: translate3d(0, 20px, 0) !important; + -ms-transform: translate3d(0, 20px, 0) !important; + transform: translate3d(0, 20px, 0) !important; + top: auto !important; + bottom: 100%; +} + +.dropdown.show .dropdown-menu, +.bootstrap-select.show .dropdown-menu:not(.inner), +.dropdown-menu.bootstrap-datetimepicker-widget.top.open, +.dropdown-menu.bootstrap-datetimepicker-widget.bottom.open, +.dropup.show:not(.bootstrap-select) .dropdown-menu, +.navbar .dropdown.show .dropdown-menu { + opacity: 1; + filter: alpha(opacity=100); + visibility: visible; + -webkit-transform: translate3d(0, 1px, 0) !important; + -moz-transform: translate3d(0, 1px, 0) !important; + -o-transform: translate3d(0, 1px, 0) !important; + -ms-transform: translate3d(0, 1px, 0) !important; + transform: translate3d(0, 1px, 0) !important; +} + +.dropdown-menu.bootstrap-datetimepicker-widget.top.open, +.dropdown-menu.bootstrap-datetimepicker-widget.bottom.open { + -webkit-transform: translate3d(0, 0px, 0) !important; + -moz-transform: translate3d(0, 0px, 0) !important; + -o-transform: translate3d(0, 0px, 0) !important; + -ms-transform: translate3d(0, 0px, 0) !important; + transform: translate3d(0, 0px, 0) !important; +} + +.dropup.show:not(.bootstrap-select) .dropdown-menu { + -webkit-transform: translate3d(0, -2px, 0) !important; + -moz-transform: translate3d(0, -2px, 0) !important; + -o-transform: translate3d(0, -2px, 0) !important; + -ms-transform: translate3d(0, -2px, 0) !important; + transform: translate3d(0, -2px, 0) !important; +} + +.dropdown-menu.dropdown-navbar { + left: -80px; +} + +.dropdown-menu.dropdown-navbar:before, +.dropdown-menu.dropdown-navbar:after { + left: auto; + right: 17px; +} + +.btn { + cursor: pointer; +} + +.btn.dropdown-toggle[data-toggle="dropdown"] { + padding: 10px; + margin: 0; + margin-bottom: 5px; +} + +.btn.dropdown-toggle[data-toggle="dropdown"]:after { + content: ""; + margin-left: 5px; +} + +.btn span.bs-caret { + display: none; +} + +.btn.btn-link.dropdown-toggle { + height: 22px; + padding: 0; + margin-right: 5px; +} + +.dropdown-toggle:after { + content: unset; +} + +.btn:not(:disabled):not(.disabled).active:focus, +.btn:not(:disabled):not(.disabled):active:focus, +.show>.btn.dropdown-toggle:focus { + box-shadow: none; +} + +.dropdown-menu-sm { + min-width: 100px; + border: 0.4285rem; +} + +.dropdown-menu-lg { + min-width: 260px; + border-radius: 0.4285rem; +} + +.dropdown-menu-xl { + min-width: 450px; + border-radius: 0.4285rem; +} + +@media (max-width: 991.98px) { + .button-dropdown { + display: none; + } + .dropdown-toggle:after { + display: inline-block; + width: 0; + height: 0; + margin-left: .255em; + vertical-align: .255em; + content: ""; + border-top: .3em solid; + border-right: .3em solid transparent; + border-bottom: 0; + border-left: .3em solid transparent; + } +} + +@media (min-width: 992px) { + .dropdown-menu .dropdown-item { + color: #9A9A9A; + } +} + +.footer { + padding: 24px 30px; +} + +.footer [class*="container-"] { + padding: 0; +} + +.footer .nav { + display: inline-block; + float: left; + margin-bottom: 0; + padding: 0; + list-style: none; +} + +.footer .nav-item { + display: inline-block; +} + +.footer .nav-item:first-child a { + padding-left: 0; +} + +.footer .nav-link { + color: #ffffff; + padding: 0.5rem; + font-size: 0.75rem; + text-transform: uppercase; + text-decoration: none; +} + +.footer .nav-link:hover { + text-decoration: none; +} + +.footer .copyright { + font-size: 0.75rem; + line-height: 1.8; + color: #ffffff; +} + +.footer:after { + display: table; + clear: both; + content: " "; +} + +@media (max-width: 991.98px) { + .footer { + padding-left: 30px; + text-align: center; + } + .footer .copyright { + text-align: right; + } +} + +@media (min-width: 1200px) { + .footer .copyright { + float: right; + margin-top: 5px; + } +} + +@media (max-width: 767.98px) { + .footer { + text-align: center; + } + .footer nav { + display: block; + margin-bottom: 5px; + float: none; + } + .footer .copyright { + text-align: center; + } +} + +@media (max-width: 767.98px) { + .footer .copyright { + text-align: center; + } + .footer .nav { + float: none; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .footer { + padding-left: 30px !important; + text-align: center; + } + .footer .copyright { + float: right; + margin-top: 5px; + } +} + +/* Form controls */ + +.form-control::-moz-placeholder { + color: #6c757c; + opacity: 1; + filter: alpha(opacity=100); +} + +.form-control:-moz-placeholder { + color: #6c757c; + opacity: 1; + filter: alpha(opacity=100); +} + +.form-control::-webkit-input-placeholder { + color: #6c757c; + opacity: 1; + filter: alpha(opacity=100); +} + +.form-control:-ms-input-placeholder { + color: #6c757c; + opacity: 1; + filter: alpha(opacity=100); +} + +.form-control { + border-color: #2b3553; + border-radius: 0.4285rem; + font-size: 0.75rem; + -webkit-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -moz-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -o-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -ms-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; +} + +.form-control:focus { + border-color: #e14eca; + background-color: transparent; + box-shadow: none; +} + +.form-control:focus+.input-group-append .input-group-text, +.form-control:focus~.input-group-append .input-group-text, +.form-control:focus+.input-group-prepend .input-group-text, +.form-control:focus~.input-group-prepend .input-group-text { + border: 1px solid #e14eca; + border-left: none; + background-color: transparent; +} + +.has-success .form-control, +.has-error .form-control, +.has-success .form-control:focus, +.has-error .form-control:focus { + box-shadow: none; +} + +.has-danger .form-control.form-control-success, +.has-danger .form-control.form-control-danger, +.has-success .form-control.form-control-success, +.has-success .form-control.form-control-danger { + background-image: none; +} + +.form-control+.form-control-feedback { + border-radius: 0.4285rem; + margin-top: -7px; + position: absolute; + right: 10px; + top: 50%; + vertical-align: middle; +} + +.open .form-control { + border-radius: 0.4285rem 0.4285rem 0 0; + border-bottom-color: transparent; +} + +.form-control+.input-group-append .input-group-text, +.form-control+.input-group-prepend .input-group-text { + background-color: #ffffff; +} + +.has-success .input-group-append .input-group-text, +.has-success .input-group-prepend .input-group-text, +.has-success .form-control { + border-color: #2b3553; +} + +.has-success .form-control:focus, +.has-success.input-group-focus .input-group-append .input-group-text, +.has-success.input-group-focus .input-group-prepend .input-group-text { + border-color: #00bf9a; +} + +.has-danger .form-control, +.has-danger .input-group-append .input-group-text, +.has-danger .input-group-prepend .input-group-text, +.has-danger.input-group-focus .input-group-prepend .input-group-text, +.has-danger.input-group-focus .input-group-append .input-group-text { + border-color: #f33620; + color: #ec250d; + background-color: rgba(222, 222, 222, 0.1); +} + +.has-danger .form-control:focus, +.has-danger .input-group-append .input-group-text:focus, +.has-danger .input-group-prepend .input-group-text:focus, +.has-danger.input-group-focus .input-group-prepend .input-group-text:focus, +.has-danger.input-group-focus .input-group-append .input-group-text:focus { + background-color: transparent; +} + +.has-success:after, +.has-danger:after { + font-family: 'nucleo'; + content: "\ea1b"; + display: inline-block; + position: absolute; + right: 20px; + top: 13px; + color: #00f2c3; + font-size: 11px; +} + +.has-success.form-control-lg:after, +.has-danger.form-control-lg:after { + font-size: 13px; + top: 24px; +} + +.has-success.has-label:after, +.has-danger.has-label:after { + top: 37px; +} + +.has-success.form-check:after, +.has-danger.form-check:after { + display: none !important; +} + +.has-success.form-check .form-check-label, +.has-danger.form-check .form-check-label { + color: #00f2c3; +} + +.has-danger:after { + content: "\ea48"; + color: #ec250d; +} + +.has-danger.form-check .form-check-label { + color: #ec250d; +} + +@media (max-width: 575.98px) { + .form-horizontal .col-form-label, + .form-horizontal .label-on-right { + text-align: inherit; + padding-top: 0; + } + .form-horizontal .col-form-label code, + .form-horizontal .label-on-right code { + padding: 0 10px; + } +} + +@media (min-width: 1200px) { + .container-lg { + max-width: 1160px; + } +} + +.icon { + width: auto; + height: auto; +} + +.icon i, +.icon svg { + font-size: auto-0.75; +} + +.icon+.icon-text { + padding-left: 1rem; + width: calc(100% - auto - 1); +} + +.icon-xl { + width: 5rem; + height: 5rem; +} + +.icon-xl i, +.icon-xl svg { + font-size: 4.25rem; +} + +.icon-xl+.icon-text { + width: calc(100% - $icon-size-xl - 1); +} + +.icon-lg { + width: 3.6rem; + height: 3.6rem; +} + +.icon-lg i, +.icon-lg svg { + font-size: 2.85rem; +} + +.icon-lg+.icon-text { + width: calc(100% - $icon-size-lg - 1); +} + +.icon-sm { + width: 1.875rem; + height: 1.875rem; +} + +.icon-sm i, +.icon-sm svg { + font-size: 1.125rem; +} + +.icon-sm+.icon-text { + width: calc(100% - $icon-size-sm - 1); +} + +.icon-shape { + padding: 12px; + text-align: center; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.icon-shape i, +.icon-shape svg { + font-size: 1.25rem; +} + +.icon-shape.icon-lg i, +.icon-shape.icon-lg svg { + font-size: 1.625rem; +} + +.icon-shape.icon-sm i, +.icon-shape.icon-sm svg { + font-size: .875rem; +} + +.icon-shape svg { + width: 30px; + height: 30px; +} + +.icon-shape-primary { + color: #e418c4; + background-color: rgba(232, 122, 215, 0.5); +} + +.icon-shape-secondary { + color: #d3d9e5; + background-color: rgba(255, 255, 255, 0.5); +} + +.icon-shape-success { + color: #00bf9a; + background-color: rgba(38, 255, 213, 0.5); +} + +.icon-shape-info { + color: #0073e2; + background-color: rgba(78, 165, 250, 0.5); +} + +.icon-shape-warning { + color: #ff643f; + background-color: rgba(255, 182, 165, 0.5); +} + +.icon-shape-danger { + color: #ff2871; + background-color: rgba(254, 143, 181, 0.5); +} + +.icon-shape-light { + color: #879cb0; + background-color: rgba(201, 207, 212, 0.5); +} + +.icon-shape-dark { + color: #090c0e; + background-color: rgba(56, 63, 69, 0.5); +} + +.icon-shape-default { + color: #1e2e58; + background-color: rgba(68, 91, 152, 0.5); +} + +.icon-shape-white { + color: #e8e3e3; + background-color: rgba(255, 255, 255, 0.5); +} + +.icon-shape-neutral { + color: #e8e3e3; + background-color: rgba(255, 255, 255, 0.5); +} + +.icon-shape-darker { + color: black; + background-color: rgba(26, 26, 26, 0.5); +} + +img { + max-width: 100%; + border-radius: 0.2857rem; +} + +.img-raised { + box-shadow: 0px 10px 25px 0px rgba(0, 0, 0, 0.3); +} + +.form-group.has-danger .error, +.input-group.has-danger .error { + color: #ec250d; +} + +.form-group.has-success .error, +.input-group.has-success .error { + color: #00f2c3; +} + +.form-group.no-border.form-control-lg .input-group-append .input-group-text, +.input-group.no-border.form-control-lg .input-group-append .input-group-text { + padding: 15px 0 15px 19px; +} + +.form-group.no-border.form-control-lg .form-control, +.input-group.no-border.form-control-lg .form-control { + padding: 15px 19px; +} + +.form-group.no-border.form-control-lg .form-control+.input-group-prepend .input-group-text, +.form-group.no-border.form-control-lg .form-control+.input-group-append .input-group-text, +.input-group.no-border.form-control-lg .form-control+.input-group-prepend .input-group-text, +.input-group.no-border.form-control-lg .form-control+.input-group-append .input-group-text { + padding: 15px 19px 15px 0; +} + +.form-group.form-control-lg .form-control, +.input-group.form-control-lg .form-control { + padding: 14px 18px; + height: 100%; +} + +.form-group.form-control-lg .form-control+.input-group-prepend .input-group-text, +.form-group.form-control-lg .form-control+.input-group-append .input-group-text, +.input-group.form-control-lg .form-control+.input-group-prepend .input-group-text, +.input-group.form-control-lg .form-control+.input-group-append .input-group-text { + padding: 14px 18px 14px 0; +} + +.form-group.form-control-lg .input-group-prepend .input-group-text, +.form-group.form-control-lg .input-group-append .input-group-text, +.input-group.form-control-lg .input-group-prepend .input-group-text, +.input-group.form-control-lg .input-group-append .input-group-text { + padding: 14px 0 15px 18px; +} + +.form-group.form-control-lg .input-group-prepend .input-group-text+.form-control, +.form-group.form-control-lg .input-group-append .input-group-text+.form-control, +.input-group.form-control-lg .input-group-prepend .input-group-text+.form-control, +.input-group.form-control-lg .input-group-append .input-group-text+.form-control { + padding: 15px 18px 15px 16px; +} + +.form-group.no-border .form-control, +.input-group.no-border .form-control { + padding: 11px 19px; +} + +.form-group.no-border .form-control+.input-group-prepend .input-group-text, +.form-group.no-border .form-control+.input-group-append .input-group-text, +.input-group.no-border .form-control+.input-group-prepend .input-group-text, +.input-group.no-border .form-control+.input-group-append .input-group-text { + padding: 11px 19px 11px 0; +} + +.form-group.no-border .input-group-prepend .input-group-text, +.form-group.no-border .input-group-append .input-group-text, +.input-group.no-border .input-group-prepend .input-group-text, +.input-group.no-border .input-group-append .input-group-text { + padding: 11px 0 11px 19px; +} + +.form-group .form-control, +.input-group .form-control { + padding: 10px 18px 10px 18px; +} + +.form-group .form-control+.input-group-prepend .input-group-text, +.form-group .form-control+.input-group-append .input-group-text, +.input-group .form-control+.input-group-prepend .input-group-text, +.input-group .form-control+.input-group-append .input-group-text { + padding: 10px 18px 10px 0; +} + +.form-group .input-group-prepend .input-group-text, +.form-group .input-group-append .input-group-text, +.input-group .input-group-prepend .input-group-text, +.input-group .input-group-append .input-group-text { + padding: 10px 0 10px 18px; +} + +.form-group .input-group-prepend .input-group-text+.form-control, +.form-group .input-group-prepend .input-group-text~.form-control, +.form-group .input-group-append .input-group-text+.form-control, +.form-group .input-group-append .input-group-text~.form-control, +.input-group .input-group-prepend .input-group-text+.form-control, +.input-group .input-group-prepend .input-group-text~.form-control, +.input-group .input-group-append .input-group-text+.form-control, +.input-group .input-group-append .input-group-text~.form-control { + padding: 10px 19px 11px 16px; +} + +.form-group.no-border .form-control, +.form-group.no-border .form-control+.input-group-prepend .input-group-text, +.form-group.no-border .form-control+.input-group-append .input-group-text, +.input-group.no-border .form-control, +.input-group.no-border .form-control+.input-group-prepend .input-group-text, +.input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: #242c45; + border: medium none; +} + +.form-group.no-border .form-control:focus, +.form-group.no-border .form-control:active, +.form-group.no-border .form-control:active, +.form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.form-group.no-border .form-control+.input-group-append .input-group-text:active, +.form-group.no-border .form-control+.input-group-append .input-group-text:active, +.input-group.no-border .form-control:focus, +.input-group.no-border .form-control:active, +.input-group.no-border .form-control:active, +.input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.input-group.no-border .form-control+.input-group-append .input-group-text:active, +.input-group.no-border .form-control+.input-group-append .input-group-text:active { + border: medium none; + background-color: #252e49; +} + +.form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: #252e49; +} + +.form-group.no-border .input-group-prepend .input-group-text, +.form-group.no-border .input-group-append .input-group-text, +.input-group.no-border .input-group-prepend .input-group-text, +.input-group.no-border .input-group-append .input-group-text { + background-color: #242c45; + border: none; +} + +.has-error .form-control-feedback, +.has-error .control-label { + color: #ec250d; +} + +.has-success .form-control-feedback, +.has-success .control-label { + color: #00f2c3; +} + +.input-group-append .input-group-text, +.input-group-prepend .input-group-text { + background-color: transparent; + border: 1px solid #2b3553; + border-radius: 0.4285rem; + color: #ffffff; + -webkit-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -moz-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -o-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + -ms-transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; + transition: color 0.3s ease-in-out, border-color 0.3s ease-in-out, background-color 0.3s ease-in-out; +} + +.input-group-append .input-group-text i, +.input-group-prepend .input-group-text i { + opacity: .5; +} + +.has-danger.input-group-focus .input-group-append .input-group-text, +.has-danger.input-group-focus .input-group-prepend .input-group-text { + background-color: transparent; +} + +.has-success .input-group-append .input-group-text, +.has-success .input-group-prepend .input-group-text { + background-color: transparent; +} + +.has-danger .form-control:focus+.input-group-append .input-group-text, +.has-danger .form-control:focus+.input-group-prepend .input-group-text { + color: #ec250d; +} + +.has-success .form-control:focus+.input-group-append .input-group-text, +.has-success .form-control:focus+.input-group-prepend .input-group-text { + color: #00f2c3; +} + +.input-group-append .input-group-text+.form-control, +.input-group-append .input-group-text~.form-control, +.input-group-prepend .input-group-text+.form-control, +.input-group-prepend .input-group-text~.form-control { + padding: -0.5rem 0.7rem; + padding-left: 18px; +} + +.input-group-append .input-group-text i, +.input-group-prepend .input-group-text i { + width: 17px; +} + +.input-group-append, +.input-group-prepend .input-group-text, +.input-group-prepend .input-group-text { + background-color: transparent; + border: 1px solid #2b3553; + border-radius: 0.4285rem; + color: #ffffff; + margin: 0; +} + +.input-group-append .input-group-text { + border-left: none; +} + +.input-group-prepend .input-group-text { + border-right: none; +} + +.input-group-focus .input-group-prepend .input-group-text, +.input-group-focus .input-group-append .input-group-text { + background-color: #ffffff; + border-color: #e14eca; + background-color: transparent; + border-color: #e14eca; +} + +.input-group-focus.no-border .input-group-prepend .input-group-text, +.input-group-focus.no-border .input-group-append .input-group-text { + background-color: #252e49; +} + +.input-group, +.form-group { + margin-bottom: 10px; + position: relative; +} + +.input-group .form-control-static, +.form-group .form-control-static { + margin-top: 9px; +} + +.input-group[disabled] .input-group-prepend .input-group-text, +.input-group[disabled] .input-group-append .input-group-text { + background-color: #E3E3E3; +} + +.input-group .form-control:not(:first-child):not(:last-child), +.input-group-btn:not(:first-child):not(:last-child) { + border-radius: 0.4285rem; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + border-left: 0 none; +} + +.input-group .form-control:first-child, +.input-group-btn:first-child>.dropdown-toggle, +.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle) { + border-right: 0 none; +} + +.input-group .form-control:last-child, +.input-group-btn:last-child>.dropdown-toggle, +.input-group-btn:first-child>.btn:not(:first-child) { + border-left: 0 none; +} + +.form-control[disabled], +.form-control[readonly], +fieldset[disabled] .form-control { + background-color: #1d253b; + color: #344675; + cursor: not-allowed; +} + +.input-group-btn .btn { + border-width: 1px; + padding: 11px 0.7rem; +} + +.input-group-btn .btn-default:not(.btn-fill) { + border-color: #DDDDDD; +} + +.input-group-btn:last-child>.btn { + margin-left: 0; +} + +textarea.form-control { + max-width: 100%; + max-height: 80px; + padding: 10px 10px 0 0; + resize: none; + border: none; + border-bottom: 1px solid #2b3553; + border-radius: 0; + line-height: 2; +} + +textarea.form-control:focus, +textarea.form-control:active { + border-left: none; + border-top: none; + border-right: none; +} + +.has-success.form-group .form-control, +.has-success.form-group.no-border .form-control, +.has-danger.form-group .form-control, +.has-danger.form-group.no-border .form-control { + padding-right: 40px; +} + +.form.form-newsletter .form-group { + float: left; + width: 78%; + margin-right: 2%; + margin-top: 9px; +} + +.input-group .input-group-btn { + padding: 0 12px; +} + +.form-group input[type=file] { + opacity: 0; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 100; +} + +.form-text { + font-size: 0.75rem; + color: rgba(255, 255, 255, 0.8); +} + +.form-control-lg { + padding: 0; + font-size: inherit; + line-height: 0; + border-radius: 0; +} + +.form-control-lg .form-control { + height: calc(2.875rem + 2px); +} + +.form-horizontal .col-form-label, +.form-horizontal .label-on-right { + padding: 10px 5px 0 15px; + text-align: right; + max-width: 180px; +} + +.form-horizontal .checkbox-radios { + margin-bottom: 15px; +} + +.form-horizontal .checkbox-radios .form-check:first-child { + margin-top: 8px; +} + +.form-horizontal .label-on-right { + text-align: left; + padding: 10px 15px 0 5px; +} + +.form-horizontal .form-check-inline { + margin-top: 6px; +} + +.form-horizontal .form-check-inline .form-check-label { + margin-right: 1.5rem; +} + +.search-bar { + margin-left: 30px; +} + +.search-bar .btn { + margin: 0; +} + +.search-bar.input-group { + border-radius: 25px; + z-index: 4; + margin-bottom: 0; + height: 43px; +} + +.search-bar.input-group .input-group-addon { + padding: 10px; + background: transparent; + border: none; + color: rgba(255, 255, 255, 0.7); +} + +.search-bar.input-group i { + font-size: 20px; + color: white; + margin-top: 0 !important; +} + +.search-bar.input-group input { + background: transparent; + border: none !important; + border-radius: 0; + padding: 12px !important; + font-size: 12px; + opacity: 0.5; +} + +.search-bar.input-group input:focus { + background: transparent; +} + +.search-bar.input-group .form-control { + opacity: 1; + color: #ffffff; +} + +.search-bar.input-group .form-control::placeholder { + color: white; +} + +.modal-search .modal-dialog { + max-width: 1000px; + margin: 20px auto; +} + +.modal-search .modal-dialog .form-control { + border: none; + color: #222a42; +} + +.modal-search .modal-dialog .form-control::placeholder { + color: #222a42; +} + +.input-group-prepend { + margin-right: 0; +} + +.input-group-prepend .tim-icons, +.input-group-append .tim-icons { + font-size: 1rem; +} + +.info.info-hover .info-title { + transition: color .4s; +} + +.info.info-hover:hover .icon { + -webkit-transform: translate3d(0, -0.5rem, 0); + -moz-transform: translate3d(0, -0.5rem, 0); + -o-transform: translate3d(0, -0.5rem, 0); + -ms-transform: translate3d(0, -0.5rem, 0); + transform: translate3d(0, -0.5rem, 0); +} + +.info.info-hover:hover .icon.icon-primary.icon-circle { + box-shadow: 0px 15px 30px 0px rgba(249, 99, 50, 0.3); +} + +.info.info-hover:hover .icon.icon-info.icon-circle { + box-shadow: 0px 15px 35px 0px rgba(44, 168, 255, 0.3); +} + +.info.info-hover:hover .icon.icon-success.icon-circle { + box-shadow: 0px 15px 35px 0px rgba(24, 206, 15, 0.3); +} + +.info.info-hover:hover .icon.icon-warning.icon-circle { + box-shadow: 0px 15px 35px 0px rgba(255, 178, 54, 0.3); +} + +.info.info-hover:hover .icon.icon-danger.icon-circle { + box-shadow: 0px 15px 35px 0px rgba(255, 54, 54, 0.3); +} + +.info.info-hover:hover .icon.icon-info+.info-title { + color: #1d8cf8; +} + +.info.info-hover:hover .icon.icon-warning+.info-title { + color: #ff8d72; +} + +.info.info-hover:hover .icon.icon-danger+.info-title { + color: #fd5d93; +} + +.info.info-hover:hover .icon.icon-primary+.info-title { + color: #e14eca; +} + +.info.info-hover:hover .icon.icon-success+.info-title { + color: #00f2c3; +} + +.info .icon { + color: #344675; + transition: transform .4s, box-shadow .4s; +} + +.info .icon>i { + font-size: 2.3em; +} + +.info .icon.icon-circle { + max-width: 70px; + width: 70px; + height: 70px; + margin: 0 auto; + border-radius: 50%; + box-shadow: 0px 9px 35px -6px rgba(0, 0, 0, 0.3); + font-size: 0.62475rem; + background-color: #ffffff; + position: relative; +} + +.info .icon.icon-circle i { + line-height: 2.6em; +} + +.info .info-title { + margin: 15px 0 5px; + padding: 0 15px; + color: #222a42; + font-weight: 600; +} + +.info p { + color: #344675; + padding: 0 15px; + font-size: 1.1em; +} + +.info-horizontal { + text-align: left !important; +} + +.info-horizontal .icon { + float: left; + margin-top: 23px; + margin-right: 10px; +} + +.info-horizontal .icon>i { + font-size: 2em; +} + +.info-horizontal .icon.icon-circle { + width: 65px; + height: 65px; + max-width: 65px; + margin-top: 8px; +} + +.info-horizontal .icon.icon-circle i { + display: table; + margin: 0 auto; + line-height: 3.5; + font-size: 1.9em; +} + +.info-horizontal .description { + overflow: hidden; +} + +.icon.icon-primary { + color: #e14eca; +} + +.icon.icon-primary.icon-circle { + box-shadow: 0px 9px 30px -6px rgba(225, 78, 202, 0.5); +} + +.icon.icon-info { + color: #1d8cf8; +} + +.icon.icon-info.icon-circle { + box-shadow: 0px 9px 30px -6px rgba(29, 140, 248, 0.5); +} + +.icon.icon-success { + color: #00f2c3; +} + +.icon.icon-success.icon-circle { + box-shadow: 0px 9px 30px -6px rgba(0, 242, 195, 0.5); +} + +.icon.icon-warning { + color: #ff8d72; +} + +.icon.icon-warning.icon-circle { + box-shadow: 0px 9px 30px -6px rgba(255, 141, 114, 0.5); +} + +.icon.icon-danger { + color: #fd5d93; +} + +.icon.icon-danger.icon-circle { + box-shadow: 0px 9px 30px -6px rgba(253, 93, 147, 0.5); +} + +.icon.icon-white { + color: #ffffff; +} + +.modal-content { + border: 0; +} + +.modal-content .modal-header { + border-bottom: none; +} + +.modal-content .modal-header button { + position: absolute; + right: 27px; + top: 24px; + outline: 0; + padding: 1rem; + margin: -1rem -1rem -1rem auto; +} + +.modal-content .modal-header .title { + color: #222a42; + margin-top: 5px; + margin-bottom: 0; +} + +.modal-content .modal-header .modal-title { + color: #222a42; +} + +.modal-content .modal-header i.tim-icons { + font-size: 16px; +} + +.modal-content .modal-body { + line-height: 1.9; +} + +.modal-content .modal-body p { + color: #222a42; +} + +.modal-content .modal-footer { + border-top: 0; + -webkit-justify-content: space-between; + /* Safari 6.1+ */ + justify-content: space-between; +} + +.modal-content .modal-footer button { + margin: 0; + padding-left: 16px; + padding-right: 16px; + width: auto; +} + +.modal-content .modal-footer button.pull-left { + padding-left: 5px; + padding-right: 5px; + position: relative; + left: -5px; +} + +.modal-content .modal-body+.modal-footer { + padding-top: 0; +} + +.modal-backdrop { + background: rgba(0, 0, 0, 0.3); +} + +.modal .modal-login { + max-width: 320px; +} + +.modal .modal-login .card-login .logo-container { + width: 65px; + margin-bottom: 38px; + margin-top: 27px; +} + +.modal.modal-mini p { + text-align: center; +} + +.modal.modal-mini .modal-dialog { + max-width: 255px; + margin: 0 auto; +} + +.modal.modal-mini.show .modal-dialog { + -webkit-transform: translate(0, 30%); + -o-transform: translate(0, 30%); + transform: translate(0, 30%); +} + +.modal.modal-mini .modal-footer button { + text-transform: uppercase; + color: #ffffff; +} + +.modal.modal-mini .modal-footer button:first-child { + opacity: .5; +} + +.modal .modal-profile { + width: 70px; + height: 70px; + background-color: #ffffff; + border-radius: 50%; + text-align: center; + line-height: 5.7; + box-shadow: 0px 5px 50px 0px rgba(0, 0, 0, 0.3); +} + +.modal .modal-profile i { + color: #e14eca; + font-size: 21px; + margin-top: -10px; +} + +.modal .modal-profile[class*="modal-profile-"] i { + color: #ffffff; +} + +.modal .modal-profile.modal-profile-primary { + background-color: #e14eca; +} + +.modal .modal-profile.modal-profile-danger { + background-color: #fd5d93; +} + +.modal .modal-profile.modal-profile-warning { + background-color: #ff8d72; +} + +.modal .modal-profile.modal-profile-success { + background-color: #00f2c3; +} + +.modal .modal-profile.modal-profile-info { + background-color: #1d8cf8; +} + +.modal.modal-default .modal-content { + background-color: #ffffff; + color: #222a42; +} + +.modal.modal-default .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-default .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-default .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-default .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-default .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-default .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-default .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-default .has-success:after, +.modal.modal-default .has-danger:after { + color: #ffffff; +} + +.modal.modal-default .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-default .input-group-prepend { + margin-right: 0; +} + +.modal.modal-default .input-group-prepend .input-group-text, +.modal.modal-default .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-default .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-default .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-default .form-group.no-border .form-control, +.modal.modal-default .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-default .form-group.no-border .form-control:focus, +.modal.modal-default .form-group.no-border .form-control:active, +.modal.modal-default .form-group.no-border .form-control:active, +.modal.modal-default .input-group.no-border .form-control:focus, +.modal.modal-default .input-group.no-border .form-control:active, +.modal.modal-default .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-default .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-default .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-default .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-default .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-default .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-default .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-default .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-default .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-default .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-default .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-default .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-default .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-default .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-default .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-default .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-default .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-default .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-default .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-default .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-default .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-default .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-default .form-group.no-border .input-group-append .input-group-text, +.modal.modal-default .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-default .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-default .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-default .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-default .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-default .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-primary .modal-content { + background-color: #e14eca; + color: #ffffff; +} + +.modal.modal-primary .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-primary .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-primary .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-primary .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-primary .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-primary .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-primary .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-primary .has-success:after, +.modal.modal-primary .has-danger:after { + color: #ffffff; +} + +.modal.modal-primary .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-primary .input-group-prepend { + margin-right: 0; +} + +.modal.modal-primary .input-group-prepend .input-group-text, +.modal.modal-primary .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-primary .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-primary .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border .form-control, +.modal.modal-primary .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border .form-control:focus, +.modal.modal-primary .form-group.no-border .form-control:active, +.modal.modal-primary .form-group.no-border .form-control:active, +.modal.modal-primary .input-group.no-border .form-control:focus, +.modal.modal-primary .input-group.no-border .form-control:active, +.modal.modal-primary .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-primary .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-primary .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-primary .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-primary .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-primary .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-primary .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-primary .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-primary .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-primary .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-primary .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-primary .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-primary .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-primary .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-primary .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-primary .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-primary .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-primary .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-primary .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-primary .form-group.no-border .input-group-append .input-group-text, +.modal.modal-primary .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-primary .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-primary .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-primary .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-primary .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-primary .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-danger .modal-content { + background-color: #fd5d93; + color: #ffffff; +} + +.modal.modal-danger .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-danger .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-danger .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-danger .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-danger .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-danger .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-danger .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-danger .has-success:after, +.modal.modal-danger .has-danger:after { + color: #ffffff; +} + +.modal.modal-danger .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-danger .input-group-prepend { + margin-right: 0; +} + +.modal.modal-danger .input-group-prepend .input-group-text, +.modal.modal-danger .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-danger .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-danger .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border .form-control, +.modal.modal-danger .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border .form-control:focus, +.modal.modal-danger .form-group.no-border .form-control:active, +.modal.modal-danger .form-group.no-border .form-control:active, +.modal.modal-danger .input-group.no-border .form-control:focus, +.modal.modal-danger .input-group.no-border .form-control:active, +.modal.modal-danger .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-danger .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-danger .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-danger .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-danger .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-danger .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-danger .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-danger .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-danger .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-danger .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-danger .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-danger .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-danger .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-danger .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-danger .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-danger .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-danger .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-danger .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-danger .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-danger .form-group.no-border .input-group-append .input-group-text, +.modal.modal-danger .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-danger .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-danger .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-danger .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-danger .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-danger .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-warning .modal-content { + background-color: #ff8d72; + color: #ffffff; +} + +.modal.modal-warning .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-warning .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-warning .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-warning .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-warning .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-warning .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-warning .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-warning .has-success:after, +.modal.modal-warning .has-danger:after { + color: #ffffff; +} + +.modal.modal-warning .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-warning .input-group-prepend { + margin-right: 0; +} + +.modal.modal-warning .input-group-prepend .input-group-text, +.modal.modal-warning .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-warning .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-warning .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border .form-control, +.modal.modal-warning .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border .form-control:focus, +.modal.modal-warning .form-group.no-border .form-control:active, +.modal.modal-warning .form-group.no-border .form-control:active, +.modal.modal-warning .input-group.no-border .form-control:focus, +.modal.modal-warning .input-group.no-border .form-control:active, +.modal.modal-warning .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-warning .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-warning .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-warning .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-warning .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-warning .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-warning .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-warning .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-warning .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-warning .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-warning .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-warning .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-warning .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-warning .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-warning .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-warning .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-warning .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-warning .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-warning .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-warning .form-group.no-border .input-group-append .input-group-text, +.modal.modal-warning .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-warning .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-warning .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-warning .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-warning .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-warning .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-success .modal-content { + background-color: #00f2c3; + color: #ffffff; +} + +.modal.modal-success .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-success .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-success .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-success .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-success .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-success .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-success .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-success .has-success:after, +.modal.modal-success .has-danger:after { + color: #ffffff; +} + +.modal.modal-success .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-success .input-group-prepend { + margin-right: 0; +} + +.modal.modal-success .input-group-prepend .input-group-text, +.modal.modal-success .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-success .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-success .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-success .form-group.no-border .form-control, +.modal.modal-success .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-success .form-group.no-border .form-control:focus, +.modal.modal-success .form-group.no-border .form-control:active, +.modal.modal-success .form-group.no-border .form-control:active, +.modal.modal-success .input-group.no-border .form-control:focus, +.modal.modal-success .input-group.no-border .form-control:active, +.modal.modal-success .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-success .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-success .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-success .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-success .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-success .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-success .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-success .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-success .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-success .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-success .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-success .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-success .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-success .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-success .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-success .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-success .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-success .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-success .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-success .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-success .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-success .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-success .form-group.no-border .input-group-append .input-group-text, +.modal.modal-success .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-success .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-success .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-success .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-success .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-success .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-info .modal-content { + background-color: #1d8cf8; + color: #ffffff; +} + +.modal.modal-info .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-info .form-control::-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-info .form-control:-moz-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-info .form-control::-webkit-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-info .form-control:-ms-input-placeholder { + color: rgba(255, 255, 255, 0.4); + opacity: 1; + filter: alpha(opacity=100); +} + +.modal.modal-info .form-control { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-info .form-control:focus { + border-color: #ffffff; + background-color: transparent; + color: #ffffff; +} + +.modal.modal-info .has-success:after, +.modal.modal-info .has-danger:after { + color: #ffffff; +} + +.modal.modal-info .has-danger .form-control { + background-color: transparent; +} + +.modal.modal-info .input-group-prepend { + margin-right: 0; +} + +.modal.modal-info .input-group-prepend .input-group-text, +.modal.modal-info .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.modal.modal-info .input-group-focus .input-group-prepend .input-group-text, +.modal.modal-info .input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + border-color: #ffffff; + color: #ffffff; +} + +.modal.modal-info .form-group.no-border .form-control, +.modal.modal-info .input-group.no-border .form-control { + background-color: rgba(30, 30, 47, 0.2); + color: #ffffff; +} + +.modal.modal-info .form-group.no-border .form-control:focus, +.modal.modal-info .form-group.no-border .form-control:active, +.modal.modal-info .form-group.no-border .form-control:active, +.modal.modal-info .input-group.no-border .form-control:focus, +.modal.modal-info .input-group.no-border .form-control:active, +.modal.modal-info .input-group.no-border .form-control:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-info .form-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-info .form-group.no-border .form-control+.input-group-append .input-group-text, +.modal.modal-info .input-group.no-border .form-control+.input-group-prepend .input-group-text, +.modal.modal-info .input-group.no-border .form-control+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); +} + +.modal.modal-info .form-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-info .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-info .form-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-info .form-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-info .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-info .form-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-info .input-group.no-border .form-control+.input-group-prepend .input-group-text:focus, +.modal.modal-info .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-info .input-group.no-border .form-control+.input-group-prepend .input-group-text:active, +.modal.modal-info .input-group.no-border .form-control+.input-group-append .input-group-text:focus, +.modal.modal-info .input-group.no-border .form-control+.input-group-append .input-group-text:active, +.modal.modal-info .input-group.no-border .form-control+.input-group-append .input-group-text:active { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-info .form-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-info .form-group.no-border .form-control:focus+.input-group-append .input-group-text, +.modal.modal-info .input-group.no-border .form-control:focus+.input-group-prepend .input-group-text, +.modal.modal-info .input-group.no-border .form-control:focus+.input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal.modal-info .form-group.no-border .input-group-prepend .input-group-text, +.modal.modal-info .form-group.no-border .input-group-append .input-group-text, +.modal.modal-info .input-group.no-border .input-group-prepend .input-group-text, +.modal.modal-info .input-group.no-border .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.2); + border: none; + color: #ffffff; +} + +.modal.modal-info .form-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-info .form-group.no-border.input-group-focus .input-group-append .input-group-text, +.modal.modal-info .input-group.no-border.input-group-focus .input-group-prepend .input-group-text, +.modal.modal-info .input-group.no-border.input-group-focus .input-group-append .input-group-text { + background-color: rgba(30, 30, 47, 0.3); + color: #ffffff; +} + +.modal .modal-header .close { + color: #fd5d93; + text-shadow: none; +} + +.modal .modal-header .close:hover, +.modal .modal-header .close:focus { + opacity: 1; +} + +.modal.modal-black .modal-content { + background: linear-gradient(to bottom, #222a42 0%, #1d253b 100%); + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-black .modal-content .modal-header .modal-title, +.modal.modal-black .modal-content .modal-header .title { + color: rgba(255, 255, 255, 0.9); +} + +.modal.modal-black .modal-content .modal-body p { + color: rgba(255, 255, 255, 0.8); +} + +.modal.modal-black h1, +.modal.modal-black h2, +.modal.modal-black h3, +.modal.modal-black h4, +.modal.modal-black h5, +.modal.modal-black h6, +.modal.modal-black p { + color: #ffffff; +} + +.modal-search .modal-dialog { + margin: 20px auto; + max-width: 650px; +} + +.modal-search .modal-dialog input { + border: none; + font-size: 17px; + font-weight: 100; +} + +.modal-search .modal-dialog span { + font-size: 35px; + color: #b7b7b7; +} + +.modal-search .modal-content .modal-header { + padding: 24px; +} + +.modal-search .modal-header .close { + color: #555555; + top: 30px !important; +} + +.modal-search .modal-footer { + border-top: 2px solid #f9f9f9; + margin: 0px 25px 20px; +} + +@media (max-width: 575.98px) { + .modal.show .modal-dialog { + transform: translateY(10%); + } + .modal.show .modal-dialog.modal-notice { + transform: translateY(3%); + } +} + +.nav-link { + color: #525f7f; +} + +.nav-link:hover { + color: #e14eca; +} + +.nav-pills .nav-link { + padding: 0.75rem 1rem; + color: #e14eca; + font-weight: 500; + background-color: #ffffff; + transition: all 0.15s ease; +} + +.nav-pills .nav-link:hover { + color: #dd38c3; +} + +.nav-pills .nav-link.active, +.nav-pills .show>.nav-link { + color: #ffffff; + background-color: #e14eca; +} + +@media (max-width: 767.98px) { + .nav-pills:not(.nav-pills-circle) .nav-item { + padding-right: 0; + } +} + +.nav-pills-circle .nav-link { + text-align: center; + height: 60px; + width: 60px; + padding: 0; + line-height: 60px; + border-radius: 50%; +} + +.nav-pills-circle .nav-link-icon i, +.nav-pills-circle .nav-link-icon svg { + font-size: 1rem; +} + +.nav-wrapper { + padding: 1rem 0; + border-top-left-radius: 0.2857rem; + border-top-right-radius: 0.2857rem; +} + +.nav-wrapper+.card { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-right-radius: 0.2857rem; + border-bottom-left-radius: 0.2857rem; +} + +@media (max-width: 767.98px) { + .nav-tabs { + display: inline-block; + width: 100%; + padding-left: 100px; + padding-right: 100px; + text-align: center; + } + .nav-tabs .nav-item>.nav-link { + margin-bottom: 5px; + } +} + +.navbar { + width: 100%; + z-index: 1050; + background: #1a1e34; +} + +.navbar .photo { + display: inline-block; + height: 30px; + width: 30px; + border-radius: 50%; + vertical-align: middle; + overflow: hidden; +} + +.navbar .photo img { + width: 100%; +} + +.navbar .navbar-wrapper { + display: flex; + align-items: center; +} + +.navbar .navbar-text { + color: #ffffff; +} + +.navbar .btn { + margin: 0 5px 0 10px; +} + +.navbar.navbar-absolute { + position: absolute; + z-index: 1050; +} + +.navbar.navbar-transparent { + background: transparent !important; +} + +.navbar.bg-white .navbar-nav .search-bar.input-group i { + color: #222a42; +} + +.navbar.bg-white .navbar-nav .search-bar.input-group .form-control { + background: rgba(34, 42, 66, 0.1); + border-radius: 4px; +} + +.navbar.bg-white .navbar-nav a.nav-link { + color: #222a42 !important; +} + +.navbar.bg-white .navbar-nav a.nav-link p { + color: #222a42; +} + +.navbar.bg-white .navbar-text, +.navbar.bg-white .navbar-brand { + color: #222a42; +} + +.navbar.bg-white .form-control { + color: #222a42 !important; +} + +.navbar.bg-white .form-control::placeholder { + color: #9A9A9A !important; +} + +.navbar.bg-dark { + background: #222a42 !important; +} + +.navbar.bg-primary { + background-color: #e14eca !important; +} + +.navbar.bg-warning { + background-color: #ff8d72 !important; +} + +.navbar.bg-info { + background-color: #1d8cf8 !important; +} + +.navbar.bg-success { + background-color: #00bf9a !important; +} + +.navbar.bg-danger { + background-color: #fd5d93 !important; +} + +.navbar .navbar-brand { + position: relative; + padding-top: .3125rem; + padding-bottom: .3125rem; + color: #ffffff; + margin-left: 17px; + text-transform: uppercase; + font-size: 1rem; +} + +.navbar .navbar-toggle button:focus, +.navbar .navbar-toggler { + outline: none; +} + +.navbar-minimize-fixed { + position: fixed; + margin-left: 40px; + margin-top: 14px; + transition: 0.3s ease; + color: white; + z-index: 20; + opacity: 0; + transition: 0.2s ease; +} + +.navbar-minimize-fixed button i { + font-size: 20px; +} + +.notification { + background: #fd5d93; + color: #ffffff; + border-radius: 0.875rem; + height: 6px; + width: 6px; + position: absolute; + text-align: center; + font-size: 12px; + font-weight: 800; + top: 10px; + right: 10px; + border: 1px solid #fd5d93; +} + +.navbar-nav li { + padding: 0 10px; +} + +.navbar-nav li a { + color: #ffffff; +} + +.navbar-nav li i { + vertical-align: middle; + font-size: 20px; +} + +@media (max-width: 991.98px) { + .navbar { + top: -70px; + } + .navbar .container-fluid { + padding-right: 15px; + padding-left: 15px; + } + .navbar .navbar-collapse .input-group { + margin: 0; + margin-top: 5px; + } + .navbar .navbar-nav .btn { + margin-left: -3px; + display: flex; + } + .navbar .navbar-nav .btn i { + margin-right: 12px; + } + .navbar .navbar-nav .btn span { + margin: 0; + text-transform: uppercase; + font-weight: 300; + } + .navbar .navbar-nav .btn span, + .navbar .navbar-nav .btn span:hover, + .navbar .navbar-nav .btn span:focus, + .navbar .navbar-nav .btn span:active, + .navbar .navbar-nav .btn span:active:focus { + color: #222a42 !important; + } + .navbar .navbar-nav a.nav-link i { + opacity: 1; + margin-left: 4px; + margin-right: 5px; + } + .navbar .navbar-nav a.nav-link p { + display: inline-block; + text-transform: uppercase; + margin-left: 7px; + } + .navbar .navbar-nav .modal-search .modal-dialog { + padding: 0 40px; + } + .navbar .navbar-nav .dropdown { + margin: 5px 0; + } + .navbar .navbar-nav .dropdown .nav-link { + padding-bottom: 0; + } + .navbar .navbar-nav .dropdown .dropdown-menu .dropdown-item { + margin-top: 0; + padding-left: 24px; + } + .navbar .dropdown.show .dropdown-menu { + display: block; + } + .navbar .dropdown .dropdown-menu { + display: none; + } + .navbar .dropdown .dropdown-menu li a { + color: #222a42; + } + .navbar .dropdown.show .dropdown-menu, + .navbar .dropdown .dropdown-menu { + background-color: transparent; + border: 0; + transition: none; + -webkit-box-shadow: none; + box-shadow: none; + width: auto; + margin: 0px 1rem; + padding-top: 0; + margin-top: 0px; + } + .navbar .dropdown.show .dropdown-menu:before, + .navbar .dropdown .dropdown-menu:before { + display: none; + } + .navbar .dropdown-menu .dropdown-item:focus, + .navbar .dropdown-menu .dropdown-item:hover { + color: #ffffff; + } + .navbar.bg-white .dropdown-menu .dropdown-item:focus, + .navbar.bg-white .dropdown-menu .dropdown-item:hover { + color: #344675; + } + .navbar .navbar-toggler-bar { + display: block; + position: relative; + width: 22px; + height: 1px; + border-radius: 1px; + background: #ffffff; + } + .navbar .navbar-toggler-bar.navbar-kebab { + height: 4px; + width: 4px; + margin-bottom: 3px; + border-radius: 50%; + } + .navbar .navbar-toggler-bar+.navbar-toggler-bar { + margin-top: 7px; + } + .navbar .navbar-toggler-bar+.navbar-toggler-bar.navbar-kebab { + margin-top: 0px; + } + .navbar .navbar-toggler-bar.bar2 { + width: 17px; + transition: width .2s linear; + } + .navbar.bg-white:not(.navbar-transparent) .navbar-toggler-bar { + background-color: #344675; + } + .navbar .toggled .navbar-toggler-bar { + width: 24px; + } + .bar1, + .bar2, + .bar3 { + outline: 1px solid transparent; + } + .bar1 { + top: 0px; + -webkit-animation: topbar-back 500ms linear 0s; + -moz-animation: topbar-back 500ms linear 0s; + animation: topbar-back 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .bar2 { + opacity: 1; + } + .bar3 { + bottom: 0px; + -webkit-animation: bottombar-back 500ms linear 0s; + -moz-animation: bottombar-back 500ms linear 0s; + animation: bottombar-back 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .toggled .bar1 { + top: 6px; + -webkit-animation: topbar-x 500ms linear 0s; + -moz-animation: topbar-x 500ms linear 0s; + animation: topbar-x 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .toggled .bar2 { + opacity: 0; + } + .toggled .bar3 { + bottom: 9px; + -webkit-animation: bottombar-x 500ms linear 0s; + -moz-animation: bottombar-x 500ms linear 0s; + animation: bottombar-x 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + @keyframes topbar-x { + 0% { + top: 0px; + transform: rotate(0deg); + } + 45% { + top: 6px; + transform: rotate(145deg); + } + 75% { + transform: rotate(130deg); + } + 100% { + transform: rotate(135deg); + } + } + @-webkit-keyframes topbar-x { + 0% { + top: 0px; + -webkit-transform: rotate(0deg); + } + 45% { + top: 6px; + -webkit-transform: rotate(145deg); + } + 75% { + -webkit-transform: rotate(130deg); + } + 100% { + -webkit-transform: rotate(135deg); + } + } + @-moz-keyframes topbar-x { + 0% { + top: 0px; + -moz-transform: rotate(0deg); + } + 45% { + top: 6px; + -moz-transform: rotate(145deg); + } + 75% { + -moz-transform: rotate(130deg); + } + 100% { + -moz-transform: rotate(135deg); + } + } + @keyframes topbar-back { + 0% { + top: 6px; + transform: rotate(135deg); + } + 45% { + transform: rotate(-10deg); + } + 75% { + transform: rotate(5deg); + } + 100% { + top: 0px; + transform: rotate(0); + } + } + @-webkit-keyframes topbar-back { + 0% { + top: 6px; + -webkit-transform: rotate(135deg); + } + 45% { + -webkit-transform: rotate(-10deg); + } + 75% { + -webkit-transform: rotate(5deg); + } + 100% { + top: 0px; + -webkit-transform: rotate(0); + } + } + @-moz-keyframes topbar-back { + 0% { + top: 6px; + -moz-transform: rotate(135deg); + } + 45% { + -moz-transform: rotate(-10deg); + } + 75% { + -moz-transform: rotate(5deg); + } + 100% { + top: 0px; + -moz-transform: rotate(0); + } + } + @keyframes bottombar-x { + 0% { + bottom: 0px; + transform: rotate(0deg); + } + 45% { + bottom: 6px; + transform: rotate(-145deg); + } + 75% { + transform: rotate(-130deg); + } + 100% { + transform: rotate(-135deg); + } + } + @-webkit-keyframes bottombar-x { + 0% { + bottom: 0px; + -webkit-transform: rotate(0deg); + } + 45% { + bottom: 6px; + -webkit-transform: rotate(-145deg); + } + 75% { + -webkit-transform: rotate(-130deg); + } + 100% { + -webkit-transform: rotate(-135deg); + } + } + @-moz-keyframes bottombar-x { + 0% { + bottom: 0px; + -moz-transform: rotate(0deg); + } + 45% { + bottom: 6px; + -moz-transform: rotate(-145deg); + } + 75% { + -moz-transform: rotate(-130deg); + } + 100% { + -moz-transform: rotate(-135deg); + } + } + @keyframes bottombar-back { + 0% { + bottom: 6px; + transform: rotate(-135deg); + } + 45% { + transform: rotate(10deg); + } + 75% { + transform: rotate(-5deg); + } + 100% { + bottom: 0px; + transform: rotate(0); + } + } + @-webkit-keyframes bottombar-back { + 0% { + bottom: 6px; + -webkit-transform: rotate(-135deg); + } + 45% { + -webkit-transform: rotate(10deg); + } + 75% { + -webkit-transform: rotate(-5deg); + } + 100% { + bottom: 0px; + -webkit-transform: rotate(0); + } + } + @-moz-keyframes bottombar-back { + 0% { + bottom: 6px; + -moz-transform: rotate(-135deg); + } + 45% { + -moz-transform: rotate(10deg); + } + 75% { + -moz-transform: rotate(-5deg); + } + 100% { + bottom: 0px; + -moz-transform: rotate(0); + } + } + @-webkit-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + @-moz-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + @keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + .navbar-nav .nav-link i.fa, + .navbar-nav .nav-link i.tim-icons { + opacity: .5; + } +} + +@media (min-width: 992px) { + .navbar-collapse { + background: none !important; + } + .navbar .navbar-toggle { + display: none; + } + .navbar-nav .nav-link.profile-photo { + padding: 0; + margin: 7px 0.7rem; + } + .navbar .caret { + position: absolute; + left: 80%; + top: 55%; + margin-left: 0; + } + .navbar-expand-lg .navbar-nav .nav-link { + padding-right: 0.5rem; + padding-left: 0.5rem; + } +} + +@media (max-width: 575.98px) { + .navbar[class*='navbar-toggleable-'] .container { + margin-left: 0; + margin-right: 0; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .bar1, + .bar2, + .bar3 { + outline: 1px solid transparent; + } + .bar1 { + top: 0px; + -webkit-animation: topbar-back 500ms linear 0s; + -moz-animation: topbar-back 500ms linear 0s; + animation: topbar-back 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .bar2 { + opacity: 1; + } + .bar3 { + bottom: 0px; + -webkit-animation: bottombar-back 500ms linear 0s; + -moz-animation: bottombar-back 500ms linear 0s; + animation: bottombar-back 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .toggled .bar1 { + top: 7px; + -webkit-animation: topbar-x 500ms linear 0s; + -moz-animation: topbar-x 500ms linear 0s; + animation: topbar-x 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + .toggled .bar2 { + opacity: 0; + } + .toggled .bar3 { + bottom: 9px; + -webkit-animation: bottombar-x 500ms linear 0s; + -moz-animation: bottombar-x 500ms linear 0s; + animation: bottombar-x 500ms 0s; + -webkit-animation-fill-mode: forwards; + -moz-animation-fill-mode: forwards; + animation-fill-mode: forwards; + } + @keyframes topbar-x { + 0% { + top: 0px; + transform: rotate(0deg); + } + 45% { + top: 6px; + transform: rotate(145deg); + } + 75% { + transform: rotate(130deg); + } + 100% { + transform: rotate(135deg); + } + } + @-webkit-keyframes topbar-x { + 0% { + top: 0px; + -webkit-transform: rotate(0deg); + } + 45% { + top: 6px; + -webkit-transform: rotate(145deg); + } + 75% { + -webkit-transform: rotate(130deg); + } + 100% { + -webkit-transform: rotate(135deg); + } + } + @-moz-keyframes topbar-x { + 0% { + top: 0px; + -moz-transform: rotate(0deg); + } + 45% { + top: 6px; + -moz-transform: rotate(145deg); + } + 75% { + -moz-transform: rotate(130deg); + } + 100% { + -moz-transform: rotate(135deg); + } + } + @keyframes topbar-back { + 0% { + top: 6px; + transform: rotate(135deg); + } + 45% { + transform: rotate(-10deg); + } + 75% { + transform: rotate(5deg); + } + 100% { + top: 0px; + transform: rotate(0); + } + } + @-webkit-keyframes topbar-back { + 0% { + top: 6px; + -webkit-transform: rotate(135deg); + } + 45% { + -webkit-transform: rotate(-10deg); + } + 75% { + -webkit-transform: rotate(5deg); + } + 100% { + top: 0px; + -webkit-transform: rotate(0); + } + } + @-moz-keyframes topbar-back { + 0% { + top: 6px; + -moz-transform: rotate(135deg); + } + 45% { + -moz-transform: rotate(-10deg); + } + 75% { + -moz-transform: rotate(5deg); + } + 100% { + top: 0px; + -moz-transform: rotate(0); + } + } + @keyframes bottombar-x { + 0% { + bottom: 0px; + transform: rotate(0deg); + } + 45% { + bottom: 6px; + transform: rotate(-145deg); + } + 75% { + transform: rotate(-130deg); + } + 100% { + transform: rotate(-135deg); + } + } + @-webkit-keyframes bottombar-x { + 0% { + bottom: 0px; + -webkit-transform: rotate(0deg); + } + 45% { + bottom: 6px; + -webkit-transform: rotate(-145deg); + } + 75% { + -webkit-transform: rotate(-130deg); + } + 100% { + -webkit-transform: rotate(-135deg); + } + } + @-moz-keyframes bottombar-x { + 0% { + bottom: 0px; + -moz-transform: rotate(0deg); + } + 45% { + bottom: 6px; + -moz-transform: rotate(-145deg); + } + 75% { + -moz-transform: rotate(-130deg); + } + 100% { + -moz-transform: rotate(-135deg); + } + } + @keyframes bottombar-back { + 0% { + bottom: 6px; + transform: rotate(-135deg); + } + 45% { + transform: rotate(10deg); + } + 75% { + transform: rotate(-5deg); + } + 100% { + bottom: 0px; + transform: rotate(0); + } + } + @-webkit-keyframes bottombar-back { + 0% { + bottom: 6px; + -webkit-transform: rotate(-135deg); + } + 45% { + -webkit-transform: rotate(10deg); + } + 75% { + -webkit-transform: rotate(-5deg); + } + 100% { + bottom: 0px; + -webkit-transform: rotate(0); + } + } + @-moz-keyframes bottombar-back { + 0% { + bottom: 6px; + -moz-transform: rotate(-135deg); + } + 45% { + -moz-transform: rotate(10deg); + } + 75% { + -moz-transform: rotate(-5deg); + } + 100% { + bottom: 0px; + -moz-transform: rotate(0); + } + } + @-webkit-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + @-moz-keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + @keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } + } + .navbar .navbar-toggler-bar.bar2 { + width: 17px; + transition: width .2s linear; + } + .navbar .navbar-toggler-bar { + display: block; + position: relative !important; + width: 22px; + height: 1px; + border-radius: 1px; + background: #ffffff; + } + .navbar .navbar-toggler-bar+.navbar-toggler-bar { + margin-top: 7px; + } + .navbar .navbar-toggle .navbar-toggler { + display: block; + } + .navbar-minimize-fixed { + display: none; + } +} + +.pagination .page-item .page-link { + border: 0; + border-radius: 30px !important; + transition: all .3s; + margin: 0 3px; + min-width: 30px; + text-align: center; + height: 30px; + line-height: 30px; + cursor: pointer; + text-transform: uppercase; + outline: none; +} + +.pagination .page-item .page-link:hover, +.pagination .page-item .page-link:focus { + background-color: rgba(255, 255, 255, 0.1); + color: #ffffff; + border: none; + box-shadow: none; +} + +.pagination .arrow-margin-left, +.pagination .arrow-margin-right { + position: absolute; +} + +.pagination .arrow-margin-right { + right: 0; +} + +.pagination .arrow-margin-left { + left: 0; +} + +.pagination .page-item.active>.page-link { + color: #ffffff; + box-shadow: 0 1px 20px 0px rgba(0, 0, 0, 0.1); +} + +.pagination .page-item.active>.page-link, +.pagination .page-item.active>.page-link:focus, +.pagination .page-item.active>.page-link:hover { + background: #e14eca; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-size: 210% 210%; + background-position: top right; + color: #ffffff; +} + +.pagination .page-item.disabled>.page-link { + opacity: .5; +} + +.pagination.pagination-info .page-item.active>.page-link, +.pagination.pagination-info .page-item.active>.page-link:focus, +.pagination.pagination-info .page-item.active>.page-link:hover { + background: #1d8cf8; + background-image: -webkit-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -o-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -moz-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-size: 210% 210%; + background-position: top right; +} + +.pagination.pagination-success .page-item.active>.page-link, +.pagination.pagination-success .page-item.active>.page-link:focus, +.pagination.pagination-success .page-item.active>.page-link:hover { + background: #00f2c3; + background-image: -webkit-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -o-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -moz-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-size: 210% 210%; + background-position: top right; +} + +.pagination.pagination-primary .page-item.active>.page-link, +.pagination.pagination-primary .page-item.active>.page-link:focus, +.pagination.pagination-primary .page-item.active>.page-link:hover { + background: #e14eca; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-size: 210% 210%; + background-position: top right; +} + +.pagination.pagination-warning .page-item.active>.page-link, +.pagination.pagination-warning .page-item.active>.page-link:focus, +.pagination.pagination-warning .page-item.active>.page-link:hover { + background: #ff8d72; + background-image: -webkit-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -o-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -moz-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-size: 210% 210%; + background-position: top right; +} + +.pagination.pagination-danger .page-item.active>.page-link, +.pagination.pagination-danger .page-item.active>.page-link:focus, +.pagination.pagination-danger .page-item.active>.page-link:hover { + background: #fd5d93; + background-image: -webkit-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -o-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -moz-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-size: 210% 210%; + background-position: top right; +} + +.pagination.pagination-neutral .page-item>.page-link { + color: #ffffff; +} + +.pagination.pagination-neutral .page-item>.page-link:focus, +.pagination.pagination-neutral .page-item>.page-link:hover { + background-color: rgba(255, 255, 255, 0.2); + color: #ffffff; +} + +.pagination.pagination-neutral .page-item.active>.page-link, +.pagination.pagination-neutral .page-item.active>.page-link:focus, +.pagination.pagination-neutral .page-item.active>.page-link:hover { + background-color: #ffffff; + border-color: #ffffff; + color: #e14eca; +} + +@media (max-width: 767.98px) { + div.dataTables_paginate ul.pagination .page-item:first-of-type, + div.dataTables_paginate ul.pagination .page-item:nth-of-type(2), + div.dataTables_paginate ul.pagination .page-item:nth-of-type(8), + div.dataTables_paginate ul.pagination .page-item:last-of-type { + display: none !important; + } +} + +.nav-tabs { + border: 0; + padding: 0.5rem 0.7rem; +} + +.nav-tabs>.nav-item>.nav-link { + color: #ffffff; + margin: 0; + margin-right: 5px; + background-color: transparent; + border-radius: 30px; + padding: 11px 23px; + line-height: 1.5; +} + +.nav-tabs>.nav-item>.nav-link:hover { + background-color: transparent; + border: 1px solid #191f31; + color: rgba(255, 255, 255, 0.8); +} + +.nav-tabs>.nav-item>.nav-link.active { + border: 1px solid #ffffff; + border-radius: 30px; +} + +.nav-tabs>.nav-item>.nav-link i.tim-icons { + font-size: 14px; + position: relative; + top: 1px; + margin-right: 3px; +} + +.nav-tabs>.nav-item.disabled>.nav-link, +.nav-tabs>.nav-item.disabled>.nav-link:hover { + color: rgba(255, 255, 255, 0.5); +} + +.nav-tabs.nav-tabs-neutral>.nav-item>.nav-link { + color: #ffffff; +} + +.nav-tabs.nav-tabs-neutral>.nav-item>.nav-link.active { + border-color: rgba(255, 255, 255, 0.5); + color: #ffffff; +} + +.nav-tabs.nav-tabs-primary>.nav-item>.nav-link.active { + border-color: #e14eca; + color: #e14eca; +} + +.nav-tabs.nav-tabs-info>.nav-item>.nav-link.active { + border-color: #1d8cf8; + color: #1d8cf8; +} + +.nav-tabs.nav-tabs-danger>.nav-item>.nav-link.active { + border-color: #fd5d93; + color: #fd5d93; +} + +.nav-tabs.nav-tabs-warning>.nav-item>.nav-link.active { + border-color: #ff8d72; + color: #ff8d72; +} + +.nav-tabs.nav-tabs-success>.nav-item>.nav-link.active { + border-color: #00f2c3; + color: #00f2c3; +} + +.nav-pills.flex-column li>a { + margin-bottom: 15px; +} + +.nav-pills.nav-pills:not(.flex-column) .nav-item:not(:last-child) .nav-link { + margin-right: 10px; + margin-bottom: 5px; +} + +.nav-pills:not(.nav-pills-icons):not(.nav-pills-just-icons) .nav-item .nav-link { + border-radius: 30px; +} + +.nav-pills.nav-pills-just-icons .nav-item .nav-link { + border-radius: 50%; + height: 80px; + max-width: 80px; + min-width: auto; + padding: 0; + width: 80px; +} + +.nav-pills.nav-pills-just-icons .nav-item .nav-link .tim-icons { + font-size: 24px; + line-height: 80px; +} + +.nav-pills .nav-item .nav-link { + padding: 0 15.5px; + text-align: center; + padding: 11px 23px; + min-width: 100px; + font-weight: 400; + color: rgba(255, 255, 255, 0.5); + background-color: #191f31; +} + +.nav-pills .nav-item .nav-link:hover { + background-color: #111520; +} + +.nav-pills .nav-item .nav-link.active, +.nav-pills .nav-item .nav-link.active:focus, +.nav-pills .nav-item .nav-link.active:hover { + background-color: #9A9A9A; + color: #ffffff; + box-shadow: 2px 2px 6px rgba(0, 0, 0, 0.4); +} + +.nav-pills .nav-item .nav-link.disabled, +.nav-pills .nav-item .nav-link:disabled, +.nav-pills .nav-item .nav-link[disabled] { + opacity: .5; +} + +.nav-pills .nav-item i { + display: block; + font-size: 24px; + line-height: 60px; +} + +.nav-pills.nav-pills-neutral .nav-item .nav-link { + background-color: rgba(255, 255, 255, 0.2); + color: #ffffff; +} + +.nav-pills.nav-pills-neutral .nav-item .nav-link.active, +.nav-pills.nav-pills-neutral .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-neutral .nav-item .nav-link.active:hover { + background-color: #ffffff; + color: #e14eca; +} + +.nav-pills.nav-pills-primary .nav-item .nav-link.active, +.nav-pills.nav-pills-primary .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-primary .nav-item .nav-link.active:hover { + background: #e14eca; + background-image: -webkit-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -o-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: -moz-linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-image: linear-gradient(to bottom left, #e14eca, #ba54f5, #e14eca); + background-size: 210% 210%; + background-position: top right; +} + +.nav-pills.nav-pills-info .nav-item .nav-link.active, +.nav-pills.nav-pills-info .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-info .nav-item .nav-link.active:hover { + background: #1d8cf8; + background-image: -webkit-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -o-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: -moz-linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-image: linear-gradient(to bottom left, #1d8cf8, #3358f4, #1d8cf8); + background-size: 210% 210%; + background-position: top right; +} + +.nav-pills.nav-pills-success .nav-item .nav-link.active, +.nav-pills.nav-pills-success .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-success .nav-item .nav-link.active:hover { + background: #00f2c3; + background-image: -webkit-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -o-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: -moz-linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-image: linear-gradient(to bottom left, #00f2c3, #0098f0, #00f2c3); + background-size: 210% 210%; + background-position: top right; +} + +.nav-pills.nav-pills-warning .nav-item .nav-link.active, +.nav-pills.nav-pills-warning .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-warning .nav-item .nav-link.active:hover { + background: #ff8d72; + background-image: -webkit-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -o-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: -moz-linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-image: linear-gradient(to bottom left, #ff8d72, #ff6491, #ff8d72); + background-size: 210% 210%; + background-position: top right; +} + +.nav-pills.nav-pills-danger .nav-item .nav-link.active, +.nav-pills.nav-pills-danger .nav-item .nav-link.active:focus, +.nav-pills.nav-pills-danger .nav-item .nav-link.active:hover { + background: #fd5d93; + background-image: -webkit-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -o-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: -moz-linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-image: linear-gradient(to bottom left, #fd5d93, #ec250d, #fd5d93); + background-size: 210% 210%; + background-position: top right; +} + +.tab-space { + padding: 20px 0 50px 0px; +} + +.tab-content.tab-subcategories { + margin-top: 20px; + background-color: transparent; + padding-left: 15px; + padding-right: 15px; +} + +.tab-content .tab-pane { + color: rgba(255, 255, 255, 0.5); +} + +.nav-align-center { + text-align: center; +} + +.nav-align-center .nav-pills { + display: inline-flex; +} + +.popover { + border: 0; +} + +.popover-header { + font-weight: 600; +} + +.popover-primary { + background-color: #e14eca; +} + +.popover-primary .popover-header { + background-color: #e14eca; + color: #ffffff; + opacity: .6; +} + +.popover-primary .popover-body { + color: #ffffff; +} + +.popover-primary .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-primary.bs-popover-top .arrow::after, +.popover-primary.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #e14eca; +} + +.popover-primary.bs-popover-right .arrow::after, +.popover-primary.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #e14eca; +} + +.popover-primary.bs-popover-bottom .arrow::after, +.popover-primary.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #e14eca; +} + +.popover-primary.bs-popover-left .arrow::after, +.popover-primary.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #e14eca; +} + +.popover-secondary { + background-color: #f4f5f7; +} + +.popover-secondary .popover-header { + background-color: #f4f5f7; + color: #212529; + opacity: .6; +} + +.popover-secondary .popover-body { + color: #212529; +} + +.popover-secondary .popover-header { + border-color: rgba(33, 37, 41, 0.2); +} + +.popover-secondary.bs-popover-top .arrow::after, +.popover-secondary.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #f4f5f7; +} + +.popover-secondary.bs-popover-right .arrow::after, +.popover-secondary.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #f4f5f7; +} + +.popover-secondary.bs-popover-bottom .arrow::after, +.popover-secondary.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #f4f5f7; +} + +.popover-secondary.bs-popover-left .arrow::after, +.popover-secondary.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #f4f5f7; +} + +.popover-success { + background-color: #00f2c3; +} + +.popover-success .popover-header { + background-color: #00f2c3; + color: #ffffff; + opacity: .6; +} + +.popover-success .popover-body { + color: #ffffff; +} + +.popover-success .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-success.bs-popover-top .arrow::after, +.popover-success.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #00f2c3; +} + +.popover-success.bs-popover-right .arrow::after, +.popover-success.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #00f2c3; +} + +.popover-success.bs-popover-bottom .arrow::after, +.popover-success.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #00f2c3; +} + +.popover-success.bs-popover-left .arrow::after, +.popover-success.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #00f2c3; +} + +.popover-info { + background-color: #1d8cf8; +} + +.popover-info .popover-header { + background-color: #1d8cf8; + color: #ffffff; + opacity: .6; +} + +.popover-info .popover-body { + color: #ffffff; +} + +.popover-info .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-info.bs-popover-top .arrow::after, +.popover-info.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #1d8cf8; +} + +.popover-info.bs-popover-right .arrow::after, +.popover-info.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #1d8cf8; +} + +.popover-info.bs-popover-bottom .arrow::after, +.popover-info.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #1d8cf8; +} + +.popover-info.bs-popover-left .arrow::after, +.popover-info.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #1d8cf8; +} + +.popover-warning { + background-color: #ff8d72; +} + +.popover-warning .popover-header { + background-color: #ff8d72; + color: #ffffff; + opacity: .6; +} + +.popover-warning .popover-body { + color: #ffffff; +} + +.popover-warning .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-warning.bs-popover-top .arrow::after, +.popover-warning.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #ff8d72; +} + +.popover-warning.bs-popover-right .arrow::after, +.popover-warning.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #ff8d72; +} + +.popover-warning.bs-popover-bottom .arrow::after, +.popover-warning.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #ff8d72; +} + +.popover-warning.bs-popover-left .arrow::after, +.popover-warning.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #ff8d72; +} + +.popover-danger { + background-color: #fd5d93; +} + +.popover-danger .popover-header { + background-color: #fd5d93; + color: #ffffff; + opacity: .6; +} + +.popover-danger .popover-body { + color: #ffffff; +} + +.popover-danger .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-danger.bs-popover-top .arrow::after, +.popover-danger.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #fd5d93; +} + +.popover-danger.bs-popover-right .arrow::after, +.popover-danger.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #fd5d93; +} + +.popover-danger.bs-popover-bottom .arrow::after, +.popover-danger.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #fd5d93; +} + +.popover-danger.bs-popover-left .arrow::after, +.popover-danger.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #fd5d93; +} + +.popover-light { + background-color: #adb5bd; +} + +.popover-light .popover-header { + background-color: #adb5bd; + color: #ffffff; + opacity: .6; +} + +.popover-light .popover-body { + color: #ffffff; +} + +.popover-light .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-light.bs-popover-top .arrow::after, +.popover-light.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #adb5bd; +} + +.popover-light.bs-popover-right .arrow::after, +.popover-light.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #adb5bd; +} + +.popover-light.bs-popover-bottom .arrow::after, +.popover-light.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #adb5bd; +} + +.popover-light.bs-popover-left .arrow::after, +.popover-light.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #adb5bd; +} + +.popover-dark { + background-color: #212529; +} + +.popover-dark .popover-header { + background-color: #212529; + color: #ffffff; + opacity: .6; +} + +.popover-dark .popover-body { + color: #ffffff; +} + +.popover-dark .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-dark.bs-popover-top .arrow::after, +.popover-dark.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #212529; +} + +.popover-dark.bs-popover-right .arrow::after, +.popover-dark.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #212529; +} + +.popover-dark.bs-popover-bottom .arrow::after, +.popover-dark.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #212529; +} + +.popover-dark.bs-popover-left .arrow::after, +.popover-dark.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #212529; +} + +.popover-default { + background-color: #344675; +} + +.popover-default .popover-header { + background-color: #344675; + color: #ffffff; + opacity: .6; +} + +.popover-default .popover-body { + color: #ffffff; +} + +.popover-default .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-default.bs-popover-top .arrow::after, +.popover-default.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #344675; +} + +.popover-default.bs-popover-right .arrow::after, +.popover-default.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #344675; +} + +.popover-default.bs-popover-bottom .arrow::after, +.popover-default.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #344675; +} + +.popover-default.bs-popover-left .arrow::after, +.popover-default.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #344675; +} + +.popover-white { + background-color: #ffffff; +} + +.popover-white .popover-header { + background-color: #ffffff; + color: #212529; + opacity: .6; +} + +.popover-white .popover-body { + color: #212529; +} + +.popover-white .popover-header { + border-color: rgba(33, 37, 41, 0.2); +} + +.popover-white.bs-popover-top .arrow::after, +.popover-white.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #ffffff; +} + +.popover-white.bs-popover-right .arrow::after, +.popover-white.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #ffffff; +} + +.popover-white.bs-popover-bottom .arrow::after, +.popover-white.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #ffffff; +} + +.popover-white.bs-popover-left .arrow::after, +.popover-white.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #ffffff; +} + +.popover-neutral { + background-color: #ffffff; +} + +.popover-neutral .popover-header { + background-color: #ffffff; + color: #212529; + opacity: .6; +} + +.popover-neutral .popover-body { + color: #212529; +} + +.popover-neutral .popover-header { + border-color: rgba(33, 37, 41, 0.2); +} + +.popover-neutral.bs-popover-top .arrow::after, +.popover-neutral.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: #ffffff; +} + +.popover-neutral.bs-popover-right .arrow::after, +.popover-neutral.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: #ffffff; +} + +.popover-neutral.bs-popover-bottom .arrow::after, +.popover-neutral.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: #ffffff; +} + +.popover-neutral.bs-popover-left .arrow::after, +.popover-neutral.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: #ffffff; +} + +.popover-darker { + background-color: black; +} + +.popover-darker .popover-header { + background-color: black; + color: #ffffff; + opacity: .6; +} + +.popover-darker .popover-body { + color: #ffffff; +} + +.popover-darker .popover-header { + border-color: rgba(255, 255, 255, 0.2); +} + +.popover-darker.bs-popover-top .arrow::after, +.popover-darker.bs-popover-auto[x-placement^="top"] .arrow::after { + border-top-color: black; +} + +.popover-darker.bs-popover-right .arrow::after, +.popover-darker.bs-popover-auto[x-placement^="right"] .arrow::after { + border-right-color: black; +} + +.popover-darker.bs-popover-bottom .arrow::after, +.popover-darker.bs-popover-auto[x-placement^="bottom"] .arrow::after { + border-bottom-color: black; +} + +.popover-darker.bs-popover-left .arrow::after, +.popover-darker.bs-popover-auto[x-placement^="left"] .arrow::after { + border-left-color: black; +} + +.progress-container { + position: relative; +} + +.progress-container.progress-sm { + margin-top: 10px; +} + +.progress-container.progress-sm .progress .progress-value { + position: absolute; + top: -3px; + left: -27px; + color: #ffffff; + font-size: 0.62475rem; +} + +.progress-container+.progress-container, +.progress-container~.progress-container { + margin-top: 15px; +} + +.progress-container .progress-badge { + color: #ffffff; + font-size: 0.75rem; + text-transform: uppercase; +} + +.progress-container .progress-badge.float-left { + margin-right: 20px; +} + +.progress-container .progress { + margin-bottom: 10px; + box-shadow: 0px 0px 0px 3px rgba(0, 0, 0, 0.3); +} + +.progress-container .progress .progress-bar { + border-radius: 0.875rem; + box-shadow: none; + background: #344675; +} + +.progress-container .progress .progress-bar .progress-value { + position: absolute; + top: 2px; + right: 0; + color: #ffffff; + font-size: 0.62475rem; +} + +.progress-container .progress.progress-bar-sm { + height: 3px; +} + +.progress-container.progress-neutral .progress { + background: rgba(255, 255, 255, 0.3); +} + +.progress-container.progress-neutral .progress-bar { + background: #ffffff; +} + +.progress-container.progress-primary .progress-bar { + background: #ba54f5; + background-image: -webkit-linear-gradient(to bottom left, #ba54f5, #e14eca, #ba54f5); + background-image: -o-linear-gradient(to bottom left, #ba54f5, #e14eca, #ba54f5); + background-image: -moz-linear-gradient(to bottom left, #ba54f5, #e14eca, #ba54f5); + background-image: linear-gradient(to bottom left, #ba54f5, #e14eca, #ba54f5); + background-size: 210% 210%; + background-position: top right; +} + +.progress-container.progress-info .progress-bar { + background: #3358f4; + background-image: -webkit-linear-gradient(to bottom left, #3358f4, #1d8cf8, #3358f4); + background-image: -o-linear-gradient(to bottom left, #3358f4, #1d8cf8, #3358f4); + background-image: -moz-linear-gradient(to bottom left, #3358f4, #1d8cf8, #3358f4); + background-image: linear-gradient(to bottom left, #3358f4, #1d8cf8, #3358f4); + background-size: 210% 210%; + background-position: top right; +} + +.progress-container.progress-success .progress-bar { + background: #0098f0; + background-image: -webkit-linear-gradient(to bottom left, #0098f0, #00f2c3, #0098f0); + background-image: -o-linear-gradient(to bottom left, #0098f0, #00f2c3, #0098f0); + background-image: -moz-linear-gradient(to bottom left, #0098f0, #00f2c3, #0098f0); + background-image: linear-gradient(to bottom left, #0098f0, #00f2c3, #0098f0); + background-size: 210% 210%; + background-position: top right; +} + +.progress-container.progress-warning .progress-bar { + background: #ff6491; + background-image: -webkit-linear-gradient(to bottom left, #ff6491, #ff8d72, #ff6491); + background-image: -o-linear-gradient(to bottom left, #ff6491, #ff8d72, #ff6491); + background-image: -moz-linear-gradient(to bottom left, #ff6491, #ff8d72, #ff6491); + background-image: linear-gradient(to bottom left, #ff6491, #ff8d72, #ff6491); + background-size: 210% 210%; + background-position: top right; +} + +.progress-container.progress-danger .progress-bar { + background: #ec250d; + background-image: -webkit-linear-gradient(to bottom left, #ec250d, #fd5d93, #ec250d); + background-image: -o-linear-gradient(to bottom left, #ec250d, #fd5d93, #ec250d); + background-image: -moz-linear-gradient(to bottom left, #ec250d, #fd5d93, #ec250d); + background-image: linear-gradient(to bottom left, #ec250d, #fd5d93, #ec250d); + background-size: 210% 210%; + background-position: top right; +} + +.card-chart .progress-container+.progress-container, +.card-chart .progress-container~.progress-container { + margin-top: 25px; +} + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { + line-height: 1.2; +} + +button, +input, +optgroup, +select, +textarea { + font-family: "Poppins", sans-serif; +} + +.card h1, +.card h2, +.card h3, +.card h4, +.card h5, +.card h6, +.card p { + color: rgba(255, 255, 255, 0.8); +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 400; + color: rgba(255, 255, 255, 0.8); +} + +a { + color: #ba54f5; + font-weight: 300; +} + +a:hover, +a:focus { + color: #e14eca; +} + +h1, +.h1 { + line-height: 1.05; + margin-bottom: 30px; +} + +h1 small, +.h1 small { + font-weight: 600; + text-transform: uppercase; + opacity: .8; +} + +h2, +.h2 { + margin-bottom: 30px; + line-height: 1.2; +} + +h3, +.h3 { + margin-bottom: 30px; + line-height: 1.4em; +} + +h4, +.h4 { + line-height: 1.45em; + margin-bottom: 15px; +} + +h4+.category, +h4.title+.category, +.h4+.category, +.h4.title+.category { + margin-top: -10px; +} + +h5, +.h5 { + line-height: 1.4em; + margin-bottom: 15px; +} + +h6, +.h6 { + text-transform: uppercase; + font-weight: 600; +} + +p { + color: rgba(255, 255, 255, 0.8); + margin-bottom: 5px; +} + +p.description { + font-size: 1.14em; +} + +.title { + font-weight: 600; +} + +.title.title-up { + text-transform: uppercase; +} + +.title.title-up a { + color: #222a42; + text-decoration: none; +} + +.title+.category { + margin-top: -10px; +} + +.description, +.card-description, +.footer-big p, +.card .footer .stats { + color: #9A9A9A; + font-weight: 300; +} + +.category, +.card-category { + text-transform: capitalize; + font-weight: 400; + color: rgba(255, 255, 255, 0.6); + font-size: 0.75rem; +} + +.card-category { + font-size: 0.75rem; +} + +.blockquote { + border-left: none; + border: 1px solid #344675; + padding: 20px; + font-size: 0.9625rem; + line-height: 1.8; +} + +.blockquote small { + color: #344675; + font-size: 0.75rem; + text-transform: uppercase; +} + +.blockquote.blockquote-primary { + border-color: #e14eca; + color: #e14eca; +} + +.blockquote.blockquote-primary small { + color: #e14eca; +} + +.blockquote.blockquote-danger { + border-color: #fd5d93; + color: #fd5d93; +} + +.blockquote.blockquote-danger small { + color: #fd5d93; +} + +.blockquote.blockquote-white { + border-color: rgba(255, 255, 255, 0.8); + color: #ffffff; +} + +.blockquote.blockquote-white small { + color: rgba(255, 255, 255, 0.8); +} + +ul li, +ol li { + color: #ffffff; +} + +pre { + color: rgba(255, 255, 255, 0.8); +} + +hr { + border-top: 1px solid rgba(0, 0, 0, 0.1); + margin-top: 1rem; + margin-bottom: 1rem; +} + +.table>tbody>tr>td { + color: rgba(255, 255, 255, 0.7) !important; +} + +.table>tbody>tr>td .photo { + height: 30px; + width: 30px; + border-radius: 50%; + overflow: hidden; + margin: 0 auto; +} + +.table>tbody>tr>td .photo img { + width: 100%; +} + +.table>tbody>tr.table-success>td { + background-color: #00bf9a; +} + +.table>tbody>tr.table-info>td { + background-color: #1d8cf8; +} + +.table>tbody>tr.table-primary>td { + background-color: #e14eca; +} + +.table>tbody>tr.table-warning>td { + background-color: #ff8d72; +} + +.table>tbody>tr.table-danger>td { + background-color: #fd5d93; +} + +.table .img-wrapper { + width: 40px; + height: 40px; + border-radius: 50%; + overflow: hidden; + margin: 0 auto; +} + +.table .img-row { + max-width: 60px; + width: 60px; +} + +.table .form-check { + margin: 0; + margin-top: 5px; +} + +.table .form-check label .form-check-sign::before, +.table .form-check label .form-check-sign::after { + top: -17px; + left: 4px; +} + +.table .btn { + margin: 0; +} + +.table small, +.table .small { + font-weight: 300; +} + +.card-tasks .card-body .table { + margin-bottom: 0; +} + +.card-tasks .card-body .table>thead>tr>th, +.card-tasks .card-body .table>tbody>tr>th, +.card-tasks .card-body .table>tfoot>tr>th, +.card-tasks .card-body .table>thead>tr>td, +.card-tasks .card-body .table>tbody>tr>td, +.card-tasks .card-body .table>tfoot>tr>td { + padding-top: 5px; + padding-bottom: 5px; +} + +.table>thead>tr>th { + border-bottom-width: 1px; + font-size: 12px; + text-transform: uppercase; + font-weight: 700; + border: 0; + color: rgba(255, 255, 255, 0.7); +} + +.table .radio, +.table .checkbox { + margin-top: 0; + margin-bottom: 0; + padding: 0; + width: 15px; +} + +.table .radio .icons, +.table .checkbox .icons { + position: relative; +} + +.table .radio label:after, +.table .radio label:before, +.table .checkbox label:after, +.table .checkbox label:before { + top: -17px; + left: -3px; +} + +.table>thead>tr>th, +.table>tbody>tr>th, +.table>tfoot>tr>th, +.table>thead>tr>td, +.table>tbody>tr>td, +.table>tfoot>tr>td { + border-color: rgba(255, 255, 255, 0.1); + padding: 12px 7px; + vertical-align: middle; +} + +.table.table-shopping tbody tr:last-child td { + border: none; +} + +.table .th-description { + max-width: 150px; +} + +.table .td-price { + font-size: 26px; + font-weight: 300; + margin-top: 5px; + position: relative; + top: 4px; + text-align: right; +} + +.table .td-total { + font-weight: 600; + font-size: 0.8125rem; + padding-top: 20px; + text-align: right; +} + +.table .td-actions .btn { + margin: 0px; +} + +.table>tbody>tr { + position: relative; +} + +.table>tfoot>tr { + color: rgba(255, 255, 255, 0.7); + text-transform: uppercase; +} + +.table-shopping>thead>tr>th { + text-transform: uppercase; +} + +.table-shopping>tbody>tr>td { + font-size: 1rem; +} + +.table-shopping>tbody>tr>td b { + display: block; + margin-bottom: 5px; +} + +.table-shopping .td-name { + font-weight: 400; + font-size: 1.5em; +} + +.table-shopping .td-name small { + color: #9A9A9A; + font-size: 0.75em; + font-weight: 300; +} + +.table-shopping .td-number { + font-weight: 300; +} + +.table-shopping .td-number .btn-group { + padding-right: 15px; +} + +.table-shopping .td-name { + min-width: 200px; +} + +.table-shopping .td-number { + text-align: right; + min-width: 170px; +} + +.table-shopping .td-number small { + margin-right: 3px; +} + +.table-shopping .img-container { + width: 120px; + max-height: 160px; + overflow: hidden; + display: block; +} + +.table-shopping .img-container img { + width: 100%; +} + +.table-responsive { + overflow: scroll; + padding-bottom: 10px; +} + +#tables .table-responsive { + margin-bottom: 30px; +} + +table.tablesorter thead tr .header { + background-image: url("../img/bg.gif"); + background-repeat: no-repeat; + background-position: center right; + cursor: pointer; +} + +table.tablesorter thead tr .headerSortUp { + background-image: url("../img/asc.gif"); +} + +table.tablesorter thead tr .headerSortDown { + background-image: url("../img/desc.gif"); +} + +.dataTables_wrapper .table-striped tbody tr:nth-of-type(odd) { + background-color: rgba(0, 0, 0, 0.05); +} + +.dataTables_wrapper .form-control-sm { + font-size: 10px; +} + +.form-check { + margin-top: .5rem; + padding-left: 0; +} + +.form-check .form-check-label { + display: inline-block; + position: relative; + cursor: pointer; + padding-left: 25px; + line-height: 18px; + margin-bottom: 0; + -webkit-transition: color 0.3s linear; + -moz-transition: color 0.3s linear; + -o-transition: color 0.3s linear; + -ms-transition: color 0.3s linear; + transition: color 0.3s linear; +} + +.radio .form-check-sign { + padding-left: 28px; +} + +.form-check-radio.form-check-inline .form-check-label { + padding-left: 5px; + margin-right: 10px; +} + +.form-check .form-check-sign::before, +.form-check .form-check-sign::after { + content: " "; + display: inline-block; + position: absolute; + width: 17px; + height: 17px; + left: 0; + cursor: pointer; + border-radius: 3px; + top: 0; + border: 1px solid #818181; + -webkit-transition: opacity 0.3s linear; + -moz-transition: opacity 0.3s linear; + -o-transition: opacity 0.3s linear; + -ms-transition: opacity 0.3s linear; + transition: opacity 0.3s linear; +} + +.form-check input[type="checkbox"]:checked+.form-check-sign::before, +.form-check input[type="checkbox"]:checked+.form-check-sign::before { + border: none; + background-color: #e14eca; +} + +.form-check .form-check-sign::after { + font-family: 'nucleo'; + content: "\ea1b"; + top: 0px; + text-align: center; + font-size: 14px; + opacity: 0; + color: #ffffff; + font-weight: 600; + border: 0; + background-color: inherit; +} + +.form-check.disabled .form-check-label, +.form-check.disabled .form-check-label { + color: #9A9A9A; + opacity: .5; + cursor: not-allowed; +} + +.form-check input[type="checkbox"], +.radio input[type="radio"] { + opacity: 0; + position: absolute; + visibility: hidden; +} + +.form-check input[type="checkbox"]:checked+.form-check-sign::after { + opacity: 1; + font-size: 10px; + margin-top: 0; +} + +.form-check input[type="checkbox"]+.form-check-sign::after { + opacity: 0; + font-size: 10px; + margin-top: 0; +} + +.form-control input[type="checkbox"]:disabled+.form-check-sign::before, +.checkbox input[type="checkbox"]:disabled+.form-check-sign::after { + cursor: not-allowed; +} + +.form-check input[type="checkbox"]:disabled+.form-check-sign, +.form-check input[type="radio"]:disabled+.form-check-sign { + pointer-events: none; +} + +.form-check-radio .form-check-label { + padding-top: 3px; +} + +.form-check-radio .form-check-sign::before, +.form-check-radio .form-check-sign::after { + content: " "; + width: 18px; + height: 18px; + border-radius: 50%; + border: 1px solid #818181; + display: inline-block; + position: absolute; + left: 0px; + top: 3px; + padding: 1px; + -webkit-transition: opacity 0.3s linear; + -moz-transition: opacity 0.3s linear; + -o-transition: opacity 0.3s linear; + -ms-transition: opacity 0.3s linear; + transition: opacity 0.3s linear; +} + +.form-check-radio input[type="radio"]+.form-check-sign:after, +.form-check-radio input[type="radio"] { + opacity: 0; +} + +.form-check-radio input[type="radio"]:checked+.form-check-sign::after { + width: 6px; + height: 6px; + background-color: #e14eca; + border-color: #e14eca; + top: 9px; + left: 6px; + opacity: 1; +} + +.form-check-radio input[type="radio"]:checked+.form-check-sign::before { + border-color: #e14eca; +} + +.form-check-radio input[type="radio"]:checked+.form-check-sign::after { + opacity: 1; +} + +.form-check-radio input[type="radio"]:disabled+.form-check-sign { + color: #9A9A9A; +} + +.form-check-radio input[type="radio"]:disabled+.form-check-sign::before, +.form-check-radio input[type="radio"]:disabled+.form-check-sign::after { + color: #9A9A9A; +} + +.fixed-plugin { + position: fixed; + right: 0; + width: 64px; + background: rgba(0, 0, 0, 0.3); + z-index: 1031; + border-radius: 8px 0 0 8px; + text-align: center; + top: 130px; +} + +.fixed-plugin li>a, +.fixed-plugin .badge { + transition: all .34s; + -webkit-transition: all .34s; + -moz-transition: all .34s; +} + +.fixed-plugin .fa-cog { + color: #ffffff; + padding: 10px; + border-radius: 0 0 6px 6px; + width: auto; +} + +.fixed-plugin .dropdown-menu { + right: 80px; + left: auto !important; + top: -52px !important; + width: 290px; + border-radius: 0.1875rem; + padding: 0 10px; + background: linear-gradient(#222a42, #1d253b); +} + +.fixed-plugin .dropdown .dropdown-menu .tim-icons { + top: 5px; +} + +.fixed-plugin .dropdown-menu:after, +.fixed-plugin .dropdown-menu:before { + right: 10px; + margin-left: auto; + left: auto; +} + +.fixed-plugin .fa-circle-thin { + color: #ffffff; +} + +.fixed-plugin .active .fa-circle-thin { + color: #00bbff; +} + +.fixed-plugin .dropdown-menu>.active>a, +.fixed-plugin .dropdown-menu>.active>a:hover, +.fixed-plugin .dropdown-menu>.active>a:focus { + color: #777777; + text-align: center; +} + +.fixed-plugin img { + border-radius: 0; + width: 100%; + height: 100px; + margin: 0 auto; +} + +.fixed-plugin .dropdown-menu li>a:hover, +.fixed-plugin .dropdown-menu li>a:focus { + box-shadow: none; +} + +.fixed-plugin .badge { + border: 2px solid #ffffff; + border-radius: 50%; + cursor: pointer; + display: inline-block; + height: 23px; + margin-right: 5px; + position: relative; + width: 23px; +} + +.fixed-plugin .badge.active, +.fixed-plugin .badge:hover { + border-color: #1d253b; +} + +.fixed-plugin .light-badge, +.fixed-plugin .dark-badge { + margin: 0; + border: 1px solid #1d8cf8; +} + +.fixed-plugin .light-badge:hover, +.fixed-plugin .dark-badge:hover { + border: 1px solid #1d8cf8; +} + +.fixed-plugin .light-badge { + background: #ffffff; +} + +.fixed-plugin .light-badge:hover { + background: #ffffff; +} + +.fixed-plugin .dark-badge { + background: #222a42; +} + +.fixed-plugin .dark-badge:hover { + background: #222a42; +} + +.fixed-plugin h5 { + margin: 10px; +} + +.fixed-plugin .dropdown-menu li { + display: block; + padding: 18px 2px; + width: 25%; + float: left; +} + +.fixed-plugin li.adjustments-line, +.fixed-plugin li.header-title, +.fixed-plugin li.button-container { + width: 100%; + height: 50px; + min-height: inherit; +} + +.fixed-plugin li.button-container { + height: auto; +} + +.fixed-plugin li.button-container div { + margin-bottom: 5px; +} + +.fixed-plugin #sharrreTitle { + text-align: center; + padding: 10px 0; + height: 50px; +} + +.fixed-plugin li.header-title { + color: #ffffff; + height: 30px; + line-height: 25px; + font-size: 12px; + font-weight: 600; + text-align: center; + text-transform: uppercase; +} + +.fixed-plugin .adjustments-line a { + color: transparent; +} + +.fixed-plugin .adjustments-line a .badge-colors { + position: relative; + top: -2px; +} + +.fixed-plugin .adjustments-line a a:hover, +.fixed-plugin .adjustments-line a a:focus { + color: transparent; +} + +.fixed-plugin .adjustments-line .togglebutton { + text-align: center; +} + +.fixed-plugin .adjustments-line .togglebutton .label-switch { + position: relative; + left: -10px; + font-size: 0.62475rem; + color: #ffffff; +} + +.fixed-plugin .adjustments-line .togglebutton .label-switch.label-right { + left: 10px; +} + +.fixed-plugin .adjustments-line .togglebutton .toggle { + margin-right: 0; +} + +.fixed-plugin .adjustments-line .color-label { + position: relative; + top: -7px; + font-size: 0.62475rem; + color: #ffffff; +} + +.fixed-plugin .adjustments-line .dropdown-menu>li.adjustments-line>a { + padding-right: 0; + padding-left: 0; + border-bottom: 1px solid #ddd; + border-radius: 0; + margin: 0; +} + +.fixed-plugin .dropdown-menu>li>a.img-holder { + font-size: 1rem; + text-align: center; + border-radius: 10px; + background-color: #ffffff; + border: 3px solid #ffffff; + padding-left: 0; + padding-right: 0; + opacity: 1; + cursor: pointer; + display: block; + max-height: 100px; + overflow: hidden; + padding: 0; +} + +.fixed-plugin .dropdown-menu>li>a.img-holder img { + margin-top: auto; +} + +.fixed-plugin .dropdown-menu>li a.switch-trigger:hover, +.fixed-plugin .dropdown-menu>li>a.switch-trigger:focus { + background-color: transparent; +} + +.fixed-plugin .dropdown-menu>li:hover>a.img-holder, +.fixed-plugin .dropdown-menu>li:focus>a.img-holder { + border-color: rgba(0, 187, 255, 0.53); +} + +.fixed-plugin .dropdown-menu>.active>a.img-holder, +.fixed-plugin .dropdown-menu>.active>a.img-holder { + border-color: #00bbff; + background-color: #ffffff; +} + +.fixed-plugin .btn-social { + width: 50%; + display: block; + width: 48%; + float: left; + font-weight: 600; +} + +.fixed-plugin .btn-social i { + margin-right: 5px; +} + +.fixed-plugin .btn-social:first-child { + margin-right: 2%; +} + +.fixed-plugin .dropdown .dropdown-menu { + -webkit-transform: translateY(-15%); + -moz-transform: translateY(-15%); + -o-transform: translateY(-15%); + -ms-transform: translateY(-15%); + transform: translateY(-15%); + top: 27px; + opacity: 0; + transform-origin: 0 0; +} + +.fixed-plugin .dropdown .dropdown-menu:before { + border-bottom: 0.4em solid rgba(0, 0, 0, 0); + border-left: 0.4em solid rgba(0, 0, 0, 0.2); + border-top: 0.4em solid rgba(0, 0, 0, 0); + right: -16px; + top: 46px; +} + +.fixed-plugin .dropdown .dropdown-menu:after { + border-bottom: 0.4em solid rgba(0, 0, 0, 0); + border-left: 0.4em solid #222a42; + border-top: 0.4em solid rgba(0, 0, 0, 0); + right: -16px; +} + +.fixed-plugin .dropdown .dropdown-menu:before, +.fixed-plugin .dropdown .dropdown-menu:after { + content: ""; + display: inline-block; + position: absolute; + top: 74px; + width: 16px; + transform: translateY(-50%); + -webkit-transform: translateY(-50%); + -moz-transform: translateY(-50%); +} + +.fixed-plugin .dropdown.show .dropdown-menu { + opacity: 1; + -webkit-transform: translateY(-13%); + -moz-transform: translateY(-13%); + -o-transform: translateY(-13%); + -ms-transform: translateY(-13%); + transform: translateY(-13%); + transform-origin: 0 0; +} + +.fixed-plugin .bootstrap-switch { + margin: 0; +} + +@media (max-width: 575.98px) { + .fixed-plugin { + top: 100px; + } + .fixed-plugin .dropdown-menu { + width: 225px; + top: -65px !important; + } +} + +.wrapper { + position: relative; + top: 0; + height: 100vh; +} + +.wrapper.wrapper-full-page { + min-height: 100vh; + height: auto; +} + +.sidebar-wrapper ul li div.collapse ul li div.collapse ul li a, +.sidebar-wrapper ul li div.collapse ul li div.collapsing ul li a, +.sidebar-wrapper ul li div.collapsing ul li div.collapse ul li a { + margin-left: 25px; +} + +.sidebar, +.off-canvas-sidebar { + background: #ba54f5; + background: -webkit-linear-gradient(0deg, #ba54f5 0%, #e14eca 100%); + background: -o-linear-gradient(0deg, #ba54f5 0%, #e14eca 100%); + background: -moz-linear-gradient(0deg, #ba54f5 0%, #e14eca 100%); + background: linear-gradient(0deg, #ba54f5 0%, #e14eca 100%); + height: calc(100vh - 90px); + width: 230px; + position: fixed; + top: 0; + left: 0; + z-index: 1; + background-size: cover; + background-position: center center; + display: block; + box-shadow: 0px 0px 45px 0px rgba(0, 0, 0, 0.6); + margin-top: 82px; + margin-left: 20px; + border-radius: 5px; +} + +.sidebar .sidebar-wrapper, +.off-canvas-sidebar .sidebar-wrapper { + width: 100%; + min-height: 100%; + max-height: calc(100vh - 705px); + z-index: 4; + position: relative; + overflow: auto; +} + +.sidebar .sidebar-wrapper .dropdown .dropdown-backdrop, +.off-canvas-sidebar .sidebar-wrapper .dropdown .dropdown-backdrop { + display: none !important; +} + +.sidebar .sidebar-wrapper .navbar-form, +.off-canvas-sidebar .sidebar-wrapper .navbar-form { + border: none; +} + +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span { + display: inline-block; +} + +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal { + margin: 0; + position: relative; + transform: translateX(0px); + opacity: 1; + white-space: nowrap; + display: block; + line-height: 23px; + z-index: 1; + color: rgba(255, 255, 255, 0.8); +} + +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon { + text-transform: uppercase; + width: 34px; + margin-right: 10px; + margin-left: 0px; + font-size: 12px; + text-align: center; + line-height: 25px; + position: relative; + float: left; + z-index: 1; + display: inherit; + line-height: 24px; + color: rgba(255, 255, 255, 0.8); +} + +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a i, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a i, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a i, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a i { + font-size: 17px; + line-height: 20px; + width: 26px; +} + +.sidebar .sidebar-wrapper [data-toggle="collapse"]~div>ul>li:hover>a .sidebar-mini-icon, +.sidebar .sidebar-wrapper [data-toggle="collapse"]~div>ul>li:hover>a .sidebar-normal, +.off-canvas-sidebar .sidebar-wrapper [data-toggle="collapse"]~div>ul>li:hover>a .sidebar-mini-icon, +.off-canvas-sidebar .sidebar-wrapper [data-toggle="collapse"]~div>ul>li:hover>a .sidebar-normal { + color: #ffffff; +} + +.sidebar .sidebar-wrapper .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.off-canvas-sidebar .sidebar-wrapper .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon { + opacity: 0; +} + +.sidebar .navbar-minimize, +.off-canvas-sidebar .navbar-minimize { + position: absolute; + right: 20px; + top: 2px; + opacity: 1; +} + +.sidebar .logo-tim, +.off-canvas-sidebar .logo-tim { + border-radius: 50%; + border: 1px solid #333; + display: block; + height: 61px; + width: 61px; + float: left; + overflow: hidden; +} + +.sidebar .logo-tim img, +.off-canvas-sidebar .logo-tim img { + width: 60px; + height: 60px; +} + +.sidebar .nav, +.off-canvas-sidebar .nav { + margin-top: 20px; + display: block; +} + +.sidebar .nav .caret, +.off-canvas-sidebar .nav .caret { + top: 14px; + position: absolute; + right: 10px; +} + +.sidebar .nav li>a+div .nav, +.off-canvas-sidebar .nav li>a+div .nav { + margin-top: 5px; +} + +.sidebar .nav li>a+div .nav li>a, +.off-canvas-sidebar .nav li>a+div .nav li>a { + margin-top: 0px; + padding: 8px 8px; +} + +.sidebar .nav li>a, +.off-canvas-sidebar .nav li>a { + margin: 10px 15px 0; + border-radius: 30px; + color: #ffffff; + display: block; + text-decoration: none; + position: relative; + text-transform: uppercase; + cursor: pointer; + font-size: 0.62475rem; + padding: 10px 8px; + line-height: 1.625rem; +} + +.sidebar .nav li:first-child>a, +.off-canvas-sidebar .nav li:first-child>a { + margin: 0 15px; +} + +.sidebar .nav li:hover:not(.active)>a p, +.sidebar .nav li:hover:not(.active)>a i, +.sidebar .nav li:focus:not(.active)>a p, +.sidebar .nav li:focus:not(.active)>a i, +.off-canvas-sidebar .nav li:hover:not(.active)>a p, +.off-canvas-sidebar .nav li:hover:not(.active)>a i, +.off-canvas-sidebar .nav li:focus:not(.active)>a p, +.off-canvas-sidebar .nav li:focus:not(.active)>a i { + color: #ffffff; +} + +.sidebar .nav li:hover:not(.active)>a i, +.sidebar .nav li:focus:not(.active)>a i, +.off-canvas-sidebar .nav li:hover:not(.active)>a i, +.off-canvas-sidebar .nav li:focus:not(.active)>a i { + color: #ffffff; +} + +.sidebar .nav li.active>a:not([data-toggle="collapse"]), +.off-canvas-sidebar .nav li.active>a:not([data-toggle="collapse"]) { + background: transparent; +} + +.sidebar .nav li.active>a:not([data-toggle="collapse"]) i, +.sidebar .nav li.active>a:not([data-toggle="collapse"]) p, +.off-canvas-sidebar .nav li.active>a:not([data-toggle="collapse"]) i, +.off-canvas-sidebar .nav li.active>a:not([data-toggle="collapse"]) p { + color: white; +} + +.sidebar .nav li.active>a:not([data-toggle="collapse"]):before, +.off-canvas-sidebar .nav li.active>a:not([data-toggle="collapse"]):before { + content: " "; + position: absolute; + height: 6px; + width: 6px; + top: 22px; + left: -4px; + background: #ffffff; + border-radius: 50%; +} + +.sidebar .nav li.active>a[data-toggle="collapse"], +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"] { + background: transparent; + box-shadow: none; + color: #ffffff; +} + +.sidebar .nav li.active>a[data-toggle="collapse"] i, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"] i { + color: #ffffff; +} + +.sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a { + box-shadow: none; +} + +.sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a .sidebar-mini-icon, +.sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a .sidebar-normal, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a .sidebar-mini-icon, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a .sidebar-normal { + color: #ffffff; + font-weight: 400; +} + +.sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a:before, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"]+div .nav .active a:before { + content: " "; + position: absolute; + height: 6px; + width: 6px; + top: 17px; + left: -4px; + background: #ffffff; + border-radius: 50%; +} + +.sidebar .nav li.active>a[data-toggle="collapse"]:before, +.off-canvas-sidebar .nav li.active>a[data-toggle="collapse"]:before { + content: " "; + position: absolute; + height: 6px; + width: 6px; + top: 22px; + left: -4px; + background: rgba(255, 255, 255, 0.6); + border-radius: 50%; +} + +.sidebar .nav p, +.off-canvas-sidebar .nav p { + margin: 0; + line-height: 30px; + position: relative; + display: block; + height: auto; + white-space: nowrap; +} + +.sidebar .nav i, +.off-canvas-sidebar .nav i { + font-size: 20px; + float: left; + margin-right: 12px; + line-height: 30px; + width: 34px; + text-align: center; + color: rgba(255, 255, 255, 0.8); + position: relative; +} + +.sidebar .sidebar-background, +.off-canvas-sidebar .sidebar-background { + position: absolute; + z-index: 1; + height: 100%; + width: 100%; + display: block; + top: 0; + left: 0; + background-size: cover; + background-position: center center; +} + +.sidebar .sidebar-background:after, +.off-canvas-sidebar .sidebar-background:after { + position: absolute; + z-index: 3; + width: 100%; + height: 100%; + content: ""; + display: block; + background: #ffffff; + opacity: 1; +} + +.sidebar .logo, +.off-canvas-sidebar .logo { + position: relative; + padding: 0.5rem 0.7rem 1rem; + z-index: 4; +} + +.sidebar .logo a.logo-mini, +.off-canvas-sidebar .logo a.logo-mini { + opacity: 1; + float: left; + width: 34px; + text-align: center; + margin-left: 10px; + margin-right: 12px; +} + +.sidebar .logo a.logo-normal, +.off-canvas-sidebar .logo a.logo-normal { + display: block; + opacity: 1; + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); +} + +.sidebar .logo:after, +.off-canvas-sidebar .logo:after { + content: ''; + position: absolute; + bottom: 0; + right: 15px; + height: 1px; + width: calc(100% - 30px); + background: rgba(255, 255, 255, 0.5); +} + +.sidebar .logo p, +.off-canvas-sidebar .logo p { + float: left; + font-size: 20px; + margin: 10px 10px; + color: #ffffff; + line-height: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; +} + +.sidebar .logo .simple-text, +.off-canvas-sidebar .logo .simple-text { + text-transform: uppercase; + padding: 0.5rem 0; + display: block; + white-space: nowrap; + font-size: 0.875rem; + color: #ffffff; + text-decoration: none; + font-weight: 400; + line-height: 30px; + overflow: hidden; +} + +.sidebar .logo-tim, +.off-canvas-sidebar .logo-tim { + border-radius: 50%; + border: 1px solid #333; + display: block; + height: 61px; + width: 61px; + float: left; + overflow: hidden; +} + +.sidebar .logo-tim img, +.off-canvas-sidebar .logo-tim img { + width: 60px; + height: 60px; +} + +.sidebar[data="blue"], +.off-canvas-sidebar[data="blue"] { + background: #3358f4; + background: -webkit-linear-gradient(0deg, #3358f4 0%, #1d8cf8 100%); + background: -o-linear-gradient(0deg, #3358f4 0%, #1d8cf8 100%); + background: -moz-linear-gradient(0deg, #3358f4 0%, #1d8cf8 100%); + background: linear-gradient(0deg, #3358f4 0%, #1d8cf8 100%); +} + +.sidebar[data="blue"]:before, +.off-canvas-sidebar[data="blue"]:before { + border-bottom-color: #1d8cf8; +} + +.sidebar[data="green"], +.off-canvas-sidebar[data="green"] { + background: #0098f0; + background: -webkit-linear-gradient(0deg, #0098f0 0%, #00f2c3 100%); + background: -o-linear-gradient(0deg, #0098f0 0%, #00f2c3 100%); + background: -moz-linear-gradient(0deg, #0098f0 0%, #00f2c3 100%); + background: linear-gradient(0deg, #0098f0 0%, #00f2c3 100%); +} + +.sidebar[data="green"]:before, +.off-canvas-sidebar[data="green"]:before { + border-bottom-color: #00f2c3; +} + +.sidebar[data="orange"], +.off-canvas-sidebar[data="orange"] { + background: #ff6491; + background: -webkit-linear-gradient(0deg, #ff6491 0%, #ff8d72 100%); + background: -o-linear-gradient(0deg, #ff6491 0%, #ff8d72 100%); + background: -moz-linear-gradient(0deg, #ff6491 0%, #ff8d72 100%); + background: linear-gradient(0deg, #ff6491 0%, #ff8d72 100%); +} + +.sidebar[data="orange"]:before, +.off-canvas-sidebar[data="orange"]:before { + border-bottom-color: #ff8d72; +} + +.sidebar[data="red"], +.off-canvas-sidebar[data="red"] { + background: #ec250d; + background: -webkit-linear-gradient(0deg, #ec250d 0%, #fd5d93 100%); + background: -o-linear-gradient(0deg, #ec250d 0%, #fd5d93 100%); + background: -moz-linear-gradient(0deg, #ec250d 0%, #fd5d93 100%); + background: linear-gradient(0deg, #ec250d 0%, #fd5d93 100%); +} + +.sidebar[data="red"]:before, +.off-canvas-sidebar[data="red"]:before { + border-bottom-color: #fd5d93; +} + +.sidebar .user, +.off-canvas-sidebar .user { + padding-bottom: 20px; + margin: 20px auto 0; + position: relative; +} + +.sidebar .user:after, +.off-canvas-sidebar .user:after { + content: ''; + position: absolute; + bottom: 0; + right: 15px; + height: 1px; + width: calc(100% - 30px); + background: rgba(255, 255, 255, 0.5); +} + +.sidebar .user .photo, +.off-canvas-sidebar .user .photo { + width: 34px; + height: 34px; + overflow: hidden; + float: left; + z-index: 5; + margin-right: 10px; + border-radius: 50%; + margin-left: 23px; + box-shadow: 0px 10px 25px 0px rgba(0, 0, 0, 0.3); +} + +.sidebar .user .photo img, +.off-canvas-sidebar .user .photo img { + width: 100%; +} + +.sidebar .user a, +.off-canvas-sidebar .user a { + color: #ffffff; + text-decoration: none; + padding: 0.5rem 15px; + white-space: nowrap; +} + +.sidebar .user .info>a, +.off-canvas-sidebar .user .info>a { + display: block; + line-height: 18px; +} + +.sidebar .user .info>a>span, +.off-canvas-sidebar .user .info>a>span { + display: block; + position: relative; + opacity: 1; +} + +.sidebar .user .info .caret, +.off-canvas-sidebar .user .info .caret { + position: absolute; + top: 8px; + right: 18px; +} + +.sidebar:before, +.off-canvas-sidebar:before { + content: ""; + position: absolute; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid #e14eca; + top: -5px; + left: 40px; + transform: translate(-50%); +} + +.visible-on-sidebar-regular { + display: inline-block !important; +} + +.visible-on-sidebar-mini { + display: none !important; +} + +.off-canvas-sidebar .nav>li>a, +.off-canvas-sidebar .nav>li>a:hover { + color: #ffffff; +} + +.off-canvas-sidebar .nav>li>a:focus { + background: rgba(200, 200, 200, 0.2); +} + +.main-panel { + position: relative; + float: right; + width: 100%; + min-height: 100vh; + border-top: 2px solid #e14eca; + background: linear-gradient(#1e1e2f, #1e1e24); + -webkit-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -moz-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -o-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -ms-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); +} + +.main-panel[data="blue"] { + border-top: 2px solid #1d8cf8; +} + +.main-panel[data="green"] { + border-top: 2px solid #00f2c3; +} + +.main-panel[data="orange"] { + border-top: 2px solid #ff8d72; +} + +.main-panel[data="red"] { + border-top: 2px solid #fd5d93; +} + +.main-panel[data="primary"] { + border-top: 2px solid #e14eca; +} + +.main-panel>.content { + padding: 80px 30px 30px 280px; + min-height: calc(100vh - 70px); +} + +.main-panel>.navbar { + margin-bottom: 0; +} + +.main-panel .header { + margin-bottom: 50px; +} + +.perfect-scrollbar-on .sidebar, +.perfect-scrollbar-on .main-panel { + height: 100%; + max-height: 100%; +} + +.panel-header { + height: 260px; + padding-top: 80px; + padding-bottom: 45px; + background: #141E30; + /* fallback for old browsers */ + background: -webkit-gradient(linear, left top, right top, from(#0c2646), color-stop(60%, #204065), to(#2a5788)); + background: linear-gradient(to right, #0c2646 0%, #204065 60%, #2a5788 100%); + position: relative; + overflow: hidden; +} + +.panel-header .header .title { + color: #ffffff; +} + +.panel-header .header .category { + max-width: 600px; + color: rgba(255, 255, 255, 0.5); + margin: 0 auto; + font-size: 13px; +} + +.panel-header .header .category a { + color: #ffffff; +} + +.panel-header-sm { + height: 135px; +} + +.panel-header-lg { + height: 380px; +} + +@media (max-width: 991.98px) { + .sidebar { + position: fixed; + display: block; + top: 0; + height: 100vh; + width: 260px; + right: auto; + left: 0; + margin: 0; + border-radius: 0; + z-index: 1032; + visibility: visible; + overflow-y: visible; + padding: 0; + transition: 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -webkit-transform: translate3d(-260px, 0, 0); + -moz-transform: translate3d(-260px, 0, 0); + -o-transform: translate3d(-260px, 0, 0); + -ms-transform: translate3d(-260px, 0, 0); + transform: translate3d(-260px, 0, 0); + } +} + +@media screen and (max-width: 991.98px) and (prefers-reduced-motion: reduce) { + .sidebar { + transition: none; + } +} + +@media (max-width: 991.98px) { + .sidebar .sidebar-wrapper { + height: 100vh; + } + .minimize-sidebar { + display: none; + } + .nav-open .main-panel { + right: 0; + -webkit-transform: translate3d(260px, 0, 0); + -moz-transform: translate3d(260px, 0, 0); + -o-transform: translate3d(260px, 0, 0); + -ms-transform: translate3d(260px, 0, 0); + transform: translate3d(260px, 0, 0); + } + .nav-open .sidebar { + transition: 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); + } +} + +@media screen and (max-width: 991.98px) and (prefers-reduced-motion: reduce) { + .nav-open .sidebar { + transition: none; + } +} + +@media (max-width: 991.98px) { + .nav-open .sidebar:before { + content: unset; + } + .nav-open body { + position: relative; + overflow-x: hidden; + } + .nav-open .menu-on-right .main-panel { + -webkit-transform: translate3d(-260px, 0, 0); + -moz-transform: translate3d(-260px, 0, 0); + -o-transform: translate3d(-260px, 0, 0); + -ms-transform: translate3d(-260px, 0, 0); + transform: translate3d(-260px, 0, 0); + } + .nav-open .menu-on-right .navbar-collapse, + .nav-open .menu-on-right .sidebar { + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); + } + .nav-open .menu-on-right .bodyClick { + right: 260px; + left: auto; + } + .menu-on-right .sidebar { + left: auto; + right: 0; + -webkit-transform: translate3d(260px, 0, 0); + -moz-transform: translate3d(260px, 0, 0); + -o-transform: translate3d(260px, 0, 0); + -ms-transform: translate3d(260px, 0, 0); + transform: translate3d(260px, 0, 0); + } + .bodyClick { + height: 100%; + width: 100%; + position: fixed; + opacity: 1; + top: 0; + right: 0; + left: 260px; + content: ""; + z-index: 9999; + overflow-x: hidden; + background-color: transparent; + -webkit-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -moz-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -o-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -ms-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + } + .wrapper { + -webkit-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -moz-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -o-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -ms-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + } + .main-panel { + width: 100%; + } + .main-panel .content { + padding-left: 30px; + } +} + +@media (min-width: 992px) { + .sidebar, + .main-panel, + .sidebar-wrapper { + -webkit-transition-property: top, bottom, width; + transition-property: top, bottom, width; + -webkit-transition-duration: .2s, .2s, .35s; + transition-duration: .2s, .2s, .35s; + -webkit-transition-timing-function: linear, linear, ease; + transition-timing-function: linear, linear, ease; + -webkit-overflow-scrolling: touch; + } + .sidebar-mini .visible-on-sidebar-regular { + display: none !important; + } + .sidebar-mini .visible-on-sidebar-mini { + display: inline-block !important; + } + .sidebar-mini .sidebar { + width: 80px; + } + .sidebar-mini .sidebar .sidebar-wrapper { + width: 100% !important; + } + .sidebar-mini .sidebar { + display: block; + z-index: 1030; + box-shadow: 0px 2px 22px 0 rgba(0, 0, 0, 0.2), 0px 2px 30px 0 rgba(0, 0, 0, 0.35); + } + .sidebar-mini .sidebar .logo a.logo-normal { + opacity: 0; + -webkit-transform: translate3d(-25px, 0, 0); + -moz-transform: translate3d(-25px, 0, 0); + -o-transform: translate3d(-25px, 0, 0); + -ms-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + } + .sidebar-mini .sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, + .sidebar-mini .sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, + .sidebar-mini .sidebar .sidebar-wrapper .user .info>a>span, + .sidebar-mini .sidebar .sidebar-wrapper>.nav li>a p { + -webkit-transform: translate3d(-25px, 0, 0); + -moz-transform: translate3d(-25px, 0, 0); + -o-transform: translate3d(-25px, 0, 0); + -ms-transform: translate3d(-25px, 0, 0); + transform: translate3d(-25px, 0, 0); + opacity: 0; + } + .sidebar-mini .sidebar .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon { + opacity: 1; + } + .sidebar-mini .sidebar:hover { + width: 260px; + } + .sidebar-mini .sidebar:hover .logo a.logo-normal { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } + .sidebar-mini .sidebar:hover .navbar-minimize { + opacity: 1; + } + .sidebar-mini .sidebar:hover .sidebar-wrapper { + width: 260px; + } + .sidebar-mini .sidebar:hover .sidebar-wrapper>.nav li>a p, + .sidebar-mini .sidebar:hover .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, + .sidebar-mini .sidebar:hover .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, + .sidebar-mini .sidebar:hover .sidebar-wrapper .user .info>a>span { + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); + opacity: 1; + } + .sidebar-mini .sidebar:hover .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon { + opacity: 0; + } + .sidebar-mini .main-panel>.content { + padding-left: 130px; + } + .sidebar-mini footer { + padding-left: 130px; + } + .navbar-minimize button { + margin-left: 10px; + } + .navbar-minimize button i { + color: #ffffff; + font-size: 20px; + } +} + +@media (max-width: 767.98px) { + .main-panel .content { + padding-left: 15px; + padding-right: 15px; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .wrapper { + -webkit-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -moz-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -o-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -ms-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + } + .sidebar { + position: fixed; + display: block; + top: 0; + height: 100vh; + width: 260px !important; + right: auto; + left: 0; + margin: 0; + border-radius: 0; + z-index: 1032; + visibility: visible; + overflow-y: visible; + padding: 0; + transition: 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -webkit-transform: translate3d(-260px, 0, 0); + -moz-transform: translate3d(-260px, 0, 0); + -o-transform: translate3d(-260px, 0, 0); + -ms-transform: translate3d(-260px, 0, 0); + transform: translate3d(-260px, 0, 0); + } +} + +@media screen and (min-width: 768px) and (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .sidebar { + transition: none; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .sidebar .sidebar-wrapper { + height: 100vh; + } + .main-panel { + transition: 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + } +} + +@media screen and (min-width: 768px) and (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .main-panel { + transition: none; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .nav-open .main-panel { + right: 0; + -webkit-transform: translate3d(260px, 0, 0); + -moz-transform: translate3d(260px, 0, 0); + -o-transform: translate3d(260px, 0, 0); + -ms-transform: translate3d(260px, 0, 0); + transform: translate3d(260px, 0, 0); + } + .nav-open .sidebar { + transition: 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); + } +} + +@media screen and (min-width: 768px) and (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { + .nav-open .sidebar { + transition: none; + } +} + +@media (min-width: 768px) and (max-width: 1199.98px) { + .nav-open .sidebar:before { + content: unset; + } + .nav-open body { + position: relative; + overflow-x: hidden; + } + .nav-open .menu-on-right .main-panel { + -webkit-transform: translate3d(-260px, 0, 0); + -moz-transform: translate3d(-260px, 0, 0); + -o-transform: translate3d(-260px, 0, 0); + -ms-transform: translate3d(-260px, 0, 0); + transform: translate3d(-260px, 0, 0); + } + .nav-open .menu-on-right .navbar-collapse, + .nav-open .menu-on-right .sidebar { + -webkit-transform: translate3d(0px, 0, 0); + -moz-transform: translate3d(0px, 0, 0); + -o-transform: translate3d(0px, 0, 0); + -ms-transform: translate3d(0px, 0, 0); + transform: translate3d(0px, 0, 0); + } + .nav-open .menu-on-right .bodyClick { + right: 260px; + left: auto; + } + .menu-on-right .sidebar { + left: auto; + right: 0; + -webkit-transform: translate3d(260px, 0, 0); + -moz-transform: translate3d(260px, 0, 0); + -o-transform: translate3d(260px, 0, 0); + -ms-transform: translate3d(260px, 0, 0); + transform: translate3d(260px, 0, 0); + } + .bodyClick { + height: 100%; + width: 100%; + position: fixed; + opacity: 1; + top: 0; + right: 0; + left: 260px; + content: ""; + z-index: 9999; + overflow-x: hidden; + background-color: transparent; + -webkit-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -moz-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -o-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + -ms-transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + transition: all 0.5s cubic-bezier(0.685, 0.0473, 0.346, 1); + } + .main-panel .content { + padding-left: 30px !important; + } + .sidebar-mini .sidebar .logo a.logo-normal { + opacity: 1; + transform: translate3d(0px, 0, 0); + } + .sidebar-mini .sidebar .sidebar-wrapper>.nav li>a p { + opacity: 1; + } + .sidebar-mini .sidebar .sidebar-wrapper .nav li a p { + opacity: 1 !important; + transform: translate3d(0px, 0, 0); + } + .minimize-sidebar { + display: none; + } +} + +/* Animations */ + +.nav-pills .nav-link, +.navbar, +.nav-tabs .nav-link, +.sidebar .nav a, +.sidebar .nav a i, +.sidebar .nav p, +.navbar-collapse .navbar-nav .nav-link, +.animation-transition-general, +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span, +.sidebar .navbar-minimize, +.off-canvas-sidebar .navbar-minimize, +.sidebar .nav p, +.off-canvas-sidebar .nav p, +.sidebar .logo a.logo-mini, +.sidebar .logo a.logo-normal, +.off-canvas-sidebar .logo a.logo-mini, +.off-canvas-sidebar .logo a.logo-normal, +.sidebar .user .photo, +.off-canvas-sidebar .user .photo, +.sidebar .user a, +.off-canvas-sidebar .user a, +.sidebar .user .info>a>span, +.off-canvas-sidebar .user .info>a>span, +.card-collapse .card .card-header a[data-toggle="collapse"] i, +.tag, +.tag [data-role="remove"], +.animation-transition-general, +.sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a span, +.off-canvas-sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a span, +.sidebar .navbar-minimize, +.off-canvas-sidebar .navbar-minimize, +.sidebar .nav p, +.off-canvas-sidebar .nav p, +.sidebar .logo a.logo-mini, +.sidebar .logo a.logo-normal, +.off-canvas-sidebar .logo a.logo-mini, +.off-canvas-sidebar .logo a.logo-normal, +.sidebar .user .photo, +.off-canvas-sidebar .user .photo, +.sidebar .user a, +.off-canvas-sidebar .user a, +.sidebar .user .info>a>span, +.off-canvas-sidebar .user .info>a>span, +.card-collapse .card .card-header a[data-toggle="collapse"] i { + -webkit-transition: all 300ms ease 0s; + -moz-transition: all 300ms ease 0s; + -o-transition: all 300ms ease 0s; + -ms-transition: all 300ms ease 0s; + transition: all 300ms ease 0s; +} + +.bootstrap-switch-label:before, +.caret { + -webkit-transition: all 150ms ease 0s; + -moz-transition: all 150ms ease 0s; + -o-transition: all 150ms ease 0s; + -ms-transition: all 150ms ease 0s; + transition: all 150ms ease 0s; +} + +.dropdown-toggle[aria-expanded="true"]:after, +a[data-toggle="collapse"][aria-expanded="true"] .caret, +.card-collapse .card a[data-toggle="collapse"][aria-expanded="true"] i, +.card-collapse .card a[data-toggle="collapse"].expanded i { + filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); + -webkit-transform: rotate(180deg); + -ms-transform: rotate(180deg); + transform: rotate(180deg); +} + +.caret { + width: 0; + height: 0; + vertical-align: middle; + border-top: 4px dashed; + border-right: 4px solid transparent; + border-left: 4px solid transparent; + margin-top: -5px; + position: absolute; + top: 30px; + margin-left: 5px; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +.card form label+.form-control { + margin-bottom: 20px; +} + +.offline-doc .page-header:before { + background: rgba(0, 0, 0, 0.75); +} + +.offline-doc .navbar { + border: none; +} + +.offline-doc .footer { + position: absolute; + width: 100%; + background: transparent; + bottom: 0; + color: #ffffff; + padding: 0.9375rem 0; +} + +.card .map-title { + color: #ffffff; +} + +.card .table tr td p.title { + padding-top: 7px; + margin-bottom: 0; +} + +.card.card-chart .gmnoprint, +.card.card-chart .gm-style-cc { + display: none !important; +} + +.bd-docs h1, +.bd-docs h2, +.bd-docs h3, +.bd-docs h4, +.bd-docs h5, +.bd-docs h6, +.bd-docs p, +.bd-docs ul li, +.bd-docs ol li { + color: #2c2c2c; +} + +.bd-docs .bd-content>table>thead>tr>th { + color: #222a42; +} + +.bd-docs .blockquote, +.bd-docs .blockquote p, +.bd-docs .card p { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs .bd-example { + background: linear-gradient(#1e1e2f, #1e1e24); +} + +.bd-docs .navbar { + border-top: none; +} + +.bd-docs .navbar .navbar-nav .nav-link { + color: rgba(255, 255, 255, 0.8) !important; +} + +.bd-docs .bd-example .btn { + margin: 4px 0; +} + +.bd-docs .bd-example .btn .badge { + display: inline-block; +} + +.bd-docs .bd-example .tim-icons { + color: #ffffff; +} + +.bd-docs .bd-example .popover .popover-header { + color: rgba(181, 181, 181, 0.6); +} + +.bd-docs .bd-example .popover-body p { + color: #212529; +} + +.bd-docs .bd-example.tooltip-demo p { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs .card.card-body, +.bd-docs .card .card-body { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs label, +.bd-docs .form-check { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs .form-check+.btn { + margin-top: 20px; +} + +.bd-docs .bd-example thead th, +.bd-docs table thead th { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs .bd-example h1, +.bd-docs .bd-example h2, +.bd-docs .bd-example h3, +.bd-docs .bd-example h4, +.bd-docs .bd-example h5, +.bd-docs .bd-example h6, +.bd-docs .bd-example .h1, +.bd-docs .bd-example .h2, +.bd-docs .bd-example .h3, +.bd-docs .bd-example .h4, +.bd-docs .bd-example .h5, +.bd-docs .bd-example .h6, +.bd-docs table h1, +.bd-docs table h2, +.bd-docs table h3, +.bd-docs table h4, +.bd-docs table h5, +.bd-docs table h6, +.bd-docs table .h1, +.bd-docs table .h2, +.bd-docs table .h3, +.bd-docs table .h4, +.bd-docs table .h5, +.bd-docs table .h6 { + color: rgba(255, 255, 255, 0.8); +} + +.bd-docs .bd-example .datepicker thead th, +.bd-docs .bd-example .datepicker table thead th, +.bd-docs .bd-example .datepicker .tim-icons, +.bd-docs table .datepicker thead th, +.bd-docs table .datepicker table thead th, +.bd-docs table .datepicker .tim-icons { + color: #e14eca; +} + +.bd-docs .bd-example .picker-switch .tim-icons, +.bd-docs table .picker-switch .tim-icons { + color: #e14eca; +} + +.bd-docs .footer .container-fluid>nav { + display: inline-block; +} + +.modal.show .modal-dialog { + -webkit-transform: translate(0, 30%); + transform: translate(0, 30%); +} + +code { + color: #f3a4b5; +} + +.rtl .sidebar, +.rtl .bootstrap-navbar { + right: 0; + left: auto; + margin-right: 20px; + margin-left: 0; +} + +.rtl .sidebar .nav i, +.rtl .bootstrap-navbar .nav i { + float: right; + margin-left: 15px; + margin-right: 0; +} + +.rtl .sidebar .nav p, +.rtl .bootstrap-navbar .nav p { + margin-right: 45px; + text-align: right; +} + +.rtl .sidebar .nav .caret, +.rtl .bootstrap-navbar .nav .caret { + left: 11px; + right: auto; +} + +.rtl .sidebar .logo a.logo-mini, +.rtl .bootstrap-navbar .logo a.logo-mini { + float: right; + margin-right: 20px; + margin-left: 10px; +} + +.rtl .sidebar .logo .simple-text, +.rtl .bootstrap-navbar .logo .simple-text { + text-align: right; +} + +.rtl .sidebar .sidebar-wrapper .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.rtl .sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.rtl .bootstrap-navbar .sidebar-wrapper .nav [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon, +.rtl .bootstrap-navbar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-mini-icon { + float: right; + margin-left: 15px; + margin-right: 0; +} + +.rtl .sidebar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.rtl .sidebar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.rtl .bootstrap-navbar .sidebar-wrapper>.nav [data-toggle="collapse"]~div>ul>li>a .sidebar-normal, +.rtl .bootstrap-navbar .sidebar-wrapper .user .info [data-toggle="collapse"]~div>ul>li>a .sidebar-normal { + text-align: right; +} + +.rtl .sidebar:before, +.rtl .bootstrap-navbar:before { + right: 30px; + left: auto; +} + +.rtl .main-panel { + position: fixed; + height: 100%; + overflow-y: scroll; + overflow-x: hidden; +} + +.rtl .main-panel .content { + padding: 110px 300px 20px 50px; +} + +.rtl .dropdown-toggle:after { + margin-right: .255em; + margin-left: 0; +} + +.rtl .dropdown-menu.dropdown-menu-right.dropdown-navbar { + right: -220px !important; + left: auto; +} + +.rtl .dropdown-menu.dropdown-menu-right.dropdown-navbar:before { + right: auto; + left: 35px; +} + +.rtl .notification { + left: 40px; + right: auto; +} + +.rtl .dropdown-menu { + right: auto; + left: 0; +} + +.rtl .card-timeline .timeline .timeline-footer .btn { + margin-left: 0; + margin-right: auto; +} + +.rtl .navbar-minimize-fixed { + margin-right: 38px; + margin-left: auto; +} + +.rtl .minimize-sidebar { + float: right; +} + +.rtl .alert { + left: 0; + margin-left: 0; + margin-right: 0; +} + +.rtl .alert button.close { + left: 10px !important; + right: auto !important; +} + +.rtl .alert span[data-notify="icon"] { + right: 15px; + left: auto; +} + +.rtl .alert.alert-with-icon { + padding-right: 65px; + padding-left: 15px; +} + +.rtl .alert.alert-with-icon i[data-notify="icon"] { + right: 15px; + left: auto; +} + +.rtl .search-bar { + margin-left: 0; +} + +.rtl .modal-search .modal-header .close { + margin-right: auto; + left: 10px; +} + +.rtl .rtl .footer { + padding: 24px 30px !important; +} + +.rtl .ps__rail-y { + right: auto !important; + left: 0; +} + +.rtl .card.card-timeline .timeline .timeline-footer .btn.dropdown-toggle i { + left: 0 !important; +} + +.rtl .card.card-timeline .timeline .timeline-footer .btn.dropdown-toggle:after { + margin-left: 5px !important; +} + +.rtl .fixed-plugin .dropdown-menu { + right: 80px; +} + +@media (min-width: 1200px) { + .rtl.sidebar-mini .main-panel .content { + padding-right: 130px; + padding-left: 50px; + } + .rtl.sidebar-mini footer { + padding-right: 130px; + padding-left: 50px; + } + .rtl .navbar-minimize button { + margin-right: -5px; + } + .rtl .footer { + padding: 24px 300px 24px 0; + } +} + +@media (max-width: 1199.98px) { + .rtl .sidebar { + margin-right: 0; + } + .rtl .main-panel .content { + padding-right: 50px; + } +} + +@media (max-width: 991.98px) { + .rtl .main-panel .content { + padding-right: 50px; + } + .rtl #bodyClick { + right: 260px; + left: auto; + } + .rtl .navbar .navbar-toggle .navbar-toggler { + display: block; + margin-right: 20px; + } + .rtl .navbar .navbar-nav { + padding-right: 0; + } + .rtl .navbar .navbar-nav a.nav-link { + text-align: right; + } + .rtl .navbar .navbar-nav a.nav-link p { + margin-right: 7px; + } + .rtl .navbar .navbar-nav .btn { + margin-right: 0; + padding: 0; + } + .rtl .navbar .navbar-nav .btn i { + margin-left: 4px; + margin-right: 5px; + } + .rtl .navbar .navbar-nav .search-bar span { + margin-right: 10px; + } + .nav-open body { + position: fixed; + } +} + +@media (max-width: 767.98px) { + .rtl .main-panel .content { + padding-left: 15px; + padding-right: 15px; + } +} + +@media (max-width: 575.98px) { + .rtl .navbar .dropdown-menu { + width: 200px !important; + } +} + +.card.card-timeline .card-body { + padding-left: 0; + padding-right: 0; +} + +.card.card-timeline .timeline { + list-style: none; + padding: 20px 0 20px; + position: relative; +} + +.card.card-timeline .timeline:before { + top: 0; + bottom: 0; + position: absolute; + content: " "; + width: 3px; + background-color: #222a42; + left: 50%; + margin-left: -1.5px; +} + +.card.card-timeline .timeline .timeline-footer .btn { + margin: 0; +} + +.card.card-timeline .timeline .timeline-footer .btn.dropdown-toggle i { + top: -1px; + left: 10px; +} + +.card.card-timeline .timeline .timeline-footer .btn.dropdown-toggle:after { + margin-left: 20px !important; +} + +.card.card-timeline .timeline h6 { + color: rgba(255, 255, 255, 0.8); + font-weight: 400; + margin: 10px 0px 0px; +} + +.card.card-timeline .timeline.timeline-simple:before { + left: 5%; +} + +.card.card-timeline .timeline.timeline-simple>li>.timeline-panel { + width: 86%; +} + +.card.card-timeline .timeline.timeline-simple>li>.timeline-badge { + left: 5%; +} + +.card.card-timeline .timeline>li { + margin-bottom: 20px; + position: relative; +} + +.card.card-timeline .timeline>li:before, +.card.card-timeline .timeline>li:after { + content: " "; + display: table; +} + +.card.card-timeline .timeline>li:after { + clear: both; +} + +.card.card-timeline .timeline>li>.timeline-panel { + background: #27293d; + width: 45%; + float: left; + padding: 20px; + border-radius: 0.2857rem; + box-shadow: 0 1px 20px 0px rgba(0, 0, 0, 0.1); + color: #222a42; + margin-bottom: 20px; + position: relative; +} + +.card.card-timeline .timeline>li>.timeline-panel:before { + position: absolute; + top: 26px; + right: -15px; + display: inline-block; + border-top: 15px solid transparent; + border-left: 15px solid #222a42; + border-right: 0 solid #222a42; + border-bottom: 15px solid transparent; + content: " "; +} + +.card.card-timeline .timeline>li>.timeline-panel:after { + position: absolute; + top: 27px; + right: -14px; + display: inline-block; + border-top: 14px solid transparent; + border-left: 14px solid #27293d; + border-right: 0 solid #27293d; + border-bottom: 14px solid transparent; + content: " "; +} + +.card.card-timeline .timeline>li>.timeline-panel.timeline-panel-white { + background: #ffffff; +} + +.card.card-timeline .timeline>li>.timeline-panel.timeline-panel-white .timeline-body p { + color: #1d253b; +} + +.card.card-timeline .timeline>li>.timeline-panel.timeline-panel-white:after, +.card.card-timeline .timeline>li>.timeline-panel.timeline-panel-white:before { + border-right-color: #ffffff; + border-left-color: #ffffff; +} + +.card.card-timeline .timeline>li>.timeline-badge { + color: #ffffff; + width: 50px; + height: 50px; + line-height: 51px; + font-size: 1.4em; + text-align: center; + position: absolute; + top: 16px; + left: 50%; + margin-left: -25px; + background-color: #9A9A9A; + z-index: 100; + border-top-right-radius: 50%; + border-top-left-radius: 50%; + border-bottom-right-radius: 50%; + border-bottom-left-radius: 50%; +} + +.card.card-timeline .timeline>li>.timeline-badge [class^="ti-"], +.card.card-timeline .timeline>li>.timeline-badge [class*=" ti-"] { + line-height: inherit; +} + +.card.card-timeline .timeline>li>.timeline-badge .tim-icons { + width: 25px; + height: 21px; + text-align: center; +} + +.card.card-timeline .timeline>li.timeline-inverted>.timeline-panel { + float: right; +} + +.card.card-timeline .timeline>li.timeline-inverted>.timeline-panel:before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; +} + +.card.card-timeline .timeline>li.timeline-inverted>.timeline-panel:after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; +} + +.card.card-timeline .timeline-heading { + margin-bottom: 15px; +} + +.card.card-timeline .timeline-badge.primary { + background-color: #1d8cf8 !important; +} + +.card.card-timeline .timeline-badge.info { + background-color: #1d8cf8 !important; +} + +.card.card-timeline .timeline-badge.success { + background-color: #00bf9a !important; +} + +.card.card-timeline .timeline-badge.warning { + background-color: #ff8d72 !important; +} + +.card.card-timeline .timeline-badge.danger { + background-color: #fd5d93 !important; +} + +.card.card-timeline .timeline-title { + margin-top: 0; + color: inherit; +} + +.card.card-timeline .timeline-body>p, +.card.card-timeline .timeline-body>ul { + margin-bottom: 0; + color: #ffffff; +} + +.card.card-timeline .timeline-body>p+p { + margin-top: 5px; +} + +@media (max-width: 767.98px) { + .card.card-timeline .timeline:before { + left: 5%; + } + .card.card-timeline .timeline>li>.timeline-badge { + left: 5%; + } + .card.card-timeline .timeline>li>.timeline-panel { + float: right; + width: 83% !important; + } + .card.card-timeline .timeline>li>.timeline-panel:before { + border-left-width: 0; + border-right-width: 15px; + left: -15px; + right: auto; + } + .card.card-timeline .timeline>li>.timeline-panel:after { + border-left-width: 0; + border-right-width: 14px; + left: -14px; + right: auto; + } +} + +.sr-only, +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after, +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after, +.bootstrap-datetimepicker-widget .picker-switch::after, +.bootstrap-datetimepicker-widget table th.prev::after, +.bootstrap-datetimepicker-widget table th.next::after { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +.bootstrap-datetimepicker-widget { + list-style: none; +} + +.bootstrap-datetimepicker-widget a .btn:hover { + background-color: transparent; +} + +.bootstrap-datetimepicker-widget.dropdown-menu { + padding: 8px 6px; + width: 254px; + max-width: 254px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .tim-icons { + opacity: 1; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .picker-switch .table-condensed:hover { + background: #eee; + border-radius: 3px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .timepicker-picker .table-condensed { + margin-top: 10px; + margin-bottom: 5px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .timepicker-picker .table-condensed .btn[data-action="togglePeriod"]:hover, +.bootstrap-datetimepicker-widget.dropdown-menu .timepicker-picker .table-condensed .separator { + color: #9A9A9A !important; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .month, +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .year, +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .decade { + color: #9A9A9A; +} + +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .month.active, +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .year.active, +.bootstrap-datetimepicker-widget.dropdown-menu .table-condensed .decade.active { + color: #ffffff; +} + +@media (min-width: 768px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} + +@media (min-width: 992px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} + +@media (min-width: 1200px) { + .bootstrap-datetimepicker-widget.dropdown-menu.timepicker-sbs { + width: 38em; + } +} + +.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + display: inline-block; + position: absolute; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + right: auto; + border-bottom: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.top:before { + display: none; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.top:after { + display: inline-block; + position: absolute; + width: 0; + height: 0; + vertical-align: middle; + content: ""; + top: auto; + bottom: -6px; + right: auto; + left: 10px; + color: #ffffff; + border-top: .4em solid; + border-right: .4em solid transparent; + border-left: .4em solid transparent; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.top { + margin-top: auto; + margin-bottom: -20px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.top.open { + margin-top: auto; + margin-bottom: 3px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:before { + left: auto; + right: 6px; +} + +.bootstrap-datetimepicker-widget.dropdown-menu.pull-right:after { + left: auto; + right: 7px; +} + +.bootstrap-datetimepicker-widget .list-unstyled { + margin: 0; +} + +.bootstrap-datetimepicker-widget a[data-action] { + padding: 0; + border-width: 0; + color: #fff; + background-color: transparent; +} + +.bootstrap-datetimepicker-widget a[data-action="togglePicker"], +.bootstrap-datetimepicker-widget a[data-action="togglePicker"]:hover { + color: #e14eca; +} + +.bootstrap-datetimepicker-widget a[data-action]:hover { + background-color: transparent; +} + +.bootstrap-datetimepicker-widget a[data-action]:active { + box-shadow: none; +} + +.bootstrap-datetimepicker-widget .timepicker-hour, +.bootstrap-datetimepicker-widget .timepicker-minute, +.bootstrap-datetimepicker-widget .timepicker-second { + width: 40px; + height: 40px; + line-height: 40px; + font-weight: 300; + font-size: 1.5em; + margin: 3px; + border-radius: 50%; + color: #9A9A9A; +} + +.bootstrap-datetimepicker-widget button[data-action] { + width: 38px; + height: 38px; + padding: 0; + box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.2); +} + +.bootstrap-datetimepicker-widget .btn { + margin: 0 !important; +} + +.bootstrap-datetimepicker-widget .btn[data-action="incrementHours"]::after { + content: "Increment Hours"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="incrementMinutes"]::after { + content: "Increment Minutes"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="decrementHours"]::after { + content: "Decrement Hours"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="decrementMinutes"]::after { + content: "Decrement Minutes"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="showHours"]::after { + content: "Show Hours"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="showMinutes"]::after { + content: "Show Minutes"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="togglePeriod"]::after { + content: "Toggle AM/PM"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="clear"]::after { + content: "Clear the picker"; +} + +.bootstrap-datetimepicker-widget .btn[data-action="today"]::after { + content: "Set the date to today"; +} + +.bootstrap-datetimepicker-widget .picker-switch { + text-align: center; + border-radius: 3px; + color: #e14eca; +} + +.bootstrap-datetimepicker-widget .picker-switch::after { + content: "Toggle Date and Time Screens"; +} + +.bootstrap-datetimepicker-widget .picker-switch td { + padding: 0; + margin: 0; + height: auto; + width: auto; + line-height: inherit; +} + +.bootstrap-datetimepicker-widget .picker-switch td span { + line-height: 2.5; + height: 2.5em; + width: 100%; + border-radius: 3px; + margin: 2px 0px !important; +} + +.bootstrap-datetimepicker-widget table { + width: 100%; + margin: 0; + text-align: center; +} + +.bootstrap-datetimepicker-widget table td>div, +.bootstrap-datetimepicker-widget table th>div { + text-align: center; +} + +.bootstrap-datetimepicker-widget table th { + height: 20px; + line-height: 20px; + width: 20px; + font-weight: 300; +} + +.bootstrap-datetimepicker-widget table th.picker-switch { + width: 145px; +} + +.bootstrap-datetimepicker-widget table th.disabled, +.bootstrap-datetimepicker-widget table th.disabled:hover { + background: none; + color: #cfcfca; + cursor: not-allowed; +} + +.bootstrap-datetimepicker-widget table th.prev span, +.bootstrap-datetimepicker-widget table th.next span { + border-radius: 4px; + height: 27px; + width: 27px; + line-height: 28px; + font-size: 12px; + border-radius: 50%; + text-align: center; + color: #e14eca; +} + +.bootstrap-datetimepicker-widget table th.prev::after { + content: "Previous Month"; +} + +.bootstrap-datetimepicker-widget table th.next::after { + content: "Next Month"; +} + +.bootstrap-datetimepicker-widget table th.dow { + text-align: center; + color: #e14eca; + padding-bottom: 5px; + padding-top: 10px; +} + +.bootstrap-datetimepicker-widget table thead tr:first-child th { + cursor: pointer; +} + +.bootstrap-datetimepicker-widget table thead tr:first-child th:hover span, +.bootstrap-datetimepicker-widget table thead tr:first-child th.picker-switch:hover { + background: #eee; +} + +.bootstrap-datetimepicker-widget table td.cw>div { + font-size: .8em; + height: 20px; + line-height: 20px; + color: #cfcfca; +} + +.bootstrap-datetimepicker-widget table td.day>div, +.bootstrap-datetimepicker-widget table td.minute>div, +.bootstrap-datetimepicker-widget table td.hour>div { + height: 30px; + line-height: 2.2; + width: 30px; + text-align: center; + padding: 0px; + border-radius: 50%; + margin: 0 auto; + z-index: -1; + color: #9A9A9A; + position: relative; + font-weight: 300; + font-size: 14px; + border: none; + cursor: pointer; + -webkit-transition: all 300ms ease 0s; + -moz-transition: all 300ms ease 0s; + -o-transition: all 300ms ease 0s; + -ms-transition: all 300ms ease 0s; + transition: all 300ms ease 0s; +} + +.bootstrap-datetimepicker-widget table td.day:hover>div, +.bootstrap-datetimepicker-widget table td.hour:hover>div, +.bootstrap-datetimepicker-widget table td.minute:hover>div, +.bootstrap-datetimepicker-widget table td.second:hover>div { + background: #eee; + cursor: pointer; +} + +.bootstrap-datetimepicker-widget table td.old>div, +.bootstrap-datetimepicker-widget table td.new>div { + color: #344675; +} + +.bootstrap-datetimepicker-widget table td.today>div:before { + content: ''; + display: inline-block; + border: 0 0 7px 7px solid transparent; + border-bottom-color: #68B3C8; + border-top-color: rgba(0, 0, 0, 0.2); + position: absolute; + bottom: 4px; + right: 4px; +} + +.bootstrap-datetimepicker-widget table td.active>div, +.bootstrap-datetimepicker-widget table td.active:hover>div { + background-color: #e14eca; + color: #ffffff; + box-shadow: 0px 1px 10px 0px rgba(0, 0, 0, 0.2); +} + +.bootstrap-datetimepicker-widget table td.active.today:before>div { + border-bottom-color: #ffffff; +} + +.bootstrap-datetimepicker-widget table td.disabled>div, +.bootstrap-datetimepicker-widget table td.disabled:hover>div { + background: none; + color: #cfcfca; + cursor: not-allowed; +} + +.bootstrap-datetimepicker-widget table td span { + display: inline-block; + width: 40px; + height: 40px; + line-height: 40px; + margin: 0 3px; + cursor: pointer; + border-radius: 50%; + text-align: center; +} + +.bootstrap-datetimepicker-widget table td span.active { + background-color: #e14eca; + color: #FFFFFF; +} + +.bootstrap-datetimepicker-widget table td span.old { + color: #cfcfca; +} + +.bootstrap-datetimepicker-widget table td span.disabled, +.bootstrap-datetimepicker-widget table td span.disabled:hover { + background: none; + color: #cfcfca; + cursor: not-allowed; +} + +.bootstrap-datetimepicker-widget .timepicker-picker span, +.bootstrap-datetimepicker-widget .timepicker-hours span, +.bootstrap-datetimepicker-widget .timepicker-minutes span { + border-radius: 50% !important; +} + +.bootstrap-datetimepicker-widget.usetwentyfour td.hour { + height: 27px; + line-height: 27px; +} + +.input-group.date .input-group-addon { + cursor: pointer; +} + +.table-condensed>tbody>tr>td, +.table-condensed>tbody>tr>th, +.table-condensed>tfoot>tr>td, +.table-condensed>tfoot>tr>th, +.table-condensed>thead>tr>td, +.table-condensed>thead>tr>th { + padding: 1px; + text-align: center; + z-index: 1; + cursor: pointer; +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget .picker-switch, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table th.prev span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table th.next span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.day>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget a[data-action="togglePicker"], +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget a[data-action="togglePicker"]:hover, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget .timepicker-hours span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget .timepicker-minutes span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget .separator, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.minute>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.hour>div { + color: #9A9A9A; +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table th.dow { + color: rgba(255, 255, 255, 0.8); +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.old>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.new>div { + color: rgba(255, 255, 255, 0.4); +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget button[data-action] { + background-color: #ffffff; +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.active:hover>div { + background-color: #ffffff; +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td:not(.active).day:hover>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.hour:hover>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.minute:hover>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td.second:hover>div, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table td span:hover { + background: rgba(255, 255, 255, 0.2); +} + +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table thead tr:first-child th:hover span, +input.datetimepicker[data-color]+.bootstrap-datetimepicker-widget table thead tr:first-child th.picker-switch:hover { + background-color: rgba(255, 255, 255, 0.2); +} + +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget { + background-color: #e14eca; +} + +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget table td.active:hover>div, +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget button[data-action], +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget.dropdown-menu.top:after, +input.datetimepicker[data-color="orange"]+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + color: #e14eca; +} + +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget { + background-color: #1d8cf8; +} + +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget table td.active:hover>div, +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget button[data-action], +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget.dropdown-menu.top:after, +input.datetimepicker[data-color="blue"]+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + color: #1d8cf8; +} + +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget { + background-color: #00f2c3; +} + +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget table td.active:hover>div, +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget button[data-action], +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget.dropdown-menu.top:after, +input.datetimepicker[data-color="green"]+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + color: #00f2c3; +} + +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget { + background-color: #fd5d93; +} + +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget table td.active:hover>div, +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget button[data-action], +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget.dropdown-menu.top:after, +input.datetimepicker[data-color="red"]+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + color: #fd5d93; +} + +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget { + background-color: #ff8d72; +} + +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget table td.active>div, +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget table td.active:hover>div, +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget button[data-action], +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget.dropdown-menu.top:after, +input.datetimepicker[data-color="yellow"]+.bootstrap-datetimepicker-widget.dropdown-menu.bottom:before { + color: #ff8d72; +} + +.bootstrap-datetimepicker-widget.dropdown-black .datepicker-days .day, +.bootstrap-datetimepicker-widget.dropdown-black .datepicker-days .minute { + color: rgba(255, 255, 255, 0.7); +} + +.bootstrap-datetimepicker-widget.dropdown-black .datepicker-days .day:hover, +.bootstrap-datetimepicker-widget.dropdown-black .datepicker-days .minute:hover { + color: #212529; +} + +.bootstrap-datetimepicker-widget.dropdown-black table td span, +.bootstrap-datetimepicker-widget.dropdown-black table td.minute, +.bootstrap-datetimepicker-widget.dropdown-black table td.hour { + color: rgba(255, 255, 255, 0.7); +} + +.bootstrap-datetimepicker-widget.dropdown-black table td span:hover, +.bootstrap-datetimepicker-widget.dropdown-black table td.minute:hover, +.bootstrap-datetimepicker-widget.dropdown-black table td.hour:hover { + background: transparent; + color: rgba(255, 255, 255, 0.4); +} + +.bootstrap-datetimepicker-widget.dropdown-black table td span:hover div, +.bootstrap-datetimepicker-widget.dropdown-black table td.minute:hover div, +.bootstrap-datetimepicker-widget.dropdown-black table td.hour:hover div { + background: transparent; +} + +.bootstrap-datetimepicker-widget.dropdown-black table td .btn[data-action] { + color: #ffffff; +} + +.bootstrap-datetimepicker-widget.dropdown-black table td .btn[data-action="togglePeriod"] { + background: transparent; +} + +/*! nouislider - 11.1.0 - 2018-04-02 11:18:13 */ + +/* Functional styling; + * These styles are required for noUiSlider to function. + * You don't need to change these rules to apply your design. + */ + +.noUi-target, +.noUi-target * { + -webkit-touch-callout: none; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-user-select: none; + -ms-touch-action: none; + touch-action: none; + -ms-user-select: none; + -moz-user-select: none; + user-select: none; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.noUi-target { + position: relative; + direction: ltr; +} + +.noUi-base, +.noUi-connects { + width: 100%; + height: 100%; + position: relative; + z-index: 1; +} + +/* Wrapper for all connect elements. + */ + +.noUi-connects { + overflow: hidden; + z-index: 0; +} + +.noUi-connect, +.noUi-origin { + will-change: transform; + position: absolute; + z-index: 1; + top: 0; + left: 0; + height: 100%; + width: 100%; + -ms-transform-origin: 0 0; + -webkit-transform-origin: 0 0; + transform-origin: 0 0; +} + +/* Offset direction + */ + +html:not([dir="rtl"]) .noUi-horizontal .noUi-origin { + left: auto; + right: 0; +} + +/* Give origins 0 height/width so they don't interfere with clicking the + * connect elements. + */ + +.noUi-vertical .noUi-origin { + width: 0; +} + +.noUi-horizontal .noUi-origin { + height: 0; +} + +.noUi-handle { + position: absolute; +} + +.noUi-state-tap .noUi-connect, +.noUi-state-tap .noUi-origin { + -webkit-transition: transform 0.3s; + transition: transform 0.3s; +} + +.noUi-state-drag * { + cursor: inherit !important; +} + +/* Slider size and handle placement; + */ + +.noUi-horizontal { + height: 1px; +} + +.noUi-horizontal .noUi-handle { + border-radius: 50%; + background-color: #ffffff; + box-shadow: 0 1px 13px 0 rgba(0, 0, 0, 0.2); + height: 15px; + width: 15px; + cursor: pointer; + margin-left: -10px; + margin-top: -7px; +} + +.noUi-vertical { + width: 18px; +} + +.noUi-vertical .noUi-handle { + width: 28px; + height: 34px; + left: -6px; + top: -17px; +} + +html:not([dir="rtl"]) .noUi-horizontal .noUi-handle { + right: -15px; + left: auto; + outline: none; +} + +/* Styling; + * Giving the connect element a border radius causes issues with using transform: scale + */ + +.noUi-target { + background-color: rgba(182, 182, 182, 0.3); + border-radius: 3px; +} + +.noUi-connects { + border-radius: 3px; +} + +.noUi-connect { + background: #344675; + border-radius: 3px; + -webkit-transition: background 450ms; + transition: background 450ms; +} + +/* Handles and cursors; + */ + +.noUi-draggable { + cursor: ew-resize; +} + +.noUi-vertical .noUi-draggable { + cursor: ns-resize; +} + +.noUi-handle { + border-radius: 3px; + background: #FFF; + cursor: default; + box-shadow: inset 0 0 1px #FFF, inset 0 1px 7px #EBEBEB, 0 3px 6px -3px #BBB; + -webkit-transition: 300ms ease 0s; + -moz-transition: 300ms ease 0s; + -ms-transition: 300ms ease 0s; + -o-transform: 300ms ease 0s; + transition: 300ms ease 0s; +} + +.noUi-active { + -webkit-transform: scale3d(1.5, 1.5, 1); + -moz-transform: scale3d(1.5, 1.5, 1); + -ms-transform: scale3d(1.5, 1.5, 1); + -o-transform: scale3d(1.5, 1.5, 1); + transform: scale3d(1.5, 1.5, 1); +} + +/* Disabled state; + */ + +[disabled] .noUi-connect { + background: #B8B8B8; +} + +[disabled].noUi-target, +[disabled].noUi-handle, +[disabled] .noUi-handle { + cursor: not-allowed; +} + +/* Base; + * + */ + +.noUi-pips, +.noUi-pips * { + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +.noUi-pips { + position: absolute; + color: #999; +} + +/* Values; + * + */ + +.noUi-value { + position: absolute; + white-space: nowrap; + text-align: center; +} + +.noUi-value-sub { + color: #ccc; + font-size: 10px; +} + +/* Markings; + * + */ + +.noUi-marker { + position: absolute; + background: #CCC; +} + +.noUi-marker-sub { + background: #AAA; +} + +.noUi-marker-large { + background: #AAA; +} + +/* Horizontal layout; + * + */ + +.noUi-pips-horizontal { + padding: 10px 0; + height: 80px; + top: 100%; + left: 0; + width: 100%; +} + +.noUi-value-horizontal { + -webkit-transform: translate(-50%, 50%, 0); + transform: translate(-50%, 50%, 0); +} + +.noUi-rtl .noUi-value-horizontal { + -webkit-transform: translate(50%, 50%); + transform: translate(50%, 50%); +} + +.noUi-marker-horizontal.noUi-marker { + margin-left: -1px; + width: 2px; + height: 5px; +} + +.noUi-marker-horizontal.noUi-marker-sub { + height: 10px; +} + +.noUi-marker-horizontal.noUi-marker-large { + height: 15px; +} + +/* Vertical layout; + * + */ + +.noUi-pips-vertical { + padding: 0 10px; + height: 100%; + top: 0; + left: 100%; +} + +.noUi-value-vertical { + -webkit-transform: translate3d(0, 50%, 0); + transform: translate3d(0, 50%, 0); + padding-left: 25px; +} + +.noUi-rtl .noUi-value-vertical { + -webkit-transform: translate(0, 50%); + transform: translate(0, 50%); +} + +.noUi-marker-vertical.noUi-marker { + width: 5px; + height: 2px; + margin-top: -1px; +} + +.noUi-marker-vertical.noUi-marker-sub { + width: 10px; +} + +.noUi-marker-vertical.noUi-marker-large { + width: 15px; +} + +.noUi-tooltip { + display: block; + position: absolute; + border: 1px solid #D9D9D9; + border-radius: 3px; + background: #fff; + color: #000; + padding: 5px; + text-align: center; + white-space: nowrap; +} + +.noUi-horizontal .noUi-tooltip { + -webkit-transform: translate(-50%, 0); + transform: translate(-50%, 0); + left: 50%; + bottom: 120%; +} + +.noUi-vertical .noUi-tooltip { + -webkit-transform: translate(0, -50%); + transform: translate(0, -50%); + top: 50%; + right: 120%; +} + +.slider.slider-neutral .noUi-connect, +.slider.slider-neutral.noUi-connect { + background-color: #ffffff; +} + +.slider.slider-neutral.noUi-target { + background-color: rgba(255, 255, 255, 0.3); +} + +.slider.slider-neutral .noUi-handle { + background-color: #ffffff; +} + +.slider.slider-primary .noUi-connect, +.slider.slider-primary.noUi-connect { + background-color: #e14eca; +} + +.slider.slider-primary.noUi-target { + background-color: rgba(249, 99, 50, 0.3); +} + +.slider.slider-primary .noUi-handle { + background-color: #e14eca; + box-shadow: 0px 0px 10px 0px #e14eca; +} + +.slider.slider-info .noUi-connect, +.slider.slider-info.noUi-connect { + background-color: #1d8cf8; +} + +.slider.slider-info.noUi-target { + background-color: rgba(44, 168, 255, 0.3); +} + +.slider.slider-info .noUi-handle { + background-color: #1d8cf8; + box-shadow: 0px 0px 10px 0px #1d8cf8; +} + +.slider.slider-success .noUi-connect, +.slider.slider-success.noUi-connect { + background-color: #00f2c3; +} + +.slider.slider-success.noUi-target { + background-color: rgba(24, 206, 15, 0.3); +} + +.slider.slider-success .noUi-handle { + background-color: #00f2c3; + box-shadow: 0px 0px 10px 0px #00f2c3; +} + +.slider.slider-warning .noUi-connect, +.slider.slider-warning.noUi-connect { + background-color: #ff8d72; +} + +.slider.slider-warning.noUi-target { + background-color: rgba(255, 178, 54, 0.3); +} + +.slider.slider-warning .noUi-handle { + background-color: #ff8d72; + box-shadow: 0px 0px 10px 0px #ff8d72; +} + +.slider.slider-danger .noUi-connect, +.slider.slider-danger.noUi-connect { + background-color: #fd5d93; +} + +.slider.slider-danger.noUi-target { + background-color: rgba(255, 54, 54, 0.3); +} + +.slider.slider-danger .noUi-handle { + background-color: #fd5d93; + box-shadow: 0px 0px 10px 0px #fd5d93; +} + +.fc { + direction: ltr; + text-align: left; +} + +.fc-rtl { + text-align: right; +} + +body .fc { + /* extra precedence to overcome jqui */ + font-size: 1em; +} + +/* Colors +--------------------------------------------------------------------------------------------------*/ + +.fc-unthemed th, +.fc-unthemed td, +.fc-unthemed thead, +.fc-unthemed tbody, +.fc-unthemed .fc-divider, +.fc-unthemed .fc-row, +.fc-unthemed .fc-content, +.fc-unthemed .fc-popover, +.fc-unthemed .fc-list-view, +.fc-unthemed .fc-list-heading td { + border-color: #2b3553; +} + +.fc-unthemed .fc-popover { + background-color: #ffffff; +} + +.fc-unthemed .fc-divider, +.fc-unthemed .fc-popover .fc-header, +.fc-unthemed .fc-list-heading td { + background: #344675; +} + +.fc-unthemed .fc-popover .fc-header .fc-close { + color: #666666; +} + +.fc-unthemed .fc-today { + background: #252e49; + color: #ffffff; +} + +.fc-highlight { + /* when user is selecting cells */ + background: #bce8f1; + opacity: .3; +} + +.fc-bgevent { + /* default look for background events */ + background: #8fdf82; + opacity: .3; +} + +.fc-nonbusiness { + /* default look for non-business-hours areas */ + /* will inherit .fc-bgevent's styles */ + background: #d7d7d7; +} + +/* Icons (inline elements with styled text that mock arrow icons) +--------------------------------------------------------------------------------------------------*/ + +.fc-icon { + display: inline-block; + height: 1em; + line-height: 1em; + font-size: 1em; + text-align: center; + overflow: hidden; + font-family: "Courier New", Courier, monospace; + /* don't allow browser text-selection */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* +Acceptable font-family overrides for individual icons: + "Arial", sans-serif + "Times New Roman", serif + +NOTE: use percentage font sizes or else old IE chokes +*/ + +.fc-icon:after { + position: relative; +} + +.fc-icon-left-single-arrow:after { + content: "\02039"; + font-weight: bold; + font-size: 200%; + top: -7%; +} + +.fc-icon-right-single-arrow:after { + content: "\0203A"; + font-weight: bold; + font-size: 200%; + top: -7%; +} + +.fc-icon-left-double-arrow:after { + content: "\000AB"; + font-size: 160%; + top: -7%; +} + +.fc-icon-right-double-arrow:after { + content: "\000BB"; + font-size: 160%; + top: -7%; +} + +.fc-icon-left-triangle:after { + content: "\25C4"; + font-size: 125%; + top: 3%; +} + +.fc-icon-right-triangle:after { + content: "\25BA"; + font-size: 125%; + top: 3%; +} + +.fc-icon-down-triangle:after { + content: "\25BC"; + font-size: 125%; + top: 2%; +} + +.fc-icon-x:after { + content: "\000D7"; + font-size: 200%; + top: 6%; +} + +/* Buttons (styled