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.
- ositah/app.py +17 -17
- ositah/apps/analysis.py +785 -785
- ositah/apps/configuration/callbacks.py +916 -916
- ositah/apps/configuration/main.py +546 -546
- ositah/apps/configuration/parameters.py +74 -74
- ositah/apps/configuration/tools.py +112 -112
- ositah/apps/export.py +1208 -1191
- ositah/apps/validation/callbacks.py +240 -240
- ositah/apps/validation/main.py +89 -89
- ositah/apps/validation/parameters.py +25 -25
- ositah/apps/validation/tables.py +646 -646
- ositah/apps/validation/tools.py +552 -552
- ositah/assets/arrow_down_up.svg +3 -3
- ositah/assets/ositah.css +53 -53
- ositah/assets/sort_ascending.svg +4 -4
- ositah/assets/sort_descending.svg +5 -5
- ositah/assets/sorttable.js +499 -499
- ositah/main.py +449 -449
- ositah/ositah.example.cfg +229 -229
- ositah/static/style.css +53 -53
- ositah/templates/base.html +22 -22
- ositah/templates/bootstrap_login.html +38 -38
- ositah/templates/login_form.html +26 -26
- ositah/utils/agents.py +124 -124
- ositah/utils/authentication.py +287 -287
- ositah/utils/cache.py +19 -19
- ositah/utils/core.py +13 -13
- ositah/utils/exceptions.py +64 -64
- ositah/utils/hito_db.py +51 -51
- ositah/utils/hito_db_model.py +253 -253
- ositah/utils/menus.py +339 -339
- ositah/utils/period.py +139 -139
- ositah/utils/projects.py +1178 -1178
- ositah/utils/teams.py +42 -42
- ositah/utils/utils.py +474 -474
- {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/METADATA +149 -150
- ositah-25.9.dev1.dist-info/RECORD +46 -0
- {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/licenses/LICENSE +29 -29
- ositah-25.6.dev1.dist-info/RECORD +0 -46
- {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/WHEEL +0 -0
- {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/entry_points.txt +0 -0
- {ositah-25.6.dev1.dist-info → ositah-25.9.dev1.dist-info}/top_level.txt +0 -0
|
@@ -1,240 +1,240 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Dash callbacks for Validation sub-application
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from datetime import datetime
|
|
6
|
-
from uuid import uuid4
|
|
7
|
-
|
|
8
|
-
import dash_bootstrap_components as dbc
|
|
9
|
-
from dash import dcc, html
|
|
10
|
-
from dash.dependencies import MATCH, Input, Output, State
|
|
11
|
-
|
|
12
|
-
from ositah.app import app
|
|
13
|
-
from ositah.apps.validation.parameters import *
|
|
14
|
-
from ositah.apps.validation.tables import (
|
|
15
|
-
build_missing_agents_table,
|
|
16
|
-
build_statistics_table,
|
|
17
|
-
build_validation_table,
|
|
18
|
-
)
|
|
19
|
-
from ositah.apps.validation.tools import get_validation_data, project_declaration_snapshot
|
|
20
|
-
from ositah.utils.hito_db import get_db
|
|
21
|
-
from ositah.utils.menus import (
|
|
22
|
-
TEAM_SELECTED_VALUE_ID,
|
|
23
|
-
TEAM_SELECTION_DATE_ID,
|
|
24
|
-
VALIDATION_PERIOD_SELECTED_ID,
|
|
25
|
-
create_progress_bar,
|
|
26
|
-
)
|
|
27
|
-
from ositah.utils.period import get_validation_period_id
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@app.callback(
|
|
31
|
-
[
|
|
32
|
-
Output(TAB_ID_DECLARATION_STATS, "children"),
|
|
33
|
-
Output(TAB_ID_VALIDATION, "children"),
|
|
34
|
-
Output(TAB_ID_MISSING_AGENTS, "children"),
|
|
35
|
-
Output(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
36
|
-
Output(VALIDATION_SAVED_ACTIVE_TAB_ID, "data"),
|
|
37
|
-
],
|
|
38
|
-
[
|
|
39
|
-
Input(VALIDATION_LOAD_INDICATOR_ID, "data"),
|
|
40
|
-
Input(VALIDATION_TAB_MENU_ID, "active_tab"),
|
|
41
|
-
Input(TEAM_SELECTED_VALUE_ID, "data"),
|
|
42
|
-
Input(VALIDATION_DECLARATIONS_SELECTED_ID, "data"),
|
|
43
|
-
],
|
|
44
|
-
[
|
|
45
|
-
State(TEAM_SELECTION_DATE_ID, "data"),
|
|
46
|
-
State(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
47
|
-
State(VALIDATION_PERIOD_SELECTED_ID, "data"),
|
|
48
|
-
State(VALIDATION_SAVED_ACTIVE_TAB_ID, "data"),
|
|
49
|
-
],
|
|
50
|
-
prevent_initial_call=True,
|
|
51
|
-
)
|
|
52
|
-
def display_validation_tables(
|
|
53
|
-
load_in_progress,
|
|
54
|
-
active_tab: str,
|
|
55
|
-
team: str,
|
|
56
|
-
declaration_set: int,
|
|
57
|
-
team_selection_date,
|
|
58
|
-
previous_load_in_progress,
|
|
59
|
-
period_date: str,
|
|
60
|
-
previous_active_tab: str,
|
|
61
|
-
):
|
|
62
|
-
"""
|
|
63
|
-
Display active tab contents after a team or an active tab change. Exact action depends on
|
|
64
|
-
the value of the load in progress indicator. If it is equal to the previous value, it means
|
|
65
|
-
this is the start of the update process: progress bar is displayed and a dcc.Interval is
|
|
66
|
-
created to schedule again this callback after incrementing the load in progress indicator.
|
|
67
|
-
This causes the callback to be reentered and this time it triggers the real processing for
|
|
68
|
-
the tab resulting in the final update of the active tab contents. An empty content is
|
|
69
|
-
returned for inactive tabs.
|
|
70
|
-
|
|
71
|
-
:param load_in_progress: load in progress indicator
|
|
72
|
-
:param tab: select tab name
|
|
73
|
-
:param team: selected team
|
|
74
|
-
:param declaration_set: selected declaration set (all, validated or non-validated ones)
|
|
75
|
-
:param team_selection_date: last time the team selection was changed
|
|
76
|
-
:param previous_load_in_progress: previous value of the load_in_progress indicator
|
|
77
|
-
:param period_date: a date that must be inside the declaration period
|
|
78
|
-
:param previous_active_tab: previously active tab
|
|
79
|
-
:return: tab content (empty if the tab is not active)
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
tab_contents = []
|
|
83
|
-
|
|
84
|
-
# Be sure to fill the return values in the same order as Output are declared
|
|
85
|
-
tab_list = [TAB_ID_DECLARATION_STATS, TAB_ID_VALIDATION, TAB_ID_MISSING_AGENTS]
|
|
86
|
-
for tab in tab_list:
|
|
87
|
-
if team and len(team) > 0 and tab == active_tab:
|
|
88
|
-
if load_in_progress > previous_load_in_progress and active_tab == previous_active_tab:
|
|
89
|
-
if active_tab == TAB_ID_DECLARATION_STATS:
|
|
90
|
-
tab_contents.append(
|
|
91
|
-
build_statistics_table(team, team_selection_date, period_date)
|
|
92
|
-
)
|
|
93
|
-
elif active_tab == TAB_ID_VALIDATION:
|
|
94
|
-
tab_contents.append(
|
|
95
|
-
build_validation_table(
|
|
96
|
-
team, team_selection_date, declaration_set, period_date
|
|
97
|
-
)
|
|
98
|
-
)
|
|
99
|
-
elif active_tab == TAB_ID_MISSING_AGENTS:
|
|
100
|
-
tab_contents.append(
|
|
101
|
-
build_missing_agents_table(team, team_selection_date, period_date)
|
|
102
|
-
)
|
|
103
|
-
else:
|
|
104
|
-
tab_contents.append(
|
|
105
|
-
dbc.Alert("Erreur interne: tab non supporté"), color="warning"
|
|
106
|
-
)
|
|
107
|
-
previous_load_in_progress += 1
|
|
108
|
-
else:
|
|
109
|
-
component = html.Div(
|
|
110
|
-
[
|
|
111
|
-
create_progress_bar(team),
|
|
112
|
-
dcc.Interval(
|
|
113
|
-
id=VALIDATION_DISPLAY_INTERVAL_ID,
|
|
114
|
-
n_intervals=0,
|
|
115
|
-
max_intervals=1,
|
|
116
|
-
interval=500,
|
|
117
|
-
),
|
|
118
|
-
]
|
|
119
|
-
)
|
|
120
|
-
tab_contents.append(component)
|
|
121
|
-
else:
|
|
122
|
-
tab_contents.append("")
|
|
123
|
-
|
|
124
|
-
tab_contents.extend([previous_load_in_progress, active_tab])
|
|
125
|
-
|
|
126
|
-
return tab_contents
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@app.callback(
|
|
130
|
-
Output(VALIDATION_LOAD_INDICATOR_ID, "data"),
|
|
131
|
-
Input(VALIDATION_DISPLAY_INTERVAL_ID, "n_intervals"),
|
|
132
|
-
State(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
133
|
-
prevent_initial_call=True,
|
|
134
|
-
)
|
|
135
|
-
def display_validation_tables_trigger(n, previous_load_indicator):
|
|
136
|
-
"""
|
|
137
|
-
Increment (change) of the input of display_validation_tables callback to get it fired a
|
|
138
|
-
second time after displaying the progress bar. The output component must be updated each
|
|
139
|
-
time the callback is entered to trigger the execution of the other callback, thus the
|
|
140
|
-
choice of incrementing it at each call.
|
|
141
|
-
|
|
142
|
-
:param n: n_interval property of the dcc.Interval (0 or 1)
|
|
143
|
-
:return: 1 increment to previous value
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
return previous_load_indicator + 1
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
@app.callback(
|
|
150
|
-
Output({"type": "validation-switch", "id": MATCH}, "value"),
|
|
151
|
-
Input({"type": "validation-switch", "id": MATCH}, "value"),
|
|
152
|
-
State({"type": "validation-agent-id", "id": MATCH}, "data"),
|
|
153
|
-
State(TEAM_SELECTED_VALUE_ID, "data"),
|
|
154
|
-
State(TEAM_SELECTION_DATE_ID, "data"),
|
|
155
|
-
State(VALIDATION_PERIOD_SELECTED_ID, "data"),
|
|
156
|
-
prevent_initial_call=True,
|
|
157
|
-
)
|
|
158
|
-
def validation_button_callback(value, agent_id, team, team_selection_date, period_date: str):
|
|
159
|
-
"""
|
|
160
|
-
Function called as a callback for the validation button. It adds a validation entry for
|
|
161
|
-
the selected agent with a timestamp allowing to get the validation history. Doesn't add an
|
|
162
|
-
entry if the validation status is unchanged (should not happen).
|
|
163
|
-
|
|
164
|
-
:param value: a list with of values with last one equals 1 if the switch is on, an empty
|
|
165
|
-
list or a list with 1 value (0) if the switch is off
|
|
166
|
-
:param agent_id: the agent ID of the selected agent
|
|
167
|
-
:param team: selected team
|
|
168
|
-
:param team_selection_date: last time the team selection was changed
|
|
169
|
-
:param period_date: a date that must be inside the declaration period
|
|
170
|
-
:return: None
|
|
171
|
-
"""
|
|
172
|
-
|
|
173
|
-
from ositah.utils.hito_db_model import OSITAHValidation
|
|
174
|
-
|
|
175
|
-
db = get_db()
|
|
176
|
-
session = db.session
|
|
177
|
-
|
|
178
|
-
validation_data = get_validation_data(agent_id, period_date, session)
|
|
179
|
-
|
|
180
|
-
# If the validation switch is on, add a new validation entry for the agent (except if there
|
|
181
|
-
# is an existing validated entry but it should not happen) and save the associated project
|
|
182
|
-
# declarations. If the switch is off and a validated entry exists, update the "validated"
|
|
183
|
-
# attribute to false and do not add a new project declarations.
|
|
184
|
-
switch_on = len(value) > 0 and value[len(value) - 1] > 0
|
|
185
|
-
if switch_on:
|
|
186
|
-
if validation_data is None or validation_data.validated != switch_on:
|
|
187
|
-
validation_id = str(uuid4())
|
|
188
|
-
validation_data = OSITAHValidation(
|
|
189
|
-
id=validation_id,
|
|
190
|
-
validated=switch_on,
|
|
191
|
-
timestamp=datetime.now(),
|
|
192
|
-
agent_id=agent_id,
|
|
193
|
-
period_id=get_validation_period_id(period_date),
|
|
194
|
-
)
|
|
195
|
-
try:
|
|
196
|
-
session.add(validation_data)
|
|
197
|
-
# A flush() is required before calling project_declaration_snapshot() else the
|
|
198
|
-
# first insert is lost
|
|
199
|
-
# MJ 2023-07-12: hack to workaround a problem with flush() leading to an undefined
|
|
200
|
-
# foreign key
|
|
201
|
-
# session.flush()
|
|
202
|
-
session.commit()
|
|
203
|
-
project_declaration_snapshot(
|
|
204
|
-
agent_id,
|
|
205
|
-
validation_id,
|
|
206
|
-
team,
|
|
207
|
-
team_selection_date,
|
|
208
|
-
period_date,
|
|
209
|
-
session,
|
|
210
|
-
)
|
|
211
|
-
session.commit()
|
|
212
|
-
except: # noqa: E722
|
|
213
|
-
session.rollback()
|
|
214
|
-
raise
|
|
215
|
-
|
|
216
|
-
elif validation_data.validated:
|
|
217
|
-
validation_data.validated = switch_on
|
|
218
|
-
# Keep track of the original timestamp
|
|
219
|
-
validation_data.initial_timestamp = validation_data.timestamp
|
|
220
|
-
validation_data.timestamp = datetime.now()
|
|
221
|
-
session.commit()
|
|
222
|
-
|
|
223
|
-
return value
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
@app.callback(
|
|
227
|
-
Output(VALIDATION_DECLARATIONS_SELECTED_ID, "data"),
|
|
228
|
-
Input(VALIDATION_DECLARATIONS_SWITCH_ID, "value"),
|
|
229
|
-
prevent_initial_call=True,
|
|
230
|
-
)
|
|
231
|
-
def select_declarations_set(new_set):
|
|
232
|
-
"""
|
|
233
|
-
This callback is used to forward to the validation table callback the selected declarations
|
|
234
|
-
set through a dcc.Store that exists permanently in the page.
|
|
235
|
-
|
|
236
|
-
:param new_set: selected declarations set
|
|
237
|
-
:return: same value
|
|
238
|
-
"""
|
|
239
|
-
|
|
240
|
-
return new_set
|
|
1
|
+
"""
|
|
2
|
+
Dash callbacks for Validation sub-application
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
import dash_bootstrap_components as dbc
|
|
9
|
+
from dash import dcc, html
|
|
10
|
+
from dash.dependencies import MATCH, Input, Output, State
|
|
11
|
+
|
|
12
|
+
from ositah.app import app
|
|
13
|
+
from ositah.apps.validation.parameters import *
|
|
14
|
+
from ositah.apps.validation.tables import (
|
|
15
|
+
build_missing_agents_table,
|
|
16
|
+
build_statistics_table,
|
|
17
|
+
build_validation_table,
|
|
18
|
+
)
|
|
19
|
+
from ositah.apps.validation.tools import get_validation_data, project_declaration_snapshot
|
|
20
|
+
from ositah.utils.hito_db import get_db
|
|
21
|
+
from ositah.utils.menus import (
|
|
22
|
+
TEAM_SELECTED_VALUE_ID,
|
|
23
|
+
TEAM_SELECTION_DATE_ID,
|
|
24
|
+
VALIDATION_PERIOD_SELECTED_ID,
|
|
25
|
+
create_progress_bar,
|
|
26
|
+
)
|
|
27
|
+
from ositah.utils.period import get_validation_period_id
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.callback(
|
|
31
|
+
[
|
|
32
|
+
Output(TAB_ID_DECLARATION_STATS, "children"),
|
|
33
|
+
Output(TAB_ID_VALIDATION, "children"),
|
|
34
|
+
Output(TAB_ID_MISSING_AGENTS, "children"),
|
|
35
|
+
Output(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
36
|
+
Output(VALIDATION_SAVED_ACTIVE_TAB_ID, "data"),
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
Input(VALIDATION_LOAD_INDICATOR_ID, "data"),
|
|
40
|
+
Input(VALIDATION_TAB_MENU_ID, "active_tab"),
|
|
41
|
+
Input(TEAM_SELECTED_VALUE_ID, "data"),
|
|
42
|
+
Input(VALIDATION_DECLARATIONS_SELECTED_ID, "data"),
|
|
43
|
+
],
|
|
44
|
+
[
|
|
45
|
+
State(TEAM_SELECTION_DATE_ID, "data"),
|
|
46
|
+
State(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
47
|
+
State(VALIDATION_PERIOD_SELECTED_ID, "data"),
|
|
48
|
+
State(VALIDATION_SAVED_ACTIVE_TAB_ID, "data"),
|
|
49
|
+
],
|
|
50
|
+
prevent_initial_call=True,
|
|
51
|
+
)
|
|
52
|
+
def display_validation_tables(
|
|
53
|
+
load_in_progress,
|
|
54
|
+
active_tab: str,
|
|
55
|
+
team: str,
|
|
56
|
+
declaration_set: int,
|
|
57
|
+
team_selection_date,
|
|
58
|
+
previous_load_in_progress,
|
|
59
|
+
period_date: str,
|
|
60
|
+
previous_active_tab: str,
|
|
61
|
+
):
|
|
62
|
+
"""
|
|
63
|
+
Display active tab contents after a team or an active tab change. Exact action depends on
|
|
64
|
+
the value of the load in progress indicator. If it is equal to the previous value, it means
|
|
65
|
+
this is the start of the update process: progress bar is displayed and a dcc.Interval is
|
|
66
|
+
created to schedule again this callback after incrementing the load in progress indicator.
|
|
67
|
+
This causes the callback to be reentered and this time it triggers the real processing for
|
|
68
|
+
the tab resulting in the final update of the active tab contents. An empty content is
|
|
69
|
+
returned for inactive tabs.
|
|
70
|
+
|
|
71
|
+
:param load_in_progress: load in progress indicator
|
|
72
|
+
:param tab: select tab name
|
|
73
|
+
:param team: selected team
|
|
74
|
+
:param declaration_set: selected declaration set (all, validated or non-validated ones)
|
|
75
|
+
:param team_selection_date: last time the team selection was changed
|
|
76
|
+
:param previous_load_in_progress: previous value of the load_in_progress indicator
|
|
77
|
+
:param period_date: a date that must be inside the declaration period
|
|
78
|
+
:param previous_active_tab: previously active tab
|
|
79
|
+
:return: tab content (empty if the tab is not active)
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
tab_contents = []
|
|
83
|
+
|
|
84
|
+
# Be sure to fill the return values in the same order as Output are declared
|
|
85
|
+
tab_list = [TAB_ID_DECLARATION_STATS, TAB_ID_VALIDATION, TAB_ID_MISSING_AGENTS]
|
|
86
|
+
for tab in tab_list:
|
|
87
|
+
if team and len(team) > 0 and tab == active_tab:
|
|
88
|
+
if load_in_progress > previous_load_in_progress and active_tab == previous_active_tab:
|
|
89
|
+
if active_tab == TAB_ID_DECLARATION_STATS:
|
|
90
|
+
tab_contents.append(
|
|
91
|
+
build_statistics_table(team, team_selection_date, period_date)
|
|
92
|
+
)
|
|
93
|
+
elif active_tab == TAB_ID_VALIDATION:
|
|
94
|
+
tab_contents.append(
|
|
95
|
+
build_validation_table(
|
|
96
|
+
team, team_selection_date, declaration_set, period_date
|
|
97
|
+
)
|
|
98
|
+
)
|
|
99
|
+
elif active_tab == TAB_ID_MISSING_AGENTS:
|
|
100
|
+
tab_contents.append(
|
|
101
|
+
build_missing_agents_table(team, team_selection_date, period_date)
|
|
102
|
+
)
|
|
103
|
+
else:
|
|
104
|
+
tab_contents.append(
|
|
105
|
+
dbc.Alert("Erreur interne: tab non supporté"), color="warning"
|
|
106
|
+
)
|
|
107
|
+
previous_load_in_progress += 1
|
|
108
|
+
else:
|
|
109
|
+
component = html.Div(
|
|
110
|
+
[
|
|
111
|
+
create_progress_bar(team),
|
|
112
|
+
dcc.Interval(
|
|
113
|
+
id=VALIDATION_DISPLAY_INTERVAL_ID,
|
|
114
|
+
n_intervals=0,
|
|
115
|
+
max_intervals=1,
|
|
116
|
+
interval=500,
|
|
117
|
+
),
|
|
118
|
+
]
|
|
119
|
+
)
|
|
120
|
+
tab_contents.append(component)
|
|
121
|
+
else:
|
|
122
|
+
tab_contents.append("")
|
|
123
|
+
|
|
124
|
+
tab_contents.extend([previous_load_in_progress, active_tab])
|
|
125
|
+
|
|
126
|
+
return tab_contents
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@app.callback(
|
|
130
|
+
Output(VALIDATION_LOAD_INDICATOR_ID, "data"),
|
|
131
|
+
Input(VALIDATION_DISPLAY_INTERVAL_ID, "n_intervals"),
|
|
132
|
+
State(VALIDATION_SAVED_INDICATOR_ID, "data"),
|
|
133
|
+
prevent_initial_call=True,
|
|
134
|
+
)
|
|
135
|
+
def display_validation_tables_trigger(n, previous_load_indicator):
|
|
136
|
+
"""
|
|
137
|
+
Increment (change) of the input of display_validation_tables callback to get it fired a
|
|
138
|
+
second time after displaying the progress bar. The output component must be updated each
|
|
139
|
+
time the callback is entered to trigger the execution of the other callback, thus the
|
|
140
|
+
choice of incrementing it at each call.
|
|
141
|
+
|
|
142
|
+
:param n: n_interval property of the dcc.Interval (0 or 1)
|
|
143
|
+
:return: 1 increment to previous value
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
return previous_load_indicator + 1
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@app.callback(
|
|
150
|
+
Output({"type": "validation-switch", "id": MATCH}, "value"),
|
|
151
|
+
Input({"type": "validation-switch", "id": MATCH}, "value"),
|
|
152
|
+
State({"type": "validation-agent-id", "id": MATCH}, "data"),
|
|
153
|
+
State(TEAM_SELECTED_VALUE_ID, "data"),
|
|
154
|
+
State(TEAM_SELECTION_DATE_ID, "data"),
|
|
155
|
+
State(VALIDATION_PERIOD_SELECTED_ID, "data"),
|
|
156
|
+
prevent_initial_call=True,
|
|
157
|
+
)
|
|
158
|
+
def validation_button_callback(value, agent_id, team, team_selection_date, period_date: str):
|
|
159
|
+
"""
|
|
160
|
+
Function called as a callback for the validation button. It adds a validation entry for
|
|
161
|
+
the selected agent with a timestamp allowing to get the validation history. Doesn't add an
|
|
162
|
+
entry if the validation status is unchanged (should not happen).
|
|
163
|
+
|
|
164
|
+
:param value: a list with of values with last one equals 1 if the switch is on, an empty
|
|
165
|
+
list or a list with 1 value (0) if the switch is off
|
|
166
|
+
:param agent_id: the agent ID of the selected agent
|
|
167
|
+
:param team: selected team
|
|
168
|
+
:param team_selection_date: last time the team selection was changed
|
|
169
|
+
:param period_date: a date that must be inside the declaration period
|
|
170
|
+
:return: None
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
from ositah.utils.hito_db_model import OSITAHValidation
|
|
174
|
+
|
|
175
|
+
db = get_db()
|
|
176
|
+
session = db.session
|
|
177
|
+
|
|
178
|
+
validation_data = get_validation_data(agent_id, period_date, session)
|
|
179
|
+
|
|
180
|
+
# If the validation switch is on, add a new validation entry for the agent (except if there
|
|
181
|
+
# is an existing validated entry but it should not happen) and save the associated project
|
|
182
|
+
# declarations. If the switch is off and a validated entry exists, update the "validated"
|
|
183
|
+
# attribute to false and do not add a new project declarations.
|
|
184
|
+
switch_on = len(value) > 0 and value[len(value) - 1] > 0
|
|
185
|
+
if switch_on:
|
|
186
|
+
if validation_data is None or validation_data.validated != switch_on:
|
|
187
|
+
validation_id = str(uuid4())
|
|
188
|
+
validation_data = OSITAHValidation(
|
|
189
|
+
id=validation_id,
|
|
190
|
+
validated=switch_on,
|
|
191
|
+
timestamp=datetime.now(),
|
|
192
|
+
agent_id=agent_id,
|
|
193
|
+
period_id=get_validation_period_id(period_date),
|
|
194
|
+
)
|
|
195
|
+
try:
|
|
196
|
+
session.add(validation_data)
|
|
197
|
+
# A flush() is required before calling project_declaration_snapshot() else the
|
|
198
|
+
# first insert is lost
|
|
199
|
+
# MJ 2023-07-12: hack to workaround a problem with flush() leading to an undefined
|
|
200
|
+
# foreign key
|
|
201
|
+
# session.flush()
|
|
202
|
+
session.commit()
|
|
203
|
+
project_declaration_snapshot(
|
|
204
|
+
agent_id,
|
|
205
|
+
validation_id,
|
|
206
|
+
team,
|
|
207
|
+
team_selection_date,
|
|
208
|
+
period_date,
|
|
209
|
+
session,
|
|
210
|
+
)
|
|
211
|
+
session.commit()
|
|
212
|
+
except: # noqa: E722
|
|
213
|
+
session.rollback()
|
|
214
|
+
raise
|
|
215
|
+
|
|
216
|
+
elif validation_data.validated:
|
|
217
|
+
validation_data.validated = switch_on
|
|
218
|
+
# Keep track of the original timestamp
|
|
219
|
+
validation_data.initial_timestamp = validation_data.timestamp
|
|
220
|
+
validation_data.timestamp = datetime.now()
|
|
221
|
+
session.commit()
|
|
222
|
+
|
|
223
|
+
return value
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@app.callback(
|
|
227
|
+
Output(VALIDATION_DECLARATIONS_SELECTED_ID, "data"),
|
|
228
|
+
Input(VALIDATION_DECLARATIONS_SWITCH_ID, "value"),
|
|
229
|
+
prevent_initial_call=True,
|
|
230
|
+
)
|
|
231
|
+
def select_declarations_set(new_set):
|
|
232
|
+
"""
|
|
233
|
+
This callback is used to forward to the validation table callback the selected declarations
|
|
234
|
+
set through a dcc.Store that exists permanently in the page.
|
|
235
|
+
|
|
236
|
+
:param new_set: selected declarations set
|
|
237
|
+
:return: same value
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
return new_set
|
ositah/apps/validation/main.py
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
"""
|
|
2
|
-
OSITAH validation sub-application
|
|
3
|
-
|
|
4
|
-
This file contains only the layout definitions
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from ositah.apps.validation.callbacks import *
|
|
8
|
-
from ositah.utils.menus import DATA_SELECTED_SOURCE_ID, TABLE_TYPE_DUMMY_STORE, team_list_dropdown
|
|
9
|
-
from ositah.utils.projects import DATA_SOURCE_HITO
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def validation_submenus():
|
|
13
|
-
"""
|
|
14
|
-
Build the tabs menus of the validation sub-application
|
|
15
|
-
|
|
16
|
-
:return: DBC Tabs
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
return dbc.Tabs(
|
|
20
|
-
[
|
|
21
|
-
dbc.Tab(
|
|
22
|
-
id=TAB_ID_DECLARATION_STATS,
|
|
23
|
-
tab_id=TAB_ID_DECLARATION_STATS,
|
|
24
|
-
label="Statistiques",
|
|
25
|
-
),
|
|
26
|
-
dbc.Tab(
|
|
27
|
-
id=TAB_ID_VALIDATION,
|
|
28
|
-
tab_id=TAB_ID_VALIDATION,
|
|
29
|
-
label="Déclarations Effectuées",
|
|
30
|
-
),
|
|
31
|
-
dbc.Tab(
|
|
32
|
-
id=TAB_ID_MISSING_AGENTS,
|
|
33
|
-
tab_id=TAB_ID_MISSING_AGENTS,
|
|
34
|
-
label="Déclarations Manquantes",
|
|
35
|
-
),
|
|
36
|
-
],
|
|
37
|
-
id=VALIDATION_TAB_MENU_ID,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def validation_layout():
|
|
42
|
-
"""
|
|
43
|
-
Build the layout for this application, after reading the data if necessary.
|
|
44
|
-
|
|
45
|
-
:return: application layout
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
from ositah.utils.hito_db_model import (
|
|
49
|
-
OSITAHProjectDeclaration,
|
|
50
|
-
OSITAHValidation,
|
|
51
|
-
OSITAHValidationPeriod,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
db = get_db()
|
|
55
|
-
|
|
56
|
-
OSITAHValidationPeriod.__table__.create(db.session.bind, checkfirst=True)
|
|
57
|
-
OSITAHValidation.__table__.create(db.session.bind, checkfirst=True)
|
|
58
|
-
OSITAHProjectDeclaration.__table__.create(db.session.bind, checkfirst=True)
|
|
59
|
-
|
|
60
|
-
return html.Div(
|
|
61
|
-
[
|
|
62
|
-
html.H1("Affichage et validation des déclarations"),
|
|
63
|
-
team_list_dropdown(),
|
|
64
|
-
dcc.Store(id=DATA_SELECTED_SOURCE_ID, data=DATA_SOURCE_HITO),
|
|
65
|
-
html.Div(
|
|
66
|
-
validation_submenus(),
|
|
67
|
-
id="validation-submenus",
|
|
68
|
-
style={"marginTop": "3em"},
|
|
69
|
-
),
|
|
70
|
-
dcc.Store(id=VALIDATION_LOAD_INDICATOR_ID, data=0),
|
|
71
|
-
dcc.Store(id=VALIDATION_SAVED_INDICATOR_ID, data=0),
|
|
72
|
-
dcc.Store(
|
|
73
|
-
id=VALIDATION_DECLARATIONS_SELECTED_ID,
|
|
74
|
-
data=VALIDATION_DECLARATIONS_SELECT_ALL,
|
|
75
|
-
),
|
|
76
|
-
dcc.Store(id=VALIDATION_SAVED_ACTIVE_TAB_ID, data=""),
|
|
77
|
-
# The following dcc.Store coupled with tables must be created in the layout for
|
|
78
|
-
# the callback to work
|
|
79
|
-
dcc.Store(id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_VALIDATION}, data=0),
|
|
80
|
-
dcc.Store(
|
|
81
|
-
id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_MISSING_AGENTS},
|
|
82
|
-
data=0,
|
|
83
|
-
),
|
|
84
|
-
dcc.Store(
|
|
85
|
-
id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_DECLARATION_STATS},
|
|
86
|
-
data=0,
|
|
87
|
-
),
|
|
88
|
-
]
|
|
89
|
-
)
|
|
1
|
+
"""
|
|
2
|
+
OSITAH validation sub-application
|
|
3
|
+
|
|
4
|
+
This file contains only the layout definitions
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from ositah.apps.validation.callbacks import *
|
|
8
|
+
from ositah.utils.menus import DATA_SELECTED_SOURCE_ID, TABLE_TYPE_DUMMY_STORE, team_list_dropdown
|
|
9
|
+
from ositah.utils.projects import DATA_SOURCE_HITO
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def validation_submenus():
|
|
13
|
+
"""
|
|
14
|
+
Build the tabs menus of the validation sub-application
|
|
15
|
+
|
|
16
|
+
:return: DBC Tabs
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
return dbc.Tabs(
|
|
20
|
+
[
|
|
21
|
+
dbc.Tab(
|
|
22
|
+
id=TAB_ID_DECLARATION_STATS,
|
|
23
|
+
tab_id=TAB_ID_DECLARATION_STATS,
|
|
24
|
+
label="Statistiques",
|
|
25
|
+
),
|
|
26
|
+
dbc.Tab(
|
|
27
|
+
id=TAB_ID_VALIDATION,
|
|
28
|
+
tab_id=TAB_ID_VALIDATION,
|
|
29
|
+
label="Déclarations Effectuées",
|
|
30
|
+
),
|
|
31
|
+
dbc.Tab(
|
|
32
|
+
id=TAB_ID_MISSING_AGENTS,
|
|
33
|
+
tab_id=TAB_ID_MISSING_AGENTS,
|
|
34
|
+
label="Déclarations Manquantes",
|
|
35
|
+
),
|
|
36
|
+
],
|
|
37
|
+
id=VALIDATION_TAB_MENU_ID,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def validation_layout():
|
|
42
|
+
"""
|
|
43
|
+
Build the layout for this application, after reading the data if necessary.
|
|
44
|
+
|
|
45
|
+
:return: application layout
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from ositah.utils.hito_db_model import (
|
|
49
|
+
OSITAHProjectDeclaration,
|
|
50
|
+
OSITAHValidation,
|
|
51
|
+
OSITAHValidationPeriod,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
db = get_db()
|
|
55
|
+
|
|
56
|
+
OSITAHValidationPeriod.__table__.create(db.session.bind, checkfirst=True)
|
|
57
|
+
OSITAHValidation.__table__.create(db.session.bind, checkfirst=True)
|
|
58
|
+
OSITAHProjectDeclaration.__table__.create(db.session.bind, checkfirst=True)
|
|
59
|
+
|
|
60
|
+
return html.Div(
|
|
61
|
+
[
|
|
62
|
+
html.H1("Affichage et validation des déclarations"),
|
|
63
|
+
team_list_dropdown(),
|
|
64
|
+
dcc.Store(id=DATA_SELECTED_SOURCE_ID, data=DATA_SOURCE_HITO),
|
|
65
|
+
html.Div(
|
|
66
|
+
validation_submenus(),
|
|
67
|
+
id="validation-submenus",
|
|
68
|
+
style={"marginTop": "3em"},
|
|
69
|
+
),
|
|
70
|
+
dcc.Store(id=VALIDATION_LOAD_INDICATOR_ID, data=0),
|
|
71
|
+
dcc.Store(id=VALIDATION_SAVED_INDICATOR_ID, data=0),
|
|
72
|
+
dcc.Store(
|
|
73
|
+
id=VALIDATION_DECLARATIONS_SELECTED_ID,
|
|
74
|
+
data=VALIDATION_DECLARATIONS_SELECT_ALL,
|
|
75
|
+
),
|
|
76
|
+
dcc.Store(id=VALIDATION_SAVED_ACTIVE_TAB_ID, data=""),
|
|
77
|
+
# The following dcc.Store coupled with tables must be created in the layout for
|
|
78
|
+
# the callback to work
|
|
79
|
+
dcc.Store(id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_VALIDATION}, data=0),
|
|
80
|
+
dcc.Store(
|
|
81
|
+
id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_MISSING_AGENTS},
|
|
82
|
+
data=0,
|
|
83
|
+
),
|
|
84
|
+
dcc.Store(
|
|
85
|
+
id={"type": TABLE_TYPE_DUMMY_STORE, "id": TABLE_ID_DECLARATION_STATS},
|
|
86
|
+
data=0,
|
|
87
|
+
),
|
|
88
|
+
]
|
|
89
|
+
)
|