ositah 23.8.dev1__tar.gz → 24.2.dev1__tar.gz

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.
Files changed (76) hide show
  1. {ositah-23.8.dev1 → ositah-24.2.dev1}/.gitlab-ci.yml +5 -5
  2. {ositah-23.8.dev1/ositah.egg-info → ositah-24.2.dev1}/PKG-INFO +15 -1
  3. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/configuration/main.py +5 -3
  4. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/export.py +38 -30
  5. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/callbacks.py +3 -2
  6. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/tables.py +52 -19
  7. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/main.py +11 -12
  8. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/exceptions.py +64 -52
  9. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/menus.py +34 -3
  10. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/projects.py +26 -19
  11. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/utils.py +3 -3
  12. {ositah-23.8.dev1 → ositah-24.2.dev1/ositah.egg-info}/PKG-INFO +15 -1
  13. {ositah-23.8.dev1 → ositah-24.2.dev1}/pyproject.toml +1 -1
  14. {ositah-23.8.dev1 → ositah-24.2.dev1}/.gitignore +0 -0
  15. {ositah-23.8.dev1 → ositah-24.2.dev1}/LICENSE +0 -0
  16. {ositah-23.8.dev1 → ositah-24.2.dev1}/README.md +0 -0
  17. {ositah-23.8.dev1 → ositah-24.2.dev1}/gunicorn.config/README.md +0 -0
  18. {ositah-23.8.dev1 → ositah-24.2.dev1}/gunicorn.config/gunicorn.ositah +0 -0
  19. {ositah-23.8.dev1 → ositah-24.2.dev1}/gunicorn.config/gunicorn@.service +0 -0
  20. {ositah-23.8.dev1 → ositah-24.2.dev1}/gunicorn.config/ositah.conf.py +0 -0
  21. {ositah-23.8.dev1 → ositah-24.2.dev1}/noxfile.py +0 -0
  22. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/__init__.py +0 -0
  23. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/app.py +0 -0
  24. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/__init__.py +0 -0
  25. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/analysis.py +0 -0
  26. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/configuration/__init__.py +0 -0
  27. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/configuration/callbacks.py +0 -0
  28. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/configuration/parameters.py +0 -0
  29. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/configuration/tools.py +0 -0
  30. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/__init__.py +0 -0
  31. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/main.py +0 -0
  32. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/parameters.py +0 -0
  33. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/apps/validation/tools.py +0 -0
  34. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/assets/arrow_down_up.svg +0 -0
  35. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/assets/ositah.css +0 -0
  36. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/assets/sort_ascending.svg +0 -0
  37. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/assets/sort_descending.svg +0 -0
  38. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/assets/sorttable.js +0 -0
  39. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/ositah.example.cfg +0 -0
  40. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/static/style.css +0 -0
  41. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/templates/base.html +0 -0
  42. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/templates/bootstrap_login.html +0 -0
  43. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/templates/login_form.html +0 -0
  44. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/__init__.py +0 -0
  45. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/agents.py +0 -0
  46. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/authentication.py +0 -0
  47. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/cache.py +0 -0
  48. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/core.py +0 -0
  49. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/hito_db.py +0 -0
  50. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/hito_db_model.py +0 -0
  51. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/period.py +0 -0
  52. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah/utils/teams.py +0 -0
  53. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah.egg-info/SOURCES.txt +0 -0
  54. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah.egg-info/dependency_links.txt +0 -0
  55. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah.egg-info/entry_points.txt +0 -0
  56. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah.egg-info/requires.txt +0 -0
  57. {ositah-23.8.dev1 → ositah-24.2.dev1}/ositah.egg-info/top_level.txt +0 -0
  58. {ositah-23.8.dev1 → ositah-24.2.dev1}/setup.cfg +0 -0
  59. {ositah-23.8.dev1 → ositah-24.2.dev1}/setup.py +0 -0
  60. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/README.md +0 -0
  61. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/accordion.py +0 -0
  62. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/authentication.py +0 -0
  63. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/checkbox.py +0 -0
  64. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/checklist.py +0 -0
  65. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/file-selector.py +0 -0
  66. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/file-upload.py +0 -0
  67. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/long_running_callback.py +0 -0
  68. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/pandas_split.py +0 -0
  69. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/pandas_split_bug_report.py +0 -0
  70. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/pattern-matching-callback.py +0 -0
  71. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/progess_bar.py +0 -0
  72. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/reset_table_checkboxes.py +0 -0
  73. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/sortable_table.py +0 -0
  74. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/sqlalchemy_test.py +0 -0
  75. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/templates/base.html +0 -0
  76. {ositah-23.8.dev1 → ositah-24.2.dev1}/test-dash/templates/login_form.html +0 -0
