gramps-webapi 3.22.2__tar.gz → 3.23.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.
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/workflows/test.yml +26 -5
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/PKG-INFO +3 -4
- gramps_webapi-3.23.0/alembic_users/versions/7b2e9f4c1a63_allow_several_access_tokens_per_scope.py +81 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/_version.py +1 -1
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/__init__.py +41 -3
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/export.py +63 -5
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/llm/__init__.py +25 -10
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/llm/agent.py +39 -9
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/report.py +113 -35
- gramps_webapi-3.23.0/gramps_webapi/api/resources/access_tokens.py +247 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/anniversaries.py +2 -6
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/base.py +0 -18
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/delete.py +3 -5
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/exporters.py +6 -4
- gramps_webapi-3.23.0/gramps_webapi/api/resources/filter_param_types.py +202 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/filters.py +98 -2
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/importers.py +12 -3
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/merge.py +9 -6
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/metadata.py +83 -61
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/object_query.py +68 -35
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/oidc.py +2 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/places.py +33 -2
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/reports.py +13 -2
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/schemas.py +229 -14
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/token.py +65 -15
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/user.py +66 -32
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/util.py +186 -25
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/__init__.py +26 -4
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/indexer.py +114 -56
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/tasks.py +48 -23
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/util.py +55 -19
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/__init__.py +183 -13
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/const.py +17 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/oidc.py +40 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/data/empty_gramps_auth.cfg +0 -1
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi.egg-info/PKG-INFO +3 -4
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi.egg-info/SOURCES.txt +8 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi.egg-info/requires.txt +2 -3
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/pyproject.toml +2 -3
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/__init__.py +6 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_deprecations.py +3 -0
- gramps_webapi-3.23.0/tests/test_endpoints/test_access_tokens.py +274 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_exporters.py +74 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_filters.py +324 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_history.py +7 -2
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_importers.py +5 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_metadata.py +31 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_people.py +7 -32
- gramps_webapi-3.23.0/tests/test_endpoints/test_place_coordinates.py +165 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_put.py +45 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_reports.py +44 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_restore.py +29 -13
- gramps_webapi-3.23.0/tests/test_endpoints/test_token_sync.py +288 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_treeless_admin.py +16 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_user.py +187 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_import_util.py +141 -18
- gramps_webapi-3.23.0/tests/test_llm_agent.py +67 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_llm_errors.py +33 -1
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_oidc_auth.py +38 -0
- gramps_webapi-3.23.0/tests/test_search_indexer.py +122 -0
- gramps_webapi-3.23.0/tests/test_search_reindex.py +83 -0
- gramps_webapi-3.23.0/tests/test_stale_semantic_index.py +198 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_util.py +127 -0
- gramps_webapi-3.22.2/gramps_webapi/api/resources/access_tokens.py +0 -106
- gramps_webapi-3.22.2/tests/test_endpoints/test_access_tokens.py +0 -106
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.devcontainer/Dockerfile +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.devcontainer/devcontainer.json +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.devcontainer/docker-compose.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.dockerignore +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.editorconfig +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.flake8 +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/workflows/base-image.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/workflows/image.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.github/workflows/pypi.yml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.gitignore +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.pre-commit-config.yaml +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.pylintrc +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.vscode/extensions.json +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.vscode/launch.json +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.vscode/settings.json +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/.vscode/tasks.json +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/CLAUDE.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/CODE_OF_CONDUCT.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/CONTRIBUTING.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/Dockerfile +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/Dockerfile.base +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/LICENSE +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/MANIFEST.in +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/README.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/ROADMAP.md +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic.ini +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/README +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/env.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/script.py.mako +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/2082445b0769_add_oidc_accounts_table_for_secure_.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/22c8d1fba959_add_trees_enabled.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/66e56620891a_add_trees_table.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/6d8f3cb50b71_add_persistent_access_tokens_table.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/84960b7d968c_use_bigint_for_quota_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/a8e57fe0d82e_add_columns_for_ai_quota.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/b0582f54029c_use_bigint_for_usage_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/b2c3d4e5f6a7_add_tree_config_column.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/c1d2e3f4a5b6_merge_task_tree_and_tree_config.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/c89728e71264_.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/d4e9a1c7b3f2_drop_unique_constraint_on_user_email.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/e176543c72a8_add_tree_column_to_user.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/e5e738d09fa7_added_configuration_table.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/alembic_users/versions/f3a1c8e92b47_add_task_tree_table.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/docker-entrypoint.sh +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/__init__.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/__main__.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/auth.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/blueprint.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/cache.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/check.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/deprecations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/dna.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/emails.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/file.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/html.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/image.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/llm/deps.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/llm/tools.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/media_importer.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/people_families_cache.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/ratelimiter.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/__init__.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/bookmarks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/chat.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/citations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/config.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/db_backend.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/dna.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/emit.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/events.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/export_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/face_detection.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/facts.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/families.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/file.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/history.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/holidays.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/import_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/living.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/match.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/name_formats.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/name_groups.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/notes.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/objects.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/ocr.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/people.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/relations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/repositories.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/restore.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/search.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/sort.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/sources.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/tags.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/tasks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/timeline.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/transactions.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/translations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/trees.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/types.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/verify.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/resources/ydna.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/s3.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/embeddings.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/metadata.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/text.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/search/text_semantic.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/api/telemetry.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/app.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/oidc_helpers.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/passwords.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/auth/sql_guid.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/celery.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/config.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/const.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/data/example_gramps_auth.cfg +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/data/face_detection_yunet_2023mar.onnx +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/data/test_auth.cfg +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/dbloader.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/dbmanager.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/logging_utils.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/profiler.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/sentry.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/static/index.html +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/templates/confirmation.html +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/templates/reset_password.html +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/templates/reset_password_base.html +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/templates/reset_password_error.html +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/translogger.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/types.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/undodb.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/util/__init__.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/util/celery.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/verify_lib.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi/wsgi.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi.egg-info/dependency_links.txt +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/gramps_webapi.egg-info/top_level.txt +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/mypy.ini +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/requirements-dev.txt +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/scripts/s3_rename.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/scripts/start_dev_container.sh +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/semantic_index.txt +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/setup.cfg +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/setup.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_check.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_cli.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_db.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_delete.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_dna_match_parser.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_embeddings.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/__init__.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/checks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_anniversaries_ics.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_bookmarks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_chat.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_citations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_config.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_db_handle.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_delete.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_dna.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_events.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_facts.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_families.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_file.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_holidays.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_import_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_living.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_media.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_media_archive.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_merge.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_name_formats.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_name_groups.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_notes.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_object_query.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_object_query_where_tree.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_ocr.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_oidc.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_paging_sort.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_people_query.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_places.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_post.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_profiler.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_ready.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_relations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_repair.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_repositories.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_s3.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_search.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_sources.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_tags.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_tasks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_timelines.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_token.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_transactions.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_translations.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_trees.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_types.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_upload.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_verify.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/test_ydna.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_endpoints/util.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_file.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_image.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_jwt.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_limiter.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_llm_tools.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_logging_utils.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_map_tile.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_media_importer.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_media_prefix_tree.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_migrate_grampsdb.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_object_query_parsing.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_openapi_operation_ids.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_password.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_private_proxy.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_s3.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_search_metadata.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_sentry.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_sort_raw_data.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_sqlauth.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_task_error.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_tasks.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_telemetry.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_text.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_undodb.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_verify_lib.py +0 -0
- {gramps_webapi-3.22.2 → gramps_webapi-3.23.0}/tests/test_version.py +0 -0
|
@@ -5,8 +5,29 @@ on:
|
|
|
5
5
|
pull_request:
|
|
6
6
|
|
|
7
7
|
jobs:
|
|
8
|
+
typecheck:
|
|
9
|
+
runs-on: ubuntu-26.04
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v5
|
|
12
|
+
- name: Set up Python
|
|
13
|
+
uses: actions/setup-python@v6
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.10"
|
|
16
|
+
- name: Install Ubuntu dependencies
|
|
17
|
+
run: sudo apt update && sudo apt-get -y install gettext appstream pkg-config libcairo2-dev gir1.2-gtk-3.0 libgirepository1.0-dev libicu-dev libopencv-dev gir1.2-pango-1.0
|
|
18
|
+
- name: Install Python dependencies
|
|
19
|
+
run: |
|
|
20
|
+
python -m pip install --upgrade pip wheel setuptools
|
|
21
|
+
# CPU-only build: the default one pulls in ~3 GB of CUDA libraries
|
|
22
|
+
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
|
23
|
+
pip install opencv-python
|
|
24
|
+
pip install -r requirements-dev.txt
|
|
25
|
+
pip install .[ai]
|
|
26
|
+
- name: Run type checker
|
|
27
|
+
run: mypy --install-types --non-interactive --ignore-missing-imports --exclude build .
|
|
28
|
+
|
|
8
29
|
build:
|
|
9
|
-
runs-on: ubuntu-
|
|
30
|
+
runs-on: ubuntu-26.04
|
|
10
31
|
strategy:
|
|
11
32
|
fail-fast: true
|
|
12
33
|
matrix:
|
|
@@ -33,10 +54,12 @@ jobs:
|
|
|
33
54
|
restore-keys: |
|
|
34
55
|
hf-models-
|
|
35
56
|
- name: Install Ubuntu dependencies
|
|
36
|
-
run: sudo apt update && sudo apt-get -y install gettext appstream pkg-config libcairo2-dev gir1.2-gtk-3.0 libgirepository1.0-dev libicu-dev libopencv-dev tesseract-ocr
|
|
57
|
+
run: sudo apt update && sudo apt-get -y install gettext appstream pkg-config libcairo2-dev gir1.2-gtk-3.0 libgirepository1.0-dev libicu-dev libopencv-dev tesseract-ocr gir1.2-pango-1.0 poppler-utils
|
|
37
58
|
- name: Install Python dependencies
|
|
38
59
|
run: |
|
|
39
60
|
python -m pip install --upgrade pip wheel setuptools
|
|
61
|
+
# CPU-only build: the default one pulls in ~3 GB of CUDA libraries
|
|
62
|
+
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
|
40
63
|
pip install opencv-python
|
|
41
64
|
pip install -r requirements-dev.txt
|
|
42
65
|
pip install .[ai]
|
|
@@ -59,15 +82,13 @@ jobs:
|
|
|
59
82
|
alembic heads
|
|
60
83
|
exit 1
|
|
61
84
|
fi
|
|
62
|
-
- name: Run type checker
|
|
63
|
-
run: mypy --install-types --non-interactive --ignore-missing-imports --exclude build .
|
|
64
85
|
- name: Test with pytest
|
|
65
86
|
env:
|
|
66
87
|
HF_HUB_OFFLINE: 1
|
|
67
88
|
run: pytest -x
|
|
68
89
|
|
|
69
90
|
test-base-image:
|
|
70
|
-
runs-on: ubuntu-
|
|
91
|
+
runs-on: ubuntu-26.04
|
|
71
92
|
container: ghcr.io/gramps-project/gramps-web-base:latest
|
|
72
93
|
steps:
|
|
73
94
|
- uses: actions/checkout@v5
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gramps-webapi
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.23.0
|
|
4
4
|
Summary: A RESTful web API for the Gramps genealogical database.
|
|
5
5
|
Author: Gramps Development Team
|
|
6
6
|
Author-email: "David M. Straub" <straub@protonmail.com>
|
|
@@ -41,12 +41,11 @@ Requires-Dist: Unidecode
|
|
|
41
41
|
Requires-Dist: pytesseract
|
|
42
42
|
Requires-Dist: gramps-object-query-language<0.6,>=0.5.1
|
|
43
43
|
Requires-Dist: gramps-ql>=0.5.0
|
|
44
|
-
Requires-Dist:
|
|
45
|
-
Requires-Dist: sifts>=1.1.0
|
|
44
|
+
Requires-Dist: sifts>=1.4.0
|
|
46
45
|
Requires-Dist: requests
|
|
47
46
|
Requires-Dist: yclade>=0.5.0
|
|
48
47
|
Requires-Dist: Authlib>=1.6.4
|
|
49
|
-
Requires-Dist: gramps-gedcom7
|
|
48
|
+
Requires-Dist: gramps-gedcom7>=0.4.0
|
|
50
49
|
Requires-Dist: flask-smorest
|
|
51
50
|
Requires-Dist: typing-extensions>=4.13.0
|
|
52
51
|
Provides-Extra: ai
|
gramps_webapi-3.23.0/alembic_users/versions/7b2e9f4c1a63_allow_several_access_tokens_per_scope.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Allow several labelled access tokens per user and scope
|
|
2
|
+
|
|
3
|
+
Sync tokens are used one per device, and each device needs its own token so
|
|
4
|
+
that it can be revoked on its own. The unique constraint on (user_id, scope)
|
|
5
|
+
is replaced by one on (user_id, scope, label); scopes with a single token per
|
|
6
|
+
user keep using an empty label. Also record when a token was last used.
|
|
7
|
+
|
|
8
|
+
Revision ID: 7b2e9f4c1a63
|
|
9
|
+
Revises: d4e9a1c7b3f2
|
|
10
|
+
Create Date: 2026-10-03 00:00:00.000000
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from alembic import op
|
|
15
|
+
import sqlalchemy as sa
|
|
16
|
+
from sqlalchemy.engine.reflection import Inspector
|
|
17
|
+
|
|
18
|
+
# revision identifiers, used by Alembic.
|
|
19
|
+
revision = "7b2e9f4c1a63"
|
|
20
|
+
down_revision = "d4e9a1c7b3f2"
|
|
21
|
+
branch_labels = None
|
|
22
|
+
depends_on = None
|
|
23
|
+
|
|
24
|
+
TABLE = "access_tokens"
|
|
25
|
+
OLD_CONSTRAINT = "uq_access_tokens_user_scope"
|
|
26
|
+
OLD_COLUMNS = ["user_id", "scope"]
|
|
27
|
+
NEW_CONSTRAINT = "uq_access_tokens_user_scope_label"
|
|
28
|
+
NEW_COLUMNS = ["user_id", "scope", "label"]
|
|
29
|
+
# lets batch mode refer to a constraint if SQLite reflects it without a name
|
|
30
|
+
NAMING_CONVENTION = {"uq": "uq_%(table_name)s_%(column_0_name)s"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _unique_constraint_name(
|
|
34
|
+
inspector: Inspector, columns: list[str], default: str
|
|
35
|
+
) -> str | None:
|
|
36
|
+
"""Return the name of the unique constraint on columns, or None if absent."""
|
|
37
|
+
for constraint in inspector.get_unique_constraints(TABLE):
|
|
38
|
+
if list(constraint["column_names"]) == columns:
|
|
39
|
+
return constraint["name"] or default
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def upgrade():
|
|
44
|
+
bind = op.get_bind()
|
|
45
|
+
inspector = Inspector.from_engine(bind)
|
|
46
|
+
columns = {column["name"] for column in inspector.get_columns(TABLE)}
|
|
47
|
+
old_name = _unique_constraint_name(inspector, OLD_COLUMNS, OLD_CONSTRAINT)
|
|
48
|
+
has_new = _unique_constraint_name(inspector, NEW_COLUMNS, NEW_CONSTRAINT)
|
|
49
|
+
# on SQLite, batch mode recreates the table, now with AUTOINCREMENT
|
|
50
|
+
with op.batch_alter_table(
|
|
51
|
+
TABLE,
|
|
52
|
+
naming_convention=NAMING_CONVENTION,
|
|
53
|
+
table_kwargs={"sqlite_autoincrement": True},
|
|
54
|
+
) as batch_op:
|
|
55
|
+
if "label" not in columns:
|
|
56
|
+
batch_op.add_column(
|
|
57
|
+
sa.Column(
|
|
58
|
+
"label", sa.String(length=100), nullable=False, server_default=""
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
if "last_used_at" not in columns:
|
|
62
|
+
batch_op.add_column(sa.Column("last_used_at", sa.DateTime(), nullable=True))
|
|
63
|
+
if old_name:
|
|
64
|
+
batch_op.drop_constraint(old_name, type_="unique")
|
|
65
|
+
if not has_new:
|
|
66
|
+
batch_op.create_unique_constraint(NEW_CONSTRAINT, NEW_COLUMNS)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def downgrade():
|
|
70
|
+
bind = op.get_bind()
|
|
71
|
+
# the old schema allows a single token per user and scope, so labelled
|
|
72
|
+
# tokens (one per device) can't be kept
|
|
73
|
+
bind.execute(sa.text(f"DELETE FROM {TABLE} WHERE label != ''"))
|
|
74
|
+
inspector = Inspector.from_engine(bind)
|
|
75
|
+
new_name = _unique_constraint_name(inspector, NEW_COLUMNS, NEW_CONSTRAINT)
|
|
76
|
+
with op.batch_alter_table(TABLE, naming_convention=NAMING_CONVENTION) as batch_op:
|
|
77
|
+
if new_name:
|
|
78
|
+
batch_op.drop_constraint(new_name, type_="unique")
|
|
79
|
+
batch_op.create_unique_constraint(OLD_CONSTRAINT, OLD_COLUMNS)
|
|
80
|
+
batch_op.drop_column("last_used_at")
|
|
81
|
+
batch_op.drop_column("label")
|
|
@@ -30,7 +30,11 @@ from .auth import jwt_required
|
|
|
30
30
|
from .blueprint import api_blueprint
|
|
31
31
|
from .cache import thumbnail_cache_decorator, tile_cache_decorator
|
|
32
32
|
from .media import get_media_handler
|
|
33
|
-
from .resources.access_tokens import
|
|
33
|
+
from .resources.access_tokens import (
|
|
34
|
+
UserAccessTokenItemResource,
|
|
35
|
+
UserAccessTokenListResource,
|
|
36
|
+
UserAccessTokenResource,
|
|
37
|
+
)
|
|
34
38
|
from .resources.anniversaries import AnniversariesIcsResource
|
|
35
39
|
from .resources.base import Resource, object_request_body
|
|
36
40
|
from .resources.bookmarks import (
|
|
@@ -82,7 +86,11 @@ from .resources.merge import (
|
|
|
82
86
|
MergeRepositoryResource,
|
|
83
87
|
MergeSourceResource,
|
|
84
88
|
)
|
|
85
|
-
from .resources.metadata import
|
|
89
|
+
from .resources.metadata import (
|
|
90
|
+
MetadataResearcherResource,
|
|
91
|
+
MetadataResource,
|
|
92
|
+
MetadataServerResource,
|
|
93
|
+
)
|
|
86
94
|
from .resources.name_formats import NameFormatsResource
|
|
87
95
|
from .resources.name_groups import NameGroupsResource
|
|
88
96
|
from .resources.notes import NoteResource, NotesResource
|
|
@@ -112,7 +120,11 @@ from .resources.oidc import (
|
|
|
112
120
|
OIDCTokenExchangeResource,
|
|
113
121
|
)
|
|
114
122
|
from .resources.people import PeopleResource, PersonResource
|
|
115
|
-
from .resources.places import
|
|
123
|
+
from .resources.places import (
|
|
124
|
+
PlaceCoordinatesResource,
|
|
125
|
+
PlaceResource,
|
|
126
|
+
PlacesResource,
|
|
127
|
+
)
|
|
116
128
|
from .resources.relations import RelationResource, RelationsResource
|
|
117
129
|
from .resources.reports import (
|
|
118
130
|
ReportFileResource,
|
|
@@ -135,6 +147,7 @@ from .resources.token import (
|
|
|
135
147
|
TokenCreateOwnerResource,
|
|
136
148
|
TokenRefreshResource,
|
|
137
149
|
TokenResource,
|
|
150
|
+
TokenSyncResource,
|
|
138
151
|
)
|
|
139
152
|
from .resources.transactions import TransactionsResource
|
|
140
153
|
from .resources.translations import TranslationResource, TranslationsResource
|
|
@@ -281,6 +294,7 @@ register_endpt(
|
|
|
281
294
|
# Token
|
|
282
295
|
register_endpt(TokenResource, "/token/", "token", tags=["Token"])
|
|
283
296
|
register_endpt(TokenRefreshResource, "/token/refresh/", "token_refresh", tags=["Token"])
|
|
297
|
+
register_endpt(TokenSyncResource, "/token/sync/", "token_sync", tags=["Token"])
|
|
284
298
|
register_endpt(
|
|
285
299
|
TokenCreateOwnerResource,
|
|
286
300
|
"/token/create_owner/",
|
|
@@ -435,6 +449,12 @@ register_endpt(
|
|
|
435
449
|
object_name="Place",
|
|
436
450
|
)
|
|
437
451
|
register_endpt(PlaceQueryResource, "/places/query/", "places-query", tags=["Places"])
|
|
452
|
+
register_endpt(
|
|
453
|
+
PlaceCoordinatesResource,
|
|
454
|
+
"/places/coordinates/",
|
|
455
|
+
"places-coordinates",
|
|
456
|
+
tags=["Places"],
|
|
457
|
+
)
|
|
438
458
|
register_endpt(
|
|
439
459
|
MergePlaceResource,
|
|
440
460
|
"/places/<string:phoenix_handle>/merge/<string:titanic_handle>",
|
|
@@ -782,6 +802,12 @@ register_endpt(
|
|
|
782
802
|
"metadata_researcher",
|
|
783
803
|
tags=["Metadata"],
|
|
784
804
|
)
|
|
805
|
+
register_endpt(
|
|
806
|
+
MetadataServerResource,
|
|
807
|
+
"/metadata/server/",
|
|
808
|
+
"metadata_server",
|
|
809
|
+
tags=["Metadata"],
|
|
810
|
+
)
|
|
785
811
|
# Anniversaries
|
|
786
812
|
register_endpt(
|
|
787
813
|
AnniversariesIcsResource,
|
|
@@ -798,6 +824,18 @@ register_endpt(
|
|
|
798
824
|
"user_access_token",
|
|
799
825
|
tags=["Users"],
|
|
800
826
|
)
|
|
827
|
+
register_endpt(
|
|
828
|
+
UserAccessTokenListResource,
|
|
829
|
+
"/users/-/access-tokens/<string:scope>/tokens/",
|
|
830
|
+
"user_access_token_list",
|
|
831
|
+
tags=["Users"],
|
|
832
|
+
)
|
|
833
|
+
register_endpt(
|
|
834
|
+
UserAccessTokenItemResource,
|
|
835
|
+
"/users/-/access-tokens/<string:scope>/tokens/<int:token_id>/",
|
|
836
|
+
"user_access_token_item",
|
|
837
|
+
tags=["Users"],
|
|
838
|
+
)
|
|
801
839
|
register_endpt(
|
|
802
840
|
UserRegisterResource,
|
|
803
841
|
"/users/<string:user_name>/register/",
|
|
@@ -27,8 +27,9 @@ import mimetypes
|
|
|
27
27
|
import os
|
|
28
28
|
import uuid
|
|
29
29
|
from pathlib import Path
|
|
30
|
-
from typing import Dict, Optional
|
|
30
|
+
from typing import Callable, Dict, Optional
|
|
31
31
|
|
|
32
|
+
import gramps_gedcom7
|
|
32
33
|
from celery import Task
|
|
33
34
|
from flask import abort, current_app
|
|
34
35
|
from gramps.gen import filters
|
|
@@ -46,6 +47,7 @@ from gramps.gen.user import User
|
|
|
46
47
|
from gramps.gen.utils.resourcepath import ResourcePath
|
|
47
48
|
|
|
48
49
|
from ..const import DISABLED_EXPORTERS
|
|
50
|
+
from .resources.util import truncate_import_report
|
|
49
51
|
from .util import UserTaskProgress, abort_with_message, get_locale_for_language
|
|
50
52
|
|
|
51
53
|
_ = glocale.translation.gettext
|
|
@@ -58,6 +60,16 @@ LIVING_FILTERS = {
|
|
|
58
60
|
"ExcludeAll": LivingProxyDb.MODE_EXCLUDE_ALL,
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
# GEDCOM 7 is exported by the gramps-gedcom7 library rather than a plugin. Its
|
|
64
|
+
# files end in .ged like GEDCOM 5.5.1, so it needs its own key in the API.
|
|
65
|
+
GEDCOM7_EXTENSION = "ged7"
|
|
66
|
+
GEDCOM7_EXPORTER = {
|
|
67
|
+
"name": "GEDCOM 7",
|
|
68
|
+
"description": "GEDCOM 7 is used to transfer data between genealogy programs.",
|
|
69
|
+
"extension": GEDCOM7_EXTENSION,
|
|
70
|
+
"module": "gramps_gedcom7",
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
# mimetypes.init()
|
|
62
74
|
|
|
63
75
|
|
|
@@ -206,6 +218,8 @@ def get_exporters(extension: str | None = None):
|
|
|
206
218
|
"module": plugin.get_module_name(),
|
|
207
219
|
}
|
|
208
220
|
exporters.append(exporter)
|
|
221
|
+
if extension is None or extension == GEDCOM7_EXTENSION:
|
|
222
|
+
exporters.append(GEDCOM7_EXPORTER)
|
|
209
223
|
return exporters
|
|
210
224
|
|
|
211
225
|
|
|
@@ -265,13 +279,26 @@ def prepare_options(db_handle: DbReadBase, args: Dict):
|
|
|
265
279
|
|
|
266
280
|
|
|
267
281
|
def run_export(
|
|
268
|
-
db_handle: DbReadBase,
|
|
269
|
-
|
|
270
|
-
|
|
282
|
+
db_handle: DbReadBase,
|
|
283
|
+
extension: str,
|
|
284
|
+
options,
|
|
285
|
+
task: Optional[Task] = None,
|
|
286
|
+
progress: Callable[[int, int], None] | None = None,
|
|
287
|
+
) -> tuple[str, str, list[str]]:
|
|
288
|
+
"""Generate the export.
|
|
289
|
+
|
|
290
|
+
Plugin exporters report progress through `task`, the GEDCOM 7 exporter
|
|
291
|
+
through `progress`, called with the records done and the total.
|
|
292
|
+
|
|
293
|
+
Returns the file name, its extension, and the exporter's messages about
|
|
294
|
+
what it left out, if it reports any. Only the GEDCOM 7 exporter does.
|
|
295
|
+
"""
|
|
271
296
|
export_path = current_app.config.get("EXPORT_DIR")
|
|
272
297
|
if not export_path:
|
|
273
298
|
raise abort_with_message(500, "EXPORT_DIR not set in configuration")
|
|
274
299
|
os.makedirs(export_path, exist_ok=True)
|
|
300
|
+
if extension == GEDCOM7_EXTENSION:
|
|
301
|
+
return run_gedcom7_export(db_handle, export_path, options, progress=progress)
|
|
275
302
|
file_name = f"{uuid.uuid4()}.{extension}"
|
|
276
303
|
file_path = os.path.join(export_path, file_name)
|
|
277
304
|
_resources = ResourcePath()
|
|
@@ -297,5 +324,36 @@ def run_export(
|
|
|
297
324
|
)
|
|
298
325
|
if not result:
|
|
299
326
|
abort_with_message(500, "Export function failed")
|
|
300
|
-
return file_name, "." + extension
|
|
327
|
+
return file_name, "." + extension, []
|
|
301
328
|
abort_with_message(404, "Exporter not found") # exporter not found
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def run_gedcom7_export(
|
|
332
|
+
db_handle: DbReadBase,
|
|
333
|
+
export_path: str,
|
|
334
|
+
options,
|
|
335
|
+
progress: Callable[[int, int], None] | None = None,
|
|
336
|
+
) -> tuple[str, str, list[str]]:
|
|
337
|
+
"""Export the database as a GEDCOM 7 file."""
|
|
338
|
+
file_name = f"{uuid.uuid4()}.ged"
|
|
339
|
+
file_path = os.path.join(export_path, file_name)
|
|
340
|
+
filters.reload_custom_filters()
|
|
341
|
+
try:
|
|
342
|
+
report = gramps_gedcom7.export_gedcom(
|
|
343
|
+
options.get_filtered_database(db_handle), file_path, progress=progress
|
|
344
|
+
)
|
|
345
|
+
except gramps_gedcom7.MissingObjectsError as e:
|
|
346
|
+
abort_with_message(
|
|
347
|
+
422,
|
|
348
|
+
f"The family tree cannot be exported: {e}. Running the check & "
|
|
349
|
+
"repair tool on the tree should fix this.",
|
|
350
|
+
)
|
|
351
|
+
except gramps_gedcom7.ExportValidationError as e:
|
|
352
|
+
if e.from_data:
|
|
353
|
+
abort_with_message(422, f"The family tree cannot be exported: {e}")
|
|
354
|
+
current_app.logger.exception("GEDCOM 7 export produced an invalid file")
|
|
355
|
+
abort_with_message(500, f"Export failed: {e}")
|
|
356
|
+
except Exception as e:
|
|
357
|
+
current_app.logger.exception("GEDCOM 7 export failed")
|
|
358
|
+
abort_with_message(500, f"Export failed: {e}")
|
|
359
|
+
return file_name, ".ged", truncate_import_report(report.messages())
|
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
import httpx
|
|
9
9
|
from flask import current_app
|
|
10
|
+
from openai import OpenAIError
|
|
10
11
|
from pydantic_ai import ModelMessagesTypeAdapter
|
|
11
12
|
from pydantic_ai.exceptions import (
|
|
12
13
|
ModelAPIError,
|
|
@@ -14,6 +15,7 @@ from pydantic_ai.exceptions import (
|
|
|
14
15
|
ModelRetry,
|
|
15
16
|
UnexpectedModelBehavior,
|
|
16
17
|
UsageLimitExceeded,
|
|
18
|
+
UserError,
|
|
17
19
|
)
|
|
18
20
|
from pydantic_ai.messages import (
|
|
19
21
|
ModelRequest,
|
|
@@ -131,13 +133,19 @@ def answer_with_agent(
|
|
|
131
133
|
max_tokens = config.get("LLM_MAX_TOKENS", 200_000)
|
|
132
134
|
|
|
133
135
|
if not model_name:
|
|
134
|
-
|
|
136
|
+
logger.error("No LLM model specified")
|
|
137
|
+
abort_with_message(500, "The AI model is not configured correctly.")
|
|
135
138
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
try:
|
|
140
|
+
agent = create_agent(
|
|
141
|
+
model_name=model_name,
|
|
142
|
+
base_url=base_url,
|
|
143
|
+
system_prompt_override=system_prompt_override,
|
|
144
|
+
)
|
|
145
|
+
except (UserError, ValueError, OpenAIError) as e:
|
|
146
|
+
# e.g. unknown provider prefix or missing provider API key/base URL
|
|
147
|
+
logger.error("Failed to set up the AI model '%s': %s", model_name, e)
|
|
148
|
+
abort_with_message(500, "The AI model is not configured correctly.")
|
|
141
149
|
|
|
142
150
|
deps = AgentDeps(
|
|
143
151
|
tree=tree,
|
|
@@ -150,7 +158,9 @@ def answer_with_agent(
|
|
|
150
158
|
message_history: list[ModelRequest | ModelResponse] = []
|
|
151
159
|
if message_history_raw:
|
|
152
160
|
try:
|
|
153
|
-
message_history = ModelMessagesTypeAdapter.validate_json(
|
|
161
|
+
message_history = ModelMessagesTypeAdapter.validate_json(
|
|
162
|
+
message_history_raw
|
|
163
|
+
)
|
|
154
164
|
except Exception as e: # pylint: disable=broad-except
|
|
155
165
|
raise ValueError(f"Invalid message_history_raw: {e}") from e
|
|
156
166
|
elif history:
|
|
@@ -186,12 +196,15 @@ def answer_with_agent(
|
|
|
186
196
|
return result
|
|
187
197
|
except UsageLimitExceeded as e:
|
|
188
198
|
logger.warning("Agent usage limit exceeded: %s", e)
|
|
189
|
-
abort_with_message(
|
|
199
|
+
abort_with_message(
|
|
200
|
+
429, "The AI agent exceeded its usage limits for this request."
|
|
201
|
+
)
|
|
190
202
|
except ModelHTTPError as e:
|
|
191
203
|
logger.error("Model provider returned an error: %s", e)
|
|
192
204
|
if e.status_code == 429:
|
|
193
205
|
abort_with_message(
|
|
194
|
-
429,
|
|
206
|
+
429,
|
|
207
|
+
"The AI model provider is rate limiting requests. Please try again later.",
|
|
195
208
|
)
|
|
196
209
|
if e.status_code in (502, 503, 504, 529):
|
|
197
210
|
# 529 is Anthropic's "overloaded"
|
|
@@ -199,7 +212,9 @@ def answer_with_agent(
|
|
|
199
212
|
abort_with_message(502, "The AI model provider returned an error.")
|
|
200
213
|
except httpx.TimeoutException as e:
|
|
201
214
|
logger.error("Model provider request timed out: %r", e)
|
|
202
|
-
abort_with_message(
|
|
215
|
+
abort_with_message(
|
|
216
|
+
504, "The AI model did not respond in time. Please try again."
|
|
217
|
+
)
|
|
203
218
|
except (ModelAPIError, httpx.TransportError) as e:
|
|
204
219
|
# connection refused, DNS failure, protocol error, or an SDK-wrapped equivalent
|
|
205
220
|
logger.error("Model provider request failed: %r", e)
|
|
@@ -21,10 +21,15 @@
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
+
import warnings
|
|
25
|
+
|
|
24
26
|
from pydantic_ai import Agent, RunContext
|
|
25
27
|
from pydantic_ai.capabilities import ProcessHistory
|
|
26
28
|
from pydantic_ai.messages import ModelMessage, ModelRequest, UserPromptPart
|
|
29
|
+
from pydantic_ai.models.ollama import OllamaModel
|
|
27
30
|
from pydantic_ai.models.openai import OpenAIChatModel
|
|
31
|
+
from pydantic_ai.providers import infer_provider_class
|
|
32
|
+
from pydantic_ai.providers.ollama import OllamaProvider
|
|
28
33
|
from pydantic_ai.providers.openai import OpenAIProvider
|
|
29
34
|
|
|
30
35
|
from .deps import AgentDeps
|
|
@@ -125,6 +130,25 @@ FORMATTING
|
|
|
125
130
|
Use Markdown freely. When tool results contain links like [Name](/person/I0044), include them in your response exactly as they appear — never modify the path and never drop the link. Every person, family, event, place, source, citation, repository, note, and media object should be linked."""
|
|
126
131
|
|
|
127
132
|
|
|
133
|
+
def _has_provider_prefix(model_name: str) -> bool:
|
|
134
|
+
"""Whether the model name starts with a provider known to Pydantic AI.
|
|
135
|
+
|
|
136
|
+
A colon alone is not enough: Ollama model names like "qwen2.5:7b" use it
|
|
137
|
+
to separate the model from its tag.
|
|
138
|
+
"""
|
|
139
|
+
if ":" not in model_name:
|
|
140
|
+
return False
|
|
141
|
+
provider_name = model_name.split(":", maxsplit=1)[0]
|
|
142
|
+
try:
|
|
143
|
+
with warnings.catch_warnings():
|
|
144
|
+
# deprecation warnings are emitted again when the model is created
|
|
145
|
+
warnings.simplefilter("ignore")
|
|
146
|
+
infer_provider_class(provider_name)
|
|
147
|
+
except ValueError:
|
|
148
|
+
return False
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
|
|
128
152
|
def create_agent(
|
|
129
153
|
model_name: str,
|
|
130
154
|
base_url: str | None = None,
|
|
@@ -133,13 +157,14 @@ def create_agent(
|
|
|
133
157
|
"""Create a Pydantic AI agent with the specified model.
|
|
134
158
|
|
|
135
159
|
Args:
|
|
136
|
-
model_name: The name of the LLM model to use. If it
|
|
137
|
-
"mistral:mistral-large-latest" or
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
compatible model name.
|
|
141
|
-
base_url: Optional base URL for the OpenAI-compatible API
|
|
142
|
-
model_name contains a provider prefix
|
|
160
|
+
model_name: The name of the LLM model to use. If it starts with a provider
|
|
161
|
+
prefix known to Pydantic AI (e.g., "mistral:mistral-large-latest" or
|
|
162
|
+
"openai:gpt-4"), Pydantic AI will handle provider detection
|
|
163
|
+
automatically. Otherwise (e.g., "gpt-4" or "qwen2.5:7b"), it will be
|
|
164
|
+
treated as an OpenAI compatible model name.
|
|
165
|
+
base_url: Optional base URL for the OpenAI-compatible API. Ignored if
|
|
166
|
+
model_name contains a provider prefix, except for "ollama:", where
|
|
167
|
+
it takes precedence over the OLLAMA_BASE_URL environment variable.
|
|
143
168
|
system_prompt_override: Optional override for the system prompt
|
|
144
169
|
|
|
145
170
|
Returns:
|
|
@@ -147,8 +172,13 @@ def create_agent(
|
|
|
147
172
|
"""
|
|
148
173
|
# If model name has a provider prefix (e.g., "mistral:model-name"),
|
|
149
174
|
# let Pydantic AI handle provider detection automatically
|
|
150
|
-
if ":"
|
|
151
|
-
model: str | OpenAIChatModel =
|
|
175
|
+
if model_name.startswith("ollama:") and base_url:
|
|
176
|
+
model: str | OpenAIChatModel = OllamaModel(
|
|
177
|
+
model_name.removeprefix("ollama:"),
|
|
178
|
+
provider=OllamaProvider(base_url=base_url),
|
|
179
|
+
)
|
|
180
|
+
elif _has_provider_prefix(model_name):
|
|
181
|
+
model = model_name
|
|
152
182
|
else:
|
|
153
183
|
# Otherwise, use OpenAI-compatible provider with optional base_url
|
|
154
184
|
provider = OpenAIProvider(base_url=base_url)
|