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.
Files changed (210) hide show
  1. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.github/workflows/coverage.yml +7 -3
  2. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.github/workflows/publish.yml +12 -6
  3. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.pre-commit-config.yaml +9 -5
  4. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/CHANGELOG.md +11 -0
  5. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/PKG-INFO +2 -2
  6. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/conf.py +3 -3
  7. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/pyproject.toml +35 -21
  8. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__version__.py +1 -1
  9. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/env.py +1 -4
  10. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/fill_form.py +1 -1
  11. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flatten_pdf.py +5 -5
  12. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/base.py +0 -2
  13. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/pypdf_backend.py +8 -3
  14. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/lgvt.py +4 -1
  15. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/migration.py +12 -11
  16. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/reports.py +0 -1
  17. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/taetigkeitsbericht_from_db.py +10 -3
  18. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/__init__.py +1 -4
  19. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/create_documentation.py +1 -4
  20. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/flatten_pdfs.py +6 -6
  21. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/get_clients.py +1 -1
  22. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/new_client.py +4 -5
  23. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/rotate_key.py +6 -7
  24. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/setup_demo.py +6 -3
  25. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/taetigkeitsbericht.py +1 -1
  26. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/encrypt.py +12 -10
  27. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/clients.py +1 -1
  28. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/info.py +3 -3
  29. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/clients_overview.py +5 -2
  30. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edit_client.py +8 -0
  31. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edit_client_app.py +1 -1
  32. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig.py +4 -2
  33. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig_app.py +7 -5
  34. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edupsyadmintui.py +3 -4
  35. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/fill_form_app.py +1 -1
  36. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/fill_form_widget.py +1 -2
  37. edupsyadmin-9.2.0/src/edupsyadmin/tui/suggesters.py +41 -0
  38. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_client_view.py +2 -1
  39. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_fill_form.py +21 -2
  40. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_lgvt.py +21 -2
  41. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_managers.py +32 -24
  42. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_migration.py +1 -7
  43. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_taetigkeitsbericht_from_db.py +4 -4
  44. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_write_form_md.py +1 -1
  45. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_config.py +5 -4
  46. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_encrypt.py +24 -1
  47. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/core/test_logger.py +0 -4
  48. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_class_int_override.py +12 -11
  49. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_clients.py +14 -15
  50. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/snapshot_utils.py +3 -3
  51. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_cli.py +10 -16
  52. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_editclient.py +27 -0
  53. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_editconfig.py +13 -2
  54. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_edupsyadmintui.py +68 -8
  55. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_fill_form_tui.py +1 -1
  56. edupsyadmin-9.2.0/test/edupsyadmin/tui/test_suggesters.py +35 -0
  57. edupsyadmin-9.2.0/test/edupsyadmin/utils/test_rounding.py +19 -0
  58. edupsyadmin-9.2.0/uv.lock +2228 -0
  59. edupsyadmin-9.1.0/uv.lock +0 -2003
  60. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.gitignore +0 -0
  61. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.python-version +0 -0
  62. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.readthedocs.yaml +0 -0
  63. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/.taplo.toml +0 -0
  64. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/LICENSE.txt +0 -0
  65. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/README.md +0 -0
  66. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/Makefile +0 -0
  67. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/_static/samplewebuntisfile.csv +0 -0
  68. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/clients_model.rst +0 -0
  69. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/faq.rst +0 -0
  70. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/index.rst +0 -0
  71. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/make.bat +0 -0
  72. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/requirements.txt +0 -0
  73. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/taetigkeitsbericht_keywords.rst +0 -0
  74. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/cli.rst +0 -0
  75. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/configuration.rst +0 -0
  76. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/creatingdocumentation.rst +0 -0
  77. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/editingclients.rst +0 -0
  78. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/encryption.rst +0 -0
  79. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/helpfunction.rst +0 -0
  80. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/installation.rst +0 -0
  81. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/taetigkeitsbericht.rst +0 -0
  82. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/tui.rst +0 -0
  83. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/docs/tutorial/update.rst +0 -0
  84. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/resources/edupsyadmin_logo.svg +0 -0
  85. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/resources/edupsyadmin_logo_readme.svg +0 -0
  86. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_iLst.sh +0 -0
  87. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_iRst.sh +0 -0
  88. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_lrst.sh +0 -0
  89. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/add_nona_nons.sh +0 -0
  90. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/change_keywordtaet_nsessions.sh +0 -0
  91. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/profile.sh +0 -0
  92. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/shellscripts/publish_to_testpypi.sh +0 -0
  93. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__init__.py +0 -0
  94. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/__main__.py +0 -0
  95. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/README +0 -0
  96. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/script.py.mako +0 -0
  97. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_02_21_0453-4087c43f0c7c_initial_migration.py +0 -0
  98. {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
  99. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_13_0545-ea4c3f900604_encrypt_class_columns.py +0 -0
  100. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_13_0620-515a72d06f10_encrypt_derived_dates.py +0 -0
  101. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_16_0900-d7e8f9a0b1c2_add_metadata_table.py +0 -0
  102. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_03_23_1030-e2b4c6d8f0a1_encrypt_sensitive_text_fields.py +0 -0
  103. {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
  104. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic/versions/2026_06_12_0509-0f1497df963e_add_class_int_override.py +0 -0
  105. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/alembic.ini +0 -0
  106. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/__init__.py +0 -0
  107. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/cft_report.py +0 -0
  108. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/client_view.py +0 -0
  109. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/display_client_details.py +0 -0
  110. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/exceptions.py +0 -0
  111. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/__init__.py +0 -0
  112. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/flattening/api.py +0 -0
  113. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/managers.py +0 -0
  114. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/migration_fs.py +0 -0
  115. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/setup_demo.py +0 -0
  116. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/api/types.py +0 -0
  117. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/__main__.py +0 -0
  118. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/delete_client.py +0 -0
  119. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/edit_config.py +0 -0
  120. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/info.py +0 -0
  121. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/mk_report.py +0 -0
  122. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/set_client.py +0 -0
  123. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/commands/tui.py +0 -0
  124. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/cli/utils.py +0 -0
  125. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/__init__.py +0 -0
  126. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/config.py +0 -0
  127. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/enums.py +0 -0
  128. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/logger.py +0 -0
  129. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/core/paths.py +0 -0
  130. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_BfsEV.txt +0 -0
  131. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_BfsKi.txt +0 -0
  132. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/Faecher_Faks.txt +0 -0
  133. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/LiberationSans-Bold.ttf +0 -0
  134. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/LiberationSans-Regular.ttf +0 -0
  135. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/OFL.txt +0 -0
  136. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/sampleconfig.yml +0 -0
  137. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/data/taetigkeitsbericht_categories.csv +0 -0
  138. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/__init__.py +0 -0
  139. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/column_types.py +0 -0
  140. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/db/converters.py +0 -0
  141. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/py.typed +0 -0
  142. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/__init__.py +0 -0
  143. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/clients_overview_app.py +0 -0
  144. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/dialogs.py +0 -0
  145. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/editconfig.tcss +0 -0
  146. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/tui/edupsyadmintui.tcss +0 -0
  147. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/__init__.py +0 -0
  148. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/academic_year.py +0 -0
  149. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/convert_measures.py +0 -0
  150. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/datediff.py +0 -0
  151. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/int_from_str.py +0 -0
  152. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/path_utils.py +0 -0
  153. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/python_type.py +0 -0
  154. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/rounding.py +0 -0
  155. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/src/edupsyadmin/utils/taetigkeitsbericht_check_key.py +0 -0
  156. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/.gitignore +0 -0
  157. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_max_none_snapshot.png +0 -0
  158. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_min_none_snapshot.png +0 -0
  159. {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
  160. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_cft_report/test_cft_report_snapshot[umlaut_user].png +0 -0
  161. {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
  162. {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
  163. {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
  164. {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
  165. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_lgvt/test_lgvt_report_grade_11_snapshot.png +0 -0
  166. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_lgvt/test_lgvt_report_grade_9_snapshot.png +0 -0
  167. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_reports/test_taetigkeitsbericht_report_snapshot.png +0 -0
  168. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/__snapshots__/test_taetigkeitsbericht_from_db/test_taetigkeitsbericht_snapshot.png +0 -0
  169. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_cft_report.py +0 -0
  170. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_decryption_behavior.py +0 -0
  171. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_flatten_pdf.py +0 -0
  172. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_flatten_pdf_snapshots.py +0 -0
  173. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_migration_fs.py +0 -0
  174. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_reports.py +0 -0
  175. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/api/test_snapshot_sensitivity.py +0 -0
  176. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_cli_benchmark.py +0 -0
  177. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_convenience_data_benchmark.py +0 -0
  178. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_decryption_benchmark.py +0 -0
  179. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_encrypt_benchmark.py +0 -0
  180. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/benchmark/test_key_derivation_benchmark.py +0 -0
  181. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/cli/test_utils.py +0 -0
  182. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/conftest.py +0 -0
  183. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/data/sample_form_anschreiben.pdf +0 -0
  184. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/data/sample_form_mantelbogen.pdf +0 -0
  185. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_metadata.py +0 -0
  186. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/db/test_types.py +0 -0
  187. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/sample_pdf_form.py +0 -0
  188. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/sample_webuntis_export.py +0 -0
  189. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_gdpr_logging.py +0 -0
  190. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/test_sync_types.py +0 -0
  191. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_clientsoverview/test_clients_overview.svg +0 -0
  192. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_clientsoverview/test_clients_overview_long_names.svg +0 -0
  193. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_dialogs/test_yes_no_dialog.svg +0 -0
  194. {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
  195. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editclient/test_initial_layout_existing_client[umlaut_user].svg +0 -0
  196. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editclient/test_initial_layout_new_client.svg +0 -0
  197. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_add_new_school_container.svg +0 -0
  198. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_edit_new_school_container.svg +0 -0
  199. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_editconfig/test_editconfig_initial_layout.svg +0 -0
  200. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_edupsyadmintui/test_edupsyadmintui_initial_layout.svg +0 -0
  201. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/__snapshots__/test_fill_form_tui/test_fill_form_initial_layout.svg +0 -0
  202. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_clientsoverview.py +0 -0
  203. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/tui/test_dialogs.py +0 -0
  204. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_academic_year.py +0 -0
  205. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_convert_measures.py +0 -0
  206. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_datediff.py +0 -0
  207. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_int_from_str.py +0 -0
  208. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_path_utils.py +0 -0
  209. {edupsyadmin-9.1.0 → edupsyadmin-9.2.0}/test/edupsyadmin/utils/test_python_type.py +0 -0
  210. {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@v4
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@v2
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@v5
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@v4
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@v5
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
- python-version-file: ".python-version"
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.15.20
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.16.4
22
+ rev: v4.18.0
23
23
  hooks:
24
24
  - id: commitizen
25
25
  - repo: https://github.com/rstcheck/rstcheck
26
- rev: v6.2.5
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.24
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.25.1"
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,3 +1,14 @@
1
+ ## 9.2.0 (2026-09-09)
2
+
3
+ ### Feat
4
+
5
+ - **tui**: add suggester to taet_key entry
6
+
7
+ ### Refactor
8
+
9
+ - address ruff errors
10
+ - move all imports to the top
11
+
1
12
  ## 9.1.0 (2026-07-06)
2
13
 
3
14
  ### Feat
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: edupsyadmin
3
- Version: 9.1.0
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, os.path.abspath(".."))
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
- copyright = f"{datetime.now().year}, {author}"
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.1.0"
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", # flake8-unused-arguments – flag unused, shadowed or *args/**kwargs
74
- "B",
75
- "C4", # flake8-comprehensions – point out sub-optimal list/dict/set comps
76
- "C901", # checks for functions with a high McCabe complexity
77
- "COM", # flake8-commas
78
- "E",
79
- "F",
80
- "FURB", # refurb - modernizing Python codebases
81
- "I",
82
- "ISC", # flake8-implicit-str-concat - implicitly string concatenation
83
- "N",
84
- "PERF", # perflint - a linter for performance anti-patterns
85
- "PTH", # flake8-use-pathlib - find use of function that could be replaced by pathlib
86
- "RET", # flake8-return – keep return statements consistent and meaningful
87
- "RUF", # Ruff-native extras – small bug-catchers & style nits not in other sets "UP",
88
- "SIM", # flake8-simplify – suggest clearer, shorter boolean / loop logic
89
- "TID", # flake8-tidy-imports – ban unused or overly-relative imports
90
- "UP", # pyupgrade – rewrite to modern Python syntax (f-strings, walrus, etc.)
91
- "W",
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"
@@ -18,4 +18,4 @@ and backwards compatibility is not guaranteed for minor versions.
18
18
 
19
19
  __all__ = ["__version__"]
20
20
 
21
- __version__ = "9.1.0"
21
+ __version__ = "9.2.0"
@@ -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 Exception:
61
+ except PyPdfError, OSError, ValueError:
62
62
  continue
63
63
 
64
64
  try:
@@ -4,8 +4,6 @@
4
4
  class InvalidPDFError(Exception):
5
5
  """Raised when the input file is not a valid PDF."""
6
6
 
7
- pass
8
-
9
7
 
10
8
  DEFAULT_PREFIX = "print_"
11
9
 
@@ -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) == 4:
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 Exception:
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) > 126:
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 < 11:
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
- try:
37
- # Use importlib.resources to access packaged data
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
- if not alembic_ini_path.is_file():
42
- raise MigrationError(f"Alembic config not found at {alembic_ini_path}")
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
- if len(clients) != expected_count:
217
- raise MigrationError(
218
- f"Client count mismatch: expected {expected_count}, "
219
- f"found {len(clients)}",
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
@@ -123,7 +123,6 @@ class BasePDFReport:
123
123
 
124
124
  def _draw_header(self, canvas: Canvas) -> None:
125
125
  """Draw the header (to be overridden if needed)."""
126
- pass
127
126
 
128
127
  def _scale_image(self, img: Image, available_width: float) -> Image:
129
128
  """
@@ -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] = (cat_sessions > 3).sum()
61
- summary.loc["count_2to3_sessions", cat] = cat_sessions.between(2, 3).sum()
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:///"))
@@ -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 Exception:
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.")