compendium-ils 1.0.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.
- compendium_ils-1.0.0/.github/workflows/release.yml +31 -0
- compendium_ils-1.0.0/.gitignore +60 -0
- compendium_ils-1.0.0/.python-version +1 -0
- compendium_ils-1.0.0/LICENSE +21 -0
- compendium_ils-1.0.0/PKG-INFO +405 -0
- compendium_ils-1.0.0/README.md +355 -0
- compendium_ils-1.0.0/alembic.ini +149 -0
- compendium_ils-1.0.0/docker/.env.example +105 -0
- compendium_ils-1.0.0/docker/Dockerfile +51 -0
- compendium_ils-1.0.0/docker/README.md +269 -0
- compendium_ils-1.0.0/docker/certs/.gitkeep +0 -0
- compendium_ils-1.0.0/docker/compendium/entrypoint.sh +41 -0
- compendium_ils-1.0.0/docker/crontab.sample +63 -0
- compendium_ils-1.0.0/docker/docker-compose.yml +125 -0
- compendium_ils-1.0.0/docker/install-cron.sh +128 -0
- compendium_ils-1.0.0/docker/nginx/entrypoint.sh +25 -0
- compendium_ils-1.0.0/docker/nginx/nginx.conf +77 -0
- compendium_ils-1.0.0/docs/architecture.md +822 -0
- compendium_ils-1.0.0/docs/compendium.service.sample +87 -0
- compendium_ils-1.0.0/docs/crontab.sample +57 -0
- compendium_ils-1.0.0/docs/deployment.md +455 -0
- compendium_ils-1.0.0/docs/operation-map.md +44 -0
- compendium_ils-1.0.0/docs/schema.md +471 -0
- compendium_ils-1.0.0/docs/security_audit_2026-04-19.md +190 -0
- compendium_ils-1.0.0/ils_parity.md +322 -0
- compendium_ils-1.0.0/pyproject.toml +108 -0
- compendium_ils-1.0.0/scripts/install-cron.sh +147 -0
- compendium_ils-1.0.0/scripts/render_label_matrix.py +197 -0
- compendium_ils-1.0.0/src/compendium/__init__.py +1 -0
- compendium_ils-1.0.0/src/compendium/__main__.py +4 -0
- compendium_ils-1.0.0/src/compendium/api/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/api/app.py +244 -0
- compendium_ils-1.0.0/src/compendium/api/deps.py +122 -0
- compendium_ils-1.0.0/src/compendium/api/routes/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/api/routes/audit.py +34 -0
- compendium_ils-1.0.0/src/compendium/api/routes/auth.py +64 -0
- compendium_ils-1.0.0/src/compendium/api/routes/branches.py +43 -0
- compendium_ils-1.0.0/src/compendium/api/routes/calendar.py +181 -0
- compendium_ils-1.0.0/src/compendium/api/routes/claims.py +160 -0
- compendium_ils-1.0.0/src/compendium/api/routes/creators.py +53 -0
- compendium_ils-1.0.0/src/compendium/api/routes/curated_lists.py +242 -0
- compendium_ils-1.0.0/src/compendium/api/routes/fines.py +167 -0
- compendium_ils-1.0.0/src/compendium/api/routes/holds.py +200 -0
- compendium_ils-1.0.0/src/compendium/api/routes/households.py +146 -0
- compendium_ils-1.0.0/src/compendium/api/routes/imports.py +432 -0
- compendium_ils-1.0.0/src/compendium/api/routes/items.py +286 -0
- compendium_ils-1.0.0/src/compendium/api/routes/labels.py +177 -0
- compendium_ils-1.0.0/src/compendium/api/routes/loans.py +148 -0
- compendium_ils-1.0.0/src/compendium/api/routes/me.py +175 -0
- compendium_ils-1.0.0/src/compendium/api/routes/notifications.py +65 -0
- compendium_ils-1.0.0/src/compendium/api/routes/patron_categories.py +93 -0
- compendium_ils-1.0.0/src/compendium/api/routes/patrons.py +186 -0
- compendium_ils-1.0.0/src/compendium/api/routes/policies.py +52 -0
- compendium_ils-1.0.0/src/compendium/api/routes/reports.py +126 -0
- compendium_ils-1.0.0/src/compendium/api/routes/settings.py +272 -0
- compendium_ils-1.0.0/src/compendium/api/routes/users.py +163 -0
- compendium_ils-1.0.0/src/compendium/api/routes/works.py +220 -0
- compendium_ils-1.0.0/src/compendium/api/schemas.py +519 -0
- compendium_ils-1.0.0/src/compendium/api/uploads.py +51 -0
- compendium_ils-1.0.0/src/compendium/cli/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/audit.py +40 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/backup.py +123 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/branch.py +54 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/bulk_ops.py +545 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/calendar.py +158 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/claim.py +134 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/creator.py +50 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/curated_list.py +280 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/db.py +47 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/fine.py +147 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/hold.py +221 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/household.py +175 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/item.py +639 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/keygen.py +46 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/labels.py +447 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/loan.py +224 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/maintenance.py +538 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/metadata.py +133 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/patron.py +281 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/patron_category.py +98 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/policy.py +188 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/reports.py +187 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/role.py +114 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/secrets.py +178 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/settings.py +245 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/user.py +284 -0
- compendium_ils-1.0.0/src/compendium/cli/commands/work.py +455 -0
- compendium_ils-1.0.0/src/compendium/cli/io.py +49 -0
- compendium_ils-1.0.0/src/compendium/cli/main.py +110 -0
- compendium_ils-1.0.0/src/compendium/config/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/config/seed.py +154 -0
- compendium_ils-1.0.0/src/compendium/config/settings.py +120 -0
- compendium_ils-1.0.0/src/compendium/db/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/db/engine.py +105 -0
- compendium_ils-1.0.0/src/compendium/db/session.py +50 -0
- compendium_ils-1.0.0/src/compendium/domain/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/domain/enums.py +85 -0
- compendium_ils-1.0.0/src/compendium/domain/errors.py +60 -0
- compendium_ils-1.0.0/src/compendium/domain/identifiers.py +114 -0
- compendium_ils-1.0.0/src/compendium/domain/models.py +555 -0
- compendium_ils-1.0.0/src/compendium/domain/types.py +32 -0
- compendium_ils-1.0.0/src/compendium/migrations/README +1 -0
- compendium_ils-1.0.0/src/compendium/migrations/env.py +68 -0
- compendium_ils-1.0.0/src/compendium/migrations/script.py.mako +28 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/11dbd4cede12_add_search_text_and_fts.py +98 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/1b17e2ba445c_consolidate_barcode_settings.py +84 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/443891bfaa50_add_item_note.py +47 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/5b9e539aca65_add_household_model.py +106 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/611abe9ea6e5_add_metadata_cache.py +39 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/6e72f68cf15a_add_auth.py +62 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/a1b2c3d4e5f6_add_app_user_password_changed_at.py +31 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/a7b8c9d0e1f2_add_hold_held_item.py +43 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/a8b9c0d1e2f3_add_branch_location_code.py +30 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/a9b9ea15933f_add_holds_and_loan_policies.py +66 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/b1c2d3e4f5a6_add_curated_list.py +140 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/b2c3d4e5f6a7_add_counters_table.py +44 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/b3c4d5e6f7a8_add_audit_log.py +54 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/b8c9d0e1f2a3_add_patron_categories.py +97 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/c1d2e3f4a5b6_add_branch_classification_scheme.py +33 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/c3d4e5f6a7b8_revamp_identifiers.py +133 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/c4d5e6f7a8b9_add_library_calendar.py +120 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/c9d0e1f2a3b4_add_hold_suspend.py +35 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/cec97d4626cf_initial_schema.py +175 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/d0e1f2a3b4c5_add_site_setting.py +46 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/d4e5f6a7b8c9_add_item_loanable.py +41 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/d7e8f9a0b1c2_add_work_sort_title.py +38 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/e1f2a3b4c5d6_split_admin_roles.py +152 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/e5f6a7b8c9d0_add_fines.py +76 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/e9f0a1b2c3d4_fix_failed_login_id_type.py +59 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/f2a3b4c5d6e7_add_failed_login_table.py +42 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/f6a7b8c9d0e1_add_notifications.py +93 -0
- compendium_ils-1.0.0/src/compendium/migrations/versions/fab1c2d3e4f5_patron_account_manage.py +86 -0
- compendium_ils-1.0.0/src/compendium/repositories/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/repositories/base.py +430 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/__init__.py +4 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/audit_log_repository.py +48 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/branch_repository.py +24 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/calendar_repository.py +78 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/counters.py +28 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/creator_repository.py +41 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/curated_list_repository.py +86 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/failed_login_repository.py +57 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/fine_repository.py +122 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/hold_repository.py +237 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/household_repository.py +38 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/item_note_repository.py +30 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/item_repository.py +60 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/loan_policy_repository.py +92 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/loan_repository.py +237 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/media_type_repository.py +16 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/notification_repository.py +104 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/patron_category_repository.py +56 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/patron_repository.py +42 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/role_repository.py +26 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/site_setting_repository.py +47 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/user_repository.py +29 -0
- compendium_ils-1.0.0/src/compendium/repositories/sql/work_repository.py +713 -0
- compendium_ils-1.0.0/src/compendium/services/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/services/_normalization.py +71 -0
- compendium_ils-1.0.0/src/compendium/services/audit.py +99 -0
- compendium_ils-1.0.0/src/compendium/services/auth.py +302 -0
- compendium_ils-1.0.0/src/compendium/services/backup.py +563 -0
- compendium_ils-1.0.0/src/compendium/services/calendar.py +275 -0
- compendium_ils-1.0.0/src/compendium/services/catalog.py +1249 -0
- compendium_ils-1.0.0/src/compendium/services/circulation.py +591 -0
- compendium_ils-1.0.0/src/compendium/services/covers.py +198 -0
- compendium_ils-1.0.0/src/compendium/services/curated_lists.py +267 -0
- compendium_ils-1.0.0/src/compendium/services/discovery.py +139 -0
- compendium_ils-1.0.0/src/compendium/services/fines.py +432 -0
- compendium_ils-1.0.0/src/compendium/services/formatting.py +23 -0
- compendium_ils-1.0.0/src/compendium/services/holds.py +391 -0
- compendium_ils-1.0.0/src/compendium/services/households.py +147 -0
- compendium_ils-1.0.0/src/compendium/services/import_export.py +1621 -0
- compendium_ils-1.0.0/src/compendium/services/item_notes.py +100 -0
- compendium_ils-1.0.0/src/compendium/services/label_canvas_svg.py +138 -0
- compendium_ils-1.0.0/src/compendium/services/labels.py +1142 -0
- compendium_ils-1.0.0/src/compendium/services/metadata.py +1244 -0
- compendium_ils-1.0.0/src/compendium/services/metadata_cache.py +304 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/__init__.py +451 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/smtp.py +81 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/due_soon/body.txt +11 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/due_soon/subject.txt +1 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/hold_ready/body.txt +12 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/hold_ready/subject.txt +1 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/overdue/body.txt +18 -0
- compendium_ils-1.0.0/src/compendium/services/notifications/templates/overdue/subject.txt +1 -0
- compendium_ils-1.0.0/src/compendium/services/patron_categories.py +116 -0
- compendium_ils-1.0.0/src/compendium/services/patrons.py +286 -0
- compendium_ils-1.0.0/src/compendium/services/policies.py +155 -0
- compendium_ils-1.0.0/src/compendium/services/rate_limit.py +119 -0
- compendium_ils-1.0.0/src/compendium/services/reports.py +179 -0
- compendium_ils-1.0.0/src/compendium/services/roles.py +127 -0
- compendium_ils-1.0.0/src/compendium/services/secrets.py +149 -0
- compendium_ils-1.0.0/src/compendium/services/settings_registry.py +984 -0
- compendium_ils-1.0.0/src/compendium/services/site_settings.py +269 -0
- compendium_ils-1.0.0/src/compendium/web/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/web/app.py +69 -0
- compendium_ils-1.0.0/src/compendium/web/csrf.py +83 -0
- compendium_ils-1.0.0/src/compendium/web/deps.py +132 -0
- compendium_ils-1.0.0/src/compendium/web/jinja.py +86 -0
- compendium_ils-1.0.0/src/compendium/web/nav_pages.py +45 -0
- compendium_ils-1.0.0/src/compendium/web/routes/__init__.py +0 -0
- compendium_ils-1.0.0/src/compendium/web/routes/admin_circulation.py +154 -0
- compendium_ils-1.0.0/src/compendium/web/routes/admin_holds.py +117 -0
- compendium_ils-1.0.0/src/compendium/web/routes/admin_settings.py +925 -0
- compendium_ils-1.0.0/src/compendium/web/routes/audit.py +72 -0
- compendium_ils-1.0.0/src/compendium/web/routes/auth.py +137 -0
- compendium_ils-1.0.0/src/compendium/web/routes/branches.py +98 -0
- compendium_ils-1.0.0/src/compendium/web/routes/bulk.py +520 -0
- compendium_ils-1.0.0/src/compendium/web/routes/catalog.py +717 -0
- compendium_ils-1.0.0/src/compendium/web/routes/circ.py +178 -0
- compendium_ils-1.0.0/src/compendium/web/routes/covers.py +36 -0
- compendium_ils-1.0.0/src/compendium/web/routes/creators.py +127 -0
- compendium_ils-1.0.0/src/compendium/web/routes/curated_lists.py +331 -0
- compendium_ils-1.0.0/src/compendium/web/routes/fines.py +542 -0
- compendium_ils-1.0.0/src/compendium/web/routes/households.py +220 -0
- compendium_ils-1.0.0/src/compendium/web/routes/items.py +685 -0
- compendium_ils-1.0.0/src/compendium/web/routes/kiosk.py +269 -0
- compendium_ils-1.0.0/src/compendium/web/routes/labels.py +628 -0
- compendium_ils-1.0.0/src/compendium/web/routes/library_hours.py +191 -0
- compendium_ils-1.0.0/src/compendium/web/routes/me.py +338 -0
- compendium_ils-1.0.0/src/compendium/web/routes/notifications.py +147 -0
- compendium_ils-1.0.0/src/compendium/web/routes/patron_categories.py +143 -0
- compendium_ils-1.0.0/src/compendium/web/routes/patrons.py +472 -0
- compendium_ils-1.0.0/src/compendium/web/routes/policies.py +191 -0
- compendium_ils-1.0.0/src/compendium/web/routes/reports.py +274 -0
- compendium_ils-1.0.0/src/compendium/web/routes/roles.py +234 -0
- compendium_ils-1.0.0/src/compendium/web/routes/users.py +458 -0
- compendium_ils-1.0.0/src/compendium/web/static/chart.min.js +20 -0
- compendium_ils-1.0.0/src/compendium/web/static/compendium.css +1313 -0
- compendium_ils-1.0.0/src/compendium/web/static/favicon.svg +13 -0
- compendium_ils-1.0.0/src/compendium/web/static/fonts/fraunces-italic-latin-ext.woff2 +0 -0
- compendium_ils-1.0.0/src/compendium/web/static/fonts/fraunces-italic-latin.woff2 +0 -0
- compendium_ils-1.0.0/src/compendium/web/static/fonts/fraunces-normal-latin-ext.woff2 +0 -0
- compendium_ils-1.0.0/src/compendium/web/static/fonts/fraunces-normal-latin.woff2 +0 -0
- compendium_ils-1.0.0/src/compendium/web/static/htmx.min.js +1 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/bluray.svg +5 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/book.svg +4 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/cd.svg +4 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/dvd.svg +5 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/generic.svg +4 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/vhs.svg +7 -0
- compendium_ils-1.0.0/src/compendium/web/static/icons/vinyl.svg +6 -0
- compendium_ils-1.0.0/src/compendium/web/static/pico.min.css +4 -0
- compendium_ils-1.0.0/src/compendium/web/static/scanner.js +172 -0
- compendium_ils-1.0.0/src/compendium/web/static/zxing/zxing-browser.min.js +1 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_macros.html +28 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/brand_glyph.html +12 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/empty_state.html +12 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/item_preview.html +64 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/media_icon.html +48 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/scanner_dialog.html +20 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/scanner_scripts.html +2 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/settings_sidebar.html +17 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/suggest.html +12 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/title_candidates.html +22 -0
- compendium_ils-1.0.0/src/compendium/web/templates/_partials/work_list.html +56 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/_import_report.html +63 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/_import_status_partial.html +26 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/claims.html +28 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/closed_dates.html +62 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/export.html +45 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/fines.html +80 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/holds.html +103 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/import.html +92 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/import_job.html +10 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/library_hours.html +45 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/loans.html +84 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/patron_categories.html +73 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/settings.html +252 -0
- compendium_ils-1.0.0/src/compendium/web/templates/admin/settings_index.html +29 -0
- compendium_ils-1.0.0/src/compendium/web/templates/audit/list.html +60 -0
- compendium_ils-1.0.0/src/compendium/web/templates/base.html +393 -0
- compendium_ils-1.0.0/src/compendium/web/templates/branches/edit.html +59 -0
- compendium_ils-1.0.0/src/compendium/web/templates/branches/list.html +38 -0
- compendium_ils-1.0.0/src/compendium/web/templates/catalog/creators.html +78 -0
- compendium_ils-1.0.0/src/compendium/web/templates/catalog/detail.html +220 -0
- compendium_ils-1.0.0/src/compendium/web/templates/catalog/edit.html +108 -0
- compendium_ils-1.0.0/src/compendium/web/templates/catalog/refresh_preview.html +80 -0
- compendium_ils-1.0.0/src/compendium/web/templates/catalog/search.html +313 -0
- compendium_ils-1.0.0/src/compendium/web/templates/circ/desk.html +72 -0
- compendium_ils-1.0.0/src/compendium/web/templates/creators/edit.html +36 -0
- compendium_ils-1.0.0/src/compendium/web/templates/curated_lists/detail.html +132 -0
- compendium_ils-1.0.0/src/compendium/web/templates/curated_lists/list.html +44 -0
- compendium_ils-1.0.0/src/compendium/web/templates/curated_lists/new.html +33 -0
- compendium_ils-1.0.0/src/compendium/web/templates/error.html +7 -0
- compendium_ils-1.0.0/src/compendium/web/templates/error_no_patron.html +20 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/declare_lost.html +33 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/mark_damaged.html +27 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/not_found.html +6 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/patron.html +95 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/verify_returned_confirm.html +26 -0
- compendium_ils-1.0.0/src/compendium/web/templates/fines/write_off_claim.html +23 -0
- compendium_ils-1.0.0/src/compendium/web/templates/households/detail.html +71 -0
- compendium_ils-1.0.0/src/compendium/web/templates/households/list.html +26 -0
- compendium_ils-1.0.0/src/compendium/web/templates/households/new.html +21 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/detail.html +200 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/edit.html +43 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/loanable.html +59 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/new.html +106 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/new_manual.html +74 -0
- compendium_ils-1.0.0/src/compendium/web/templates/items/withdraw_confirm.html +30 -0
- compendium_ils-1.0.0/src/compendium/web/templates/kiosk/landing.html +31 -0
- compendium_ils-1.0.0/src/compendium/web/templates/kiosk/session.html +53 -0
- compendium_ils-1.0.0/src/compendium/web/templates/kiosk_base.html +26 -0
- compendium_ils-1.0.0/src/compendium/web/templates/labels/_label_preview.html +4 -0
- compendium_ils-1.0.0/src/compendium/web/templates/labels/_symbology_banner.html +15 -0
- compendium_ils-1.0.0/src/compendium/web/templates/labels/index.html +23 -0
- compendium_ils-1.0.0/src/compendium/web/templates/labels/items.html +154 -0
- compendium_ils-1.0.0/src/compendium/web/templates/labels/patrons.html +138 -0
- compendium_ils-1.0.0/src/compendium/web/templates/lists/index.html +35 -0
- compendium_ils-1.0.0/src/compendium/web/templates/lists/view.html +55 -0
- compendium_ils-1.0.0/src/compendium/web/templates/login.html +35 -0
- compendium_ils-1.0.0/src/compendium/web/templates/me/fines.html +34 -0
- compendium_ils-1.0.0/src/compendium/web/templates/me/holds.html +78 -0
- compendium_ils-1.0.0/src/compendium/web/templates/me/loans.html +49 -0
- compendium_ils-1.0.0/src/compendium/web/templates/me/password.html +26 -0
- compendium_ils-1.0.0/src/compendium/web/templates/me/preferences.html +27 -0
- compendium_ils-1.0.0/src/compendium/web/templates/notifications/list.html +79 -0
- compendium_ils-1.0.0/src/compendium/web/templates/patrons/detail.html +225 -0
- compendium_ils-1.0.0/src/compendium/web/templates/patrons/list.html +41 -0
- compendium_ils-1.0.0/src/compendium/web/templates/patrons/loans.html +68 -0
- compendium_ils-1.0.0/src/compendium/web/templates/patrons/new.html +60 -0
- compendium_ils-1.0.0/src/compendium/web/templates/policies/list.html +80 -0
- compendium_ils-1.0.0/src/compendium/web/templates/policies/new.html +46 -0
- compendium_ils-1.0.0/src/compendium/web/templates/reports/checkouts.html +71 -0
- compendium_ils-1.0.0/src/compendium/web/templates/reports/dormant.html +56 -0
- compendium_ils-1.0.0/src/compendium/web/templates/reports/index.html +29 -0
- compendium_ils-1.0.0/src/compendium/web/templates/reports/overdues.html +49 -0
- compendium_ils-1.0.0/src/compendium/web/templates/reports/popular.html +83 -0
- compendium_ils-1.0.0/src/compendium/web/templates/roles/detail.html +110 -0
- compendium_ils-1.0.0/src/compendium/web/templates/roles/list.html +30 -0
- compendium_ils-1.0.0/src/compendium/web/templates/roles/new.html +59 -0
- compendium_ils-1.0.0/src/compendium/web/templates/users/detail.html +163 -0
- compendium_ils-1.0.0/src/compendium/web/templates/users/list.html +42 -0
- compendium_ils-1.0.0/src/compendium/web/templates/users/new.html +113 -0
- compendium_ils-1.0.0/tests/__init__.py +0 -0
- compendium_ils-1.0.0/tests/conftest.py +79 -0
- compendium_ils-1.0.0/tests/e2e/__init__.py +0 -0
- compendium_ils-1.0.0/tests/e2e/conftest.py +229 -0
- compendium_ils-1.0.0/tests/e2e/test_audit_viewer_pagination.py +63 -0
- compendium_ils-1.0.0/tests/e2e/test_csp_no_console_errors.py +91 -0
- compendium_ils-1.0.0/tests/e2e/test_inline_policy_edit.py +45 -0
- compendium_ils-1.0.0/tests/e2e/test_kiosk_session_flow.py +80 -0
- compendium_ils-1.0.0/tests/e2e/test_login_csrf_roundtrip.py +76 -0
- compendium_ils-1.0.0/tests/e2e/test_place_hold_htmx.py +54 -0
- compendium_ils-1.0.0/tests/e2e/test_scanner_mocked.py +114 -0
- compendium_ils-1.0.0/tests/e2e/test_theme_toggle_no_fouc.py +51 -0
- compendium_ils-1.0.0/tests/helpers.py +148 -0
- compendium_ils-1.0.0/tests/integration/__init__.py +0 -0
- compendium_ils-1.0.0/tests/integration/test_admin_circulation.py +548 -0
- compendium_ils-1.0.0/tests/integration/test_admin_holds.py +503 -0
- compendium_ils-1.0.0/tests/integration/test_admin_settings.py +844 -0
- compendium_ils-1.0.0/tests/integration/test_admin_settings_secrets.py +404 -0
- compendium_ils-1.0.0/tests/integration/test_api_audit.py +160 -0
- compendium_ils-1.0.0/tests/integration/test_api_auth.py +189 -0
- compendium_ils-1.0.0/tests/integration/test_api_authz.py +323 -0
- compendium_ils-1.0.0/tests/integration/test_api_households.py +220 -0
- compendium_ils-1.0.0/tests/integration/test_api_item_notes.py +279 -0
- compendium_ils-1.0.0/tests/integration/test_api_me.py +323 -0
- compendium_ils-1.0.0/tests/integration/test_api_patch.py +464 -0
- compendium_ils-1.0.0/tests/integration/test_api_patron_account.py +343 -0
- compendium_ils-1.0.0/tests/integration/test_audit_log.py +191 -0
- compendium_ils-1.0.0/tests/integration/test_audit_prune.py +162 -0
- compendium_ils-1.0.0/tests/integration/test_backup.py +584 -0
- compendium_ils-1.0.0/tests/integration/test_book_metadata_source_preference.py +535 -0
- compendium_ils-1.0.0/tests/integration/test_calendar.py +130 -0
- compendium_ils-1.0.0/tests/integration/test_calendar_api.py +142 -0
- compendium_ils-1.0.0/tests/integration/test_calendar_cli.py +94 -0
- compendium_ils-1.0.0/tests/integration/test_calendar_web.py +173 -0
- compendium_ils-1.0.0/tests/integration/test_catalog.py +645 -0
- compendium_ils-1.0.0/tests/integration/test_catalog_refresh_bulk.py +347 -0
- compendium_ils-1.0.0/tests/integration/test_circulation.py +264 -0
- compendium_ils-1.0.0/tests/integration/test_circulation_calendar.py +207 -0
- compendium_ils-1.0.0/tests/integration/test_claims_api.py +288 -0
- compendium_ils-1.0.0/tests/integration/test_claims_returned.py +269 -0
- compendium_ils-1.0.0/tests/integration/test_claims_web.py +226 -0
- compendium_ils-1.0.0/tests/integration/test_classification.py +105 -0
- compendium_ils-1.0.0/tests/integration/test_classification_ddc_fallback.py +89 -0
- compendium_ils-1.0.0/tests/integration/test_classification_fallback.py +108 -0
- compendium_ils-1.0.0/tests/integration/test_cli_commands.py +1765 -0
- compendium_ils-1.0.0/tests/integration/test_cli_household.py +96 -0
- compendium_ils-1.0.0/tests/integration/test_cli_item_note.py +158 -0
- compendium_ils-1.0.0/tests/integration/test_counter_repository.py +35 -0
- compendium_ils-1.0.0/tests/integration/test_cover_proxy.py +421 -0
- compendium_ils-1.0.0/tests/integration/test_csp_inline_handlers.py +57 -0
- compendium_ils-1.0.0/tests/integration/test_csp_nonce.py +125 -0
- compendium_ils-1.0.0/tests/integration/test_db_binding.py +174 -0
- compendium_ils-1.0.0/tests/integration/test_discovery.py +216 -0
- compendium_ils-1.0.0/tests/integration/test_discovery_api.py +182 -0
- compendium_ils-1.0.0/tests/integration/test_discovery_web.py +150 -0
- compendium_ils-1.0.0/tests/integration/test_engine_sqlite.py +109 -0
- compendium_ils-1.0.0/tests/integration/test_fines.py +410 -0
- compendium_ils-1.0.0/tests/integration/test_fines_api.py +351 -0
- compendium_ils-1.0.0/tests/integration/test_fines_calendar.py +259 -0
- compendium_ils-1.0.0/tests/integration/test_fines_circulation.py +326 -0
- compendium_ils-1.0.0/tests/integration/test_fines_cli.py +242 -0
- compendium_ils-1.0.0/tests/integration/test_fines_web.py +463 -0
- compendium_ils-1.0.0/tests/integration/test_hold_suspend.py +341 -0
- compendium_ils-1.0.0/tests/integration/test_hold_suspend_api.py +277 -0
- compendium_ils-1.0.0/tests/integration/test_hold_suspend_web.py +212 -0
- compendium_ils-1.0.0/tests/integration/test_holds.py +402 -0
- compendium_ils-1.0.0/tests/integration/test_holds_calendar.py +141 -0
- compendium_ils-1.0.0/tests/integration/test_household_model.py +76 -0
- compendium_ils-1.0.0/tests/integration/test_household_permission.py +49 -0
- compendium_ils-1.0.0/tests/integration/test_import_csv_preserve_barcodes.py +171 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_api.py +387 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_cli.py +380 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_csv.py +232 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_lt.py +377 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_marc.py +294 -0
- compendium_ils-1.0.0/tests/integration/test_import_export_web.py +346 -0
- compendium_ils-1.0.0/tests/integration/test_import_metadata_cache.py +235 -0
- compendium_ils-1.0.0/tests/integration/test_item_note_autolog.py +218 -0
- compendium_ils-1.0.0/tests/integration/test_item_note_model.py +138 -0
- compendium_ils-1.0.0/tests/integration/test_kiosk_web.py +347 -0
- compendium_ils-1.0.0/tests/integration/test_labels_api.py +292 -0
- compendium_ils-1.0.0/tests/integration/test_labels_web.py +505 -0
- compendium_ils-1.0.0/tests/integration/test_loanable.py +327 -0
- compendium_ils-1.0.0/tests/integration/test_loanable_endpoints.py +348 -0
- compendium_ils-1.0.0/tests/integration/test_maintenance_refresh_metadata_cli.py +201 -0
- compendium_ils-1.0.0/tests/integration/test_notifications.py +447 -0
- compendium_ils-1.0.0/tests/integration/test_notifications_api.py +216 -0
- compendium_ils-1.0.0/tests/integration/test_notifications_circulation.py +167 -0
- compendium_ils-1.0.0/tests/integration/test_notifications_cli.py +248 -0
- compendium_ils-1.0.0/tests/integration/test_notifications_web.py +203 -0
- compendium_ils-1.0.0/tests/integration/test_patron_categories.py +266 -0
- compendium_ils-1.0.0/tests/integration/test_patron_categories_api.py +213 -0
- compendium_ils-1.0.0/tests/integration/test_patron_categories_web.py +146 -0
- compendium_ils-1.0.0/tests/integration/test_policy_resolution.py +114 -0
- compendium_ils-1.0.0/tests/integration/test_rate_limit_login.py +373 -0
- compendium_ils-1.0.0/tests/integration/test_refresh_and_enrich.py +435 -0
- compendium_ils-1.0.0/tests/integration/test_refresh_metadata_per_source.py +481 -0
- compendium_ils-1.0.0/tests/integration/test_reports.py +258 -0
- compendium_ils-1.0.0/tests/integration/test_reports_api.py +257 -0
- compendium_ils-1.0.0/tests/integration/test_reports_web.py +241 -0
- compendium_ils-1.0.0/tests/integration/test_search.py +250 -0
- compendium_ils-1.0.0/tests/integration/test_secrets_cli.py +171 -0
- compendium_ils-1.0.0/tests/integration/test_security_headers.py +144 -0
- compendium_ils-1.0.0/tests/integration/test_site_settings.py +165 -0
- compendium_ils-1.0.0/tests/integration/test_uploads_bounded.py +175 -0
- compendium_ils-1.0.0/tests/integration/test_web_curated_lists.py +435 -0
- compendium_ils-1.0.0/tests/integration/test_web_households.py +145 -0
- compendium_ils-1.0.0/tests/integration/test_web_item_notes.py +244 -0
- compendium_ils-1.0.0/tests/integration/test_web_patron_detail_household.py +126 -0
- compendium_ils-1.0.0/tests/integration/test_web_ui.py +1886 -0
- compendium_ils-1.0.0/tests/integration/test_withdrawn_hidden.py +259 -0
- compendium_ils-1.0.0/tests/postgres/__init__.py +0 -0
- compendium_ils-1.0.0/tests/postgres/conftest.py +45 -0
- compendium_ils-1.0.0/tests/postgres/test_backup_cross_backend.py +210 -0
- compendium_ils-1.0.0/tests/postgres/test_pg_smoke.py +182 -0
- compendium_ils-1.0.0/tests/unit/__init__.py +0 -0
- compendium_ils-1.0.0/tests/unit/services/test_normalization.py +110 -0
- compendium_ils-1.0.0/tests/unit/test_audit_service.py +86 -0
- compendium_ils-1.0.0/tests/unit/test_auth_service.py +434 -0
- compendium_ils-1.0.0/tests/unit/test_backup_safe_ident.py +44 -0
- compendium_ils-1.0.0/tests/unit/test_calendar_service.py +269 -0
- compendium_ils-1.0.0/tests/unit/test_classification_metadata.py +59 -0
- compendium_ils-1.0.0/tests/unit/test_cover_and_mds_lookups.py +123 -0
- compendium_ils-1.0.0/tests/unit/test_curated_lists_service.py +536 -0
- compendium_ils-1.0.0/tests/unit/test_deactivate.py +173 -0
- compendium_ils-1.0.0/tests/unit/test_discovery_service.py +100 -0
- compendium_ils-1.0.0/tests/unit/test_goodreads_import.py +312 -0
- compendium_ils-1.0.0/tests/unit/test_hold_service.py +258 -0
- compendium_ils-1.0.0/tests/unit/test_household_repository.py +100 -0
- compendium_ils-1.0.0/tests/unit/test_household_service.py +166 -0
- compendium_ils-1.0.0/tests/unit/test_identifiers.py +230 -0
- compendium_ils-1.0.0/tests/unit/test_item_note_repository.py +126 -0
- compendium_ils-1.0.0/tests/unit/test_item_note_service.py +148 -0
- compendium_ils-1.0.0/tests/unit/test_label_canvas_svg.py +276 -0
- compendium_ils-1.0.0/tests/unit/test_labels_service.py +1704 -0
- compendium_ils-1.0.0/tests/unit/test_loc_ddc_lookup.py +133 -0
- compendium_ils-1.0.0/tests/unit/test_loc_lcc_lookup.py +190 -0
- compendium_ils-1.0.0/tests/unit/test_lt_import.py +402 -0
- compendium_ils-1.0.0/tests/unit/test_marcxml_entity_guard.py +56 -0
- compendium_ils-1.0.0/tests/unit/test_metadata_adapters.py +236 -0
- compendium_ils-1.0.0/tests/unit/test_metadata_cache.py +349 -0
- compendium_ils-1.0.0/tests/unit/test_metadata_google_books.py +326 -0
- compendium_ils-1.0.0/tests/unit/test_metadata_source_and_fallback.py +353 -0
- compendium_ils-1.0.0/tests/unit/test_patron_service_account.py +160 -0
- compendium_ils-1.0.0/tests/unit/test_policy_service.py +79 -0
- compendium_ils-1.0.0/tests/unit/test_rate_limit_service.py +58 -0
- compendium_ils-1.0.0/tests/unit/test_reports_service.py +182 -0
- compendium_ils-1.0.0/tests/unit/test_role_service.py +183 -0
- compendium_ils-1.0.0/tests/unit/test_secrets_service.py +137 -0
- compendium_ils-1.0.0/tests/unit/test_settings.py +51 -0
- compendium_ils-1.0.0/tests/unit/test_settings_migration.py +318 -0
- compendium_ils-1.0.0/tests/unit/test_settings_registry.py +415 -0
- compendium_ils-1.0.0/tests/unit/test_settings_secret_file.py +57 -0
- compendium_ils-1.0.0/tests/unit/test_smtp_sender.py +130 -0
- compendium_ils-1.0.0/tests/unit/test_startup_warning.py +78 -0
- compendium_ils-1.0.0/tests/unit/test_utc_datetime.py +91 -0
- compendium_ils-1.0.0/tests/visual/__init__.py +0 -0
- compendium_ils-1.0.0/tests/visual/test_label_matrix.py +112 -0
- compendium_ils-1.0.0/uv.lock +1773 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
publish:
|
|
9
|
+
name: Build and publish to PyPI
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write # required for OIDC Trusted Publishing
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
uses: astral-sh/setup-uv@v4
|
|
26
|
+
|
|
27
|
+
- name: Build distributions
|
|
28
|
+
run: uv build
|
|
29
|
+
|
|
30
|
+
- name: Publish to PyPI
|
|
31
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Virtual environments
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
env/
|
|
11
|
+
|
|
12
|
+
# Distribution / packaging
|
|
13
|
+
build/
|
|
14
|
+
dist/
|
|
15
|
+
*.egg-info/
|
|
16
|
+
*.egg
|
|
17
|
+
wheels/
|
|
18
|
+
|
|
19
|
+
# Tools
|
|
20
|
+
.pytest_cache/
|
|
21
|
+
.ruff_cache/
|
|
22
|
+
.mypy_cache/
|
|
23
|
+
.coverage
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# Environments
|
|
27
|
+
.env
|
|
28
|
+
.env.local
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.vscode/
|
|
32
|
+
.idea/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
|
|
36
|
+
# OS
|
|
37
|
+
.DS_Store
|
|
38
|
+
Thumbs.db
|
|
39
|
+
|
|
40
|
+
# Project data
|
|
41
|
+
*.db
|
|
42
|
+
*.db-shm
|
|
43
|
+
*.db-wal
|
|
44
|
+
compendium.db
|
|
45
|
+
dev.db
|
|
46
|
+
|
|
47
|
+
# Docker deployment
|
|
48
|
+
docker/.env
|
|
49
|
+
docker/certs/*
|
|
50
|
+
!docker/certs/.gitkeep
|
|
51
|
+
|
|
52
|
+
# Local-only project notes (not published)
|
|
53
|
+
CLAUDE.md
|
|
54
|
+
ils_parity.txt
|
|
55
|
+
docs/claude-deferred.md
|
|
56
|
+
docs/superpowers/
|
|
57
|
+
web_import_test_db
|
|
58
|
+
|
|
59
|
+
# Agent / dev output
|
|
60
|
+
out/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.11
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shawn Moore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: compendium-ils
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A library card catalog system for physical items
|
|
5
|
+
Project-URL: Homepage, https://github.com/statyk/compendium
|
|
6
|
+
Project-URL: Repository, https://github.com/statyk/compendium
|
|
7
|
+
Project-URL: Issues, https://github.com/statyk/compendium/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/statyk/compendium/blob/master/README.md
|
|
9
|
+
Author-email: Shawn <spmoore01@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: books,catalog,circulation,ils,library,lms,marc,opac
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: Intended Audience :: Other Audience
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: POSIX
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Other/Nonlisted Topic
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: alembic>=1.13
|
|
26
|
+
Requires-Dist: bcrypt>=4.0
|
|
27
|
+
Requires-Dist: cryptography>=42.0
|
|
28
|
+
Requires-Dist: defusedxml>=0.7.1
|
|
29
|
+
Requires-Dist: fastapi>=0.111
|
|
30
|
+
Requires-Dist: httpx>=0.27
|
|
31
|
+
Requires-Dist: jinja2>=3.1
|
|
32
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
33
|
+
Requires-Dist: pyjwt>=2.8
|
|
34
|
+
Requires-Dist: pymarc>=5.2
|
|
35
|
+
Requires-Dist: python-barcode>=0.16.1
|
|
36
|
+
Requires-Dist: python-multipart>=0.0.9
|
|
37
|
+
Requires-Dist: reportlab>=4.0
|
|
38
|
+
Requires-Dist: sqlalchemy<3.0,>=2.0
|
|
39
|
+
Requires-Dist: typer>=0.12
|
|
40
|
+
Requires-Dist: uvicorn[standard]>=0.29
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
44
|
+
Requires-Dist: testcontainers[postgres]>=4.8; extra == 'dev'
|
|
45
|
+
Provides-Extra: e2e
|
|
46
|
+
Requires-Dist: pytest-playwright>=0.5; extra == 'e2e'
|
|
47
|
+
Provides-Extra: postgres
|
|
48
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
|
|
49
|
+
Description-Content-Type: text/markdown
|
|
50
|
+
|
|
51
|
+
# Compendium
|
|
52
|
+
|
|
53
|
+
A library card catalog system for physical items — books, vinyl records, DVDs, CDs.
|
|
54
|
+
|
|
55
|
+
WARNING NOTICE CAVEAT EMPTOR:
|
|
56
|
+
This project is 100% vibe coded. Not only did I not write the code, I've barely even looked at it.
|
|
57
|
+
I guided the models (Sonnet and Opus, mostly) on design decisions and the like, but it's pretty much all AI-generated code and documentation.
|
|
58
|
+
This paragraph here is about the only part of the project written by a human.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
**Status:** v1.0.0 — core feature-complete. Catalog, circulation, holds, fines, notifications, bulk import/export, web UI, REST API, and CLI are all shipped. See [`docs/architecture.md`](docs/architecture.md) for architecture and design decisions.
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
- **Catalog** — add items by ISBN / UPC / MusicBrainz ID / TMDb ID / title search (Google Books / Open Library, MusicBrainz, TMDb) or manually for obscure items; search and browse works and copies; faceted discovery (media type, decade, availability)
|
|
66
|
+
- **Circulation** — checkout, checkin, loan renewal with category-aware per-media-type loan policies; lost / damaged / claims-returned states; self-checkout kiosk mode; library hours and holiday calendar so due dates never land on closed days and overdue fines skip closed days
|
|
67
|
+
- **Holds** — patron reservation queue; immediate promotion when a copy is available; suspend/resume; auto-expiry via maintenance command
|
|
68
|
+
- **Fines** — configurable per-policy overdue rates with caps and grace periods; lost/damaged fees; threshold-based checkout/hold blocking; pay/waive workflow; per-patron and bulk overdue assessment
|
|
69
|
+
- **Notifications** — outbox-pattern email delivery (hold-ready, due-soon, overdue) drained by a cron-invoked CLI; admin viewer + retry; per-patron opt-out; configurable retention
|
|
70
|
+
- **Reports** — checkouts/month, popular works, dormant items (weeding list), current overdues; CSV export; Chart.js trendlines
|
|
71
|
+
- **Patrons & cards** — patron categories (Adult/Child/Staff/Teacher seeded), card expiry with maintenance auto-deactivation, optional 1:1 patron↔user link for self-service
|
|
72
|
+
- **Bulk import/export** — round-trippable CSV; MARC21 binary + MARCXML import/export; LibraryThing TSV import; GoodReads CSV import (all with lenient encoding for messy real-world exports)
|
|
73
|
+
- **Backup/restore** — portable JSONL tarballs; backend-agnostic (SQLite ↔ Postgres); doubles as a DB migration path
|
|
74
|
+
- **Labels** — Avery-template item labels (spine / pocket) and patron cards (full / sticker) as PDFs. Spine templates: `avery-5167-spine` (½" narrow face), `avery-5160-spine` (1" medium face, rotated), `avery-5160` (flat wrap-around, centered text), `avery-22805`/`avery-22806` (square classification labels). Live in-page SVG preview updates as you change kind/template/fields — no PDF round-trip needed.
|
|
75
|
+
- **Curated lists** — librarian-editable named collections of Works ("Staff picks", "Summer reads") with slugs, per-work annotations, public/private and featured toggles; featured lists appear as a shelf on the OPAC landing page; public browse at `/ui/lists`
|
|
76
|
+
- **Auth** — five preset roles (ReadOnly, Patron, Librarian, SystemAdmin, Administrator) plus custom roles via the admin UI; JWT for API, cookie-based for web UI
|
|
77
|
+
- **Audit log** — synchronous trail of administrative mutations (Librarian + system tier); queryable via web UI, CLI, or REST
|
|
78
|
+
- **DB-editable settings** — most configuration knobs (library name, fines, kiosk timeout, SMTP, retention, configurable nav shortcuts, etc.) editable from the UI / CLI / API; env vars still win as a break-glass
|
|
79
|
+
- **Web UI** — HTMX + Jinja2 browser interface with catalog search, circulation desk (camera-based barcode scanning), patron self-service, light/dark/auto theme; per-user nav shortcut overrides via browser localStorage
|
|
80
|
+
- **REST API** — FastAPI; consumed by the web UI and available for integrations
|
|
81
|
+
- **CLI** — full librarian + sysadmin workflow without running a server, including stdin/stdout (`-`) for backup, import/export, and labels
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Install the CLI as a standalone tool (recommended for production)
|
|
87
|
+
uv tool install compendium-ils # SQLite only
|
|
88
|
+
uv tool install "compendium-ils[postgres]" # + Postgres support
|
|
89
|
+
|
|
90
|
+
# Or with pip
|
|
91
|
+
pip install compendium-ils
|
|
92
|
+
pip install "compendium-ils[postgres]"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The installed command is **`compendium`**.
|
|
96
|
+
|
|
97
|
+
## Quick start
|
|
98
|
+
|
|
99
|
+
### Prerequisites
|
|
100
|
+
|
|
101
|
+
Python 3.11.4 or newer. (Backup restore uses the tarfile `data` filter from
|
|
102
|
+
PEP 706, which was backported to 3.11.4.)
|
|
103
|
+
|
|
104
|
+
**Debian / Ubuntu**
|
|
105
|
+
```bash
|
|
106
|
+
sudo apt-get update
|
|
107
|
+
sudo apt-get install -y python3 python3-venv python3-dev build-essential
|
|
108
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
109
|
+
source $HOME/.local/bin/env
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**RedHat / CentOS / Fedora**
|
|
113
|
+
```bash
|
|
114
|
+
sudo dnf install -y python3 python3-devel gcc
|
|
115
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
116
|
+
source $HOME/.local/bin/env
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**macOS**
|
|
120
|
+
```bash
|
|
121
|
+
brew install python uv
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Run
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Install dependencies
|
|
128
|
+
uv sync --extra dev
|
|
129
|
+
|
|
130
|
+
# Initialise the database (creates ./compendium.db with SQLite by default)
|
|
131
|
+
uv run compendium db init
|
|
132
|
+
|
|
133
|
+
# Create an Administrator account
|
|
134
|
+
uv run compendium user add --username admin --role Administrator
|
|
135
|
+
|
|
136
|
+
# Add a book by ISBN (looks up metadata from Google Books when key is set, else Open Library)
|
|
137
|
+
uv run compendium item add --isbn 9780441013593
|
|
138
|
+
|
|
139
|
+
# Add a patron
|
|
140
|
+
uv run compendium patron add --name "Alice Example"
|
|
141
|
+
|
|
142
|
+
# Start the server (web UI at http://localhost:8000/ui/catalog)
|
|
143
|
+
uv run compendium serve
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Log in at `http://localhost:8000/ui/login` with the username and password you set above.
|
|
147
|
+
|
|
148
|
+
## CLI reference
|
|
149
|
+
|
|
150
|
+
Run `compendium --help` for the full command tree, or `compendium <group> --help` for the subcommands of a specific group.
|
|
151
|
+
|
|
152
|
+
**Catalog & cataloging**
|
|
153
|
+
| Group | Common subcommands |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `item` | `add` (--isbn / --upc / --mbid / --tmdb-id / --title), `add-manual`, `show`, `list`, `withdraw`, `set-loanable`; `note add/list/delete`; `declare-lost`, `mark-damaged`, `clear-lost`, `clear-damage` |
|
|
156
|
+
| `work` | `search`, `show`, `new-arrivals`, `recently-returned` |
|
|
157
|
+
| `creator` | `list`, `show`, `merge` |
|
|
158
|
+
| `curated-list` | `create`, `list`, `show`, `edit`, `delete`, `add-work`, `remove-work`, `reorder` |
|
|
159
|
+
| `branch` | `list`, `set` |
|
|
160
|
+
| `import` | `csv`, `marc` (use `-` for stdin) |
|
|
161
|
+
| `export` | `csv`, `marc` (use `-` for stdout) |
|
|
162
|
+
|
|
163
|
+
**Circulation**
|
|
164
|
+
| Group | Common subcommands |
|
|
165
|
+
|---|---|
|
|
166
|
+
| `loan` | `checkout`, `checkin`, `renew`, `active`, `list` (system-wide), `history`, `item-history` |
|
|
167
|
+
| `claim` | `returned` (patron disputes), `verify` (found), `write-off`, `list` |
|
|
168
|
+
| `hold` | `place`, `cancel`, `list`, `queue`, `suspend`, `resume`, `list-suspended` |
|
|
169
|
+
| `fine` | `list`, `pay`, `waive`, `assess`, `assess-overdue` |
|
|
170
|
+
|
|
171
|
+
**Patrons & accounts**
|
|
172
|
+
| Group | Common subcommands |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `patron` | `add`, `list` (`--include-inactive`), `set`, `link-user`, `unlink-user`, `deactivate`, `reactivate` |
|
|
175
|
+
| `patron-category` | `list`, `create`, `update`, `delete` |
|
|
176
|
+
| `user` | `add` (default --role Administrator), `list` (`--include-inactive`), `set-role`, `set-password`, `deactivate`, `reactivate` |
|
|
177
|
+
| `role` | `list`, `create`, `update`, `clone` |
|
|
178
|
+
| `policy` | `list`, `create`, `set` (configures fines, category-aware) |
|
|
179
|
+
|
|
180
|
+
**Reporting & labels**
|
|
181
|
+
| Group | Common subcommands |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `reports` | `checkouts`, `popular`, `dormant`, `overdues` (each supports `--format csv`) |
|
|
184
|
+
| `labels` | `templates`, `spine`, `pocket`, `barcode`, `patron-card`, `patron-sticker` (output `-o -` writes PDF to stdout; use `--show`/`--hide` for field toggles, e.g. `--show barcode` on spine) |
|
|
185
|
+
| `audit` | `list` (filters: `--entity`, `--id`, `--user-id`, `--limit`) |
|
|
186
|
+
|
|
187
|
+
**Operations**
|
|
188
|
+
| Command | Description |
|
|
189
|
+
|---|---|
|
|
190
|
+
| `compendium db init` / `db upgrade` / `db history` | Migrate the database |
|
|
191
|
+
| `compendium serve` | Start the API + web UI server |
|
|
192
|
+
| `compendium backup --output <path-or->` | Write a portable JSONL tarball backup |
|
|
193
|
+
| `compendium restore <path-or->` | Restore from a backup tarball (lenient — auto-migrates) |
|
|
194
|
+
| `compendium settings list/get/set/reset` | Inspect & edit DB-backed site settings |
|
|
195
|
+
| `compendium maintenance ...` | Cron-invoked tasks: `expire-holds`, `resume-expired-suspends`, `assess-overdue-fines`, `queue-due-soon-notices`, `queue-overdue-notices`, `send-queued-notifications`, `prune-notifications`, `prune-audit-log`, `deactivate-expired-patrons`, `prune-cover-cache`, `prune-metadata-cache`, `refresh-metadata` |
|
|
196
|
+
| `compendium metadata cache stats` | Show metadata cache row counts by adapter and TTL status |
|
|
197
|
+
| `compendium metadata cache clear` | Delete all metadata cache rows (audited) |
|
|
198
|
+
|
|
199
|
+
File-argument commands (`backup`, `restore`, `import`, `export`, `labels spine/pocket/barcode/patron-card/patron-sticker`) accept `-` for stdin/stdout. Status messages are routed to stderr in stdio mode so they don't corrupt binary output.
|
|
200
|
+
|
|
201
|
+
## Web UI
|
|
202
|
+
|
|
203
|
+
Start the server with `compendium serve` and open your browser to `http://localhost:8000/ui/catalog`.
|
|
204
|
+
|
|
205
|
+
"Audience" lists the *minimum* preset that can access each route. Administrator can access everything. SystemAdmin gets the System-tier rows; slimmed Librarian gets librarian-tier rows but not System-tier.
|
|
206
|
+
|
|
207
|
+
| URL | Audience | Description |
|
|
208
|
+
|---|---|---|
|
|
209
|
+
| `/ui/catalog` | Anyone | Search catalog with facets (media type, decade, availability); featured curated lists shelf |
|
|
210
|
+
| `/ui/catalog/{work_id}` | Anyone | Work detail, items, place-hold button |
|
|
211
|
+
| `/ui/lists` | Anyone | Public OPAC browse of public curated lists |
|
|
212
|
+
| `/ui/lists/{slug}` | Anyone | Public curated list detail with annotated works |
|
|
213
|
+
| `/ui/login` | Anyone | Login form |
|
|
214
|
+
| `/ui/me/loans` | Patron | Active loans with inline renew + "I returned this" claim |
|
|
215
|
+
| `/ui/me/holds` | Patron | Active holds with inline cancel + suspend/resume |
|
|
216
|
+
| `/ui/me/fines` | Patron | Outstanding and historical fines |
|
|
217
|
+
| `/ui/me/preferences` | Patron | Notification opt-out |
|
|
218
|
+
| `/ui/me/password` | Patron | Self-service password change |
|
|
219
|
+
| `/ui/circ` | Librarian | Circulation desk — checkout / checkin / renew |
|
|
220
|
+
| `/ui/kiosk` | Librarian | Self-checkout kiosk landing/session (patron card-number-only auth) |
|
|
221
|
+
| `/ui/items/new` | Librarian | Add item by ISBN / UPC / MBID / TMDb ID / title search (with barcode scanner) |
|
|
222
|
+
| `/ui/items/new/manual` | Librarian | Manually add an item not found in external sources |
|
|
223
|
+
| `/ui/items/{barcode}` | Librarian | Item detail, loan history, withdraw, set-loanable, lost/damaged/claims actions |
|
|
224
|
+
| `/ui/patrons` | Librarian | Patron list |
|
|
225
|
+
| `/ui/patrons/new` | Librarian | Create patron (with category + expiry) |
|
|
226
|
+
| `/ui/patrons/{card}` | Librarian | Patron detail with active loans, holds, link/unlink user, deactivate, reactivate |
|
|
227
|
+
| `/ui/patrons/{card}/loans` | Librarian | Patron loan history (active / returned / all) |
|
|
228
|
+
| `/ui/patrons/{card}/fines` | Librarian | Patron fines with pay/waive |
|
|
229
|
+
| `/ui/admin/loans` | Librarian | All active loans (system-wide) with overdue/due-soon filters |
|
|
230
|
+
| `/ui/admin/fines` | Librarian | All outstanding fines (system-wide) with running total |
|
|
231
|
+
| `/ui/admin/holds` | Librarian | All active holds with status/branch/work filters |
|
|
232
|
+
| `/ui/admin/claims` | Librarian | Outstanding claims-returned investigations |
|
|
233
|
+
| `/ui/admin/notifications` | Librarian | Notification log + manual retry |
|
|
234
|
+
| `/ui/admin/import` | Librarian | Bulk CSV / GoodReads CSV / LibraryThing TSV / MARC import |
|
|
235
|
+
| `/ui/admin/export` | Librarian | Bulk CSV/MARC export |
|
|
236
|
+
| `/ui/admin/patron-categories` | Librarian | Manage patron categories |
|
|
237
|
+
| `/ui/curated-lists` | Librarian | Curated list admin CRUD (`curatedlist.manage`) |
|
|
238
|
+
| `/ui/admin/settings/general` | Librarian | Library name, default theme, guest search |
|
|
239
|
+
| `/ui/admin/settings/circulation` | Librarian | Currency, fine thresholds, hold/overdue/due-soon defaults |
|
|
240
|
+
| `/ui/admin/settings/kiosk` | Librarian | Kiosk idle timeout |
|
|
241
|
+
| `/ui/policies` | Librarian | Loan policy list with inline edit |
|
|
242
|
+
| `/ui/policies/new` | Librarian | Create loan policy (per media type + patron category) |
|
|
243
|
+
| `/ui/branches` | Librarian | Branch list with classification scheme |
|
|
244
|
+
| `/ui/branches/{id}/edit` | Librarian | Edit a branch's default classification scheme |
|
|
245
|
+
| `/ui/reports` | Librarian | Reports landing — checkouts, popular, dormant, overdues |
|
|
246
|
+
| `/ui/labels` | Librarian | Generate item-label and patron-card PDFs (Avery templates) |
|
|
247
|
+
| `/ui/audit` | Librarian | Audit log viewer |
|
|
248
|
+
| `/ui/users` | SystemAdmin | User list |
|
|
249
|
+
| `/ui/users/new` | SystemAdmin | Create user |
|
|
250
|
+
| `/ui/users/{username}` | SystemAdmin | User detail — change role, deactivate, reactivate, reset password |
|
|
251
|
+
| `/ui/roles` | SystemAdmin | Role list |
|
|
252
|
+
| `/ui/roles/new` | SystemAdmin | Create custom role |
|
|
253
|
+
| `/ui/roles/{id}` | SystemAdmin | Role detail — edit permissions, clone |
|
|
254
|
+
| `/ui/admin/system/smtp` | SystemAdmin | SMTP host/port/from settings |
|
|
255
|
+
| `/ui/admin/system/secrets` | SystemAdmin | Encrypted secrets (SMTP password, TMDb / Google Books API keys) — requires `COMPENDIUM_SECRET_KEY` |
|
|
256
|
+
| `/ui/admin/system/retention` | SystemAdmin | Notification + audit retention, batch sizes |
|
|
257
|
+
|
|
258
|
+
Guest catalog search is enabled by default (toggle via `/ui/admin/settings/general` or `COMPENDIUM_GUEST_SEARCH_ENABLED=false`).
|
|
259
|
+
|
|
260
|
+
## REST API endpoints
|
|
261
|
+
|
|
262
|
+
The API is mounted at the root. Authenticate with `POST /auth/login` and pass the result as `Authorization: Bearer <token>`. Interactive docs live at `http://localhost:8000/docs` when the server is running.
|
|
263
|
+
|
|
264
|
+
Below is a high-level inventory grouped by concern; the OpenAPI document is the source of truth for parameters and bodies.
|
|
265
|
+
|
|
266
|
+
| Group | Common endpoints | Min permission |
|
|
267
|
+
|---|---|---|
|
|
268
|
+
| **Auth** | `POST /auth/login` | none |
|
|
269
|
+
| **Catalog** | `GET /works/search`, `/works/new-arrivals`, `/works/recently-returned`; `GET /items/{barcode}`; `POST /items/{barcode}/{withdraw,loanable,lost,damaged,clear-lost,clear-damage}` | guest / `item.view` / `item.delete` / `fine.manage` |
|
|
270
|
+
| **Patrons** | `GET/POST /patrons`, `PATCH /patrons/{card}`, `POST /patrons/{card}/{deactivate,reactivate}`, `POST /patrons/{card}/account`, `GET/POST /patron-categories`, `PATCH/DELETE /patron-categories/{id}` | `patron.manage` / `patron.account.manage` for account endpoints |
|
|
271
|
+
| **Loans** | `POST /loans/checkout`, `/loans/{id}/{checkin,renew}`; `GET /loans` (system-wide), `/loans/patron/{card}`, `/loans/item/{barcode}` | `loan.*` (see below) |
|
|
272
|
+
| **Claims** | `GET /claims`; `POST /claims/{barcode}/{returned,verify,write-off}` | `loan.checkin` |
|
|
273
|
+
| **Holds** | `GET /holds`, `/holds/queue/{work_id}`; `POST/DELETE /holds`, `/holds/{id}`; `POST /holds/{id}/{suspend,resume}` | `hold.*` |
|
|
274
|
+
| **Fines** | `GET /fines`, `GET/POST /patrons/{card}/fines`, `POST /fines/{id}/{pay,waive}`, `POST /patrons/{card}/fines/assess-overdue` | `fine.manage` / `fine.view.self` |
|
|
275
|
+
| **Self-service** | `GET /me/loans`, `/me/holds`; `POST /me/holds`, `/me/holds/{id}/{suspend,resume}`; `DELETE /me/holds/{id}`; `POST /me/loans/{id}/{renew,claim-returned}` | `*.self` permissions |
|
|
276
|
+
| **Notifications** | `GET /notifications`, `POST /notifications/{id}/retry` | `notification.manage` |
|
|
277
|
+
| **Reports** | `GET /reports/{checkouts,popular,dormant,overdues}` | `report.view` |
|
|
278
|
+
| **Bulk import/export** | `POST /import/{csv,goodreads,librarything,marc}` (multipart), `GET /export/{csv,marc}` (streaming) | `catalog.import` / `item.view` |
|
|
279
|
+
| **Labels** | `GET /labels/items`, `/labels/patrons` (PDF) | `labels.generate` |
|
|
280
|
+
| **Policies** | `GET/POST /policies` | `item.view` / `policy.edit` |
|
|
281
|
+
| **Users** | `POST /users`, `POST /users/{username}/{deactivate,reactivate}`, `POST/DELETE /users/{username}/patron` | `user.manage` |
|
|
282
|
+
| **Curated lists** | `GET/POST /curated-lists`, `PATCH/DELETE /curated-lists/{slug}`, `POST/DELETE /curated-lists/{slug}/works` | guest / `curatedlist.manage` |
|
|
283
|
+
| **Settings** | `GET /settings/`, `GET/PATCH/DELETE /settings/{key}` | `patron.manage` (librarian-tier) / `system.manage` (system-tier) |
|
|
284
|
+
| **Calendar** | `GET /library-hours/`, `PATCH /library-hours/{weekday}`; `GET/POST /closed-dates/`, `PATCH/DELETE /closed-dates/{id}` | `calendar.manage` |
|
|
285
|
+
| **Audit** | `GET /audit/` | `audit.view` |
|
|
286
|
+
|
|
287
|
+
The "min permission" column lists the lowest preset role that's allowed. Administrator (wildcard) covers everything. Slimmed Librarian covers librarian-tier endpoints; SystemAdmin covers user/role/system-tier endpoints.
|
|
288
|
+
|
|
289
|
+
## Configuration
|
|
290
|
+
|
|
291
|
+
Compendium settings come from three layers, in order of precedence:
|
|
292
|
+
|
|
293
|
+
1. **Environment variable** (`COMPENDIUM_<KEY>`) — break-glass override, wins over everything.
|
|
294
|
+
2. **`site_setting` table** — DB-backed, editable from `/ui/admin/settings/*`, the CLI (`compendium settings ...`), or the API (`PATCH /settings/{key}`). Changes take effect on the next page render — no restart.
|
|
295
|
+
3. **Registry default** — fallback hard-coded in `services/settings_registry.py`.
|
|
296
|
+
|
|
297
|
+
Most settings (library name, theme, fine thresholds, hold/overdue defaults, kiosk timeout, SMTP host/port/from, retention) are **DB-editable**. A handful stay env-only because they're either secrets or required before the DB is reachable:
|
|
298
|
+
|
|
299
|
+
| Env var | Why env-only |
|
|
300
|
+
|---|---|
|
|
301
|
+
| `COMPENDIUM_DATABASE_URL` | Bootstrap — needed before any DB read |
|
|
302
|
+
| `COMPENDIUM_JWT_SECRET_KEY` | Secret — **required**; server refuses to start with the built-in default. Generate with `python -c "import secrets; print(secrets.token_urlsafe(48))"`. Set `COMPENDIUM_ALLOW_INSECURE_JWT=1` to bypass for first-run/dev only. |
|
|
303
|
+
| `COMPENDIUM_JWT_ALGORITHM` / `COMPENDIUM_JWT_EXPIRE_MINUTES` | Auth deployment knobs |
|
|
304
|
+
| `COMPENDIUM_SSL_CERTFILE` / `COMPENDIUM_SSL_KEYFILE` | OS-level paths |
|
|
305
|
+
| `COMPENDIUM_SECURE_COOKIES` | Default `true`; set `false` only for plain-HTTP LAN deployments (browsers won't send `Secure` cookies over non-HTTPS, except localhost). |
|
|
306
|
+
| `COMPENDIUM_SECRET_KEY` | Encryption key for secrets stored in the DB (SMTP password, API keys). Optional — set to enable the `/ui/admin/system/secrets` page. Generate with `compendium keygen --secret`. |
|
|
307
|
+
| `COMPENDIUM_TMDB_API_KEY` | TMDb API key. Override — also settable via Admin → System → Secrets when `COMPENDIUM_SECRET_KEY` is configured. |
|
|
308
|
+
| `COMPENDIUM_GOOGLE_BOOKS_API_KEY` | Google Books API key (primary book metadata source when set). Override — also settable via Admin → System → Secrets when `COMPENDIUM_SECRET_KEY` is configured. |
|
|
309
|
+
| `COMPENDIUM_BOOK_METADATA_SOURCE_PREFERENCE` | Primary book metadata source: `googlebooks` (default; uses GB when key is set, else OL) or `openlibrary`. DB-editable at **Admin → System → Metadata Sources**. |
|
|
310
|
+
| `COMPENDIUM_BOOK_METADATA_FALLBACK_ENABLED` | When `true` (default), a miss from the primary book adapter automatically tries the secondary (GB→OL and OL→GB symmetric). Set `false` to disable. DB-editable at **Admin → System → Metadata Sources**. |
|
|
311
|
+
| `COMPENDIUM_METADATA_CACHE_TTL_DAYS` | Positive-hit TTL for the metadata cache in days (default 30). Negative (not-found) entries always expire after 24 hours. DB-editable at **Admin → System → Metadata Sources**. |
|
|
312
|
+
| `COMPENDIUM_SMTP_PASSWORD` | SMTP password. Override — also settable via Admin → System → Secrets when `COMPENDIUM_SECRET_KEY` is configured. |
|
|
313
|
+
| `COMPENDIUM_MAX_UPLOAD_BYTES` | Hard cap on bulk-import upload size (default 100 MB / 104857600). Env-only so a compromised admin token can't raise it. |
|
|
314
|
+
| `COMPENDIUM_LOGIN_MAX_FAILURES` | Max consecutive failed logins before the identity is throttled (default 10). DB-editable at **System → Security**. |
|
|
315
|
+
| `COMPENDIUM_LOGIN_FAILURE_WINDOW_SECONDS` | Sliding window for counting failures in seconds (default 300 = 5 min). DB-editable at **System → Security**. |
|
|
316
|
+
|
|
317
|
+
For everything else, run `compendium settings list` to see the current value, source (env vs db/default), and per-key help text. The web admin UI shows the same with an "⚠ Overridden by env var" indicator on rows where an env var is currently masking the DB value.
|
|
318
|
+
|
|
319
|
+
See [`docs/deployment.md`](docs/deployment.md) for full deployment guidance, or [`docker/README.md`](docker/README.md) for the bundled Docker Compose setup (app + Postgres + nginx with auto-generated self-signed TLS).
|
|
320
|
+
|
|
321
|
+
### PostgreSQL
|
|
322
|
+
|
|
323
|
+
SQLite is the default and fine for home or classroom use (up to ~10k items). For larger collections or multiple concurrent writers, use PostgreSQL:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
uv sync --extra postgres
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Then set:
|
|
330
|
+
|
|
331
|
+
```dotenv
|
|
332
|
+
COMPENDIUM_DATABASE_URL=postgresql+psycopg://compendium:<password>@localhost:5432/compendium
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
and run `compendium db init` to apply migrations. Full setup (creating the role and database, TLS, backups) is in [`docs/deployment.md`](docs/deployment.md#postgresql-setup).
|
|
336
|
+
|
|
337
|
+
## Scheduled maintenance
|
|
338
|
+
|
|
339
|
+
Several maintenance commands need to run on a cadence — most importantly the
|
|
340
|
+
email outbox drain (`send-queued-notifications`), without which queued
|
|
341
|
+
notifications never go out. Install the bundled crontab via:
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
scripts/install-cron.sh
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
By default this writes the project path into the crontab and points logs at
|
|
348
|
+
`$HOME/.local/state/compendium/maintenance.log`. Override either with flags:
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
scripts/install-cron.sh --project-dir /opt/compendium --log-file journal
|
|
352
|
+
scripts/install-cron.sh --log-file /var/log/compendium/maintenance.log
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`--log-file journal` routes output to the systemd journal (view with
|
|
356
|
+
`journalctl -t compendium-maintenance -f`). For paths the installer can't
|
|
357
|
+
create unprivileged (e.g. `/var/log/...`), it prints the one-time `sudo`
|
|
358
|
+
command and exits without touching the crontab.
|
|
359
|
+
|
|
360
|
+
See [`docs/crontab.sample`](docs/crontab.sample) for the full schedule and
|
|
361
|
+
[`docs/compendium.service.sample`](docs/compendium.service.sample) for
|
|
362
|
+
running the daemon under systemd.
|
|
363
|
+
|
|
364
|
+
## Running tests
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
uv run pytest -q
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Tests are split into `tests/unit/` (no DB, mock repos) and `tests/integration/` (SQLite in-memory).
|
|
371
|
+
|
|
372
|
+
### Browser tests (E2E)
|
|
373
|
+
|
|
374
|
+
Browser tests run against a real `compendium serve` subprocess in Chromium. They are excluded from the default `pytest` run and require a one-time install:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
uv sync --extra e2e
|
|
378
|
+
playwright install chromium
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Then:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
uv run pytest -m e2e
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Expected wall time: 30–60 seconds. Tests live in `tests/e2e/`. The `test_csp_no_console_errors.py` test is the keystone: it navigates to every major page and asserts no console errors, which catches CSP/HTMX loading regressions that unit tests miss.
|
|
388
|
+
|
|
389
|
+
## Layout
|
|
390
|
+
|
|
391
|
+
```
|
|
392
|
+
src/compendium/
|
|
393
|
+
├── domain/ # models, enums, permissions, errors
|
|
394
|
+
├── repositories/ # base protocols + SQLAlchemy implementations
|
|
395
|
+
├── services/ # business logic (catalog, circulation, holds, patrons, policies, auth, audit)
|
|
396
|
+
├── api/ # FastAPI routes + Pydantic schemas + JWT auth
|
|
397
|
+
├── web/ # HTMX + Jinja2 web UI + CSRF protection
|
|
398
|
+
├── cli/ # Typer CLI commands
|
|
399
|
+
├── config/ # settings, seed data
|
|
400
|
+
└── db/ # engine factory, session lifecycle
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## License
|
|
404
|
+
|
|
405
|
+
MIT (to be finalised before first release).
|