codex 1.7.3a2__py3-none-any.whl → 1.7.4__py3-none-any.whl
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.
Potentially problematic release.
This version of codex might be problematic. Click here for more details.
- codex/asgi.py +2 -1
- codex/choices.py +12 -12
- codex/choices_to_json.py +8 -8
- codex/db.py +2 -2
- codex/exceptions.py +6 -7
- codex/librarian/bookmark/bookmarkd.py +8 -4
- codex/librarian/bookmark/update.py +82 -48
- codex/librarian/covers/create.py +10 -8
- codex/librarian/covers/path.py +6 -5
- codex/librarian/covers/purge.py +2 -2
- codex/librarian/importer/aggregate.py +2 -2
- codex/librarian/importer/cache.py +1 -4
- codex/librarian/importer/create_comics.py +2 -2
- codex/librarian/importer/create_covers.py +1 -1
- codex/librarian/importer/create_fks.py +4 -3
- codex/librarian/importer/deleted.py +3 -3
- codex/librarian/importer/extract.py +56 -37
- codex/librarian/importer/failed_imports.py +1 -1
- codex/librarian/importer/importer.py +3 -1
- codex/librarian/importer/importerd.py +3 -3
- codex/librarian/importer/link_comics.py +2 -1
- codex/librarian/importer/moved.py +2 -7
- codex/librarian/importer/query_fks.py +1 -4
- codex/librarian/importer/tasks.py +1 -1
- codex/librarian/janitor/integrity.py +27 -20
- codex/librarian/janitor/janitor.py +1 -1
- codex/librarian/janitor/latest_version.py +1 -1
- codex/librarian/janitor/update.py +2 -2
- codex/librarian/janitor/vacuum.py +1 -1
- codex/librarian/librariand.py +3 -2
- codex/librarian/notifier/notifierd.py +2 -1
- codex/librarian/search/optimize.py +1 -1
- codex/librarian/search/remove.py +1 -1
- codex/librarian/search/update.py +4 -4
- codex/librarian/telemeter/stats.py +7 -6
- codex/librarian/telemeter/telemeter.py +0 -1
- codex/librarian/watchdog/db_snapshot.py +3 -3
- codex/librarian/watchdog/dir_snapshot_diff.py +3 -4
- codex/librarian/watchdog/emitter.py +4 -4
- codex/librarian/watchdog/event_batcherd.py +2 -1
- codex/librarian/watchdog/events.py +1 -2
- codex/librarian/watchdog/observers.py +15 -10
- codex/logger/logging.py +1 -1
- codex/memory.py +3 -2
- codex/migrations/0001_init.py +1 -2
- codex/migrations/0014_pdf_issue_suffix_remove_cover_image_sort_name.py +1 -2
- codex/migrations/0018_rename_userbookmark_bookmark.py +1 -2
- codex/migrations/0028_telemeter.py +1 -5
- codex/models/base.py +3 -1
- codex/models/bookmark.py +4 -7
- codex/models/comic.py +2 -2
- codex/models/functions.py +12 -5
- codex/models/groups.py +1 -1
- codex/models/library.py +1 -1
- codex/models/paths.py +4 -4
- codex/models/query.py +8 -4
- codex/models/util.py +1 -1
- codex/permissions.py +1 -1
- codex/run.py +1 -1
- codex/serializers/admin/libraries.py +1 -3
- codex/serializers/admin/stats.py +4 -4
- codex/serializers/auth.py +4 -2
- codex/serializers/browser/choices.py +87 -9
- codex/serializers/browser/filters.py +9 -37
- codex/serializers/browser/metadata.py +2 -1
- codex/serializers/browser/mixins.py +7 -3
- codex/serializers/browser/mtime.py +3 -5
- codex/serializers/browser/page.py +5 -4
- codex/serializers/browser/settings.py +7 -5
- codex/serializers/fields/__init__.py +37 -0
- codex/serializers/fields/auth.py +45 -0
- codex/serializers/fields/browser.py +85 -0
- codex/serializers/fields/group.py +13 -0
- codex/serializers/fields/reader.py +22 -0
- codex/serializers/fields/sanitized.py +13 -0
- codex/serializers/fields/session.py +15 -0
- codex/serializers/fields/stats.py +36 -0
- codex/serializers/fields/vuetify.py +62 -0
- codex/serializers/models/base.py +1 -1
- codex/serializers/models/comic.py +2 -2
- codex/serializers/models/groups.py +0 -2
- codex/serializers/models/named.py +1 -1
- codex/serializers/models/pycountry.py +1 -1
- codex/serializers/opds/authentication.py +4 -2
- codex/serializers/opds/urls.py +2 -1
- codex/serializers/opds/v1.py +4 -3
- codex/serializers/opds/v2/feed.py +2 -1
- codex/serializers/opds/v2/links.py +23 -21
- codex/serializers/opds/v2/metadata.py +4 -8
- codex/serializers/opds/v2/progression.py +5 -3
- codex/serializers/opds/v2/publication.py +16 -18
- codex/serializers/opds/v2/unused.py +15 -8
- codex/serializers/reader.py +16 -14
- codex/serializers/redirect.py +2 -1
- codex/serializers/route.py +6 -4
- codex/serializers/settings.py +5 -3
- codex/serializers/versions.py +2 -1
- codex/settings/settings.py +4 -3
- codex/signals/django_signals.py +1 -1
- codex/static_root/assets/{VCheckbox-FaT6MGfu.f6732060f734.js → VCheckbox-CtTi6T7d.87331fa7e77f.js} +1 -1
- codex/static_root/assets/VCheckbox-CtTi6T7d.87331fa7e77f.js.br +0 -0
- codex/static_root/assets/VCheckbox-CtTi6T7d.87331fa7e77f.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-FaT6MGfu.js → VCheckbox-CtTi6T7d.js} +1 -1
- codex/static_root/assets/VCheckbox-CtTi6T7d.js.br +0 -0
- codex/static_root/assets/VCheckbox-CtTi6T7d.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.847452d574cf.js → VCheckboxBtn-DQafuWTH.dada5e0634ef.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DQafuWTH.dada5e0634ef.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DQafuWTH.dada5e0634ef.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-CZ_P-qUM.js → VCheckboxBtn-DQafuWTH.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-DQafuWTH.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DQafuWTH.js.gz +0 -0
- codex/static_root/assets/{VCombobox-C8QLNlSl.6f431e19a453.js → VCombobox-C6TX3Dhp.e619423a6463.js} +1 -1
- codex/static_root/assets/VCombobox-C6TX3Dhp.e619423a6463.js.br +0 -0
- codex/static_root/assets/VCombobox-C6TX3Dhp.e619423a6463.js.gz +0 -0
- codex/static_root/assets/{VCombobox-C8QLNlSl.js → VCombobox-C6TX3Dhp.js} +1 -1
- codex/static_root/assets/VCombobox-C6TX3Dhp.js.br +0 -0
- codex/static_root/assets/VCombobox-C6TX3Dhp.js.gz +0 -0
- codex/static_root/assets/{VDialog-RVLeW7je.c0c7eef71eec.js → VDialog-CknWq2kd.b2d301ec6ac5.js} +1 -1
- codex/static_root/assets/VDialog-CknWq2kd.b2d301ec6ac5.js.br +0 -0
- codex/static_root/assets/VDialog-CknWq2kd.b2d301ec6ac5.js.gz +0 -0
- codex/static_root/assets/{VDialog-RVLeW7je.js → VDialog-CknWq2kd.js} +1 -1
- codex/static_root/assets/VDialog-CknWq2kd.js.br +0 -0
- codex/static_root/assets/VDialog-CknWq2kd.js.gz +0 -0
- codex/static_root/assets/{VDivider-D1Ot4vR2.9dbee744fb3c.js → VDivider-F4DUgr74.62bc92cab97d.js} +1 -1
- codex/static_root/assets/VDivider-F4DUgr74.62bc92cab97d.js.br +0 -0
- codex/static_root/assets/VDivider-F4DUgr74.62bc92cab97d.js.gz +0 -0
- codex/static_root/assets/{VDivider-D1Ot4vR2.js → VDivider-F4DUgr74.js} +1 -1
- codex/static_root/assets/VDivider-F4DUgr74.js.br +0 -0
- codex/static_root/assets/VDivider-F4DUgr74.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-DQilGHZk.43f06d0a45e0.js → VExpansionPanels-CprYUTGT.39b8a50cde43.js} +1 -1
- codex/static_root/assets/VExpansionPanels-CprYUTGT.39b8a50cde43.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CprYUTGT.39b8a50cde43.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-DQilGHZk.js → VExpansionPanels-CprYUTGT.js} +1 -1
- codex/static_root/assets/VExpansionPanels-CprYUTGT.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-CprYUTGT.js.gz +0 -0
- codex/static_root/assets/{VForm-DNmY-qFJ.81f3a5ff7ec3.js → VForm-DnWAUsrl.4ab0786d658e.js} +1 -1
- codex/static_root/assets/VForm-DnWAUsrl.4ab0786d658e.js.br +0 -0
- codex/static_root/assets/VForm-DnWAUsrl.4ab0786d658e.js.gz +0 -0
- codex/static_root/assets/{VForm-DNmY-qFJ.js → VForm-DnWAUsrl.js} +1 -1
- codex/static_root/assets/VForm-DnWAUsrl.js.br +0 -0
- codex/static_root/assets/VForm-DnWAUsrl.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DoayJpcS.fe5e0e74ffee.js → VRadioGroup-D71Rszum.befc87b0c476.js} +1 -1
- codex/static_root/assets/VRadioGroup-D71Rszum.befc87b0c476.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D71Rszum.befc87b0c476.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-DoayJpcS.js → VRadioGroup-D71Rszum.js} +1 -1
- codex/static_root/assets/VRadioGroup-D71Rszum.js.br +0 -0
- codex/static_root/assets/VRadioGroup-D71Rszum.js.gz +0 -0
- codex/static_root/assets/{VSelect-BSLmHX_P.9ab865ec0fad.js → VSelect-CbPDrHNj.4809fbbf00f0.js} +1 -1
- codex/static_root/assets/VSelect-CbPDrHNj.4809fbbf00f0.js.br +0 -0
- codex/static_root/assets/VSelect-CbPDrHNj.4809fbbf00f0.js.gz +0 -0
- codex/static_root/assets/{VSelect-BSLmHX_P.js → VSelect-CbPDrHNj.js} +1 -1
- codex/static_root/assets/VSelect-CbPDrHNj.js.br +0 -0
- codex/static_root/assets/VSelect-CbPDrHNj.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-TaKCeZgb.ad6c96efe57c.js → VSelectionControl-It389cbo.ac75aee480a0.js} +1 -1
- codex/static_root/assets/VSelectionControl-It389cbo.ac75aee480a0.js.br +0 -0
- codex/static_root/assets/VSelectionControl-It389cbo.ac75aee480a0.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-TaKCeZgb.js → VSelectionControl-It389cbo.js} +1 -1
- codex/static_root/assets/VSelectionControl-It389cbo.js.br +0 -0
- codex/static_root/assets/VSelectionControl-It389cbo.js.gz +0 -0
- codex/static_root/assets/{VTable-BfcOiEpa.77ef3973bb54.js → VTable-D9e3lKk6.7cd0760008c7.js} +1 -1
- codex/static_root/assets/VTable-D9e3lKk6.7cd0760008c7.js.br +0 -0
- codex/static_root/assets/VTable-D9e3lKk6.7cd0760008c7.js.gz +0 -0
- codex/static_root/assets/{VTable-BfcOiEpa.js → VTable-D9e3lKk6.js} +1 -1
- codex/static_root/assets/VTable-D9e3lKk6.js.br +0 -0
- codex/static_root/assets/VTable-D9e3lKk6.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-D8lWcbQY.e4c75ad78718.js → VWindowItem-B4VZ37cq.e45c5018cc73.js} +1 -1
- codex/static_root/assets/VWindowItem-B4VZ37cq.e45c5018cc73.js.br +0 -0
- codex/static_root/assets/VWindowItem-B4VZ37cq.e45c5018cc73.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-D8lWcbQY.js → VWindowItem-B4VZ37cq.js} +1 -1
- codex/static_root/assets/VWindowItem-B4VZ37cq.js.br +0 -0
- codex/static_root/assets/VWindowItem-B4VZ37cq.js.gz +0 -0
- codex/static_root/assets/{admin-CBLHOrM9.8c3c2268bc96.js → admin-Bkd8zuSF.2a29386f3488.js} +1 -1
- codex/static_root/assets/admin-Bkd8zuSF.2a29386f3488.js.br +0 -0
- codex/static_root/assets/admin-Bkd8zuSF.2a29386f3488.js.gz +0 -0
- codex/static_root/assets/{admin-CBLHOrM9.js → admin-Bkd8zuSF.js} +1 -1
- codex/static_root/assets/admin-Bkd8zuSF.js.br +0 -0
- codex/static_root/assets/admin-Bkd8zuSF.js.gz +0 -0
- codex/static_root/assets/{admin-80eqCqtz.d6deb9edb8cb.js → admin-CFTKGwmx.18915c5df4d3.js} +1 -1
- codex/static_root/assets/admin-CFTKGwmx.18915c5df4d3.js.br +0 -0
- codex/static_root/assets/admin-CFTKGwmx.18915c5df4d3.js.gz +0 -0
- codex/static_root/assets/{admin-80eqCqtz.js → admin-CFTKGwmx.js} +1 -1
- codex/static_root/assets/admin-CFTKGwmx.js.br +0 -0
- codex/static_root/assets/admin-CFTKGwmx.js.gz +0 -0
- codex/static_root/assets/{admin-menu-_aAGknKO.0c08c9bd2e7d.js → admin-menu-CxZqtPES.7b3a06ce4b92.js} +1 -1
- codex/static_root/assets/admin-menu-CxZqtPES.7b3a06ce4b92.js.br +0 -0
- codex/static_root/assets/admin-menu-CxZqtPES.7b3a06ce4b92.js.gz +0 -0
- codex/static_root/assets/{admin-menu-_aAGknKO.js → admin-menu-CxZqtPES.js} +1 -1
- codex/static_root/assets/admin-menu-CxZqtPES.js.br +0 -0
- codex/static_root/assets/admin-menu-CxZqtPES.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js → admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.8a9d411ee79d.js.gz +0 -0
- codex/static_root/assets/{admin-settings-button-progress-BqRFyhjf.js → admin-settings-button-progress-D2TfW_5T.js} +1 -1
- codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-D2TfW_5T.js.gz +0 -0
- codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js +1 -0
- codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js.br +0 -0
- codex/static_root/assets/browser-Br2DgAdi.433cca495d61.js.gz +0 -0
- codex/static_root/assets/browser-Br2DgAdi.js +1 -0
- codex/static_root/assets/browser-Br2DgAdi.js.br +0 -0
- codex/static_root/assets/browser-Br2DgAdi.js.gz +0 -0
- codex/static_root/assets/{browser-chTZ1CM4.521193ab0710.css → browser-DNzQvgkY.aea8b7d7ca40.css} +1 -1
- codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.br +0 -0
- codex/static_root/assets/browser-DNzQvgkY.aea8b7d7ca40.css.gz +0 -0
- codex/static_root/assets/{browser-chTZ1CM4.css → browser-DNzQvgkY.css} +1 -1
- codex/static_root/assets/browser-DNzQvgkY.css.br +0 -0
- codex/static_root/assets/browser-DNzQvgkY.css.gz +0 -0
- codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js → change-password-dialog-CSylx5Qb.319466c891c9.js} +1 -1
- codex/static_root/assets/change-password-dialog-CSylx5Qb.319466c891c9.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CSylx5Qb.319466c891c9.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-1ZVP_Q2w.js → change-password-dialog-CSylx5Qb.js} +1 -1
- codex/static_root/assets/change-password-dialog-CSylx5Qb.js.br +0 -0
- codex/static_root/assets/change-password-dialog-CSylx5Qb.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-DqCey9Iq.e315d7f8f752.js → confirm-dialog-Bx2R2vgR.d42b6449c495.js} +1 -1
- codex/static_root/assets/confirm-dialog-Bx2R2vgR.d42b6449c495.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Bx2R2vgR.d42b6449c495.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-DqCey9Iq.js → confirm-dialog-Bx2R2vgR.js} +1 -1
- codex/static_root/assets/confirm-dialog-Bx2R2vgR.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Bx2R2vgR.js.gz +0 -0
- codex/static_root/assets/{datetime-column-CBA8bYeO.17a31f189d66.js → datetime-column-sOrWMDcU.9ad225af18db.js} +1 -1
- codex/static_root/assets/datetime-column-sOrWMDcU.9ad225af18db.js.br +2 -0
- codex/static_root/assets/datetime-column-sOrWMDcU.9ad225af18db.js.gz +0 -0
- codex/static_root/assets/{datetime-column-CBA8bYeO.js → datetime-column-sOrWMDcU.js} +1 -1
- codex/static_root/assets/datetime-column-sOrWMDcU.js.br +2 -0
- codex/static_root/assets/datetime-column-sOrWMDcU.js.gz +0 -0
- codex/static_root/assets/{filter-mnO3lLPo.659442401b16.js → filter-SgjvoAvL.094069d6978d.js} +1 -1
- codex/static_root/assets/filter-SgjvoAvL.094069d6978d.js.br +0 -0
- codex/static_root/assets/filter-SgjvoAvL.094069d6978d.js.gz +0 -0
- codex/static_root/assets/{filter-mnO3lLPo.js → filter-SgjvoAvL.js} +1 -1
- codex/static_root/assets/filter-SgjvoAvL.js.br +0 -0
- codex/static_root/assets/filter-SgjvoAvL.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Bc677pNb.1b8a2f7c0dc3.js → flag-tab-B_0tGWPg.e46d1782b327.js} +1 -1
- codex/static_root/assets/flag-tab-B_0tGWPg.e46d1782b327.js.br +0 -0
- codex/static_root/assets/flag-tab-B_0tGWPg.e46d1782b327.js.gz +0 -0
- codex/static_root/assets/{flag-tab-Bc677pNb.js → flag-tab-B_0tGWPg.js} +1 -1
- codex/static_root/assets/flag-tab-B_0tGWPg.js.br +0 -0
- codex/static_root/assets/flag-tab-B_0tGWPg.js.gz +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css +1 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.br +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.0f57e109c01b.css.gz +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css +1 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css.br +0 -0
- codex/static_root/assets/flag-tab-D2MYXCHk.css.gz +0 -0
- codex/static_root/assets/{forwardRefs-DPRKg5wq.6f09e9d22fa6.js → forwardRefs-DqbPK1-k.bb9d6863ac16.js} +1 -1
- codex/static_root/assets/forwardRefs-DqbPK1-k.bb9d6863ac16.js.br +0 -0
- codex/static_root/assets/forwardRefs-DqbPK1-k.bb9d6863ac16.js.gz +0 -0
- codex/static_root/assets/{forwardRefs-DPRKg5wq.js → forwardRefs-DqbPK1-k.js} +1 -1
- codex/static_root/assets/forwardRefs-DqbPK1-k.js.br +0 -0
- codex/static_root/assets/forwardRefs-DqbPK1-k.js.gz +0 -0
- codex/static_root/assets/{group-tab-BVlBrMwo.1127b51be6e7.js → group-tab-DXxQUa6C.f1f12f39b167.js} +1 -1
- codex/static_root/assets/group-tab-DXxQUa6C.f1f12f39b167.js.br +0 -0
- codex/static_root/assets/group-tab-DXxQUa6C.f1f12f39b167.js.gz +0 -0
- codex/static_root/assets/{group-tab-BVlBrMwo.js → group-tab-DXxQUa6C.js} +1 -1
- codex/static_root/assets/group-tab-DXxQUa6C.js.br +0 -0
- codex/static_root/assets/group-tab-DXxQUa6C.js.gz +0 -0
- codex/static_root/assets/{http-error-C9-_aEBF.d29b164e80de.js → http-error-BVMwwrFo.a7be6a26c9eb.js} +1 -1
- codex/static_root/assets/http-error-BVMwwrFo.a7be6a26c9eb.js.br +0 -0
- codex/static_root/assets/http-error-BVMwwrFo.a7be6a26c9eb.js.gz +0 -0
- codex/static_root/assets/{http-error-C9-_aEBF.js → http-error-BVMwwrFo.js} +1 -1
- codex/static_root/assets/http-error-BVMwwrFo.js.br +0 -0
- codex/static_root/assets/http-error-BVMwwrFo.js.gz +0 -0
- codex/static_root/assets/{index-9nBCksDQ.4561d2608773.js → index-DK0lSHDP.86f18c078fa9.js} +1 -1
- codex/static_root/assets/index-DK0lSHDP.86f18c078fa9.js.br +0 -0
- codex/static_root/assets/index-DK0lSHDP.86f18c078fa9.js.gz +0 -0
- codex/static_root/assets/{index-9nBCksDQ.js → index-DK0lSHDP.js} +1 -1
- codex/static_root/assets/index-DK0lSHDP.js.br +0 -0
- codex/static_root/assets/index-DK0lSHDP.js.gz +0 -0
- codex/static_root/assets/{library-tab-BMe3dKzy.22b6e123ecfb.js → library-tab-NhXuvZ4F.1e8ac3b7c168.js} +1 -1
- codex/static_root/assets/library-tab-NhXuvZ4F.1e8ac3b7c168.js.br +0 -0
- codex/static_root/assets/library-tab-NhXuvZ4F.1e8ac3b7c168.js.gz +0 -0
- codex/static_root/assets/{library-tab-BMe3dKzy.js → library-tab-NhXuvZ4F.js} +1 -1
- codex/static_root/assets/library-tab-NhXuvZ4F.js.br +0 -0
- codex/static_root/assets/library-tab-NhXuvZ4F.js.gz +0 -0
- codex/static_root/assets/{main-BbNUiWEb.565b616eb122.js → main-CYgubU7H.50aa39cf9565.js} +3 -3
- codex/static_root/assets/main-CYgubU7H.50aa39cf9565.js.br +0 -0
- codex/static_root/assets/main-CYgubU7H.50aa39cf9565.js.gz +0 -0
- codex/static_root/assets/{main-BbNUiWEb.js → main-CYgubU7H.js} +3 -3
- codex/static_root/assets/main-CYgubU7H.js.br +0 -0
- codex/static_root/assets/main-CYgubU7H.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js +1 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js.br +0 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.a6269ab85085.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.js +1 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.js.br +0 -0
- codex/static_root/assets/pager-full-pdf---TZiAf5.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js → pagination-toolbar-CeWXU_qA.abe0805da1cc.js} +1 -1
- codex/static_root/assets/pagination-toolbar-CeWXU_qA.abe0805da1cc.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CeWXU_qA.abe0805da1cc.js.gz +0 -0
- codex/static_root/assets/{pagination-toolbar-Dx5JwWG-.js → pagination-toolbar-CeWXU_qA.js} +1 -1
- codex/static_root/assets/pagination-toolbar-CeWXU_qA.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-CeWXU_qA.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-6ZE_HmLT.10d1e16abc78.js → pdf-doc-Dws_otkd.d215eca27da5.js} +1 -1
- codex/static_root/assets/pdf-doc-Dws_otkd.d215eca27da5.js.br +0 -0
- codex/static_root/assets/pdf-doc-Dws_otkd.d215eca27da5.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-6ZE_HmLT.js → pdf-doc-Dws_otkd.js} +1 -1
- codex/static_root/assets/pdf-doc-Dws_otkd.js.br +0 -0
- codex/static_root/assets/pdf-doc-Dws_otkd.js.gz +0 -0
- codex/static_root/assets/{reader-DdPKDuh5.0c55cef1e98b.js → reader-BCxYgZHF.dde3c4ac39a5.js} +2 -2
- codex/static_root/assets/reader-BCxYgZHF.dde3c4ac39a5.js.br +0 -0
- codex/static_root/assets/reader-BCxYgZHF.dde3c4ac39a5.js.gz +0 -0
- codex/static_root/assets/{reader-DdPKDuh5.js → reader-BCxYgZHF.js} +2 -2
- codex/static_root/assets/reader-BCxYgZHF.js.br +0 -0
- codex/static_root/assets/reader-BCxYgZHF.js.gz +0 -0
- codex/static_root/assets/{relation-chips-C96hi5jm.js → relation-chips-DNRRCvrw.b1bc75fb9349.js} +1 -1
- codex/static_root/assets/relation-chips-DNRRCvrw.b1bc75fb9349.js.br +0 -0
- codex/static_root/assets/relation-chips-DNRRCvrw.b1bc75fb9349.js.gz +0 -0
- codex/static_root/assets/{relation-chips-C96hi5jm.9f07c9eb533a.js → relation-chips-DNRRCvrw.js} +1 -1
- codex/static_root/assets/relation-chips-DNRRCvrw.js.br +0 -0
- codex/static_root/assets/relation-chips-DNRRCvrw.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-CqbT1rid.js → settings-drawer-BuB29L5p.fe2eeea0058f.js} +2 -2
- codex/static_root/assets/settings-drawer-BuB29L5p.fe2eeea0058f.js.br +0 -0
- codex/static_root/assets/settings-drawer-BuB29L5p.fe2eeea0058f.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-CqbT1rid.5639fa00b301.js → settings-drawer-BuB29L5p.js} +2 -2
- codex/static_root/assets/settings-drawer-BuB29L5p.js.br +0 -0
- codex/static_root/assets/settings-drawer-BuB29L5p.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CkT2ZGUQ.b704fa46dc88.js → stats-tab-D0zymxU6.d5267113b51e.js} +1 -1
- codex/static_root/assets/stats-tab-D0zymxU6.d5267113b51e.js.br +0 -0
- codex/static_root/assets/stats-tab-D0zymxU6.d5267113b51e.js.gz +0 -0
- codex/static_root/assets/{stats-tab-CkT2ZGUQ.js → stats-tab-D0zymxU6.js} +1 -1
- codex/static_root/assets/stats-tab-D0zymxU6.js.br +0 -0
- codex/static_root/assets/stats-tab-D0zymxU6.js.gz +0 -0
- codex/static_root/assets/{task-tab-a-DcU3Tj.8c2f7bf3c4c9.js → task-tab-CI9RLdMS.120f395dc9c6.js} +1 -1
- codex/static_root/assets/task-tab-CI9RLdMS.120f395dc9c6.js.br +0 -0
- codex/static_root/assets/task-tab-CI9RLdMS.120f395dc9c6.js.gz +0 -0
- codex/static_root/assets/{task-tab-a-DcU3Tj.js → task-tab-CI9RLdMS.js} +1 -1
- codex/static_root/assets/task-tab-CI9RLdMS.js.br +0 -0
- codex/static_root/assets/task-tab-CI9RLdMS.js.gz +0 -0
- codex/static_root/assets/{unauthorized-BOl5YpL3.f3c4b52718d5.js → unauthorized-BNwg_Xvu.a894b96156e0.js} +1 -1
- codex/static_root/assets/unauthorized-BNwg_Xvu.a894b96156e0.js.br +0 -0
- codex/static_root/assets/unauthorized-BNwg_Xvu.a894b96156e0.js.gz +0 -0
- codex/static_root/assets/{unauthorized-BOl5YpL3.js → unauthorized-BNwg_Xvu.js} +1 -1
- codex/static_root/assets/unauthorized-BNwg_Xvu.js.br +0 -0
- codex/static_root/assets/unauthorized-BNwg_Xvu.js.gz +0 -0
- codex/static_root/assets/{user-tab-BBDZvD8G.aa4263a68b22.js → user-tab-w9LgIMMi.4a89c7b37486.js} +1 -1
- codex/static_root/assets/user-tab-w9LgIMMi.4a89c7b37486.js.br +0 -0
- codex/static_root/assets/user-tab-w9LgIMMi.4a89c7b37486.js.gz +0 -0
- codex/static_root/assets/{user-tab-BBDZvD8G.js → user-tab-w9LgIMMi.js} +1 -1
- codex/static_root/assets/user-tab-w9LgIMMi.js.br +0 -0
- codex/static_root/assets/user-tab-w9LgIMMi.js.gz +0 -0
- codex/static_root/{manifest.225989a17f49.json → manifest.514e63b9f805.json} +253 -253
- codex/static_root/manifest.514e63b9f805.json.br +0 -0
- codex/static_root/manifest.514e63b9f805.json.gz +0 -0
- codex/static_root/manifest.json +253 -253
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/status_controller.py +9 -12
- codex/urls/root.py +4 -3
- codex/views/admin/api_key.py +1 -4
- codex/views/admin/library.py +4 -4
- codex/views/admin/stats.py +10 -10
- codex/views/admin/tasks.py +9 -9
- codex/views/auth.py +3 -8
- codex/views/bookmark.py +3 -3
- codex/views/browser/annotate/card.py +2 -2
- codex/views/browser/annotate/order.py +6 -7
- codex/views/browser/bookmark.py +3 -1
- codex/views/browser/breadcrumbs.py +6 -5
- codex/views/browser/browser.py +2 -2
- codex/views/browser/choices.py +6 -7
- codex/views/browser/cover.py +6 -3
- codex/views/browser/download.py +2 -1
- codex/views/browser/filters/bookmark.py +1 -1
- codex/views/browser/filters/field.py +1 -1
- codex/views/browser/filters/filter.py +6 -8
- codex/views/browser/filters/group.py +8 -8
- codex/views/browser/filters/search/field/expression.py +1 -2
- codex/views/browser/filters/search/field/optimize.py +1 -1
- codex/views/browser/filters/search/field/parse.py +2 -2
- codex/views/browser/filters/search/parse.py +4 -19
- codex/views/browser/group_mtime.py +1 -1
- codex/views/browser/metadata/copy_intersections.py +1 -1
- codex/views/browser/metadata/metadata.py +1 -1
- codex/views/browser/mtime.py +1 -1
- codex/views/browser/order_by.py +3 -5
- codex/views/browser/params.py +1 -1
- codex/views/browser/settings.py +1 -1
- codex/views/browser/validate.py +8 -5
- codex/views/const.py +0 -3
- codex/views/frontend.py +1 -1
- codex/views/mixins.py +3 -2
- codex/views/opds/authentication_v1.py +1 -1
- codex/views/opds/const.py +4 -4
- codex/views/opds/util.py +3 -3
- codex/views/opds/v1/entry/entry.py +4 -2
- codex/views/opds/v1/entry/links.py +4 -5
- codex/views/opds/v1/facets.py +8 -4
- codex/views/opds/v1/feed.py +5 -5
- codex/views/opds/v1/links.py +11 -6
- codex/views/opds/v2/const.py +5 -2
- codex/views/opds/v2/feed.py +3 -3
- codex/views/opds/v2/progression.py +8 -9
- codex/views/opds/v2/publications.py +10 -20
- codex/views/public.py +1 -1
- codex/views/reader/books.py +3 -2
- codex/views/reader/page.py +3 -3
- codex/views/reader/params.py +2 -2
- codex/views/reader/reader.py +6 -6
- codex/views/reader/settings.py +1 -1
- codex/views/session.py +8 -2
- codex/views/settings.py +2 -2
- codex/views/timezone.py +2 -2
- codex/views/util.py +10 -9
- codex/views/version.py +1 -1
- {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/METADATA +3 -1
- {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/RECORD +409 -401
- {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/WHEEL +1 -1
- codex/serializers/fields.py +0 -183
- codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.br +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.f6732060f734.js.gz +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.js.br +0 -0
- codex/static_root/assets/VCheckbox-FaT6MGfu.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.847452d574cf.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-CZ_P-qUM.js.gz +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.br +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.6f431e19a453.js.gz +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.js.br +0 -0
- codex/static_root/assets/VCombobox-C8QLNlSl.js.gz +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.br +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.c0c7eef71eec.js.gz +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.js.br +0 -0
- codex/static_root/assets/VDialog-RVLeW7je.js.gz +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.br +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.9dbee744fb3c.js.gz +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.js.br +0 -0
- codex/static_root/assets/VDivider-D1Ot4vR2.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.43f06d0a45e0.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DQilGHZk.js.gz +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.br +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.81f3a5ff7ec3.js.gz +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.js.br +0 -0
- codex/static_root/assets/VForm-DNmY-qFJ.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.fe5e0e74ffee.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DoayJpcS.js.gz +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.br +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.9ab865ec0fad.js.gz +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.js.br +0 -0
- codex/static_root/assets/VSelect-BSLmHX_P.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.ad6c96efe57c.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.js.br +0 -0
- codex/static_root/assets/VSelectionControl-TaKCeZgb.js.gz +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.br +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.77ef3973bb54.js.gz +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.js.br +0 -0
- codex/static_root/assets/VTable-BfcOiEpa.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.br +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.e4c75ad78718.js.gz +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.js.br +0 -0
- codex/static_root/assets/VWindowItem-D8lWcbQY.js.gz +0 -0
- codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.br +0 -0
- codex/static_root/assets/admin-80eqCqtz.d6deb9edb8cb.js.gz +0 -0
- codex/static_root/assets/admin-80eqCqtz.js.br +0 -0
- codex/static_root/assets/admin-80eqCqtz.js.gz +0 -0
- codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.br +0 -0
- codex/static_root/assets/admin-CBLHOrM9.8c3c2268bc96.js.gz +0 -0
- codex/static_root/assets/admin-CBLHOrM9.js.br +0 -0
- codex/static_root/assets/admin-CBLHOrM9.js.gz +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.br +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.0c08c9bd2e7d.js.gz +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.js.br +0 -0
- codex/static_root/assets/admin-menu-_aAGknKO.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.2bb1194b21e2.js.gz +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.br +0 -0
- codex/static_root/assets/admin-settings-button-progress-BqRFyhjf.js.gz +0 -0
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js +0 -1
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.br +0 -0
- codex/static_root/assets/browser-DAprXYuP.8bb92965dd9d.js.gz +0 -0
- codex/static_root/assets/browser-DAprXYuP.js +0 -1
- codex/static_root/assets/browser-DAprXYuP.js.br +0 -0
- codex/static_root/assets/browser-DAprXYuP.js.gz +0 -0
- codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.br +0 -0
- codex/static_root/assets/browser-chTZ1CM4.521193ab0710.css.gz +0 -0
- codex/static_root/assets/browser-chTZ1CM4.css.br +0 -0
- codex/static_root/assets/browser-chTZ1CM4.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.ae0d9e5bd42b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.br +0 -0
- codex/static_root/assets/change-password-dialog-1ZVP_Q2w.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.e315d7f8f752.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DqCey9Iq.js.gz +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.br +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.17a31f189d66.js.gz +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.js.br +0 -0
- codex/static_root/assets/datetime-column-CBA8bYeO.js.gz +0 -0
- codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.br +0 -0
- codex/static_root/assets/filter-mnO3lLPo.659442401b16.js.gz +0 -0
- codex/static_root/assets/filter-mnO3lLPo.js.br +0 -0
- codex/static_root/assets/filter-mnO3lLPo.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.br +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.1b8a2f7c0dc3.js.gz +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.js.br +0 -0
- codex/static_root/assets/flag-tab-Bc677pNb.js.gz +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css +0 -1
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.br +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.be8eed14384e.css.gz +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.css +0 -1
- codex/static_root/assets/flag-tab-glFvEgEl.css.br +0 -0
- codex/static_root/assets/flag-tab-glFvEgEl.css.gz +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.br +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.6f09e9d22fa6.js.gz +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.js.br +0 -0
- codex/static_root/assets/forwardRefs-DPRKg5wq.js.gz +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.br +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.1127b51be6e7.js.gz +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.js.br +0 -0
- codex/static_root/assets/group-tab-BVlBrMwo.js.gz +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.br +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.d29b164e80de.js.gz +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.js.br +0 -0
- codex/static_root/assets/http-error-C9-_aEBF.js.gz +0 -0
- codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.br +0 -0
- codex/static_root/assets/index-9nBCksDQ.4561d2608773.js.gz +0 -0
- codex/static_root/assets/index-9nBCksDQ.js.br +0 -0
- codex/static_root/assets/index-9nBCksDQ.js.gz +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.br +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.22b6e123ecfb.js.gz +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.js.br +0 -0
- codex/static_root/assets/library-tab-BMe3dKzy.js.gz +0 -0
- codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.br +0 -0
- codex/static_root/assets/main-BbNUiWEb.565b616eb122.js.gz +0 -0
- codex/static_root/assets/main-BbNUiWEb.js.br +0 -0
- codex/static_root/assets/main-BbNUiWEb.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js +0 -1
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.6d4ec47c14f7.js.gz +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js +0 -1
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.br +0 -0
- codex/static_root/assets/pager-full-pdf-JwAUlsNI.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.ba0e8b645dfa.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Dx5JwWG-.js.gz +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.br +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.10d1e16abc78.js.gz +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.js.br +0 -0
- codex/static_root/assets/pdf-doc-6ZE_HmLT.js.gz +0 -0
- codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.br +0 -0
- codex/static_root/assets/reader-DdPKDuh5.0c55cef1e98b.js.gz +0 -0
- codex/static_root/assets/reader-DdPKDuh5.js.br +0 -0
- codex/static_root/assets/reader-DdPKDuh5.js.gz +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.br +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.9f07c9eb533a.js.gz +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.js.br +0 -0
- codex/static_root/assets/relation-chips-C96hi5jm.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.br +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.5639fa00b301.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.js.br +0 -0
- codex/static_root/assets/settings-drawer-CqbT1rid.js.gz +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.br +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.b704fa46dc88.js.gz +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.js.br +0 -0
- codex/static_root/assets/stats-tab-CkT2ZGUQ.js.gz +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.br +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.8c2f7bf3c4c9.js.gz +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.js.br +0 -0
- codex/static_root/assets/task-tab-a-DcU3Tj.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.br +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.f3c4b52718d5.js.gz +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.js.br +0 -0
- codex/static_root/assets/unauthorized-BOl5YpL3.js.gz +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.br +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.aa4263a68b22.js.gz +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.js.br +0 -0
- codex/static_root/assets/user-tab-BBDZvD8G.js.gz +0 -0
- codex/static_root/manifest.225989a17f49.json.br +0 -0
- codex/static_root/manifest.225989a17f49.json.gz +0 -0
- {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/LICENSE +0 -0
- {codex-1.7.3a2.dist-info → codex-1.7.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,68 +1,40 @@
|
|
|
1
1
|
"""Browser Settings Filter Serializers."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.serializers import
|
|
3
|
+
from rest_framework.serializers import ListField, Serializer
|
|
4
4
|
|
|
5
5
|
from codex.serializers.fields import (
|
|
6
|
-
BookmarkFilterField,
|
|
7
6
|
VuetifyBooleanField,
|
|
8
|
-
VuetifyCharField,
|
|
9
7
|
VuetifyDecadeField,
|
|
10
8
|
VuetifyFloatField,
|
|
11
9
|
VuetifyIntegerField,
|
|
12
10
|
)
|
|
11
|
+
from codex.serializers.fields.browser import BookmarkFilterField
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
class
|
|
16
|
-
"""All dynamic filters."""
|
|
17
|
-
|
|
18
|
-
age_rating = BooleanField(read_only=True)
|
|
19
|
-
community_rating = BooleanField(read_only=True)
|
|
20
|
-
characters = BooleanField(read_only=True)
|
|
21
|
-
country = BooleanField(read_only=True)
|
|
22
|
-
critical_rating = BooleanField(read_only=True)
|
|
23
|
-
contributors = BooleanField(read_only=True)
|
|
24
|
-
decade = BooleanField(read_only=True)
|
|
25
|
-
genres = BooleanField(read_only=True)
|
|
26
|
-
file_type = BooleanField(read_only=True)
|
|
27
|
-
identifier_type = BooleanField(read_only=True)
|
|
28
|
-
monochrome = BooleanField(read_only=True)
|
|
29
|
-
language = BooleanField(read_only=True)
|
|
30
|
-
locations = BooleanField(read_only=True)
|
|
31
|
-
original_format = BooleanField(read_only=True)
|
|
32
|
-
reading_direction = BooleanField(read_only=True)
|
|
33
|
-
series_groups = BooleanField(read_only=True)
|
|
34
|
-
stories = BooleanField(read_only=True)
|
|
35
|
-
story_arcs = BooleanField(read_only=True)
|
|
36
|
-
tagger = BooleanField(read_only=True)
|
|
37
|
-
tags = BooleanField(read_only=True)
|
|
38
|
-
teams = BooleanField(read_only=True)
|
|
39
|
-
year = BooleanField(read_only=True)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class BrowserSettingsFilterSerializer(Serializer):
|
|
14
|
+
class BrowserSettingsFilterInputSerializer(Serializer):
|
|
43
15
|
"""Filter values for settings."""
|
|
44
16
|
|
|
45
17
|
bookmark = BookmarkFilterField(required=False)
|
|
46
18
|
# Dynamic filters
|
|
47
|
-
age_rating = ListField(child=
|
|
19
|
+
age_rating = ListField(child=VuetifyIntegerField(), required=False)
|
|
48
20
|
characters = ListField(child=VuetifyIntegerField(), required=False)
|
|
49
21
|
community_rating = ListField(child=VuetifyFloatField(), required=False)
|
|
50
22
|
country = ListField(child=VuetifyIntegerField(), required=False)
|
|
51
23
|
contributors = ListField(child=VuetifyIntegerField(), required=False)
|
|
52
24
|
critical_rating = ListField(child=VuetifyFloatField(), required=False)
|
|
53
25
|
decade = ListField(child=VuetifyDecadeField(), required=False)
|
|
54
|
-
file_type = ListField(child=
|
|
26
|
+
file_type = ListField(child=VuetifyIntegerField(), required=False)
|
|
55
27
|
genres = ListField(child=VuetifyIntegerField(), required=False)
|
|
56
|
-
identifier_type = ListField(child=
|
|
28
|
+
identifier_type = ListField(child=VuetifyIntegerField(), required=False)
|
|
57
29
|
language = ListField(child=VuetifyIntegerField(), required=False)
|
|
58
30
|
locations = ListField(child=VuetifyIntegerField(), required=False)
|
|
59
31
|
monochrome = ListField(child=VuetifyBooleanField(), required=False)
|
|
60
|
-
original_format = ListField(child=
|
|
61
|
-
reading_direction = ListField(child=
|
|
32
|
+
original_format = ListField(child=VuetifyIntegerField(), required=False)
|
|
33
|
+
reading_direction = ListField(child=VuetifyIntegerField(), required=False)
|
|
62
34
|
series_groups = ListField(child=VuetifyIntegerField(), required=False)
|
|
63
35
|
stories = ListField(child=VuetifyIntegerField(), required=False)
|
|
64
36
|
story_arcs = ListField(child=VuetifyIntegerField(), required=False)
|
|
65
|
-
tagger = ListField(child=
|
|
37
|
+
tagger = ListField(child=VuetifyIntegerField(), required=False)
|
|
66
38
|
tags = ListField(child=VuetifyIntegerField(), required=False)
|
|
67
39
|
teams = ListField(child=VuetifyIntegerField(), required=False)
|
|
68
40
|
year = ListField(child=VuetifyIntegerField(), required=False)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Codex Serializers for the metadata box."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.
|
|
3
|
+
from rest_framework.fields import CharField
|
|
4
|
+
from rest_framework.serializers import IntegerField, ListField, Serializer
|
|
4
5
|
|
|
5
6
|
from codex.serializers.browser.mixins import BrowserAggregateSerializerMixin
|
|
6
7
|
from codex.serializers.models.comic import ComicSerializer
|
|
@@ -5,7 +5,6 @@ from itertools import chain
|
|
|
5
5
|
|
|
6
6
|
from rest_framework.serializers import (
|
|
7
7
|
BooleanField,
|
|
8
|
-
CharField,
|
|
9
8
|
DecimalField,
|
|
10
9
|
IntegerField,
|
|
11
10
|
ListField,
|
|
@@ -14,6 +13,7 @@ from rest_framework.serializers import (
|
|
|
14
13
|
)
|
|
15
14
|
|
|
16
15
|
from codex.logger.logging import get_logger
|
|
16
|
+
from codex.serializers.fields.browser import TopGroupField
|
|
17
17
|
from codex.util import max_none
|
|
18
18
|
from codex.views.const import EPOCH_START
|
|
19
19
|
|
|
@@ -23,7 +23,7 @@ LOG = get_logger(__name__)
|
|
|
23
23
|
class BrowserAggregateSerializerMixin(Serializer):
|
|
24
24
|
"""Mixin for browser, opds & metadata serializers."""
|
|
25
25
|
|
|
26
|
-
group =
|
|
26
|
+
group = TopGroupField(read_only=True)
|
|
27
27
|
ids = ListField(child=IntegerField(), read_only=True)
|
|
28
28
|
|
|
29
29
|
# Aggregate Annotations
|
|
@@ -64,5 +64,9 @@ class BrowserAggregateSerializerMixin(Serializer):
|
|
|
64
64
|
)
|
|
65
65
|
mtime = self._get_max_updated_at(EPOCH_START, updated_ats)
|
|
66
66
|
if obj.bmua_is_max:
|
|
67
|
-
mtime: datetime = max_none(
|
|
67
|
+
mtime: datetime | None = max_none(
|
|
68
|
+
mtime, obj.bookmark_updated_at, EPOCH_START
|
|
69
|
+
)
|
|
70
|
+
if mtime is None:
|
|
71
|
+
return 0
|
|
68
72
|
return int(mtime.timestamp() * 1000)
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
"""Group Mtimes."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.serializers import
|
|
3
|
+
from rest_framework.serializers import Serializer
|
|
4
4
|
|
|
5
|
-
from codex.serializers.browser.settings import
|
|
5
|
+
from codex.serializers.browser.settings import BrowserFilterChoicesInputSerilalizer
|
|
6
6
|
from codex.serializers.fields import TimestampField
|
|
7
7
|
from codex.serializers.route import SimpleRouteSerializer
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class GroupsMtimeSerializer(
|
|
10
|
+
class GroupsMtimeSerializer(BrowserFilterChoicesInputSerilalizer):
|
|
11
11
|
"""Groups Mtimes."""
|
|
12
12
|
|
|
13
|
-
filters = BrowserSettingsFilterSerializer(required=False)
|
|
14
|
-
q = CharField(allow_blank=True, required=False)
|
|
15
13
|
groups = SimpleRouteSerializer(many=True, required=True)
|
|
16
14
|
|
|
17
15
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"""Browser Page Serializer."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.
|
|
3
|
+
from rest_framework.fields import (
|
|
4
4
|
BooleanField,
|
|
5
5
|
CharField,
|
|
6
6
|
DecimalField,
|
|
7
7
|
IntegerField,
|
|
8
|
-
Serializer,
|
|
9
8
|
)
|
|
9
|
+
from rest_framework.serializers import Serializer
|
|
10
10
|
|
|
11
11
|
from codex.serializers.browser.mixins import (
|
|
12
12
|
BrowserAggregateSerializerMixin,
|
|
13
13
|
)
|
|
14
|
-
from codex.serializers.fields import
|
|
14
|
+
from codex.serializers.fields import TimestampField
|
|
15
|
+
from codex.serializers.fields.browser import BreadcrumbsField, TopGroupField
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class BrowserCardSerializer(BrowserAggregateSerializerMixin):
|
|
@@ -57,7 +58,7 @@ class BrowserPageSerializer(Serializer):
|
|
|
57
58
|
title = BrowserTitleSerializer(read_only=True)
|
|
58
59
|
zero_pad = IntegerField(read_only=True)
|
|
59
60
|
libraries_exist = BooleanField(read_only=True)
|
|
60
|
-
model_group =
|
|
61
|
+
model_group = TopGroupField(read_only=True)
|
|
61
62
|
num_pages = IntegerField(read_only=True)
|
|
62
63
|
groups = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
63
64
|
books = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
@@ -9,8 +9,9 @@ from rest_framework.serializers import (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
from codex.choices import BROWSER_ORDER_BY_CHOICES
|
|
12
|
-
from codex.serializers.browser.filters import
|
|
13
|
-
from codex.serializers.fields import
|
|
12
|
+
from codex.serializers.browser.filters import BrowserSettingsFilterInputSerializer
|
|
13
|
+
from codex.serializers.fields import TimestampField
|
|
14
|
+
from codex.serializers.fields.browser import BreadcrumbsField, TopGroupField
|
|
14
15
|
from codex.serializers.route import SimpleRouteSerializer
|
|
15
16
|
|
|
16
17
|
|
|
@@ -26,7 +27,8 @@ class BrowserSettingsShowGroupFlagsSerializer(Serializer):
|
|
|
26
27
|
class BrowserFilterChoicesInputSerilalizer(Serializer):
|
|
27
28
|
"""Browser Settings for the filter choices response."""
|
|
28
29
|
|
|
29
|
-
filters =
|
|
30
|
+
filters = BrowserSettingsFilterInputSerializer(required=False)
|
|
31
|
+
# NOT Sanitized because so complex.
|
|
30
32
|
q = CharField(allow_blank=True, required=False)
|
|
31
33
|
|
|
32
34
|
|
|
@@ -51,7 +53,6 @@ class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase):
|
|
|
51
53
|
class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase):
|
|
52
54
|
"""Base Serializer for Browser & OPDS Settings."""
|
|
53
55
|
|
|
54
|
-
# search_results_limit = IntegerField(required=False)
|
|
55
56
|
top_group = TopGroupField(required=False)
|
|
56
57
|
|
|
57
58
|
|
|
@@ -64,7 +65,8 @@ class OPDSSettingsSerializer(BrowserSettingsSerializerBase):
|
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
class BrowserSettingsSerializer(BrowserSettingsSerializerBase):
|
|
67
|
-
"""
|
|
68
|
+
"""
|
|
69
|
+
Browser Settings that the user can change.
|
|
68
70
|
|
|
69
71
|
This is the only browse serializer that's submitted.
|
|
70
72
|
"""
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Custom Serializer Fields."""
|
|
2
|
+
|
|
3
|
+
from codex.serializers.fields.auth import TimestampField, TimezoneField
|
|
4
|
+
from codex.serializers.fields.group import BrowseGroupField
|
|
5
|
+
from codex.serializers.fields.reader import FitToField, ReadingDirectionField
|
|
6
|
+
from codex.serializers.fields.sanitized import SanitizedCharField
|
|
7
|
+
from codex.serializers.fields.session import SessionKeyField
|
|
8
|
+
from codex.serializers.fields.stats import (
|
|
9
|
+
CountDictField,
|
|
10
|
+
SerializerChoicesField,
|
|
11
|
+
StringListMultipleChoiceField,
|
|
12
|
+
)
|
|
13
|
+
from codex.serializers.fields.vuetify import (
|
|
14
|
+
VuetifyBooleanField,
|
|
15
|
+
VuetifyCharField,
|
|
16
|
+
VuetifyDecadeField,
|
|
17
|
+
VuetifyFloatField,
|
|
18
|
+
VuetifyIntegerField,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
__all__ = (
|
|
22
|
+
"TimestampField",
|
|
23
|
+
"TimezoneField",
|
|
24
|
+
"BrowseGroupField",
|
|
25
|
+
"SanitizedCharField",
|
|
26
|
+
"StringListMultipleChoiceField",
|
|
27
|
+
"SerializerChoicesField",
|
|
28
|
+
"CountDictField",
|
|
29
|
+
"VuetifyFloatField",
|
|
30
|
+
"VuetifyIntegerField",
|
|
31
|
+
"VuetifyCharField",
|
|
32
|
+
"VuetifyBooleanField",
|
|
33
|
+
"VuetifyDecadeField",
|
|
34
|
+
"FitToField",
|
|
35
|
+
"ReadingDirectionField",
|
|
36
|
+
"SessionKeyField",
|
|
37
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Custom fields."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
|
5
|
+
|
|
6
|
+
from rest_framework.exceptions import ValidationError
|
|
7
|
+
from rest_framework.serializers import (
|
|
8
|
+
CharField,
|
|
9
|
+
IntegerField,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from codex.logger.logging import get_logger
|
|
13
|
+
|
|
14
|
+
LOG = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TimestampField(IntegerField):
|
|
18
|
+
"""IntegerTimestampField."""
|
|
19
|
+
|
|
20
|
+
def to_representation(self, value) -> int:
|
|
21
|
+
"""Convert to Jascript millisecond int timestamp from datetime, or castable."""
|
|
22
|
+
if isinstance(value, datetime):
|
|
23
|
+
value = value.timestamp()
|
|
24
|
+
return int(float(value) * 1000)
|
|
25
|
+
|
|
26
|
+
def to_internal_value(self, data) -> datetime: # type: ignore[reportIncompatibleMethodOverride]
|
|
27
|
+
"""Convert from castable, likely string to datetime."""
|
|
28
|
+
return datetime.fromtimestamp(float(data) / 1000, tz=timezone.utc)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def validate_timezone(data):
|
|
32
|
+
"""Validate Timezone."""
|
|
33
|
+
try:
|
|
34
|
+
ZoneInfo(data)
|
|
35
|
+
except ZoneInfoNotFoundError as exc:
|
|
36
|
+
raise ValidationError from exc
|
|
37
|
+
return data
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TimezoneField(CharField):
|
|
41
|
+
"""Timezone field."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, *args, **kwargs):
|
|
44
|
+
"""Call Charfield with defaults."""
|
|
45
|
+
super().__init__(*args, min_length=2, validators=[validate_timezone], **kwargs)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Custom fields."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC
|
|
4
|
+
|
|
5
|
+
import pycountry
|
|
6
|
+
from rest_framework.serializers import (
|
|
7
|
+
ChoiceField,
|
|
8
|
+
ListField,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from codex.choices import (
|
|
12
|
+
BROWSER_BOOKMARK_FILTER_CHOICES,
|
|
13
|
+
BROWSER_TOP_GROUP_CHOICES,
|
|
14
|
+
DUMMY_NULL_NAME,
|
|
15
|
+
)
|
|
16
|
+
from codex.logger.logging import get_logger
|
|
17
|
+
from codex.serializers.fields.sanitized import SanitizedCharField
|
|
18
|
+
from codex.serializers.route import RouteSerializer
|
|
19
|
+
|
|
20
|
+
LOG = get_logger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TopGroupField(ChoiceField):
|
|
24
|
+
"""Valid Top Groups Only."""
|
|
25
|
+
|
|
26
|
+
class_choices = tuple(BROWSER_TOP_GROUP_CHOICES.keys())
|
|
27
|
+
|
|
28
|
+
def __init__(self, *args, **kwargs):
|
|
29
|
+
"""Initialize with choices."""
|
|
30
|
+
super().__init__(*args, choices=self.class_choices, **kwargs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BookmarkFilterField(ChoiceField):
|
|
34
|
+
"""Bookmark Choice Field."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, *args, **kwargs):
|
|
37
|
+
"""Use bookmark filter choices."""
|
|
38
|
+
super().__init__(
|
|
39
|
+
*args, choices=tuple(BROWSER_BOOKMARK_FILTER_CHOICES.keys()), **kwargs
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PyCountryField(SanitizedCharField, ABC):
|
|
44
|
+
"""Serialize to a long pycountry name."""
|
|
45
|
+
|
|
46
|
+
LOOKUP_MODULE = pycountry.countries
|
|
47
|
+
_ALPHA_2_LEN = 2
|
|
48
|
+
|
|
49
|
+
def to_representation(self, value):
|
|
50
|
+
"""Lookup the name with pycountry, just copy the value on fail."""
|
|
51
|
+
if not value:
|
|
52
|
+
return ""
|
|
53
|
+
if value == DUMMY_NULL_NAME:
|
|
54
|
+
return value
|
|
55
|
+
try:
|
|
56
|
+
# fix for https://github.com/flyingcircusio/pycountry/issues/41
|
|
57
|
+
lookup_obj = (
|
|
58
|
+
self.LOOKUP_MODULE.get(alpha_2=value)
|
|
59
|
+
if len(value) == self._ALPHA_2_LEN
|
|
60
|
+
else self.LOOKUP_MODULE.lookup(value)
|
|
61
|
+
)
|
|
62
|
+
# If lookup fails, return the key as the name
|
|
63
|
+
except Exception:
|
|
64
|
+
LOG.warning(f"Could not serialize name with pycountry {value}")
|
|
65
|
+
return value
|
|
66
|
+
else:
|
|
67
|
+
return lookup_obj.name if lookup_obj else value
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class CountryField(PyCountryField):
|
|
71
|
+
"""Serializer to long country name."""
|
|
72
|
+
|
|
73
|
+
LOOKUP_MODULE = pycountry.countries
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class LanguageField(PyCountryField):
|
|
77
|
+
"""Serializer to long language name."""
|
|
78
|
+
|
|
79
|
+
LOOKUP_MODULE = pycountry.languages
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class BreadcrumbsField(ListField):
|
|
83
|
+
"""And Array of Routes."""
|
|
84
|
+
|
|
85
|
+
child = RouteSerializer()
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Browser Group Field."""
|
|
2
|
+
|
|
3
|
+
from rest_framework.fields import ChoiceField
|
|
4
|
+
|
|
5
|
+
from codex.choices import BROWSER_GROUP_CHOICES
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BrowseGroupField(ChoiceField):
|
|
9
|
+
"""BrowseGroup Field."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, **kwargs):
|
|
12
|
+
"""Add choices."""
|
|
13
|
+
super().__init__(*args, choices=tuple(BROWSER_GROUP_CHOICES.keys()), **kwargs)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Reader Fields."""
|
|
2
|
+
|
|
3
|
+
from rest_framework.serializers import ChoiceField
|
|
4
|
+
|
|
5
|
+
from codex.models import Bookmark
|
|
6
|
+
from codex.models.comic import ReadingDirection
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FitToField(ChoiceField):
|
|
10
|
+
"""Bookmark FitTo Fieild."""
|
|
11
|
+
|
|
12
|
+
def __init__(self, *args, **kwargs):
|
|
13
|
+
"""Add Choices."""
|
|
14
|
+
super().__init__(*args, choices=Bookmark.FitTo.values, **kwargs)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ReadingDirectionField(ChoiceField):
|
|
18
|
+
"""Reading Direction Field."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, *args, **kwargs):
|
|
21
|
+
"""Add Choices."""
|
|
22
|
+
super().__init__(*args, choices=ReadingDirection.values, **kwargs)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Sanitied Fields."""
|
|
2
|
+
|
|
3
|
+
from nh3 import clean
|
|
4
|
+
from rest_framework.fields import CharField
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SanitizedCharField(CharField):
|
|
8
|
+
"""Sanitize CharField using NH3."""
|
|
9
|
+
|
|
10
|
+
def to_internal_value(self, data):
|
|
11
|
+
"""Sanitize CharField using NH3."""
|
|
12
|
+
sanitized_data = clean(data)
|
|
13
|
+
return super().to_internal_value(sanitized_data)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Session Custom Fields."""
|
|
2
|
+
|
|
3
|
+
from rest_framework.fields import ChoiceField
|
|
4
|
+
|
|
5
|
+
from codex.choices import BROWSER_DEFAULTS, READER_DEFAULTS
|
|
6
|
+
|
|
7
|
+
_SESSION_KEYS = (*READER_DEFAULTS.keys(), *BROWSER_DEFAULTS.keys(), "filters")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class SessionKeyField(ChoiceField):
|
|
11
|
+
"""Session Key Field."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, *args, **kwargs):
|
|
14
|
+
"""Add choices."""
|
|
15
|
+
super().__init__(*args, choices=_SESSION_KEYS, **kwargs)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Custom Vuetify fields."""
|
|
2
|
+
|
|
3
|
+
from rest_framework.fields import DictField, IntegerField
|
|
4
|
+
from rest_framework.serializers import MultipleChoiceField
|
|
5
|
+
|
|
6
|
+
from codex.logger.logging import get_logger
|
|
7
|
+
|
|
8
|
+
LOG = get_logger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StringListMultipleChoiceField(MultipleChoiceField):
|
|
12
|
+
"""A Multiple Choice Field expressed as as a comma delimited string."""
|
|
13
|
+
|
|
14
|
+
def to_internal_value(self, data):
|
|
15
|
+
"""Convert comma delimited strings to sets."""
|
|
16
|
+
if isinstance(data, str):
|
|
17
|
+
data = frozenset(data.split(","))
|
|
18
|
+
return super().to_internal_value(data) # type: ignore[reportIncompatibleMethodOverride]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SerializerChoicesField(StringListMultipleChoiceField):
|
|
22
|
+
"""A String List Multiple Choice Field limited to a specified serializer's fields."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, *args, serializer=None, **kwargs):
|
|
25
|
+
"""Limit choices to fields from serializers."""
|
|
26
|
+
if not serializer:
|
|
27
|
+
reason = "serializer required for this field."
|
|
28
|
+
raise ValueError(reason)
|
|
29
|
+
choices = serializer().get_fields().keys()
|
|
30
|
+
super().__init__(*args, choices=choices, **kwargs)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CountDictField(DictField):
|
|
34
|
+
"""Dict for counting things."""
|
|
35
|
+
|
|
36
|
+
child = IntegerField(read_only=True)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Custom Vuetify fields."""
|
|
2
|
+
|
|
3
|
+
from django.utils.translation import gettext_lazy as _
|
|
4
|
+
from rest_framework.exceptions import ValidationError
|
|
5
|
+
from rest_framework.serializers import (
|
|
6
|
+
BooleanField,
|
|
7
|
+
CharField,
|
|
8
|
+
FloatField,
|
|
9
|
+
IntegerField,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
from codex.choices import VUETIFY_NULL_CODE
|
|
13
|
+
from codex.logger.logging import get_logger
|
|
14
|
+
|
|
15
|
+
LOG = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class VuetifyNullCodeFieldMixin:
|
|
19
|
+
"""Convert Vuetify null codes to None."""
|
|
20
|
+
|
|
21
|
+
NULL_CODE = VUETIFY_NULL_CODE
|
|
22
|
+
|
|
23
|
+
def to_internal_value(self, data):
|
|
24
|
+
"""Convert numeric null code to None."""
|
|
25
|
+
return None if data == self.NULL_CODE else data
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class VuetifyFloatField(VuetifyNullCodeFieldMixin, FloatField): # type: ignore[reportIncompatibleMethodOverride]
|
|
29
|
+
"""Float Field with null code conversion."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VuetifyIntegerField(VuetifyNullCodeFieldMixin, IntegerField): # type: ignore[reportIncompatibleMethodOverride]
|
|
33
|
+
"""Integer Field with null code conversion."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class VuetifyCharField(VuetifyNullCodeFieldMixin, CharField): # type: ignore[reportIncompatibleMethodOverride]
|
|
37
|
+
"""Char Field with null code conversion."""
|
|
38
|
+
|
|
39
|
+
NULL_CODE = str(VUETIFY_NULL_CODE)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class VuetifyBooleanField(VuetifyNullCodeFieldMixin, BooleanField): # type: ignore[reportIncompatibleMethodOverride]
|
|
43
|
+
"""Boolean Field with null code conversion."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def validate_decade(decade):
|
|
47
|
+
"""Validate decades."""
|
|
48
|
+
# * We don't need a whole db call just to be perfectly accurate
|
|
49
|
+
# * -1s are decoded back into None before validation
|
|
50
|
+
if decade is not None and decade % 10 != 0:
|
|
51
|
+
raise ValidationError(_("Invalid decade: ") + f"{decade}")
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class VuetifyDecadeField(VuetifyIntegerField):
|
|
56
|
+
"""Integer field with null code conversion and decade validation."""
|
|
57
|
+
|
|
58
|
+
VALIDATORS = (validate_decade,)
|
|
59
|
+
|
|
60
|
+
def __init__(self, *args, **kwargs):
|
|
61
|
+
"""Use decade validator."""
|
|
62
|
+
super().__init__(*args, validators=self.VALIDATORS, **kwargs)
|
codex/serializers/models/base.py
CHANGED
|
@@ -6,7 +6,7 @@ from rest_framework.serializers import ModelSerializer, SerializerMetaclass
|
|
|
6
6
|
class BaseModelSerializer(ModelSerializer):
|
|
7
7
|
"""BaseModel Serializer for inheritance."""
|
|
8
8
|
|
|
9
|
-
class Meta(SerializerMetaclass): # type: ignore
|
|
9
|
+
class Meta(SerializerMetaclass): # type: ignore[reportIncompatibleVariableOverride]
|
|
10
10
|
"""Use explicit metaclass instead of python 3 method."""
|
|
11
11
|
|
|
12
12
|
abstract = True
|
|
@@ -38,8 +38,8 @@ class ComicSerializer(BaseModelSerializer):
|
|
|
38
38
|
pk = IntegerField(source="id")
|
|
39
39
|
|
|
40
40
|
# Annotations
|
|
41
|
-
# issue_count = IntegerField(allow_null=True)
|
|
42
|
-
# volume_count = IntegerField(allow_null=True)
|
|
41
|
+
# issue_count = IntegerField(allow_null=True) could be 1
|
|
42
|
+
# volume_count = IntegerField(allow_null=True) could be 1
|
|
43
43
|
|
|
44
44
|
# Group FKs
|
|
45
45
|
publisher = PublisherSerializer(allow_null=True)
|
|
@@ -43,7 +43,6 @@ class SeriesSerializer(GroupModelSerializer):
|
|
|
43
43
|
"""Configure model."""
|
|
44
44
|
|
|
45
45
|
model = Series
|
|
46
|
-
# fields = (*NamedModelMeta.fields, "volume_count")
|
|
47
46
|
|
|
48
47
|
|
|
49
48
|
class VolumeSerializer(GroupModelSerializer):
|
|
@@ -53,4 +52,3 @@ class VolumeSerializer(GroupModelSerializer):
|
|
|
53
52
|
"""Configure model."""
|
|
54
53
|
|
|
55
54
|
model = Volume
|
|
56
|
-
# fields = (*NamedModelMeta.fields, "issue_count")
|
|
@@ -5,7 +5,7 @@ from codex.models import (
|
|
|
5
5
|
Country,
|
|
6
6
|
Language,
|
|
7
7
|
)
|
|
8
|
-
from codex.serializers.fields import CountryField, LanguageField
|
|
8
|
+
from codex.serializers.fields.browser import CountryField, LanguageField
|
|
9
9
|
from codex.serializers.models.named import NamedModelSerializer
|
|
10
10
|
|
|
11
11
|
LOG = get_logger(__name__)
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
OPDS Authentication 1.0 Serializer.
|
|
2
3
|
|
|
3
4
|
https://drafts.opds.io/schema/authentication.schema.json
|
|
4
5
|
"""
|
|
5
6
|
|
|
6
|
-
from rest_framework.
|
|
7
|
+
from rest_framework.fields import CharField
|
|
8
|
+
from rest_framework.serializers import IntegerField, Serializer
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class OPDSAuth1LinksSerializer(Serializer):
|
codex/serializers/opds/urls.py
CHANGED
codex/serializers/opds/v1.py
CHANGED
|
@@ -2,16 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
from zoneinfo import ZoneInfo
|
|
4
4
|
|
|
5
|
-
from rest_framework.
|
|
5
|
+
from rest_framework.fields import (
|
|
6
6
|
BooleanField,
|
|
7
7
|
CharField,
|
|
8
8
|
DateField,
|
|
9
9
|
DateTimeField,
|
|
10
10
|
DictField,
|
|
11
11
|
IntegerField,
|
|
12
|
-
Serializer,
|
|
13
12
|
)
|
|
13
|
+
from rest_framework.serializers import Serializer
|
|
14
14
|
|
|
15
|
+
from codex.serializers.fields.browser import TopGroupField
|
|
15
16
|
from codex.serializers.models.pycountry import LanguageSerializer
|
|
16
17
|
|
|
17
18
|
UTC_TZ = ZoneInfo("UTC")
|
|
@@ -25,7 +26,7 @@ class OPDS1TemplateLinkSerializer(Serializer):
|
|
|
25
26
|
mime_type = CharField(read_only=True)
|
|
26
27
|
title = CharField(read_only=True, required=False)
|
|
27
28
|
length = IntegerField(read_only=True, required=False)
|
|
28
|
-
facet_group =
|
|
29
|
+
facet_group = TopGroupField(read_only=True, required=False)
|
|
29
30
|
facet_active = BooleanField(read_only=True, required=False)
|
|
30
31
|
thr_count = IntegerField(read_only=True, required=False)
|
|
31
32
|
pse_count = IntegerField(read_only=True, required=False)
|