@@ -1,14 +1,14 @@
1
- default:
2
- tags:
3
- # Ensure that the pipeline is executed in IJCLab intranet - TO BE CHANGED
4
- - oval-checks
5
-
6
1
  stages:
7
2
  - build
8
3
 
9
4
  build:
10
5
  stage: build
11
6
  script:
7
+ - apk add --update python3
8
+ - python --version
9
+ - python -m venv .venv
10
+ - . .venv/bin/activate
11
+ - python -m ensurepip
12
12
  - pip install nox pyproject-parser consolekit
13
13
  - nox -s lint
14
14
  - pyproject-parser check
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ositah
3
- Version: 23.8.dev1
3
+ Version: 24.2.dev1
4
4
  Summary: Outils de Suivi d'Activités basé sur Hito
5
5
  Author-email: Michel Jouvin <michel.jouvin@ijclab.in2p3.fr>
6
6
  License: BSD 3-Clause License
@@ -12,6 +12,20 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.8
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
+ Requires-Dist: blinker
16
+ Requires-Dist: dash
17
+ Requires-Dist: dash-bootstrap-components
18
+ Requires-Dist: flask~=2.2
19
+ Requires-Dist: flask-multipass
20
+ Requires-Dist: flask-sqlalchemy~=3.0
21
+ Requires-Dist: flask-wtf
22
+ Requires-Dist: hito-tools>=23.2
23
+ Requires-Dist: pandas==2.*
24
+ Requires-Dist: pymysql
25
+ Requires-Dist: python-ldap
26
+ Requires-Dist: pyyaml
27
+ Requires-Dist: simplejson
28
+ Requires-Dist: sqlalchemy~=2.0
15
29
 
16
30
  # OSITAH : Outil de Suivi de Temps et d'Activités basé sur Hito
17
31
 
@@ -197,9 +197,11 @@ def declaration_periods_layout():
197
197
  width={"size": 3, "offset": 0},
198
198
  ),
199
199
  dbc.Tooltip(
200
- "Le semestre courant existe déjà"
201
- if current_period_exists
202
- else "Ajoute le semestre courant",
200
+ (
201
+ "Le semestre courant existe déjà"
202
+ if current_period_exists
203
+ else "Ajoute le semestre courant"
204
+ ),
203
205
  target=DECLARATION_PERIODS_CREATE_DIV_ID,
204
206
  placement="bottom",
205
207
  ),
