ositah 25.6.dev1__py3-none-any.whl → 25.9.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ositah might be problematic. Click here for more details.

Files changed (42) hide show
  1. ositah/app.py +17 -17
  2. ositah/apps/analysis.py +785 -785
  3. ositah/apps/configuration/callbacks.py +916 -916
  4. ositah/apps/configuration/main.py +546 -546
  5. ositah/apps/configuration/parameters.py +74 -74
  6. ositah/apps/configuration/tools.py +112 -112
  7. ositah/apps/export.py +1208 -1191
  8. ositah/apps/validation/callbacks.py +240 -240
  9. ositah/apps/validation/main.py +89 -89
  10. ositah/apps/validation/parameters.py +25 -25
  11. ositah/apps/validation/tables.py +646 -646
  12. ositah/apps/validation/tools.py +552 -552
  13. ositah/assets/arrow_down_up.svg +3 -3
  14. ositah/assets/ositah.css +53 -53
  15. ositah/assets/sort_ascending.svg +4 -4
  16. ositah/assets/sort_descending.svg +5 -5
  17. ositah/assets/sorttable.js +499 -499
  18. ositah/main.py +449 -449
  19. ositah/ositah.example.cfg +229 -229
  20. ositah/static/style.css +53 -53
  21. ositah/templates/base.html +22 -22
  22. ositah/templates/bootstrap_login.html +38 -38
  23. ositah/templates/login_form.html +26 -26
  24. ositah/utils/agents.py +124 -124
  25. ositah/utils/authentication.py +287 -287
  26. ositah/utils/cache.py +19 -19
  27. ositah/utils/core.py +13 -13
  28. ositah/utils/exceptions.py +64 -64
  29. ositah/utils/hito_db.py +51 -51
  30. ositah/utils/hito_db_model.py +253 -253
  31. ositah/utils/menus.py +339 -339
  32. ositah/utils/period.py +139 -139
  33. ositah/utils/projects.py +1178 -1178
  34. ositah/utils/teams.py +42 -42
  35. ositah/utils/utils.py +474 -474
  36. {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/METADATA +149 -150
  37. ositah-25.9.dev1.dist-info/RECORD +46 -0
  38. {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/licenses/LICENSE +29 -29
  39. ositah-25.6.dev1.dist-info/RECORD +0 -46
  40. {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/WHEEL +0 -0
  41. {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/entry_points.txt +0 -0
  42. {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/top_level.txt +0 -0
ositah/ositah.example.cfg CHANGED
@@ -1,229 +1,229 @@
1
- # Configuration file for OSITAH application
2
-
3
- # Where to read the data
4
- hito:
5
- db:
6
- # type muste be sqlite or mysql
7
- #type: sqlite
8
- type: mysql
9
- # location must be a filename for sqlite or server/database for mysql
10
- #location: 'c:\temp\hito-dev.sqlite'
11
- location: 'localhost/hito'
12
- user: hitousery
13
- password: *verysecret*
14
- # After this time (in seconds), DB connection is recycled
15
- inactivity_timeout: 900
16
- # For some unidentified reason, in SQLite, "stagiaire" carriere type is not found with an exact match...
17
- agent_query: |
18
- SELECT
19
- DISTINCT(agent.id)
20
- , agent.nom
21
- , agent.prenom
22
- , agent.team_id as team_id
23
- , team.nom as team
24
- , agent.statut as statut
25
- , agent.email as email
26
- , agent.corps_exercice_metier as cem
27
- , CASE
28
- WHEN agent.email_auth IS NULL THEN agent.email
29
- ELSE agent.email_auth
30
- END AS email_auth
31
- FROM
32
- agent
33
- LEFT JOIN team on team.id = agent.team_id
34
- JOIN carriere on carriere.agent_id = agent.id
35
- WHERE
36
- agent.archive = 0
37
- AND
38
- carriere.date_debut = (
39
- SELECT MAX(c.date_debut)
40
- FROM carriere AS c
41
- WHERE
42
- agent.id = c.agent_id
43
- AND
44
- (c.type IN ("apprenti", "cdd", "emeritat", "stagiaire", "visiteur") OR c.type LIKE "arrivee%" OR c.type LIKE "stagiaire%")
45
- )
46
- AND
47
- carriere.date_debut <= $$TODAY$$
48
- # Patterns to identify the different activity categories from the activity name
49
- categories:
50
- consultance: 'Consultances & Expertises'
51
- enseignement: 'Enseignement Supérieur'
52
- local_project: 'Local projects'
53
- service: 'Services & Support'
54
- # Time unit (default is hours for categories not listed). Supported values h(our), w(eek)
55
- time_unit:
56
- local_project: w
57
- nsip_project: w
58
- service: w
59
- # Column titles to use when displying the information about categories and agents
60
- titles:
61
- consultance: 'Consultance'
62
- declarations_number: 'Déclarations effectuées'
63
- enseignement: 'Enseignement'
64
- fullname: 'Agent'
65
- local_project: 'projets locaux'
66
- missings_number: 'Déclarations obligatoires manquantes'
67
- nsip_project: 'projets NSIP'
68
- percent_global: 'Temps déclarés (%)'
69
- service: 'Service'
70
- statut: 'Statut'
71
- team: 'Equipe'
72
-
73
- # Web server information
74
- server:
75
- port: 9999
76
- authentication:
77
- ldap:
78
- # uri can be space-separated list
79
- uri: 'ldaps://ijclabad1.ijclab.in2p3.fr ldaps://ijclabad2.ijclab.in2p3.fr ldaps://ccijclabad01.ijclab.in2p3.fr'
80
- base_dn: 'ou=ijclab,dc=ijclab,dc=in2p3,dc=fr'
81
- bind_dn: 'CN=Non-Interactive Queries,OU=SI,OU=Services techniques,OU=Laboratoire,DC=lal,DC=in2p3,DC=fr'
82
- password: 'ldap_bind_password'
83
- provider_name: "MyLab account"
84
-
85
- # Options related to declarations
86
- declaration:
87
- # Default date must be a date included into the selected validation period. It is a default, if no other explicitly selection
88
- #default_date: 2021-07-01
89
- # Delay (in days) before setting the default period to the next one, after the end of the period. Ignored if default_date is defined
90
- period_change_delay: 60
91
- # max_hours specifies the upper valid value for activities declared in hours
92
- max_hours: 400
93
- # Agent statut whose declaration is not mandatory
94
- optional_statutes:
95
- - benevole
96
- - emerite
97
- - stagiaire
98
- - visiteur
99
- # Team who are not required to declare: values must match the beginning of the team name
100
- optional_teams:
101
- - Administration
102
- - Support
103
- # Thresholds used to mark declarations as low, suspect or good
104
- # The value is the upper bound for the corresponding class
105
- # Thresholds are declared by semester as they are typically different
106
- thresholds:
107
- # S1: assume ho holidays as the default
108
- s1:
109
- low: 50
110
- suspect: 80
111
- good: 100
112
- # S2: assume 4 weeks of holidays by default (summer + Christmas)
113
- s2:
114
- low: 50
115
- suspect: 70
116
- good: 85
117
-
118
- # Information related to declaration analysis
119
- analysis:
120
- contributions_sorted_by_name: False
121
-
122
- # Information related to validation
123
- validation:
124
- override_period:
125
- - ROLE_SUPER_ADMIN
126
- - ROLE_PROJECT_MANAGER
127
-
128
- # Users with specific rights
129
- roles:
130
- # List users (with their connection email) who are given a read-only access to OSITAH (no validation right)
131
- read-only:
132
- - user@my.dom.ain
133
-
134
- # NSIP related parameters
135
-
136
- nsip:
137
- # The following sections configures information about NSIP service
138
- # For each part of the API, there must be a base URL and some operation sub-urls
139
- agent_api:
140
- base_url: /api_labo/agent
141
- declaration_add: /declaration/create
142
- declaration_delete: /declaration/delete
143
- declaration_update: /declaration/update
144
- institute_api:
145
- base_url: /api_institut
146
- declaration_period_list: /declaration_period/list
147
- lab_api:
148
- base_url: /api_labo
149
- agent_list: /agent/list
150
- declaration_list: /declaration/list
151
- project_list: /project/list
152
- reference_list: /reference/list
153
- server_url: https://nsip.in2p3.fr
154
- token: nsip_lab_token
155
-
156
- # teaching dictionary allows to define a ratio to apply to teaching activities.
157
- # It also allows to define (optionally) a list of CEM that this ratio must
158
- # be applied to (instead of all agents).
159
- # master allows to define the masterproject identifying teaching activities
160
- # (default: Enseignement Supérieur)
161
- teaching:
162
- masterproject: Enseignement Supérieur
163
- ratio: 4.4
164
- cem:
165
- - cem_enseignant_chercheur
166
-
167
- # Match against NSIP reference types to define the OSITAH masterproject.
168
- # A reference with no value will cause the entry to be ignored by OSITAH.
169
- # Reference types can be regex.
170
- reference_masterprojects:
171
- consultancyexpertise: Consultances & Expertises
172
- highereducation: Enseignement Supérieur
173
- servicesupport: Services & Support
174
-
175
-
176
- # project_teams allow to define a list of teams for a project
177
- # The project name is matched against the list of defined projects and must be a regex matching
178
- # the beginning of the project name. A team can be specified as a regex.
179
- # Example below is for IJCLab: update according to your needs.
180
- project_teams:
181
- Consultances & Expertises:
182
- - .*
183
- Enseignement Supérieur:
184
- - Accélérateurs.*
185
- - A2C.*
186
- - Direction
187
- - Energie.*
188
- - Ingénierie.*
189
- - Nucléaire.*
190
- - PHE.*
191
- - Plateforme.*
192
- - Santé.*
193
- - Théorie.*
194
- Services & Support / Assurance-Qualité:
195
- - Support - Projets
196
- Services & Support / Budget-Finances:
197
- - Administration - Division financière
198
- Services & Support / Communication-Documentation:
199
- - Support - Communication
200
- #Services & Support / Environnement:
201
- Services & Support / Formation:
202
- - Support - Enseignement
203
- #Services & Support / Hygiène-Sécurité:
204
- Services & Support / Management:
205
- - .*
206
- Services & Support / Partenariat:
207
- - Support - STIRI
208
- Services & Support / Patrimoine:
209
- - Support - Infrastructure
210
- Services & Support / Radioprotection:
211
- - Support - Service SPR
212
- Services & Support / Ressources Humaines:
213
- - Administration.* - RH.*
214
- Services & Support / Secrétariat:
215
- - Direction - Assistantes
216
- #Services & Support / Services Généraux:
217
- Services & Support / Support-Biologie:
218
- - Santé.*
219
- Services & Support / Support-Electronique:
220
- - Ingénierie - Electronique.*
221
- Services & Support / Support-Informatique:
222
- - Ingénierie - Informatique
223
- Services & Support / Support-Instrumentation:
224
- - Ingénierie - Détecteurs.*
225
- Services & Support / Support-Mécanique:
226
- - Ingénierie - Mécanique
227
- #Services & Support / Sûreté:
228
- Services & Support / Valorisation:
229
- - Support - STIRI
1
+ # Configuration file for OSITAH application
2
+
3
+ # Where to read the data
4
+ hito:
5
+ db:
6
+ # type muste be sqlite or mysql
7
+ #type: sqlite
8
+ type: mysql
9
+ # location must be a filename for sqlite or server/database for mysql
10
+ #location: 'c:\temp\hito-dev.sqlite'
11
+ location: 'localhost/hito'
12
+ user: hitousery
13
+ password: *verysecret*
14
+ # After this time (in seconds), DB connection is recycled
15
+ inactivity_timeout: 900
16
+ # For some unidentified reason, in SQLite, "stagiaire" carriere type is not found with an exact match...
17
+ agent_query: |
18
+ SELECT
19
+ DISTINCT(agent.id)
20
+ , agent.nom
21
+ , agent.prenom
22
+ , agent.team_id as team_id
23
+ , team.nom as team
24
+ , agent.statut as statut
25
+ , agent.email as email
26
+ , agent.corps_exercice_metier as cem
27
+ , CASE
28
+ WHEN agent.email_auth IS NULL THEN agent.email
29
+ ELSE agent.email_auth
30
+ END AS email_auth
31
+ FROM
32
+ agent
33
+ LEFT JOIN team on team.id = agent.team_id
34
+ JOIN carriere on carriere.agent_id = agent.id
35
+ WHERE
36
+ agent.archive = 0
37
+ AND
38
+ carriere.date_debut = (
39
+ SELECT MAX(c.date_debut)
40
+ FROM carriere AS c
41
+ WHERE
42
+ agent.id = c.agent_id
43
+ AND
44
+ (c.type IN ("apprenti", "cdd", "emeritat", "stagiaire", "visiteur") OR c.type LIKE "arrivee%" OR c.type LIKE "stagiaire%")
45
+ )
46
+ AND
47
+ carriere.date_debut <= $$TODAY$$
48
+ # Patterns to identify the different activity categories from the activity name
49
+ categories:
50
+ consultance: 'Consultances & Expertises'
51
+ enseignement: 'Enseignement Supérieur'
52
+ local_project: 'Local projects'
53
+ service: 'Services & Support'
54
+ # Time unit (default is hours for categories not listed). Supported values h(our), w(eek)
55
+ time_unit:
56
+ local_project: w
57
+ nsip_project: w
58
+ service: w
59
+ # Column titles to use when displying the information about categories and agents
60
+ titles:
61
+ consultance: 'Consultance'
62
+ declarations_number: 'Déclarations effectuées'
63
+ enseignement: 'Enseignement'
64
+ fullname: 'Agent'
65
+ local_project: 'projets locaux'
66
+ missings_number: 'Déclarations obligatoires manquantes'
67
+ nsip_project: 'projets NSIP'
68
+ percent_global: 'Temps déclarés (%)'
69
+ service: 'Service'
70
+ statut: 'Statut'
71
+ team: 'Equipe'
72
+
73
+ # Web server information
74
+ server:
75
+ port: 9999
76
+ authentication:
77
+ ldap:
78
+ # uri can be space-separated list
79
+ uri: 'ldaps://ijclabad1.ijclab.in2p3.fr ldaps://ijclabad2.ijclab.in2p3.fr ldaps://ccijclabad01.ijclab.in2p3.fr'
80
+ base_dn: 'ou=ijclab,dc=ijclab,dc=in2p3,dc=fr'
81
+ bind_dn: 'CN=Non-Interactive Queries,OU=SI,OU=Services techniques,OU=Laboratoire,DC=lal,DC=in2p3,DC=fr'
82
+ password: 'ldap_bind_password'
83
+ provider_name: "MyLab account"
84
+
85
+ # Options related to declarations
86
+ declaration:
87
+ # Default date must be a date included into the selected validation period. It is a default, if no other explicitly selection
88
+ #default_date: 2021-07-01
89
+ # Delay (in days) before setting the default period to the next one, after the end of the period. Ignored if default_date is defined
90
+ period_change_delay: 60
91
+ # max_hours specifies the upper valid value for activities declared in hours
92
+ max_hours: 400
93
+ # Agent statut whose declaration is not mandatory
94
+ optional_statutes:
95
+ - benevole
96
+ - emerite
97
+ - stagiaire
98
+ - visiteur
99
+ # Team who are not required to declare: values must match the beginning of the team name
100
+ optional_teams:
101
+ - Administration
102
+ - Support
103
+ # Thresholds used to mark declarations as low, suspect or good
104
+ # The value is the upper bound for the corresponding class
105
+ # Thresholds are declared by semester as they are typically different
106
+ thresholds:
107
+ # S1: assume ho holidays as the default
108
+ s1:
109
+ low: 50
110
+ suspect: 80
111
+ good: 100
112
+ # S2: assume 4 weeks of holidays by default (summer + Christmas)
113
+ s2:
114
+ low: 50
115
+ suspect: 70
116
+ good: 85
117
+
118
+ # Information related to declaration analysis
119
+ analysis:
120
+ contributions_sorted_by_name: False
121
+
122
+ # Information related to validation
123
+ validation:
124
+ override_period:
125
+ - ROLE_SUPER_ADMIN
126
+ - ROLE_PROJECT_MANAGER
127
+
128
+ # Users with specific rights
129
+ roles:
130
+ # List users (with their connection email) who are given a read-only access to OSITAH (no validation right)
131
+ read-only:
132
+ - user@my.dom.ain
133
+
134
+ # NSIP related parameters
135
+
136
+ nsip:
137
+ # The following sections configures information about NSIP service
138
+ # For each part of the API, there must be a base URL and some operation sub-urls
139
+ agent_api:
140
+ base_url: /api_labo/agent
141
+ declaration_add: /declaration/create
142
+ declaration_delete: /declaration/delete
143
+ declaration_update: /declaration/update
144
+ institute_api:
145
+ base_url: /api_institut
146
+ declaration_period_list: /declaration_period/list
147
+ lab_api:
148
+ base_url: /api_labo
149
+ agent_list: /agent/list
150
+ declaration_list: /declaration/list
151
+ project_list: /project/list
152
+ reference_list: /reference/list
153
+ server_url: https://nsip.in2p3.fr
154
+ token: nsip_lab_token
155
+
156
+ # teaching dictionary allows to define a ratio to apply to teaching activities.
157
+ # It also allows to define (optionally) a list of CEM that this ratio must
158
+ # be applied to (instead of all agents).
159
+ # master allows to define the masterproject identifying teaching activities
160
+ # (default: Enseignement Supérieur)
161
+ teaching:
162
+ masterproject: Enseignement Supérieur
163
+ ratio: 4.4
164
+ cem:
165
+ - cem_enseignant_chercheur
166
+
167
+ # Match against NSIP reference types to define the OSITAH masterproject.
168
+ # A reference with no value will cause the entry to be ignored by OSITAH.
169
+ # Reference types can be regex.
170
+ reference_masterprojects:
171
+ consultancyexpertise: Consultances & Expertises
172
+ highereducation: Enseignement Supérieur
173
+ servicesupport: Services & Support
174
+
175
+
176
+ # project_teams allow to define a list of teams for a project
177
+ # The project name is matched against the list of defined projects and must be a regex matching
178
+ # the beginning of the project name. A team can be specified as a regex.
179
+ # Example below is for IJCLab: update according to your needs.
180
+ project_teams:
181
+ Consultances & Expertises:
182
+ - .*
183
+ Enseignement Supérieur:
184
+ - Accélérateurs.*
185
+ - A2C.*
186
+ - Direction
187
+ - Energie.*
188
+ - Ingénierie.*
189
+ - Nucléaire.*
190
+ - PHE.*
191
+ - Plateforme.*
192
+ - Santé.*
193
+ - Théorie.*
194
+ Services & Support / Assurance-Qualité:
195
+ - Support - Projets
196
+ Services & Support / Budget-Finances:
197
+ - Administration - Division financière
198
+ Services & Support / Communication-Documentation:
199
+ - Support - Communication
200
+ #Services & Support / Environnement:
201
+ Services & Support / Formation:
202
+ - Support - Enseignement
203
+ #Services & Support / Hygiène-Sécurité:
204
+ Services & Support / Management:
205
+ - .*
206
+ Services & Support / Partenariat:
207
+ - Support - STIRI
208
+ Services & Support / Patrimoine:
209
+ - Support - Infrastructure
210
+ Services & Support / Radioprotection:
211
+ - Support - Service SPR
212
+ Services & Support / Ressources Humaines:
213
+ - Administration.* - RH.*
214
+ Services & Support / Secrétariat:
215
+ - Direction - Assistantes
216
+ #Services & Support / Services Généraux:
217
+ Services & Support / Support-Biologie:
218
+ - Santé.*
219
+ Services & Support / Support-Electronique:
220
+ - Ingénierie - Electronique.*
221
+ Services & Support / Support-Informatique:
222
+ - Ingénierie - Informatique
223
+ Services & Support / Support-Instrumentation:
224
+ - Ingénierie - Détecteurs.*
225
+ Services & Support / Support-Mécanique:
226
+ - Ingénierie - Mécanique
227
+ #Services & Support / Sûreté:
228
+ Services & Support / Valorisation:
229
+ - Support - STIRI
ositah/static/style.css CHANGED
@@ -1,54 +1,54 @@
1
- /* CSS for the validation part of OSITAH */
2
-
3
- div.login_page {
4
- margin-left: 50px;
5
- }
6
-
7
- div.login_form_field {
8
- font-weight: bold;
9
- margin-top: 20px;
10
- }
11
-
12
- input.login_button {
13
- margin-top: 30px;
14
- margin-left: 45px;
15
- }
16
-
17
- ul.flash-message {
18
- display: inline-block;
19
- list-style-type: none;
20
- padding-left: 0;
21
- }
22
-
23
-
24
- /* CSS for Dash components */
25
-
26
- .team_list_dropdown {
27
- margin-top: 15px;
28
- }
29
-
30
- .validated_hito_missing {
31
- background-color: tomato;
32
- }
33
-
34
- table.sortable th::after, th.sorttable_sorted::after, th.sorttable_sorted_reverse::after {
35
- content: " ";
36
- display: inline-block;
37
- width: 24px;
38
- height: 24px;
39
- }
40
-
41
- table.sortable th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
42
- content: url("arrow_down_up.svg");
43
- }
44
-
45
- th.sorttable_sorted::after {
46
- background: url("sort_ascending.svg");
47
- background-size: contain;
48
- }
49
- th.sorttable_sorted_reverse::after {
50
- background: url("sort_descending.svg");
51
- background-size: cover;
52
- }
53
-
1
+ /* CSS for the validation part of OSITAH */
2
+
3
+ div.login_page {
4
+ margin-left: 50px;
5
+ }
6
+
7
+ div.login_form_field {
8
+ font-weight: bold;
9
+ margin-top: 20px;
10
+ }
11
+
12
+ input.login_button {
13
+ margin-top: 30px;
14
+ margin-left: 45px;
15
+ }
16
+
17
+ ul.flash-message {
18
+ display: inline-block;
19
+ list-style-type: none;
20
+ padding-left: 0;
21
+ }
22
+
23
+
24
+ /* CSS for Dash components */
25
+
26
+ .team_list_dropdown {
27
+ margin-top: 15px;
28
+ }
29
+
30
+ .validated_hito_missing {
31
+ background-color: tomato;
32
+ }
33
+
34
+ table.sortable th::after, th.sorttable_sorted::after, th.sorttable_sorted_reverse::after {
35
+ content: " ";
36
+ display: inline-block;
37
+ width: 24px;
38
+ height: 24px;
39
+ }
40
+
41
+ table.sortable th:not(.sorttable_sorted):not(.sorttable_sorted_reverse):not(.sorttable_nosort):after {
42
+ content: url("arrow_down_up.svg");
43
+ }
44
+
45
+ th.sorttable_sorted::after {
46
+ background: url("sort_ascending.svg");
47
+ background-size: contain;
48
+ }
49
+ th.sorttable_sorted_reverse::after {
50
+ background: url("sort_descending.svg");
51
+ background-size: cover;
52
+ }
53
+
54
54
  #sorttable_sortfwdind, #sorttable_sortrevind { display: none; }
@@ -1,22 +1,22 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>OSITAH Login page</title>
6
- <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
7
- <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
- </head>
9
- <body>
10
- {% with messages = get_flashed_messages(with_categories=True) %}
11
- {% if messages %}
12
- <ul class="bg-warning flash-message">
13
- {%- for category, message in messages %}
14
- <li>{{ category }}: {{ message }}</li>
15
- {% endfor -%}
16
- </ul>
17
- {% endif %}
18
- {% endwith %}
19
-
20
- {% block content %}{% endblock %}
21
- </body>
22
- </html>
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>OSITAH Login page</title>
6
+ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
+ </head>
9
+ <body>
10
+ {% with messages = get_flashed_messages(with_categories=True) %}
11
+ {% if messages %}
12
+ <ul class="bg-warning flash-message">
13
+ {%- for category, message in messages %}
14
+ <li>{{ category }}: {{ message }}</li>
15
+ {% endfor -%}
16
+ </ul>
17
+ {% endif %}
18
+ {% endwith %}
19
+
20
+ {% block content %}{% endblock %}
21
+ </body>
22
+ </html>