dipex 5.7.0__tar.gz → 5.7.2__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.
- {dipex-5.7.0 → dipex-5.7.2}/PKG-INFO +1 -1
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/lora_cache.py +2 -2
- {dipex-5.7.0 → dipex-5.7.2}/pyproject.toml +1 -1
- dipex-5.7.0/integrations/SD_Lon/alembic/env.py +0 -68
- dipex-5.7.0/integrations/SD_Lon/alembic/versions/26d00ae9c67f_add_payload_table.py +0 -47
- dipex-5.7.0/integrations/SD_Lon/payload_db/__init__.py +0 -40
- dipex-5.7.0/integrations/SD_Lon/payload_db/engine.py +0 -14
- dipex-5.7.0/integrations/SD_Lon/payload_db/models.py +0 -23
- dipex-5.7.0/integrations/SD_Lon/sdlon/config.py +0 -201
- dipex-5.7.0/integrations/SD_Lon/sdlon/date_utils.py +0 -181
- dipex-5.7.0/integrations/SD_Lon/sdlon/employees.py +0 -51
- dipex-5.7.0/integrations/SD_Lon/sdlon/engagement.py +0 -144
- dipex-5.7.0/integrations/SD_Lon/sdlon/exceptions.py +0 -2
- dipex-5.7.0/integrations/SD_Lon/sdlon/fix_departments.py +0 -568
- dipex-5.7.0/integrations/SD_Lon/sdlon/graphql.py +0 -36
- dipex-5.7.0/integrations/SD_Lon/sdlon/it_systems.py +0 -131
- dipex-5.7.0/integrations/SD_Lon/sdlon/log.py +0 -66
- dipex-5.7.0/integrations/SD_Lon/sdlon/main.py +0 -129
- dipex-5.7.0/integrations/SD_Lon/sdlon/models.py +0 -42
- dipex-5.7.0/integrations/SD_Lon/sdlon/scripts/fix_monthly_hourly_divide.py +0 -223
- dipex-5.7.0/integrations/SD_Lon/sdlon/scripts/fix_status8.py +0 -306
- dipex-5.7.0/integrations/SD_Lon/sdlon/scripts/generate_org_unit_level_uuid.py +0 -36
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_changed_at.py +0 -1645
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_changed_at_redo.py +0 -75
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_cli.py +0 -29
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_common.py +0 -299
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_fixup.py +0 -475
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_importer.py +0 -906
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_log_analyzer.py +0 -223
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_payloads.py +0 -164
- dipex-5.7.0/integrations/SD_Lon/sdlon/sd_to_pydantic.py +0 -38
- dipex-5.7.0/integrations/SD_Lon/sdlon/skip.py +0 -61
- dipex-5.7.0/integrations/SD_Lon/sdlon/sync_job_id.py +0 -255
- dipex-5.7.0/integrations/SD_Lon/sdlon/test_mo_against_sd.py +0 -359
- dipex-5.7.0/integrations/SD_Lon/sdlon/test_sd_connectivity.py +0 -82
- dipex-5.7.0/integrations/SD_Lon/sdtool/config.py +0 -62
- dipex-5.7.0/integrations/SD_Lon/sdtool/main.py +0 -156
- dipex-5.7.0/integrations/SD_Lon/tests/__init__.py +0 -9
- dipex-5.7.0/integrations/SD_Lon/tests/fixtures.py +0 -746
- dipex-5.7.0/integrations/SD_Lon/tests/org_manipulations.py +0 -360
- dipex-5.7.0/integrations/SD_Lon/tests/payload_db/test_engine.py +0 -34
- dipex-5.7.0/integrations/SD_Lon/tests/payload_db/test_log_payload.py +0 -33
- dipex-5.7.0/integrations/SD_Lon/tests/sdtool/test_app.py +0 -61
- dipex-5.7.0/integrations/SD_Lon/tests/sdtool/test_config.py +0 -102
- dipex-5.7.0/integrations/SD_Lon/tests/sdtool/utils.py +0 -59
- dipex-5.7.0/integrations/SD_Lon/tests/test_config.py +0 -282
- dipex-5.7.0/integrations/SD_Lon/tests/test_date_utils.py +0 -374
- dipex-5.7.0/integrations/SD_Lon/tests/test_employee.py +0 -57
- dipex-5.7.0/integrations/SD_Lon/tests/test_engagement.py +0 -135
- dipex-5.7.0/integrations/SD_Lon/tests/test_fix_departments.py +0 -522
- dipex-5.7.0/integrations/SD_Lon/tests/test_graphql.py +0 -23
- dipex-5.7.0/integrations/SD_Lon/tests/test_it_systems.py +0 -121
- dipex-5.7.0/integrations/SD_Lon/tests/test_log.py +0 -12
- dipex-5.7.0/integrations/SD_Lon/tests/test_main.py +0 -61
- dipex-5.7.0/integrations/SD_Lon/tests/test_sd_changed_at.py +0 -1616
- dipex-5.7.0/integrations/SD_Lon/tests/test_sd_common.py +0 -75
- dipex-5.7.0/integrations/SD_Lon/tests/test_sd_importer.py +0 -717
- dipex-5.7.0/integrations/SD_Lon/tests/test_sd_log_analyzer.py +0 -221
- dipex-5.7.0/integrations/SD_Lon/tests/test_sd_to_pydantic.py +0 -94
- dipex-5.7.0/integrations/SD_Lon/tests/test_skip.py +0 -62
- dipex-5.7.0/integrations/SD_Lon/tests/test_sync_job_id.py +0 -31
- dipex-5.7.0/integrations/aarhus/tests/__init__.py +0 -0
- dipex-5.7.0/integrations/ad_integration/__init__.py +0 -0
- dipex-5.7.0/integrations/ad_integration/tests/__init__.py +0 -0
- dipex-5.7.0/integrations/calculate_primary/__init__.py +0 -0
- dipex-5.7.0/integrations/calculate_primary/tests/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/gql_lora_cache_async.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/lc_for_jobs_db.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/log.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/main.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/old_lora_cache.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/sql_export.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/sql_table_defs.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/sql_url.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/sql_export/trigger.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/exporters/utils/priority_by_class.py +0 -0
- {dipex-5.7.0/integrations/SD_Lon/sdlon → dipex-5.7.2/integrations}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/config.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/dar_helper.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/initial.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/initial_classes.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_files.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_import.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_leder.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_org.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_pers.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/los_stam.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/payloads.py +0 -0
- {dipex-5.7.0/integrations/SD_Lon/sdlon/scripts → dipex-5.7.2/integrations/aarhus/tests}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/conftest.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/helpers.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/strategies.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_config.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_initial.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_files.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_import.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_leder.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_org.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_pers.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_los_stam.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_payloads.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/tests/test_util.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/util.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/aarhus/uuids.py +0 -0
- {dipex-5.7.0/integrations/SD_Lon/sdtool → dipex-5.7.2/integrations/ad_integration}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_common.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_exceptions.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_fix_enddate.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_jinja_filters.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_life_cycle.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_logger.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_reader.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_sync.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_template_engine.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_templates.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/ad_writer.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/import_ad_group_into_mo.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/mo_to_ad_sync.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/payloads.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/read_ad_conf_settings.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/sync_mo_uuid_to_ad.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/test_connectivity.py +0 -0
- {dipex-5.7.0/integrations/SD_Lon/tests/payload_db → dipex-5.7.2/integrations/ad_integration/tests}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/mocks.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/name_simulator.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_common.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_fix_enddate.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_jinja_filters.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_life_cycle.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_logger.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_reader.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_sync.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_ad_writer.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_dot_mapping.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_first_included.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_import_ad_group_into_mo.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_location_element.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_mo_data_source.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_mo_to_ad_sync.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_morestsource.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_read_ad_conf_settings.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_recursive_dict_update.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_sync_mo_uuid_to_ad.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_usernames.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/tests/test_utils.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/user_names.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/username_rules/method_2.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/ad_integration/utils.py +0 -0
- {dipex-5.7.0/integrations/SD_Lon/tests/sdtool → dipex-5.7.2/integrations/calculate_primary}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/calculate_primary.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/common.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/default.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/opus.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/sd.py +0 -0
- {dipex-5.7.0/integrations → dipex-5.7.2/integrations/calculate_primary/tests}/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/calculate_primary/tests/test_primary.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/cpr_mapper.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/dawa_helper.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/kle/__init__.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/kle/kle_import.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/kle/kle_import_export.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/kle/kle_xlsx.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/kle/payloads.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/clear_and_import_opus.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/opus_diff_import.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/opus_exceptions.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/opus_file_reader.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/opus_helpers.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/opus_reimport_one.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/org_tree_print/main.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/payloads.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/tests/test_opus_diff_import.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/tests/test_opus_file_reader.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/opus/tests/test_opus_helpers.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/rundb/db_overview.py +0 -0
- {dipex-5.7.0 → dipex-5.7.2}/integrations/rundb/tests/test_db_overview.py +0 -0
|
@@ -19,8 +19,8 @@ PICKLE_PROTOCOL = pickle.DEFAULT_PROTOCOL
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def get_cache(resolve_dar=True, full_history=False, skip_past=False, settings=None):
|
|
22
|
-
if GqlLoraCacheSettings().sentry_dsn:
|
|
23
|
-
sentry_sdk.init(dsn=GqlLoraCacheSettings().sentry_dsn)
|
|
22
|
+
if GqlLoraCacheSettings().job_settings.sentry_dsn:
|
|
23
|
+
sentry_sdk.init(dsn=GqlLoraCacheSettings().job_settings.sentry_dsn)
|
|
24
24
|
|
|
25
25
|
if GqlLoraCacheSettings().use_new_cache:
|
|
26
26
|
return GQLLoraCache(
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Magenta ApS <https://magenta.dk>
|
|
2
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
-
# Initial version generated using `poetry run alembic init alembic`
|
|
4
|
-
from logging.config import fileConfig
|
|
5
|
-
|
|
6
|
-
from alembic import context
|
|
7
|
-
from payload_db.engine import get_db_url
|
|
8
|
-
from payload_db.engine import get_engine
|
|
9
|
-
from payload_db.models import Base
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
# this is the Alembic Config object, which provides
|
|
13
|
-
# access to the values within the .ini file in use.
|
|
14
|
-
config = context.config
|
|
15
|
-
|
|
16
|
-
# Interpret the config file for Python logging.
|
|
17
|
-
# This line sets up loggers basically.
|
|
18
|
-
if config.config_file_name is not None:
|
|
19
|
-
fileConfig(config.config_file_name)
|
|
20
|
-
|
|
21
|
-
# add your model's MetaData object here
|
|
22
|
-
# for 'autogenerate' support
|
|
23
|
-
# from myapp import mymodel
|
|
24
|
-
# target_metadata = mymodel.Base.metadata
|
|
25
|
-
target_metadata = Base.metadata
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def run_migrations_offline() -> None:
|
|
29
|
-
"""Run migrations in 'offline' mode.
|
|
30
|
-
|
|
31
|
-
This configures the context with just a URL
|
|
32
|
-
and not an Engine, though an Engine is acceptable
|
|
33
|
-
here as well. By skipping the Engine creation
|
|
34
|
-
we don't even need a DBAPI to be available.
|
|
35
|
-
|
|
36
|
-
Calls to context.execute() here emit the given string to the
|
|
37
|
-
script output.
|
|
38
|
-
|
|
39
|
-
"""
|
|
40
|
-
context.configure(
|
|
41
|
-
url=get_db_url(),
|
|
42
|
-
target_metadata=target_metadata,
|
|
43
|
-
literal_binds=True,
|
|
44
|
-
dialect_opts={"paramstyle": "named"},
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
with context.begin_transaction():
|
|
48
|
-
context.run_migrations()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def run_migrations_online() -> None:
|
|
52
|
-
"""Run migrations in 'online' mode.
|
|
53
|
-
|
|
54
|
-
In this scenario we need to create an Engine
|
|
55
|
-
and associate a connection with the context.
|
|
56
|
-
|
|
57
|
-
"""
|
|
58
|
-
connectable = get_engine()
|
|
59
|
-
with connectable.connect() as connection:
|
|
60
|
-
context.configure(connection=connection, target_metadata=target_metadata)
|
|
61
|
-
with context.begin_transaction():
|
|
62
|
-
context.run_migrations()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if context.is_offline_mode():
|
|
66
|
-
run_migrations_offline()
|
|
67
|
-
else:
|
|
68
|
-
run_migrations_online()
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Magenta ApS
|
|
2
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
-
"""Add Payload table
|
|
4
|
-
|
|
5
|
-
Revision ID: 26d00ae9c67f
|
|
6
|
-
Revises:
|
|
7
|
-
Create Date: 2023-09-29 12:31:28.477508
|
|
8
|
-
|
|
9
|
-
"""
|
|
10
|
-
from typing import Sequence
|
|
11
|
-
from typing import Union
|
|
12
|
-
|
|
13
|
-
import sqlalchemy as sa
|
|
14
|
-
from alembic import op
|
|
15
|
-
from sqlalchemy.dialects import postgresql
|
|
16
|
-
|
|
17
|
-
# revision identifiers, used by Alembic.
|
|
18
|
-
revision: str = "26d00ae9c67f"
|
|
19
|
-
down_revision: Union[str, None] = None
|
|
20
|
-
branch_labels: Union[str, Sequence[str], None] = None
|
|
21
|
-
depends_on: Union[str, Sequence[str], None] = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def upgrade() -> None:
|
|
25
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
26
|
-
op.create_table(
|
|
27
|
-
"payload",
|
|
28
|
-
sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False),
|
|
29
|
-
sa.Column(
|
|
30
|
-
"timestamp",
|
|
31
|
-
sa.DateTime(timezone=True),
|
|
32
|
-
server_default=sa.text("now()"),
|
|
33
|
-
nullable=True,
|
|
34
|
-
),
|
|
35
|
-
sa.Column("full_url", sa.TEXT(), nullable=True),
|
|
36
|
-
sa.Column("params", sa.TEXT(), nullable=True),
|
|
37
|
-
sa.Column("response", sa.TEXT(), nullable=True),
|
|
38
|
-
sa.Column("status_code", sa.Integer(), nullable=True),
|
|
39
|
-
sa.PrimaryKeyConstraint("id"),
|
|
40
|
-
)
|
|
41
|
-
# ### end Alembic commands ###
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def downgrade() -> None:
|
|
45
|
-
# ### commands auto generated by Alembic - please adjust! ###
|
|
46
|
-
op.drop_table("payload")
|
|
47
|
-
# ### end Alembic commands ###
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Magenta ApS
|
|
2
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
-
from uuid import UUID
|
|
4
|
-
|
|
5
|
-
from sqlalchemy.orm import sessionmaker
|
|
6
|
-
|
|
7
|
-
from .engine import get_engine
|
|
8
|
-
from .models import Payload
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Session = sessionmaker()
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def log_payload(
|
|
15
|
-
request_uuid: UUID,
|
|
16
|
-
full_url: str,
|
|
17
|
-
params: str,
|
|
18
|
-
response: str,
|
|
19
|
-
status_code: int,
|
|
20
|
-
) -> None:
|
|
21
|
-
"""Log a given SD payload to the payload database
|
|
22
|
-
|
|
23
|
-
Args:
|
|
24
|
-
request_uuid: Unique UUID of this API request. Must be generated by the caller.
|
|
25
|
-
full_url: Full URL of API endpoint called, e.g. containing the SD server name.
|
|
26
|
-
params: Stringified version of the `payload` variable in `sd_lookup`.
|
|
27
|
-
response: The raw/unparsed XML response from the SD API endpoint called.
|
|
28
|
-
status_code: The HTTP status code from the SD API endpoint called.
|
|
29
|
-
"""
|
|
30
|
-
Session.configure(bind=get_engine())
|
|
31
|
-
session = Session()
|
|
32
|
-
payload = Payload(
|
|
33
|
-
id=request_uuid,
|
|
34
|
-
full_url=full_url,
|
|
35
|
-
params=params,
|
|
36
|
-
response=response,
|
|
37
|
-
status_code=status_code,
|
|
38
|
-
)
|
|
39
|
-
session.add(payload)
|
|
40
|
-
session.commit()
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Magenta ApS
|
|
2
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
from sqlalchemy import create_engine
|
|
6
|
-
from sqlalchemy.engine import Engine
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_db_url() -> str:
|
|
10
|
-
return f"postgresql+psycopg2://{os.environ['APP_DBUSER']}:{os.environ['APP_DBPASSWORD']}@{os.environ['PGHOST']}/{os.environ['APP_DATABASE']}"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def get_engine() -> Engine:
|
|
14
|
-
return create_engine(get_db_url())
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Magenta ApS
|
|
2
|
-
# SPDX-License-Identifier: MPL-2.0
|
|
3
|
-
from sqlalchemy import Column
|
|
4
|
-
from sqlalchemy import DateTime
|
|
5
|
-
from sqlalchemy import Integer
|
|
6
|
-
from sqlalchemy.dialects.postgresql import TEXT
|
|
7
|
-
from sqlalchemy.dialects.postgresql import UUID
|
|
8
|
-
from sqlalchemy.orm import declarative_base
|
|
9
|
-
from sqlalchemy.sql import func
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Base = declarative_base()
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class Payload(Base): # type: ignore
|
|
16
|
-
__tablename__ = "payload"
|
|
17
|
-
|
|
18
|
-
id = Column(UUID(as_uuid=True), primary_key=True)
|
|
19
|
-
timestamp = Column(DateTime(timezone=True), server_default=func.now())
|
|
20
|
-
full_url = Column(TEXT)
|
|
21
|
-
params = Column(TEXT)
|
|
22
|
-
response = Column(TEXT)
|
|
23
|
-
status_code = Column(Integer)
|
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
#
|
|
3
|
-
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
4
|
-
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
5
|
-
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
6
|
-
#
|
|
7
|
-
from datetime import date
|
|
8
|
-
from functools import lru_cache
|
|
9
|
-
from typing import Any
|
|
10
|
-
from typing import Dict
|
|
11
|
-
from typing import List
|
|
12
|
-
from typing import Optional
|
|
13
|
-
from typing import Type
|
|
14
|
-
|
|
15
|
-
from pydantic import AnyHttpUrl
|
|
16
|
-
from pydantic import BaseSettings
|
|
17
|
-
from pydantic import conint
|
|
18
|
-
from pydantic import constr
|
|
19
|
-
from pydantic import Extra
|
|
20
|
-
from pydantic import Field
|
|
21
|
-
from pydantic import PositiveInt
|
|
22
|
-
from pydantic import SecretStr
|
|
23
|
-
from pydantic import UUID4
|
|
24
|
-
from ra_utils.load_settings import load_settings
|
|
25
|
-
from ra_utils.job_settings import JobSettings
|
|
26
|
-
|
|
27
|
-
from .log import LogLevel
|
|
28
|
-
from .models import JobFunction
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class CommonSettings(BaseSettings): # type: ignore
|
|
32
|
-
"""
|
|
33
|
-
Settings common to both the SD importer and SD-changed-at
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
mora_base: AnyHttpUrl = Field("http://mo-service:5000")
|
|
37
|
-
mox_base: AnyHttpUrl = Field("http://mo-service:5000/lora")
|
|
38
|
-
|
|
39
|
-
sd_employment_field: Optional[str] = Field(default=None, regex="extension_[0-9]+")
|
|
40
|
-
sd_global_from_date: date
|
|
41
|
-
sd_import_run_db: str
|
|
42
|
-
sd_import_too_deep: List[str] = []
|
|
43
|
-
sd_institution_identifier: str
|
|
44
|
-
sd_password: SecretStr
|
|
45
|
-
sd_user: str
|
|
46
|
-
sd_job_function: JobFunction
|
|
47
|
-
sd_monthly_hourly_divide: PositiveInt
|
|
48
|
-
|
|
49
|
-
# List of SD JobPositionIdentifiers which should not result in creation
|
|
50
|
-
# of engagements
|
|
51
|
-
sd_skip_employment_types: List[str] = []
|
|
52
|
-
|
|
53
|
-
# If true, the <TelephoneNumberIdentifier> in <ContactInformation> in
|
|
54
|
-
# <Employment> in the <Person> tag will be used to set a value that can
|
|
55
|
-
# trigger AD-creation of the user
|
|
56
|
-
# (see https://redmine.magenta-aps.dk/issues/56089)
|
|
57
|
-
sd_phone_number_id_for_ad_creation: bool = False
|
|
58
|
-
# SD can only set <TelephoneNumberIdentifier> to an integer value, hence
|
|
59
|
-
# the value 14 is used (letters A=1 and D=4 for "AD")
|
|
60
|
-
sd_phone_number_id_trigger: str = "14"
|
|
61
|
-
sd_phone_number_id_for_ad_string: str = "AD-bruger fra SD"
|
|
62
|
-
|
|
63
|
-
cpr_uuid_map_path: str = (
|
|
64
|
-
"/opt/dipex/os2mo-data-import-and-export/settings/cpr_uuid_map.csv"
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
log_level: LogLevel = LogLevel.DEBUG
|
|
68
|
-
|
|
69
|
-
job_settings: JobSettings = JobSettings()
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def gen_json_file_settings_func(settings_class: Type[CommonSettings]):
|
|
73
|
-
def json_file_settings(settings: BaseSettings) -> Dict[str, Any]: # type: ignore
|
|
74
|
-
try:
|
|
75
|
-
json_settings = load_settings()
|
|
76
|
-
except FileNotFoundError:
|
|
77
|
-
return dict()
|
|
78
|
-
|
|
79
|
-
# Remove the "integrations.SD_Lon." part of the key name
|
|
80
|
-
json_settings = {
|
|
81
|
-
key.replace("integrations.SD_Lon.", "sd_"): value
|
|
82
|
-
for key, value in json_settings.items()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# Remove any double "sd_sd_" in the keys
|
|
86
|
-
json_settings = {
|
|
87
|
-
key.replace("sd_sd_", "sd_"): value for key, value in json_settings.items()
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
# Replace dots with underscores to be Pydantic compliant
|
|
91
|
-
json_settings = {
|
|
92
|
-
key.replace(".", "_"): value for key, value in json_settings.items()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
# Remove settings forbidden according to the Settings model
|
|
96
|
-
properties = settings_class.schema()["properties"].keys()
|
|
97
|
-
json_settings = {
|
|
98
|
-
key: value for key, value in json_settings.items() if key in properties
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return json_settings
|
|
102
|
-
|
|
103
|
-
return json_file_settings
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
class SDCommonSettings(CommonSettings):
|
|
107
|
-
class Config:
|
|
108
|
-
extra = Extra.forbid
|
|
109
|
-
|
|
110
|
-
@classmethod
|
|
111
|
-
def customise_sources(
|
|
112
|
-
cls,
|
|
113
|
-
init_settings,
|
|
114
|
-
env_settings,
|
|
115
|
-
file_secret_settings,
|
|
116
|
-
):
|
|
117
|
-
return (
|
|
118
|
-
init_settings,
|
|
119
|
-
env_settings,
|
|
120
|
-
gen_json_file_settings_func(SDCommonSettings),
|
|
121
|
-
file_secret_settings,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
class ImporterSettings(CommonSettings):
|
|
126
|
-
municipality_code: conint(ge=100, le=999) # type: ignore
|
|
127
|
-
municipality_name: str
|
|
128
|
-
sd_importer_create_associations: bool = True
|
|
129
|
-
sd_importer_create_email_addresses: bool = True
|
|
130
|
-
|
|
131
|
-
# Deprecated
|
|
132
|
-
sd_importer_employment_date_as_engagement_start_date: bool = False
|
|
133
|
-
|
|
134
|
-
sd_no_salary_minimum_id: Optional[int] = None
|
|
135
|
-
sd_use_ad_integration: bool = True
|
|
136
|
-
|
|
137
|
-
class Config:
|
|
138
|
-
extra = Extra.forbid
|
|
139
|
-
|
|
140
|
-
@classmethod
|
|
141
|
-
def customise_sources(
|
|
142
|
-
cls,
|
|
143
|
-
init_settings,
|
|
144
|
-
env_settings,
|
|
145
|
-
file_secret_settings,
|
|
146
|
-
):
|
|
147
|
-
return (
|
|
148
|
-
init_settings,
|
|
149
|
-
env_settings,
|
|
150
|
-
gen_json_file_settings_func(ImporterSettings),
|
|
151
|
-
file_secret_settings,
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class ChangedAtSettings(CommonSettings):
|
|
156
|
-
sd_fix_departments_root: Optional[UUID4] = None
|
|
157
|
-
sd_no_salary_minimum_id: Optional[int] = None
|
|
158
|
-
sd_overwrite_existing_employment_name = True
|
|
159
|
-
|
|
160
|
-
# List of CRPs to either include OR exclude in the run
|
|
161
|
-
sd_cprs: List[constr(regex="^[0-9]{10}$")] = [] # type: ignore # noqa
|
|
162
|
-
|
|
163
|
-
# If true, the sd_cprs will be excluded and if false, only the
|
|
164
|
-
# CPRs in sd_cprs will be included in the run
|
|
165
|
-
sd_exclude_cprs_mode: bool = True
|
|
166
|
-
|
|
167
|
-
sd_read_forced_uuids: bool = True
|
|
168
|
-
sd_update_primary_engagement: bool = True
|
|
169
|
-
sd_use_ad_integration: bool = True
|
|
170
|
-
|
|
171
|
-
class Config:
|
|
172
|
-
extra = Extra.forbid
|
|
173
|
-
|
|
174
|
-
@classmethod
|
|
175
|
-
def customise_sources(
|
|
176
|
-
cls,
|
|
177
|
-
init_settings,
|
|
178
|
-
env_settings,
|
|
179
|
-
file_secret_settings,
|
|
180
|
-
):
|
|
181
|
-
return (
|
|
182
|
-
init_settings,
|
|
183
|
-
env_settings,
|
|
184
|
-
gen_json_file_settings_func(ChangedAtSettings),
|
|
185
|
-
file_secret_settings,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@lru_cache()
|
|
190
|
-
def get_common_settings(*args, **kwargs) -> SDCommonSettings:
|
|
191
|
-
return SDCommonSettings(*args, **kwargs)
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@lru_cache()
|
|
195
|
-
def get_importer_settings(*args, **kwargs) -> ImporterSettings:
|
|
196
|
-
return ImporterSettings(*args, **kwargs)
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@lru_cache()
|
|
200
|
-
def get_changed_at_settings(*args, **kwargs) -> ChangedAtSettings:
|
|
201
|
-
return ChangedAtSettings(*args, **kwargs)
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
import re
|
|
3
|
-
from datetime import date
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from datetime import timedelta
|
|
6
|
-
from itertools import chain
|
|
7
|
-
from itertools import takewhile
|
|
8
|
-
from typing import OrderedDict
|
|
9
|
-
from typing import Union
|
|
10
|
-
from typing import Iterator
|
|
11
|
-
from typing import Optional
|
|
12
|
-
from typing import Tuple
|
|
13
|
-
|
|
14
|
-
from more_itertools import pairwise
|
|
15
|
-
from more_itertools import tabulate
|
|
16
|
-
|
|
17
|
-
from .log import get_logger
|
|
18
|
-
from .sd_common import EmploymentStatus
|
|
19
|
-
|
|
20
|
-
# TODO: move constants elsewhere
|
|
21
|
-
# TODO: set back to "infinity" when MO can handle this
|
|
22
|
-
# MO_INFINITY: str = "infinity"
|
|
23
|
-
|
|
24
|
-
MO_INFINITY = None
|
|
25
|
-
SD_INFINITY: str = "9999-12-31"
|
|
26
|
-
DATE_REGEX_STR = "[0-9]{4}-(0[1-9]|1[0-2])-([0-2][0-9]|3[0-1])"
|
|
27
|
-
|
|
28
|
-
logger = get_logger()
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def date_to_datetime(d: date) -> datetime:
|
|
32
|
-
return datetime(d.year, d.month, d.day)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def format_date(d: datetime | date) -> str:
|
|
36
|
-
return d.strftime("%Y-%m-%d").zfill(10)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def parse_datetime(date_str: str) -> datetime:
|
|
40
|
-
return datetime.strptime(date_str, "%Y-%m-%d")
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
def datetime_to_sd_date(date_time: Union[datetime, date]) -> str:
|
|
44
|
-
return date_time.strftime("%d.%m.%Y")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _get_employment_from_date(employment: OrderedDict) -> datetime:
|
|
48
|
-
"""
|
|
49
|
-
Get the latest date of all the dates in the payload from the SD
|
|
50
|
-
GetEmployment endpoint (see https://redmine.magenta-aps.dk/issues/51898)
|
|
51
|
-
|
|
52
|
-
Args:
|
|
53
|
-
employment: the SD employment
|
|
54
|
-
|
|
55
|
-
Returns: the maximum of all the date in the SD payload
|
|
56
|
-
|
|
57
|
-
"""
|
|
58
|
-
MIN_DATE = format_date(datetime.min)
|
|
59
|
-
|
|
60
|
-
employment_date = employment.get("EmploymentDate", MIN_DATE)
|
|
61
|
-
employment_department_date = employment.get("EmploymentDepartment", dict()).get(
|
|
62
|
-
"ActivationDate", MIN_DATE
|
|
63
|
-
)
|
|
64
|
-
employment_status_date = employment.get("EmploymentStatus", dict()).get(
|
|
65
|
-
"ActivationDate", MIN_DATE
|
|
66
|
-
)
|
|
67
|
-
profession_date = employment.get("Profession", dict()).get(
|
|
68
|
-
"ActivationDate", MIN_DATE
|
|
69
|
-
)
|
|
70
|
-
working_time_date = employment.get("WorkingTime", dict()).get(
|
|
71
|
-
"ActivationDate", MIN_DATE
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
return max(
|
|
75
|
-
parse_datetime(employment_date),
|
|
76
|
-
parse_datetime(employment_department_date),
|
|
77
|
-
parse_datetime(employment_status_date),
|
|
78
|
-
parse_datetime(profession_date),
|
|
79
|
-
parse_datetime(working_time_date),
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def get_employment_datetimes(employment: OrderedDict) -> Tuple[datetime, datetime]:
|
|
84
|
-
"""
|
|
85
|
-
Get the "from" and "to" date from the SD employment
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
employment: The SD employment
|
|
89
|
-
|
|
90
|
-
Returns:
|
|
91
|
-
Tuple containing the "from" and "to" dates.
|
|
92
|
-
"""
|
|
93
|
-
|
|
94
|
-
status = EmploymentStatus(employment["EmploymentStatus"]["EmploymentStatusCode"])
|
|
95
|
-
|
|
96
|
-
if status in EmploymentStatus.let_go():
|
|
97
|
-
datetime_from = parse_datetime(employment["EmploymentDate"])
|
|
98
|
-
termination_date = str(
|
|
99
|
-
sd_to_mo_termination_date(employment["EmploymentStatus"]["ActivationDate"])
|
|
100
|
-
)
|
|
101
|
-
datetime_to = parse_datetime(termination_date)
|
|
102
|
-
return datetime_from, datetime_to
|
|
103
|
-
|
|
104
|
-
datetime_from = _get_employment_from_date(employment)
|
|
105
|
-
datetime_to = parse_datetime(employment["EmploymentStatus"]["DeactivationDate"])
|
|
106
|
-
|
|
107
|
-
return datetime_from, datetime_to
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
# TODO: Create "MoValidity" and "SdValidity" classes based on the RA Models
|
|
111
|
-
# "Validity" class and use these as input to the function below
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def sd_to_mo_termination_date(sd_date: str) -> Optional[str]:
|
|
115
|
-
"""
|
|
116
|
-
Convert SD termination date to MO termination date.
|
|
117
|
-
|
|
118
|
-
Args:
|
|
119
|
-
sd_date: SD termination date formatted as "YYYY-MM-DD"
|
|
120
|
-
|
|
121
|
-
Returns:
|
|
122
|
-
MO termination date formatted as "YYYY-MM-DD"
|
|
123
|
-
"""
|
|
124
|
-
|
|
125
|
-
assert isinstance(sd_date, str)
|
|
126
|
-
assert re.compile(DATE_REGEX_STR).match(sd_date)
|
|
127
|
-
|
|
128
|
-
if sd_date == SD_INFINITY:
|
|
129
|
-
return MO_INFINITY
|
|
130
|
-
|
|
131
|
-
return sd_date
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def to_midnight(dt: datetime) -> datetime:
|
|
135
|
-
"""Get previous midnight from datetime."""
|
|
136
|
-
return dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def is_midnight(dt: datetime) -> bool:
|
|
140
|
-
"""Check if datetime is at midnight."""
|
|
141
|
-
return dt == to_midnight(dt)
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def gen_cut_dates(from_datetime: datetime, to_datetime: datetime) -> Iterator[datetime]:
|
|
145
|
-
"""Get iterator of cut-dates between from_datetime and to_datetime.
|
|
146
|
-
|
|
147
|
-
Args:
|
|
148
|
-
from_datetime: the start date
|
|
149
|
-
to_datetime: the end date
|
|
150
|
-
|
|
151
|
-
Yields:
|
|
152
|
-
The from_datetime, then all intermediate midnight datetimes and the to_datetime.
|
|
153
|
-
"""
|
|
154
|
-
assert from_datetime < to_datetime
|
|
155
|
-
|
|
156
|
-
# Tabulate to infinite iterator of midnights starting after from_datetime
|
|
157
|
-
def midnight_at_offset(offset: int) -> datetime:
|
|
158
|
-
return to_midnight(from_datetime) + timedelta(days=offset)
|
|
159
|
-
|
|
160
|
-
midnights = takewhile(
|
|
161
|
-
lambda midnight: midnight < to_datetime, tabulate(midnight_at_offset, start=1)
|
|
162
|
-
)
|
|
163
|
-
return chain([from_datetime], midnights, [to_datetime])
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
def gen_date_intervals(
|
|
167
|
-
from_datetime: datetime, to_datetime: datetime
|
|
168
|
-
) -> Iterator[Tuple[datetime, datetime]]:
|
|
169
|
-
"""
|
|
170
|
-
Get iterator capable of generating a sequence of datetime pairs
|
|
171
|
-
incrementing one day at a time. The latter date in a pair is
|
|
172
|
-
advanced by exactly one day compared to the former date in the pair.
|
|
173
|
-
|
|
174
|
-
Args:
|
|
175
|
-
from_datetime: the start date
|
|
176
|
-
to_datetime: the end date
|
|
177
|
-
|
|
178
|
-
Yields:
|
|
179
|
-
The next date pair in the sequence of pairs
|
|
180
|
-
"""
|
|
181
|
-
return pairwise(gen_cut_dates(from_datetime, to_datetime))
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from uuid import UUID
|
|
2
|
-
|
|
3
|
-
from gql import gql
|
|
4
|
-
from more_itertools import one
|
|
5
|
-
from raclients.graph.client import GraphQLClient
|
|
6
|
-
from sdlon.models import MOBasePerson
|
|
7
|
-
|
|
8
|
-
QUERY_GET_EMPLOYEE = gql(
|
|
9
|
-
"""
|
|
10
|
-
query GetEmployee($cpr: [CPR!]!) {
|
|
11
|
-
employees(cpr_numbers: $cpr) {
|
|
12
|
-
objects {
|
|
13
|
-
current {
|
|
14
|
-
name
|
|
15
|
-
givenname
|
|
16
|
-
surname
|
|
17
|
-
uuid
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
"""
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def get_employee(gql_client: GraphQLClient, cpr: str) -> MOBasePerson | None:
|
|
27
|
-
"""
|
|
28
|
-
Get employee from MO
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
gql_client: The GraphQl client for calling MO
|
|
32
|
-
cpr: The CPR number of the employee
|
|
33
|
-
|
|
34
|
-
Returns:
|
|
35
|
-
The MO employee or None if the employee does not exist in MO
|
|
36
|
-
"""
|
|
37
|
-
r = gql_client.execute(QUERY_GET_EMPLOYEE, variable_values={"cpr": cpr})
|
|
38
|
-
|
|
39
|
-
obj = r["employees"]["objects"]
|
|
40
|
-
if not obj:
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
employee = one(obj)["current"]
|
|
44
|
-
|
|
45
|
-
return MOBasePerson(
|
|
46
|
-
cpr=cpr,
|
|
47
|
-
givenname=employee["givenname"],
|
|
48
|
-
surname=employee["surname"],
|
|
49
|
-
name=employee["name"],
|
|
50
|
-
uuid=UUID(employee["uuid"]),
|
|
51
|
-
)
|