@@ -338,10 +338,14 @@ def nsip_export_table(team, team_selection_date, period_date: str, declarations_
338
338
  declaration_list.loc[
339
339
  declaration_list["_merge"] == "both", "mgr_val_time_mismatch"
340
340
  ] = declaration_list[declaration_list["_merge"] == "both"].apply(
341
- lambda r: False
342
- if r["managerValidationDate"]
343
- and re.match(r["managerValidationDate"], r["validation_time"].date().isoformat())
344
- else True,
341
+ lambda r: (
342
+ False
343
+ if r["managerValidationDate"]
344
+ and re.match(
345
+ r["managerValidationDate"], r["validation_time"].date().isoformat()
346
+ )
347
+ else True
348
+ ),
345
349
  axis=1,
346
350
  )
347
351
  # nsip_missing is True if the OSITAH declaration has no matching declaration in NSIP
@@ -358,9 +362,9 @@ def nsip_export_table(team, team_selection_date, period_date: str, declarations_
358
362
  "time": "time_nsipd",
359
363
  "time_unit": "volume",
360
364
  }.items():
361
- declaration_list.loc[
362
- declaration_list["ositah_missing"], ositah_column
363
- ] = declaration_list[nsip_column]
365
+ declaration_list.loc[declaration_list["ositah_missing"], ositah_column] = (
366
+ declaration_list[nsip_column]
367
+ )
364
368
  # Fix nsip_project if it was set to the project name when the activity is a reference
365
369
  declaration_list.loc[
366
370
  declaration_list["nsip_project"].isna()
@@ -416,9 +420,9 @@ def nsip_export_table(team, team_selection_date, period_date: str, declarations_
416
420
  # Rset nsip_project_id and nsip_reference_id to NaN if they are equal to 0 so that the
417
421
  # corresponding cell is empty
418
422
  declaration_list.loc[declaration_list["nsip_project_id"] == 0, "nsip_project_id"] = np.NaN
419
- declaration_list.loc[
420
- declaration_list["nsip_reference_id"] == 0, "nsip_reference_id"
421
- ] = np.NaN
423
+ declaration_list.loc[declaration_list["nsip_reference_id"] == 0, "nsip_reference_id"] = (
424
+ np.NaN
425
+ )
422
426
 
423
427
  # Sort declarations by email_auth and add index value as column for easier further
424
428
  # processing
@@ -483,9 +487,11 @@ def nsip_export_table(team, team_selection_date, period_date: str, declarations_
483
487
  [
484
488
  html.Th(
485
489
  [
486
- dbc.Checkbox(id=NSIP_EXPORT_SELECT_ALL_ID)
487
- if selected_declarations["selectable"].any()
488
- else html.Div(),
490
+ (
491
+ dbc.Checkbox(id=NSIP_EXPORT_SELECT_ALL_ID)
492
+ if selected_declarations["selectable"].any()
493
+ else html.Div()
494
+ ),
489
495
  dcc.Store(id=NSIP_EXPORT_ALL_SELECTED_ID, data=0),
490
496
  ]
491
497
  ),
@@ -560,23 +566,25 @@ def nsip_build_user_declarations(declarations, agent_email, data_columns):
560
566
  # (rowSpan is in fact the number of Tr following this one attached to it)
561
567
  html.Tr(
562
568
  [
563
- html.Td(
564
- [
565
- dbc.Checkbox(
566
- id={"type": "nsip-export-user", "id": agent_email},
567
- class_name="nsip-agent-selector",
568
- value=False,
569
- ),
570
- dcc.Store(
571
- id={"type": "nsip-export-user-selected", "id": agent_email},
572
- data=0,
573
- ),
574
- ],
575
- className="align-middle ",
576
- rowSpan=len(user_declarations) + 1,
577
- )
578
- if user_declarations["selectable"].any()
579
- else html.Td(rowSpan=len(user_declarations) + 1),
569
+ (
570
+ html.Td(
571
+ [
572
+ dbc.Checkbox(
573
+ id={"type": "nsip-export-user", "id": agent_email},
574
+ class_name="nsip-agent-selector",
575
+ value=False,
576
+ ),
577
+ dcc.Store(
578
+ id={"type": "nsip-export-user-selected", "id": agent_email},
579
+ data=0,
580
+ ),
581
+ ],
582
+ className="align-middle ",
583
+ rowSpan=len(user_declarations) + 1,
584
+ )
585
+ if user_declarations["selectable"].any()
586
+ else html.Td(rowSpan=len(user_declarations) + 1)
587
+ ),
580
588
  nsip_build_poject_declaration_cell(user_declarations, "email_auth", None),
581
589
  dcc.Store(
582
590
  id={"type": "nsip-selected-user", "id": agent_email},
@@ -196,8 +196,9 @@ def validation_button_callback(value, agent_id, team, team_selection_date, perio
196
196
  session.add(validation_data)
197
197
  # A flush() is required before calling project_declaration_snapshot() else the
198
198
  # first insert is lost
199
- # MJ 2023-07-12: hack to workaround a problem with flush() leading to an undefined foreign key
200
- #session.flush()
199
+ # MJ 2023-07-12: hack to workaround a problem with flush() leading to an undefined
200
+ # foreign key
201
+ # session.flush()
201
202
  session.commit()
202
203
  project_declaration_snapshot(
203
204
  agent_id,
@@ -27,8 +27,8 @@ from ositah.apps.validation.tools import (
27
27
  validation_started,
28
28
  )
29
29
  from ositah.utils.agents import get_agents
30
- from ositah.utils.exceptions import SessionDataMissing
31
- from ositah.utils.menus import TABLE_TYPE_TABLE, build_accordion
30
+ from ositah.utils.exceptions import InvalidHitoProjectName, SessionDataMissing
31
+ from ositah.utils.menus import TABLE_TYPE_TABLE, build_accordion, ositah_jumbotron
32
32
  from ositah.utils.projects import (
33
33
  CATEGORY_DEFAULT,
34
34
  DATA_SOURCE_HITO,
@@ -72,9 +72,17 @@ def build_validation_table(team, team_selection_date, declaration_set: int, peri
72
72
 
73
73
  validation_data = get_all_validation_status(period_date)
74
74
 
75
- project_declarations = get_team_projects(
76
- team, team_selection_date, period_date, DATA_SOURCE_HITO
77
- )
75
+ try:
76
+ project_declarations = get_team_projects(
77
+ team, team_selection_date, period_date, DATA_SOURCE_HITO
78
+ )
79
+ except InvalidHitoProjectName as e:
80
+ return ositah_jumbotron(
81
+ "Error loading projects",
82
+ e.msg,
83
+ title_class="text-danger",
84
+ )
85
+
78
86
  if project_declarations is None:
79
87
  return html.Div(
80
88
  [
@@ -95,9 +103,17 @@ def build_validation_table(team, team_selection_date, declaration_set: int, peri
95
103
  data_columns = [CATEGORY_DEFAULT]
96
104
  data_columns.extend(global_params.project_categories.keys())
97
105
 
98
- validated_project_declarations = get_team_projects(
99
- team, team_selection_date, period_date, DATA_SOURCE_OSITAH, use_cache=False
100
- )
106
+ try:
107
+ validated_project_declarations = get_team_projects(
108
+ team, team_selection_date, period_date, DATA_SOURCE_OSITAH, use_cache=False
109
+ )
110
+ except InvalidHitoProjectName as e:
111
+ return ositah_jumbotron(
112
+ "Error loading projects",
113
+ e.msg,
114
+ title_class="text-danger",
115
+ )
116
+
101
117
  if validated_project_declarations is None:
102
118
  declaration_list["validated"] = False
103
119
  declaration_list["hito_missing"] = False
@@ -127,12 +143,12 @@ def build_validation_table(team, team_selection_date, declaration_set: int, peri
127
143
  declaration_list.loc[declaration_list["hito_missing"]] = declaration_list.fillna(
128
144
  value=columns_fillna
129
145
  )
130
- declaration_list.loc[
131
- declaration_list["hito_missing"], column_names["fullname"]
132
- ] = declaration_list[f"{column_names['fullname']}_val"]
133
- declaration_list.loc[
134
- declaration_list["hito_missing"], column_names["team"]
135
- ] = declaration_list[f"{column_names['team']}_val"]
146
+ declaration_list.loc[declaration_list["hito_missing"], column_names["fullname"]] = (
147
+ declaration_list[f"{column_names['fullname']}_val"]
148
+ )
149
+ declaration_list.loc[declaration_list["hito_missing"], column_names["team"]] = (
150
+ declaration_list[f"{column_names['team']}_val"]
151
+ )
136
152
  declaration_list.loc[declaration_list["hito_missing"], "suspect"] = True
137
153
  declaration_list.loc[declaration_list["hito_missing"], "validation_disabled"] = True
138
154
  declaration_list.sort_values(by=column_names["fullname"], inplace=True)
@@ -203,9 +219,11 @@ def build_validation_table(team, team_selection_date, declaration_set: int, peri
203
219
  selected_declarations.iloc[i - 1][column_names["fullname"]]
204
220
  ),
205
221
  agent_tooltip_txt(selected_declarations.iloc[i - 1], data_columns),
206
- "validated_hito_missing"
207
- if selected_declarations.iloc[i - 1]["hito_missing"]
208
- else "",
222
+ (
223
+ "validated_hito_missing"
224
+ if selected_declarations.iloc[i - 1]["hito_missing"]
225
+ else ""
226
+ ),
209
227
  ),
210
228
  className="accordion",
211
229
  key=f"validation-table-cell-{i}-fullname",
@@ -314,7 +332,14 @@ def build_missing_agents_table(team, team_selection_date, period_date: str):
314
332
  column_names = global_params.columns
315
333
  declaration_options = global_params.declaration_options
316
334
 
317
- declarations = get_team_projects(team, team_selection_date, period_date)
335
+ try:
336
+ declarations = get_team_projects(team, team_selection_date, period_date)
337
+ except InvalidHitoProjectName as e:
338
+ return ositah_jumbotron(
339
+ "Error loading projects",
340
+ e.msg,
341
+ title_class="text-danger",
342
+ )
318
343
 
319
344
  agents = get_agents(period_date)
320
345
 
@@ -425,7 +450,15 @@ def build_statistics_table(team, team_selection_date, period_date: str):
425
450
  except SessionDataMissing:
426
451
  return no_session_id_jumbotron()
427
452
 
428
- declarations = get_team_projects(team, team_selection_date, period_date)
453
+ try:
454
+ declarations = get_team_projects(team, team_selection_date, period_date)
455
+ except InvalidHitoProjectName as e:
456
+ return ositah_jumbotron(
457
+ "Error loading projects",
458
+ e.msg,
459
+ title_class="text-danger",
460
+ )
461
+
429
462
  agents = get_agents(period_date)
430
463
  if team == TEAM_LIST_ALL_AGENTS:
431
464
  agent_list = agents
@@ -33,6 +33,7 @@ from ositah.utils.menus import (
33
33
  TEAM_SELECTED_VALUE_ID,
34
34
  TEAM_SELECTION_DATE_ID,
35
35
  VALIDATION_PERIOD_SELECTED_ID,
36
+ ositah_jumbotron,
36
37
  )
37
38
  from ositah.utils.utils import (
38
39
  AUTHORIZED_ROLES,
@@ -104,23 +105,21 @@ def default_config_path() -> str:
104
105
  return config_file
105
106
 
106
107
 
107
- # URL not found jumbletron
108
+ # URL not found jumbotron
108
109
  def url_not_found(path):
109
- return dbc.Jumbotron(
110
- [
111
- html.H1("404: Not found", className="text-danger"),
112
- html.Hr(),
113
- html.P(f"URL {path} was not recognised..."),
114
- ]
110
+ return ositah_jumbotron(
111
+ "404: Not found",
112
+ f"URL {path} was not recognised...",
113
+ title_class="text-danger",
115
114
  )
116
115
 
117
116
 
118
- # URL not found jumbletron
117
+ # valid role missing jumbotron
119
118
  def valid_role_missing(msg):
120
- return dbc.Jumbotron(
121
- [
122
- html.P(msg),
123
- ]
119
+ return ositah_jumbotron(
120
+ "You don't havea valid Hito role to OSITAH",
121
+ msg,
122
+ title_class="text-warning",
124
123
  )
125
124
 
126
125
 
@@ -1,52 +1,64 @@
1
- # OSITAH exceptions not specific to one of the sub-application
2
-
3
- from hito_tools.exceptions import EXIT_STATUS_GENERAL_ERROR
4
-
5
-
6
- class InvalidCallbackInput(Exception):
7
- def __init__(self, input_name):
8
- self.msg = f"internal error: invalid input ({input_name}) in callback"
9
- self.status = EXIT_STATUS_GENERAL_ERROR
10
-
11
- def __str__(self):
12
- return repr(self.msg)
13
-
14
-
15
- class InvalidDataSource(Exception):
16
- def __init__(self, source):
17
- self.msg = f"attempt to use and invalid data source ({source})"
18
- self.status = EXIT_STATUS_GENERAL_ERROR
19
-
20
- def __str__(self):
21
- return repr(self.msg)
22
-
23
-
24
- class SessionDataMissing(Exception):
25
- def __init__(self, session_id=None):
26
- if session_id:
27
- session_txt = f" (session={session_id})"
28
- else:
29
- session_txt = ""
30
- self.msg = f"Attempt to use non existing session data{session_txt}"
31
- self.status = EXIT_STATUS_GENERAL_ERROR
32
-
33
- def __str__(self):
34
- return repr(self.msg)
35
-
36
-
37
- class ValidationPeriodAmbiguous(Exception):
38
- def __init__(self, date):
39
- self.msg = f"Configuration error: several periods matching {date}"
40
- self.status = EXIT_STATUS_GENERAL_ERROR
41
-
42
- def __str__(self):
43
- return repr(self.msg)
44
-
45
-
46
- class ValidationPeriodMissing(Exception):
47
- def __init__(self, date):
48
- self.msg = f"No defined declaration period matching {date}"
49
- self.status = EXIT_STATUS_GENERAL_ERROR
50
-
51
- def __str__(self):
52
- return repr(self.msg)
1
+ # OSITAH exceptions not specific to one of the sub-application
2
+
3
+ from hito_tools.exceptions import EXIT_STATUS_GENERAL_ERROR
4
+
5
+
6
+ class InvalidCallbackInput(Exception):
7
+ def __init__(self, input_name):
8
+ self.msg = f"internal error: invalid input ({input_name}) in callback"
9
+ self.status = EXIT_STATUS_GENERAL_ERROR
10
+
11
+ def __str__(self):
12
+ return repr(self.msg)
13
+
14
+
15
+ class InvalidDataSource(Exception):
16
+ def __init__(self, source):
17
+ self.msg = f"attempt to use and invalid data source ({source})"
18
+ self.status = EXIT_STATUS_GENERAL_ERROR
19
+
20
+ def __str__(self):
21
+ return repr(self.msg)
22
+
23
+
24
+ class InvalidHitoProjectName(Exception):
25
+ def __init__(self, projects):
26
+ self.msg = (
27
+ f"The following Hito project names don't match the format 'masterproject / project' :"
28
+ f" {', '.join(projects)}"
29
+ )
30
+ self.status = EXIT_STATUS_GENERAL_ERROR
31
+
32
+ def __str__(self):
33
+ return repr(self.msg)
34
+
35
+
36
+ class SessionDataMissing(Exception):
37
+ def __init__(self, session_id=None):
38
+ if session_id:
39
+ session_txt = f" (session={session_id})"
40
+ else:
41
+ session_txt = ""
42
+ self.msg = f"Attempt to use non existing session data{session_txt}"
43
+ self.status = EXIT_STATUS_GENERAL_ERROR
44
+
45
+ def __str__(self):
46
+ return repr(self.msg)
47
+
48
+
49
+ class ValidationPeriodAmbiguous(Exception):
50
+ def __init__(self, date):
51
+ self.msg = f"Configuration error: several periods matching {date}"
52
+ self.status = EXIT_STATUS_GENERAL_ERROR
53
+
54
+ def __str__(self):
55
+ return repr(self.msg)
56
+
57
+
58
+ class ValidationPeriodMissing(Exception):
59
+ def __init__(self, date):
60
+ self.msg = f"No defined declaration period matching {date}"
61
+ self.status = EXIT_STATUS_GENERAL_ERROR
62
+
63
+ def __str__(self):
64
+ return repr(self.msg)
@@ -153,9 +153,11 @@ def create_progress_bar(
153
153
 
154
154
  return html.Div(
155
155
  [
156
- html.Div(f"Chargement des données de l'équipe {team} en cours...")
157
- if team
158
- else html.Div(),
156
+ (
157
+ html.Div(f"Chargement des données de l'équipe {team} en cours...")
158
+ if team
159
+ else html.Div()
160
+ ),
159
161
  dcc.Interval(
160
162
  id="progress-interval",
161
163
  max_intervals=max_intervals,
@@ -306,3 +308,32 @@ app.clientside_callback(
306
308
  State({"type": TABLE_TYPE_TABLE, "id": MATCH}, "id"),
307
309
  prevent_initial_call=True,
308
310
  )
311
+
312
+
313
+ # Emulate former Jumbotron
314
+ def ositah_jumbotron(title: str, main_text: str, details: str = None, title_class: str = None):
315
+ """
316
+ Emulate Jumbotron component available in Bootstrap v4
317
+
318
+ :param title: Jumbotron title
319
+ :param main_text: main text
320
+ :param details: optional additional text
321
+ :return: html.div()
322
+ """
323
+
324
+ return html.Div(
325
+ dbc.Container(
326
+ [
327
+ html.H1(title, className=f"display-3 {title_class if ( title_class ) else ''}"),
328
+ html.P(
329
+ main_text,
330
+ className="lead",
331
+ ),
332
+ html.Hr(className="my-2"),
333
+ html.P(details),
334
+ ],
335
+ fluid=True,
336
+ className="py-3",
337
+ ),
338
+ className="p-3 bg-light rounded-3",
339
+ )
@@ -10,7 +10,7 @@ from sqlalchemy.orm import joinedload
10
10
 
11
11
  from ositah.utils.agents import get_agents
12
12
  from ositah.utils.cache import clear_cached_data
13
- from ositah.utils.exceptions import InvalidDataSource
13
+ from ositah.utils.exceptions import InvalidDataSource, InvalidHitoProjectName
14
14
  from ositah.utils.hito_db import get_db
15
15
  from ositah.utils.period import get_validation_period_data
16
16
  from ositah.utils.utils import (
@@ -306,9 +306,9 @@ def get_team_projects(
306
306
  # Drop statut column to avoid conflicts in future merge with the Agent table
307
307
  declarations.drop(columns=["statut"], inplace=True)
308
308
  # Ensure that email_auth is defined and if it is not, replace it by the email.
309
- declarations.loc[
310
- declarations[columns["email_auth"]].isna(), columns["email_auth"]
311
- ] = declarations[columns["email"]]
309
+ declarations.loc[declarations[columns["email_auth"]].isna(), columns["email_auth"]] = (
310
+ declarations[columns["email"]]
311
+ )
312
312
  declarations[columns["activity"]] = declarations.apply(
313
313
  lambda row: ositah2hito_project_name(
314
314
  row[columns["masterproject"]], row[columns["project"]]
@@ -410,16 +410,23 @@ def get_team_projects(
410
410
  if column not in saved_projects.columns:
411
411
  saved_projects[column] = np.NaN
412
412
  declarations = declarations.join(saved_projects)
413
- declarations.loc[
414
- declarations.project_saved.notna(), columns["masterproject"]
415
- ] = declarations.newmaster
416
- declarations.loc[
417
- declarations.project_saved.notna(), columns["project"]
418
- ] = declarations.newproject
413
+ declarations.loc[declarations.project_saved.notna(), columns["masterproject"]] = (
414
+ declarations.newmaster
415
+ )
416
+ declarations.loc[declarations.project_saved.notna(), columns["project"]] = (
417
+ declarations.newproject
418
+ )
419
419
  declarations.drop(columns=["newmaster", "newproject"], inplace=True)
420
- declarations.loc[
421
- declarations.project_saved.notna(), columns["activity"]
422
- ] = declarations.project_saved
420
+ declarations.loc[declarations.project_saved.notna(), columns["activity"]] = (
421
+ declarations.project_saved
422
+ )
423
+
424
+ # Detect project names not matching the format "masterproject / project"
425
+ invalid_hito_projects = declarations.loc[declarations[columns["project"]].isnull()]
426
+ if not invalid_hito_projects.empty:
427
+ raise InvalidHitoProjectName(
428
+ pd.Series(invalid_hito_projects[columns["masterproject"]]).unique()
429
+ )
423
430
 
424
431
  else:
425
432
  raise InvalidDataSource(source)
@@ -577,12 +584,12 @@ def get_hito_nsip_activities(project_activity: bool = True):
577
584
  np.NaN,
578
585
  ]
579
586
  else:
580
- activities[
581
- ["nsip_master", "nsip_project", "nsip_project_id", "nsip_reference_id"]
582
- ] = activities.apply(
583
- lambda v: nsip_activity_name_id(v["nsip_name_id"], v["class"]),
584
- axis=1,
585
- result_type="expand",
587
+ activities[["nsip_master", "nsip_project", "nsip_project_id", "nsip_reference_id"]] = (
588
+ activities.apply(
589
+ lambda v: nsip_activity_name_id(v["nsip_name_id"], v["class"]),
590
+ axis=1,
591
+ result_type="expand",
592
+ )
586
593
  )
587
594
  activities["nsip_project_id"] = activities["nsip_project_id"].astype(int)
588
595
  activities["nsip_reference_id"] = activities["nsip_reference_id"].astype(int)
@@ -328,9 +328,9 @@ def define_config_params(file):
328
328
  if "agent_query" not in config["hito"]["db"]:
329
329
  raise ConfigMissingParam("hito/db/agent_query", file)
330
330
  if config["hito"]["db"]["type"] == "sqlite":
331
- app.server.config[
332
- "SQLALCHEMY_DATABASE_URI"
333
- ] = f"sqlite:///{config['hito']['db']['location']}"
331
+ app.server.config["SQLALCHEMY_DATABASE_URI"] = (
332
+ f"sqlite:///{config['hito']['db']['location']}"
333
+ )
334
334
  elif config["hito"]["db"]["type"] == "mysql":
335
335
  if "user" not in config["hito"]["db"]:
336
336
  raise ConfigMissingParam("hito/db/user", file)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ositah
3
- Version: 23.8.dev1
3
+ Version: 24.2.dev1
4
4
  Summary: Outils de Suivi d'Activités basé sur Hito
5
5
  Author-email: Michel Jouvin <michel.jouvin@ijclab.in2p3.fr>
6
6
  License: BSD 3-Clause License
@@ -12,6 +12,20 @@ Classifier: Programming Language :: Python :: 3
12
12
  Requires-Python: >=3.8
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
+ Requires-Dist: blinker
16
+ Requires-Dist: dash
17
+ Requires-Dist: dash-bootstrap-components
18
+ Requires-Dist: flask~=2.2
19
+ Requires-Dist: flask-multipass
20
+ Requires-Dist: flask-sqlalchemy~=3.0
21
+ Requires-Dist: flask-wtf
22
+ Requires-Dist: hito-tools>=23.2
23
+ Requires-Dist: pandas==2.*
24
+ Requires-Dist: pymysql
25
+ Requires-Dist: python-ldap
26
+ Requires-Dist: pyyaml
27
+ Requires-Dist: simplejson
28
+ Requires-Dist: sqlalchemy~=2.0
15
29
 
16
30
  # OSITAH : Outil de Suivi de Temps et d'Activités basé sur Hito
17
31
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ositah"
7
- version = "23.8.dev1"
7
+ version = "24.2.dev1"
8
8
  description = "Outils de Suivi d'Activités basé sur Hito"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes