edupsyadmin 9.1.0__tar.gz → 9.2.0__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.
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.github/workflows/coverage.yml +7 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.github/workflows/publish.yml +12 -6
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.pre-commit-config.yaml +9 -5
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/CHANGELOG.md +11 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/PKG-INFO +2 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/conf.py +3 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/pyproject.toml +35 -21
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__version__.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/env.py +1 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/fill_form.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flatten_pdf.py +5 -5
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/base.py +0 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/pypdf_backend.py +8 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/lgvt.py +4 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/migration.py +12 -11
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/reports.py +0 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/taetigkeitsbericht_from_db.py +10 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/__init__.py +1 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/create_documentation.py +1 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/flatten_pdfs.py +6 -6
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/get_clients.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/new_client.py +4 -5
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/rotate_key.py +6 -7
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/setup_demo.py +6 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/taetigkeitsbericht.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/encrypt.py +12 -10
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/clients.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/info.py +3 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/clients_overview.py +5 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edit_client.py +8 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edit_client_app.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig.py +4 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig_app.py +7 -5
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edupsyadmintui.py +3 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/fill_form_app.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/fill_form_widget.py +1 -2
- edupsyadmin-9.2.0/src/edupsyadmin/tui/suggesters.py +41 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_client_view.py +2 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_fill_form.py +21 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_lgvt.py +21 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_managers.py +32 -24
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_migration.py +1 -7
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_taetigkeitsbericht_from_db.py +4 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_write_form_md.py +1 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_config.py +5 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_encrypt.py +24 -1
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_logger.py +0 -4
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_class_int_override.py +12 -11
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_clients.py +14 -15
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/snapshot_utils.py +3 -3
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_cli.py +10 -16
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_editclient.py +27 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_editconfig.py +13 -2
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_edupsyadmintui.py +68 -8
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_fill_form_tui.py +1 -1
- edupsyadmin-9.2.0/test/edupsyadmin/tui/test_suggesters.py +35 -0
- edupsyadmin-9.2.0/test/edupsyadmin/utils/test_rounding.py +19 -0
- edupsyadmin-9.2.0/uv.lock +2228 -0
- edupsyadmin-9.1.0/uv.lock +0 -2003
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.gitignore +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.python-version +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.readthedocs.yaml +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.taplo.toml +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/LICENSE.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/README.md +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/Makefile +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/_static/samplewebuntisfile.csv +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/clients_model.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/faq.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/index.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/make.bat +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/requirements.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/taetigkeitsbericht_keywords.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/cli.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/configuration.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/creatingdocumentation.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/editingclients.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/encryption.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/helpfunction.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/installation.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/taetigkeitsbericht.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/tui.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/update.rst +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/resources/edupsyadmin_logo.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/resources/edupsyadmin_logo_readme.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_iLst.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_iRst.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_lrst.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_nona_nons.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/change_keywordtaet_nsessions.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/profile.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/publish_to_testpypi.sh +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__main__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/README +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/script.py.mako +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_02_21_0453-4087c43f0c7c_initial_migration.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_02_1222-635a9d68fbe8_add_case_active_column_to_clients.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_13_0545-ea4c3f900604_encrypt_class_columns.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_13_0620-515a72d06f10_encrypt_derived_dates.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_16_0900-d7e8f9a0b1c2_add_metadata_table.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_23_1030-e2b4c6d8f0a1_encrypt_sensitive_text_fields.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_04_10_1200-f3a5b7c9d1e2_set_encrypted_text_fields_non_nullable.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_06_12_0509-0f1497df963e_add_class_int_override.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic.ini +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/cft_report.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/client_view.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/display_client_details.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/exceptions.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/api.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/managers.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/migration_fs.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/setup_demo.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/types.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/__main__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/delete_client.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/edit_config.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/info.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/mk_report.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/set_client.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/tui.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/utils.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/config.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/enums.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/logger.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/paths.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_BfsEV.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_BfsKi.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_Faks.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/LiberationSans-Bold.ttf +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/LiberationSans-Regular.ttf +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/OFL.txt +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/sampleconfig.yml +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/taetigkeitsbericht_categories.csv +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/column_types.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/converters.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/py.typed +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/clients_overview_app.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/dialogs.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig.tcss +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edupsyadmintui.tcss +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/__init__.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/academic_year.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/convert_measures.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/datediff.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/int_from_str.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/path_utils.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/python_type.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/rounding.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/taetigkeitsbericht_check_key.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/.gitignore +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_max_none_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_min_none_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_snapshot[nos_nta_user].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_snapshot[umlaut_user].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_flatten_pdf_snapshots/test_flatten_pdf_pypdf_snapshot[nos_nta_internal-libreoffice].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_flatten_pdf_snapshots/test_flatten_pdf_pypdf_snapshot[nos_nta_internal-reportlab].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_flatten_pdf_snapshots/test_flatten_pdf_pypdf_snapshot[umlaut_internal-libreoffice].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_flatten_pdf_snapshots/test_flatten_pdf_pypdf_snapshot[umlaut_internal-reportlab].png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_lgvt/test_lgvt_report_grade_11_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_lgvt/test_lgvt_report_grade_9_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_reports/test_taetigkeitsbericht_report_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_taetigkeitsbericht_from_db/test_taetigkeitsbericht_snapshot.png +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_cft_report.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_decryption_behavior.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_flatten_pdf.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_flatten_pdf_snapshots.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_migration_fs.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_reports.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_snapshot_sensitivity.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_cli_benchmark.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_convenience_data_benchmark.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_decryption_benchmark.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_encrypt_benchmark.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_key_derivation_benchmark.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/cli/test_utils.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/conftest.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/data/sample_form_anschreiben.pdf +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/data/sample_form_mantelbogen.pdf +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_metadata.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_types.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/sample_pdf_form.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/sample_webuntis_export.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_gdpr_logging.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_sync_types.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_clientsoverview/test_clients_overview.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_clientsoverview/test_clients_overview_long_names.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_dialogs/test_yes_no_dialog.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editclient/test_initial_layout_existing_client[nos_nta_user].svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editclient/test_initial_layout_existing_client[umlaut_user].svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editclient/test_initial_layout_new_client.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_add_new_school_container.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_edit_new_school_container.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_editconfig_initial_layout.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_edupsyadmintui/test_edupsyadmintui_initial_layout.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_fill_form_tui/test_fill_form_initial_layout.svg +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_clientsoverview.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_dialogs.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_academic_year.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_convert_measures.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_datediff.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_int_from_str.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_path_utils.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_python_type.py +0 -0
- {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_taetigkeitsbericht_check_key.py +0 -0
|
@@ -9,12 +9,16 @@ on:
|
|
|
9
9
|
jobs:
|
|
10
10
|
coverage:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
12
14
|
|
|
13
15
|
steps:
|
|
14
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
15
19
|
|
|
16
20
|
- name: Install uv
|
|
17
|
-
uses: astral-sh/setup-uv@
|
|
21
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
18
22
|
|
|
19
23
|
- name: Install poppler and fonts
|
|
20
24
|
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends poppler-utils fonts-liberation poppler-data
|
|
@@ -26,6 +30,6 @@ jobs:
|
|
|
26
30
|
run: uv run pytest --cov --cov-branch --cov-report=xml
|
|
27
31
|
|
|
28
32
|
- name: Upload coverage to Codecov
|
|
29
|
-
uses: codecov/codecov-action@
|
|
33
|
+
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
|
|
30
34
|
with:
|
|
31
35
|
token: ${{ secrets.CODECOV_TOKEN }}
|
|
@@ -8,17 +8,23 @@ jobs:
|
|
|
8
8
|
run:
|
|
9
9
|
name: "Build and publish release"
|
|
10
10
|
runs-on: ubuntu-latest
|
|
11
|
-
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
12
15
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
16
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
17
|
+
with:
|
|
18
|
+
persist-credentials: false
|
|
14
19
|
|
|
15
20
|
- name: "Install uv"
|
|
16
|
-
uses: astral-sh/setup-uv@
|
|
21
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
17
22
|
|
|
18
|
-
- name: "Set up Python"
|
|
19
|
-
uses: actions/setup-python@v5
|
|
20
23
|
with:
|
|
21
|
-
|
|
24
|
+
enable-cache: false
|
|
25
|
+
|
|
26
|
+
- name: "Set up Python"
|
|
27
|
+
run: uv python install
|
|
22
28
|
|
|
23
29
|
- name: "Build"
|
|
24
30
|
run: uv build
|
|
@@ -9,7 +9,7 @@ repos:
|
|
|
9
9
|
- id: mixed-line-ending
|
|
10
10
|
args: ['--fix=lf']
|
|
11
11
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
12
|
-
rev: v0.
|
|
12
|
+
rev: v0.16.6
|
|
13
13
|
hooks:
|
|
14
14
|
- id: ruff-check
|
|
15
15
|
args: [ "--config=pyproject.toml", "--fix" ]
|
|
@@ -19,11 +19,11 @@ repos:
|
|
|
19
19
|
hooks:
|
|
20
20
|
- id: gitleaks
|
|
21
21
|
- repo: https://github.com/commitizen-tools/commitizen
|
|
22
|
-
rev: v4.
|
|
22
|
+
rev: v4.18.0
|
|
23
23
|
hooks:
|
|
24
24
|
- id: commitizen
|
|
25
25
|
- repo: https://github.com/rstcheck/rstcheck
|
|
26
|
-
rev: v6.
|
|
26
|
+
rev: v6.3.0
|
|
27
27
|
hooks:
|
|
28
28
|
- id: rstcheck
|
|
29
29
|
additional_dependencies: ['rstcheck[sphinx,toml]']
|
|
@@ -33,7 +33,7 @@ repos:
|
|
|
33
33
|
- id: sphinx-lint
|
|
34
34
|
args: ["--enable=all"]
|
|
35
35
|
- repo: https://github.com/rvben/rumdl-pre-commit
|
|
36
|
-
rev: v0.2.
|
|
36
|
+
rev: v0.2.68
|
|
37
37
|
hooks:
|
|
38
38
|
- id: rumdl-fmt # Auto-format and fail if issues remain
|
|
39
39
|
- repo: local
|
|
@@ -46,6 +46,10 @@ repos:
|
|
|
46
46
|
args: [--python=.venv/]
|
|
47
47
|
pass_filenames: true
|
|
48
48
|
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
49
|
-
rev: "v2.
|
|
49
|
+
rev: "v2.29.3"
|
|
50
50
|
hooks:
|
|
51
51
|
- id: pyproject-fmt
|
|
52
|
+
- repo: https://github.com/zizmorcore/zizmor-pre-commit
|
|
53
|
+
rev: v1.30.0
|
|
54
|
+
hooks:
|
|
55
|
+
- id: zizmor
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: edupsyadmin
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.2.0
|
|
4
4
|
Summary: edupsyadmin provides tools to help school psychologists with their documentation
|
|
5
5
|
Project-URL: documentation, https://edupsyadmin.readthedocs.io
|
|
6
6
|
Project-URL: homepage, https://github.com/LKirst/edupsyadmin.git
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
# For the full list of built-in configuration values, see the documentation:
|
|
4
4
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
5
|
|
|
6
|
-
import os
|
|
7
6
|
import sys
|
|
8
7
|
from datetime import datetime
|
|
8
|
+
from pathlib import Path
|
|
9
9
|
|
|
10
|
-
sys.path.insert(0,
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
11
11
|
|
|
12
12
|
# -- Project information -----------------------------------------------------
|
|
13
13
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
14
14
|
|
|
15
15
|
project = "edupsyadmin"
|
|
16
16
|
author = "Lukas Liebermann"
|
|
17
|
-
|
|
17
|
+
project_copyright = f"{datetime.now().year}, {author}"
|
|
18
18
|
|
|
19
19
|
# -- General configuration ---------------------------------------------------
|
|
20
20
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
@@ -4,7 +4,7 @@ requires = [ "hatchling" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "edupsyadmin"
|
|
7
|
-
version = "9.
|
|
7
|
+
version = "9.2.0"
|
|
8
8
|
description = "edupsyadmin provides tools to help school psychologists with their documentation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -70,27 +70,41 @@ exclude-newer = "3d"
|
|
|
70
70
|
[tool.ruff]
|
|
71
71
|
lint.select = [
|
|
72
72
|
"A",
|
|
73
|
-
"ARG",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
"
|
|
73
|
+
"ARG", # flake8-unused-arguments – flag unused, shadowed or *args/**kwargs
|
|
74
|
+
"ASYNC", # flake8-async
|
|
75
|
+
"B", # flake8-bugbear
|
|
76
|
+
"BLE001", # flake8-blind-except
|
|
77
|
+
"C4", # flake8-comprehensions – point out sub-optimal list/dict/set comps
|
|
78
|
+
"C901", # checks for functions with a high McCabe complexity
|
|
79
|
+
"COM", # flake8-commas
|
|
80
|
+
"D419", # pydocstyle
|
|
81
|
+
"E", # pycodestyle - error
|
|
82
|
+
"F", # pyflakes
|
|
83
|
+
"FURB", # refurb - modernizing Python codebases
|
|
84
|
+
"I", # isort
|
|
85
|
+
"ISC", # flake8-implicit-str-concat - implicitly string concatenation
|
|
86
|
+
"N", # pap8-naming
|
|
87
|
+
"PERF", # perflint - a linter for performance anti-patterns
|
|
88
|
+
"PGH", # pygrep-hooks
|
|
89
|
+
"PIE", # flake8-pie
|
|
90
|
+
"PLC", # pylint - convention
|
|
91
|
+
"PLE", # pylint - error
|
|
92
|
+
"PLR", # pylint - refactor
|
|
93
|
+
"PLW", # pylint - warning
|
|
94
|
+
"PTH", # flake8-use-pathlib - find use of function that could be replaced by pathlib
|
|
95
|
+
"PYI", # flake8-pyi - a linter that provides specializsations for type hinting stub files
|
|
96
|
+
"RET", # flake8-return – keep return statements consistent and meaningful
|
|
97
|
+
"RUF", # Ruff-native extras – small bug-catchers & style nits not in other sets "UP",
|
|
98
|
+
"S102", # Use of exec detected
|
|
99
|
+
"S110", # try-except-pass detected, consider logging the exception
|
|
100
|
+
"S112", # try-except-continue detected, consider logging the exception
|
|
101
|
+
"SIM", # flake8-simplify – suggest clearer, shorter boolean / loop logic
|
|
102
|
+
"TID", # flake8-tidy-imports – ban unused or overly-relative imports
|
|
103
|
+
"TRY", # tryceratops
|
|
104
|
+
"UP", # pyupgrade – rewrite to modern Python syntax (f-strings, walrus, etc.)
|
|
105
|
+
"W", # pycodestyle - warning
|
|
92
106
|
]
|
|
93
|
-
lint.ignore = [ "COM812" ]
|
|
107
|
+
lint.ignore = [ "COM812", "PLR09", "TRY003" ]
|
|
94
108
|
lint.per-file-ignores."src/edupsyadmin/tui/*" = [ "A002", "ARG" ]
|
|
95
109
|
lint.per-file-ignores."test/*" = [ "ARG" ]
|
|
96
110
|
lint.flake8-tidy-imports.ban-relative-imports = "all"
|
|
@@ -3,6 +3,7 @@ from logging.config import fileConfig
|
|
|
3
3
|
from alembic import context
|
|
4
4
|
from sqlalchemy import engine_from_config, pool
|
|
5
5
|
|
|
6
|
+
from edupsyadmin.core.paths import DEFAULT_DB_URL
|
|
6
7
|
from edupsyadmin.db import Base
|
|
7
8
|
|
|
8
9
|
# this is the Alembic Config object, which provides
|
|
@@ -40,8 +41,6 @@ def run_migrations_offline() -> None:
|
|
|
40
41
|
"""
|
|
41
42
|
url = config.get_main_option("sqlalchemy.url")
|
|
42
43
|
if not url:
|
|
43
|
-
from edupsyadmin.core.paths import DEFAULT_DB_URL
|
|
44
|
-
|
|
45
44
|
x_args = context.get_x_argument(as_dictionary=True)
|
|
46
45
|
url = x_args.get("db_url") or DEFAULT_DB_URL
|
|
47
46
|
|
|
@@ -68,8 +67,6 @@ def run_migrations_online() -> None:
|
|
|
68
67
|
# If the URL is not in the config file, try to get it from the command line
|
|
69
68
|
# (passed via -x db_url=...) or fallback to the default database URL.
|
|
70
69
|
if not section.get("sqlalchemy.url"):
|
|
71
|
-
from edupsyadmin.core.paths import DEFAULT_DB_URL
|
|
72
|
-
|
|
73
70
|
x_args = context.get_x_argument(as_dictionary=True)
|
|
74
71
|
section["sqlalchemy.url"] = x_args.get("db_url") or DEFAULT_DB_URL
|
|
75
72
|
|
|
@@ -349,7 +349,7 @@ def batch_fill_forms(
|
|
|
349
349
|
results.append(
|
|
350
350
|
{"client_id": client_id, "success": True, "error": None},
|
|
351
351
|
)
|
|
352
|
-
except Exception as e:
|
|
352
|
+
except Exception as e: # noqa: BLE001
|
|
353
353
|
results.append(
|
|
354
354
|
{"client_id": client_id, "success": False, "error": e},
|
|
355
355
|
)
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
"""CLI tool for flattening PDF forms."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
|
+
import getpass
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
8
|
+
from pypdf import PdfReader
|
|
9
|
+
from pypdf.errors import PyPdfError
|
|
10
|
+
|
|
7
11
|
from edupsyadmin.api.flattening import (
|
|
8
12
|
DEFAULT_PREFIX,
|
|
9
13
|
InvalidPDFError,
|
|
@@ -44,21 +48,17 @@ def main() -> None:
|
|
|
44
48
|
|
|
45
49
|
password = args.password
|
|
46
50
|
if not password:
|
|
47
|
-
from pypdf import PdfReader
|
|
48
|
-
|
|
49
51
|
for p in args.inpaths:
|
|
50
52
|
p_path = Path(p)
|
|
51
53
|
if p_path.exists() and p_path.suffix.lower() == ".pdf":
|
|
52
54
|
try:
|
|
53
55
|
reader = PdfReader(str(p_path))
|
|
54
56
|
if reader.is_encrypted:
|
|
55
|
-
import getpass
|
|
56
|
-
|
|
57
57
|
password = getpass.getpass(
|
|
58
58
|
f"Passwort für verschlüsselte PDF '{p_path.name}': ",
|
|
59
59
|
)
|
|
60
60
|
break
|
|
61
|
-
except
|
|
61
|
+
except PyPdfError, OSError, ValueError:
|
|
62
62
|
continue
|
|
63
63
|
|
|
64
64
|
try:
|
|
@@ -28,8 +28,10 @@ import re
|
|
|
28
28
|
from dataclasses import dataclass
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
from textwrap import wrap
|
|
31
|
+
from typing import Final
|
|
31
32
|
|
|
32
33
|
from pypdf import PdfReader, PdfWriter
|
|
34
|
+
from pypdf.errors import PyPdfError
|
|
33
35
|
from pypdf.generic import (
|
|
34
36
|
ArrayObject,
|
|
35
37
|
DecodedStreamObject,
|
|
@@ -44,6 +46,9 @@ from pypdf.generic import (
|
|
|
44
46
|
StreamObject,
|
|
45
47
|
)
|
|
46
48
|
|
|
49
|
+
RECT_NUM_COORDINATES: Final[int] = 4
|
|
50
|
+
MAX_PRINTABLE_ASCII: Final[int] = 126
|
|
51
|
+
|
|
47
52
|
_Rect = tuple[float, float, float, float] # x0, y0, x1, y1
|
|
48
53
|
|
|
49
54
|
# Matches the font-setting operator in a /DA string, e.g. "/Helv 12 Tf"
|
|
@@ -146,7 +151,7 @@ def _rect_to_floats(rect_obj: object) -> _Rect:
|
|
|
146
151
|
float(rect_obj.right),
|
|
147
152
|
float(rect_obj.top),
|
|
148
153
|
)
|
|
149
|
-
if isinstance(rect_obj, ArrayObject) and len(rect_obj) ==
|
|
154
|
+
if isinstance(rect_obj, ArrayObject) and len(rect_obj) == RECT_NUM_COORDINATES:
|
|
150
155
|
floats = [float(v) for v in rect_obj]
|
|
151
156
|
return (floats[0], floats[1], floats[2], floats[3])
|
|
152
157
|
raise ValueError(f"Cannot parse rectangle: {rect_obj!r}")
|
|
@@ -272,7 +277,7 @@ def _ap_stream_bytes_and_resources(
|
|
|
272
277
|
|
|
273
278
|
try: # because of the checks above, I know that n_obj is a stream object
|
|
274
279
|
data = n_obj.get_data() # ty: ignore[unresolved-attribute]
|
|
275
|
-
except
|
|
280
|
+
except PyPdfError, TypeError, ValueError, KeyError, AttributeError:
|
|
276
281
|
return None
|
|
277
282
|
|
|
278
283
|
if _is_empty_stream(data):
|
|
@@ -351,7 +356,7 @@ def _escape_pdf_string(text: str) -> str:
|
|
|
351
356
|
result.append("\\r")
|
|
352
357
|
elif char == "\n":
|
|
353
358
|
result.append("\\n")
|
|
354
|
-
elif ord(char) >
|
|
359
|
+
elif ord(char) > MAX_PRINTABLE_ASCII:
|
|
355
360
|
# Octal escape for WinAnsi (cp1252).
|
|
356
361
|
try:
|
|
357
362
|
# Most standard fonts use an encoding close to CP1252.
|
|
@@ -4,6 +4,7 @@ import math
|
|
|
4
4
|
import os
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from pathlib import Path
|
|
7
|
+
from typing import Final
|
|
7
8
|
|
|
8
9
|
from edupsyadmin.api.managers import ClientsManager
|
|
9
10
|
from edupsyadmin.api.reports import (
|
|
@@ -19,6 +20,8 @@ from edupsyadmin.utils.datediff import mydatediff
|
|
|
19
20
|
from edupsyadmin.utils.path_utils import normalize_path
|
|
20
21
|
from edupsyadmin.utils.rounding import round_half_up
|
|
21
22
|
|
|
23
|
+
MAX_KORREKTUR_SCHOOLYEAR: Final[int] = 11
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
def askyn(prompt: str) -> int:
|
|
24
27
|
s_yes = {"yes", "ye", "y"}
|
|
@@ -202,7 +205,7 @@ def mk_report(
|
|
|
202
205
|
lv_rw = correct_answ * 2 - incorrect_answ
|
|
203
206
|
lgs_rw = words_until_last_item + words_after_last_item
|
|
204
207
|
|
|
205
|
-
if schoolyear <
|
|
208
|
+
if schoolyear < MAX_KORREKTUR_SCHOOLYEAR:
|
|
206
209
|
lv_korr_faktor = float(input("Korrekturfaktor LV:"))
|
|
207
210
|
lv_rw_korr_floor = math.floor(lv_rw * lv_korr_faktor)
|
|
208
211
|
lv_rw_korr_ceil = math.ceil(lv_rw * lv_korr_faktor)
|
|
@@ -33,14 +33,13 @@ def upgrade_db(database_url: str, salt_path: Path | None = None) -> None:
|
|
|
33
33
|
A backup is only created if a migration is actually required.
|
|
34
34
|
"""
|
|
35
35
|
logger.info("Checking for database migrations...")
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
pkg_path = resources.files("edupsyadmin")
|
|
39
|
-
alembic_ini_path = pkg_path.joinpath("alembic.ini")
|
|
36
|
+
pkg_path = resources.files("edupsyadmin")
|
|
37
|
+
alembic_ini_path = pkg_path.joinpath("alembic.ini")
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
if not alembic_ini_path.is_file():
|
|
40
|
+
raise MigrationError(f"Alembic config not found at {alembic_ini_path}")
|
|
43
41
|
|
|
42
|
+
try:
|
|
44
43
|
alembic_script_location = pkg_path.joinpath("alembic")
|
|
45
44
|
|
|
46
45
|
alembic_cfg = Config(str(alembic_ini_path))
|
|
@@ -212,13 +211,15 @@ def _verify_migration(db_session: Session, expected_count: int) -> None:
|
|
|
212
211
|
try:
|
|
213
212
|
stmt = select(Client)
|
|
214
213
|
clients = list(db_session.scalars(stmt))
|
|
214
|
+
except Exception as e:
|
|
215
|
+
raise MigrationError(f"Verification failed: {e}") from e
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
217
|
+
if len(clients) != expected_count:
|
|
218
|
+
raise MigrationError(
|
|
219
|
+
f"Client count mismatch: expected {expected_count}, found {len(clients)}",
|
|
220
|
+
)
|
|
221
221
|
|
|
222
|
+
try:
|
|
222
223
|
sample_size = min(10, len(clients))
|
|
223
224
|
for client in clients[:sample_size]:
|
|
224
225
|
_ = client.first_name_encr
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass, field
|
|
2
2
|
from datetime import date
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, Final
|
|
5
5
|
|
|
6
6
|
import pandas as pd
|
|
7
7
|
|
|
@@ -9,6 +9,9 @@ from edupsyadmin.api.managers import ClientsManager
|
|
|
9
9
|
from edupsyadmin.api.reports import TaetigkeitsberichtReport
|
|
10
10
|
from edupsyadmin.core.config import config
|
|
11
11
|
|
|
12
|
+
SESSION_BRACKET_HIGH: Final[int] = 3
|
|
13
|
+
SESSION_BRACKET_LOW: Final[int] = 2
|
|
14
|
+
|
|
12
15
|
pd.set_option("display.precision", 1)
|
|
13
16
|
|
|
14
17
|
|
|
@@ -57,8 +60,12 @@ def add_categories_to_df(
|
|
|
57
60
|
mask = df[cat].notna()
|
|
58
61
|
cat_sessions = df.loc[mask, "n_sessions"]
|
|
59
62
|
|
|
60
|
-
summary.loc["count_mt3_sessions", cat] = (
|
|
61
|
-
|
|
63
|
+
summary.loc["count_mt3_sessions", cat] = (
|
|
64
|
+
cat_sessions > SESSION_BRACKET_HIGH
|
|
65
|
+
).sum()
|
|
66
|
+
summary.loc["count_2to3_sessions", cat] = cat_sessions.between(
|
|
67
|
+
SESSION_BRACKET_LOW, SESSION_BRACKET_HIGH
|
|
68
|
+
).sum()
|
|
62
69
|
summary.loc["count_1_session", cat] = (cat_sessions == 1).sum()
|
|
63
70
|
|
|
64
71
|
return df, summary
|
|
@@ -19,6 +19,7 @@ from edupsyadmin.core.paths import (
|
|
|
19
19
|
DEFAULT_DB_URL,
|
|
20
20
|
DEFAULT_SALT_PATH,
|
|
21
21
|
)
|
|
22
|
+
from edupsyadmin.utils.path_utils import normalize_path
|
|
22
23
|
|
|
23
24
|
__all__ = ("main",)
|
|
24
25
|
|
|
@@ -137,8 +138,6 @@ def _args(argv: list[str] | None) -> argparse.Namespace:
|
|
|
137
138
|
parser.print_help()
|
|
138
139
|
raise SystemExit(1)
|
|
139
140
|
|
|
140
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
141
|
-
|
|
142
141
|
# don't specify this as an argument default or else it will always be
|
|
143
142
|
# included in the list.
|
|
144
143
|
if not args.config_path:
|
|
@@ -247,8 +246,6 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
247
246
|
"""
|
|
248
247
|
args = _args(argv)
|
|
249
248
|
|
|
250
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
251
|
-
|
|
252
249
|
# Migrate versioned paths to stable paths if necessary
|
|
253
250
|
if args.database_url.startswith("sqlite:///"):
|
|
254
251
|
db_path = normalize_path(args.database_url.removeprefix("sqlite:///"))
|
{edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/create_documentation.py
RENAMED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import getpass
|
|
1
2
|
import textwrap
|
|
2
3
|
from argparse import ArgumentParser, Namespace
|
|
3
4
|
from pathlib import Path
|
|
@@ -37,8 +38,6 @@ COMMAND_EPILOG = textwrap.dedent(
|
|
|
37
38
|
|
|
38
39
|
def add_arguments(parser: ArgumentParser) -> None:
|
|
39
40
|
"""CLI adaptor for the create-documentation command."""
|
|
40
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
41
|
-
|
|
42
41
|
parser.set_defaults(command=execute)
|
|
43
42
|
parser.add_argument(
|
|
44
43
|
"--tui",
|
|
@@ -99,8 +98,6 @@ def _get_pdf_password(args: Namespace) -> str | None:
|
|
|
99
98
|
if args.password:
|
|
100
99
|
return args.password
|
|
101
100
|
if not args.tui:
|
|
102
|
-
import getpass
|
|
103
|
-
|
|
104
101
|
return getpass.getpass("Passwort für die PDF-Verschlüsselung: ")
|
|
105
102
|
return None
|
|
106
103
|
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import getpass
|
|
1
2
|
import textwrap
|
|
2
3
|
from argparse import ArgumentParser, Namespace
|
|
3
4
|
|
|
5
|
+
from pypdf import PdfReader
|
|
6
|
+
from pypdf.errors import PyPdfError
|
|
7
|
+
|
|
4
8
|
from edupsyadmin.cli.utils import lazy_import
|
|
9
|
+
from edupsyadmin.utils.path_utils import normalize_path
|
|
5
10
|
|
|
6
11
|
COMMAND_DESCRIPTION = "Flatten pdf forms"
|
|
7
12
|
COMMAND_HELP = "Flatten pdf forms"
|
|
@@ -18,8 +23,6 @@ COMMAND_EPILOG = textwrap.dedent(
|
|
|
18
23
|
|
|
19
24
|
def add_arguments(parser: ArgumentParser) -> None:
|
|
20
25
|
"""CLI adaptor for the flatten-pdfs command."""
|
|
21
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
22
|
-
|
|
23
26
|
parser.set_defaults(command=execute)
|
|
24
27
|
parser.add_argument("form_paths", nargs="+", type=normalize_path)
|
|
25
28
|
parser.add_argument(
|
|
@@ -33,7 +36,6 @@ def add_arguments(parser: ArgumentParser) -> None:
|
|
|
33
36
|
|
|
34
37
|
def execute(args: Namespace) -> None:
|
|
35
38
|
"""Execute the flatten-pdfs command."""
|
|
36
|
-
from pypdf import PdfReader
|
|
37
39
|
|
|
38
40
|
password = args.password
|
|
39
41
|
if not password:
|
|
@@ -43,13 +45,11 @@ def execute(args: Namespace) -> None:
|
|
|
43
45
|
try:
|
|
44
46
|
reader = PdfReader(str(p))
|
|
45
47
|
if reader.is_encrypted:
|
|
46
|
-
import getpass
|
|
47
|
-
|
|
48
48
|
password = getpass.getpass(
|
|
49
49
|
f"Passwort für verschlüsselte PDF '{p.name}': ",
|
|
50
50
|
)
|
|
51
51
|
break
|
|
52
|
-
except
|
|
52
|
+
except PyPdfError, OSError, ValueError:
|
|
53
53
|
continue
|
|
54
54
|
|
|
55
55
|
flatten_pdfs = lazy_import("edupsyadmin.api.flatten_pdf").flatten_pdfs
|
|
@@ -7,6 +7,7 @@ from rich.table import Table
|
|
|
7
7
|
|
|
8
8
|
from edupsyadmin.cli.utils import lazy_import
|
|
9
9
|
from edupsyadmin.core.logger import logger
|
|
10
|
+
from edupsyadmin.utils.path_utils import normalize_path
|
|
10
11
|
|
|
11
12
|
COMMAND_DESCRIPTION = "Show clients overview or single client"
|
|
12
13
|
COMMAND_HELP = "Show clients overview or single client"
|
|
@@ -32,7 +33,6 @@ COMMAND_EPILOG = textwrap.dedent(
|
|
|
32
33
|
|
|
33
34
|
def add_arguments(parser: ArgumentParser) -> None:
|
|
34
35
|
"""CLI adaptor for the get-clients command."""
|
|
35
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
36
36
|
|
|
37
37
|
parser.set_defaults(command=execute)
|
|
38
38
|
parser.add_argument(
|
|
@@ -5,9 +5,13 @@ from datetime import datetime
|
|
|
5
5
|
from inspect import signature
|
|
6
6
|
from typing import TYPE_CHECKING
|
|
7
7
|
|
|
8
|
+
from sqlalchemy import inspect as sa_inspect
|
|
9
|
+
|
|
8
10
|
from edupsyadmin.cli.utils import lazy_import
|
|
9
11
|
from edupsyadmin.core.config import config
|
|
10
12
|
from edupsyadmin.core.logger import logger
|
|
13
|
+
from edupsyadmin.db import clients
|
|
14
|
+
from edupsyadmin.utils.path_utils import normalize_path
|
|
11
15
|
|
|
12
16
|
if TYPE_CHECKING:
|
|
13
17
|
from edupsyadmin.api.managers import ClientsManager
|
|
@@ -49,7 +53,6 @@ def _enter_client_csv(
|
|
|
49
53
|
return: client_id
|
|
50
54
|
"""
|
|
51
55
|
pd = lazy_import("pandas")
|
|
52
|
-
from edupsyadmin.db import clients
|
|
53
56
|
|
|
54
57
|
client_cls = clients.Client
|
|
55
58
|
|
|
@@ -135,8 +138,6 @@ def _enter_client_csv(
|
|
|
135
138
|
school = next(iter(config.school.keys()))
|
|
136
139
|
client_data["school"] = school
|
|
137
140
|
|
|
138
|
-
from sqlalchemy import inspect as sa_inspect
|
|
139
|
-
|
|
140
141
|
# Filter data to only include valid columns for the Client model
|
|
141
142
|
valid_keys = {c.key for c in sa_inspect(client_cls).column_attrs}
|
|
142
143
|
init_sig = signature(client_cls.__init__)
|
|
@@ -199,8 +200,6 @@ def execute(args: Namespace) -> None:
|
|
|
199
200
|
)
|
|
200
201
|
|
|
201
202
|
if args.csv:
|
|
202
|
-
from edupsyadmin.utils.path_utils import normalize_path
|
|
203
|
-
|
|
204
203
|
if args.name is None:
|
|
205
204
|
raise ValueError("Pass a name to read a client from a csv.")
|
|
206
205
|
|
|
@@ -4,6 +4,11 @@ from argparse import ArgumentParser, Namespace
|
|
|
4
4
|
|
|
5
5
|
from edupsyadmin.api.exceptions import MigrationError
|
|
6
6
|
from edupsyadmin.cli.utils import lazy_import
|
|
7
|
+
from edupsyadmin.core.encrypt import (
|
|
8
|
+
delete_legacy_key_from_keyring,
|
|
9
|
+
get_keys_from_keyring,
|
|
10
|
+
set_keys_in_keyring,
|
|
11
|
+
)
|
|
7
12
|
from edupsyadmin.core.logger import logger
|
|
8
13
|
|
|
9
14
|
COMMAND_DESCRIPTION = textwrap.dedent(
|
|
@@ -72,12 +77,6 @@ def execute(args: Namespace) -> None:
|
|
|
72
77
|
.lower()
|
|
73
78
|
)
|
|
74
79
|
if cleanup_response in ("yes", "y"):
|
|
75
|
-
from edupsyadmin.core.encrypt import (
|
|
76
|
-
delete_legacy_key_from_keyring,
|
|
77
|
-
get_keys_from_keyring,
|
|
78
|
-
set_keys_in_keyring,
|
|
79
|
-
)
|
|
80
|
-
|
|
81
80
|
delete_legacy_key_from_keyring(args.app_uid, args.app_username)
|
|
82
81
|
|
|
83
82
|
# Also clean up versioned keys: only keep the newest primary key
|
|
@@ -93,7 +92,7 @@ def execute(args: Namespace) -> None:
|
|
|
93
92
|
print(f"\nERROR: {e}")
|
|
94
93
|
print("The database has been rolled back to its previous state.")
|
|
95
94
|
sys.exit(1)
|
|
96
|
-
except Exception as e:
|
|
95
|
+
except Exception as e: # noqa: BLE001
|
|
97
96
|
logger.critical(f"An unexpected error occurred during re-encryption: {e}")
|
|
98
97
|
print(f"\nCRITICAL ERROR: {e}")
|
|
99
98
|
print("Please restore your database from a backup.")
|