codex 1.6.0__py3-none-any.whl → 1.6.0a0__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/exceptions.py +18 -22
- codex/integrity.py +3 -66
- codex/librarian/covers/coverd.py +2 -2
- codex/librarian/covers/create.py +29 -50
- codex/librarian/covers/path.py +6 -8
- codex/librarian/covers/purge.py +18 -28
- codex/librarian/covers/tasks.py +2 -3
- codex/librarian/importer/{aggregate.py → aggregate_metadata.py} +88 -58
- codex/librarian/importer/{extract.py → clean_metadata.py} +4 -33
- codex/librarian/importer/const.py +13 -46
- codex/librarian/importer/create_comics.py +17 -101
- codex/librarian/importer/create_fks.py +134 -99
- codex/librarian/importer/deleted.py +28 -103
- codex/librarian/importer/failed_imports.py +64 -42
- codex/librarian/importer/importerd.py +290 -71
- codex/librarian/importer/link_comics.py +26 -26
- codex/librarian/importer/moved.py +81 -128
- codex/librarian/importer/query_fks.py +122 -142
- codex/librarian/importer/status.py +39 -8
- codex/librarian/importer/tasks.py +9 -26
- codex/librarian/importer/update_comics.py +89 -0
- codex/librarian/janitor/cleanup.py +2 -26
- codex/librarian/janitor/failed_imports.py +1 -1
- codex/librarian/janitor/janitor.py +1 -13
- codex/librarian/janitor/janitord.py +1 -1
- codex/librarian/janitor/status.py +0 -1
- codex/librarian/janitor/tasks.py +0 -5
- codex/librarian/librariand.py +5 -9
- codex/librarian/notifier/notifierd.py +1 -1
- codex/librarian/notifier/tasks.py +4 -4
- codex/librarian/search/update.py +1 -1
- codex/librarian/watchdog/db_snapshot.py +6 -9
- codex/librarian/watchdog/emitter.py +0 -3
- codex/librarian/watchdog/event_batcherd.py +3 -19
- codex/librarian/watchdog/events.py +36 -176
- codex/librarian/watchdog/observers.py +3 -13
- codex/middleware.py +2 -1
- codex/migrations/0027_sort_name.py +149 -0
- codex/models/admin.py +2 -0
- codex/models/base.py +0 -3
- codex/models/comic.py +9 -13
- codex/models/{util.py → const.py} +2 -15
- codex/models/groups.py +14 -28
- codex/models/library.py +3 -11
- codex/models/paths.py +14 -61
- codex/permissions.py +1 -1
- codex/search/backend_search.py +8 -5
- codex/search/query.py +2 -25
- codex/search/writing.py +1 -1
- codex/serializers/admin.py +44 -53
- codex/serializers/browser/choices.py +42 -37
- codex/serializers/browser/filters.py +131 -32
- codex/serializers/browser/metadata.py +1 -45
- codex/serializers/browser/mixins.py +4 -37
- codex/serializers/browser/page.py +6 -3
- codex/serializers/browser/settings.py +11 -42
- codex/serializers/choices.py +1 -14
- codex/serializers/models/pycountry.py +51 -11
- codex/serializers/opds/v2.py +16 -10
- codex/serializers/opds/v2_unused.py +3 -5
- codex/serializers/reader.py +7 -21
- codex/serializers/redirect.py +2 -3
- codex/serializers/route.py +7 -16
- codex/settings/settings.py +8 -23
- codex/startup.py +3 -77
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js +1 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.br +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.da711f8b6a15.js.gz +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js +1 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js.br +0 -0
- codex/static_root/assets/VCheckbox-Aq_ImMNC.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js → VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.54f4d7f48bbf.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-DxNOKONG.js → VCheckboxBtn-D3izCBa7.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D3izCBa7.js.gz +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js +1 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.br +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.55622a0cd4ab.js.gz +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js +1 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js.br +0 -0
- codex/static_root/assets/VCombobox-DLL1EsSN.js.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.1c801ad756b2.css → VDialog-C5Nxd_zP.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.css.gz +0 -0
- codex/static_root/assets/{VDialog-lQ13FnRR.css → VDialog-C5Nxd_zP.d327f505ef0e.css} +1 -1
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.br +0 -0
- codex/static_root/assets/VDialog-C5Nxd_zP.d327f505ef0e.css.gz +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js +1 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.br +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.e8dba78ac8db.js.gz +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js +1 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js.br +0 -0
- codex/static_root/assets/VDialog-yDKzaGEj.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.d733b7906e00.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js +1 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-Cpo8QLLq.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js +1 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.4a609e74e507.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js +1 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DEezv8bL.js.gz +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js +1 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.br +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.8871035f5634.js.gz +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.js +1 -0
- codex/static_root/assets/VSelect-BKXcujPy.js.br +0 -0
- codex/static_root/assets/VSelect-BKXcujPy.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js +1 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.402a7ad84a8e.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js +1 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js.br +0 -0
- codex/static_root/assets/VSelectionControl-DJAPiluk.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js +1 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.8c9e31362dc5.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js +1 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C-kO93Va.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.f0f2106b158b.js → VTable-9vsIQObs.0a135029e924.js} +1 -1
- codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.br +0 -0
- codex/static_root/assets/VTable-9vsIQObs.0a135029e924.js.gz +0 -0
- codex/static_root/assets/{VTable-2RHFqfg6.js → VTable-9vsIQObs.js} +1 -1
- codex/static_root/assets/VTable-9vsIQObs.js.br +0 -0
- codex/static_root/assets/VTable-9vsIQObs.js.gz +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js +1 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.br +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.0de01739bbf9.js.gz +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js +1 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js.br +0 -0
- codex/static_root/assets/VTextField-DI9HAmWh.js.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.580110ad24dc.css.gz +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css +1 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.br +0 -0
- codex/static_root/assets/VTextField-DuIIp-VY.css.gz +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js +1 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.br +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.017d99c27b7b.js.gz +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js +1 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js.br +0 -0
- codex/static_root/assets/VWindowItem-DpnYkF3y.js.gz +0 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js +1 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.br +0 -0
- codex/static_root/assets/admin-2BTkfSQA.e7ef29cb106d.js.gz +0 -0
- codex/static_root/assets/admin-2BTkfSQA.js +1 -0
- codex/static_root/assets/admin-2BTkfSQA.js.br +0 -0
- codex/static_root/assets/admin-2BTkfSQA.js.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.82c98714bcf1.css → admin-BbrOU9Y9.29936748af9e.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.29936748af9e.css.gz +0 -0
- codex/static_root/assets/{admin-B8Bp2Uih.css → admin-BbrOU9Y9.css} +1 -1
- codex/static_root/assets/admin-BbrOU9Y9.css.br +0 -0
- codex/static_root/assets/admin-BbrOU9Y9.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.1514926c78e8.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css +1 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF0dMvEE.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js +30 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.c172dbae9463.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js +30 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mtdde7vY.js.gz +0 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js +1 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.br +0 -0
- codex/static_root/assets/browser-BA-AwUn-.23bb4b2f6a29.js.gz +0 -0
- codex/static_root/assets/browser-BA-AwUn-.js +1 -0
- codex/static_root/assets/browser-BA-AwUn-.js.br +0 -0
- codex/static_root/assets/browser-BA-AwUn-.js.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.ccfca9a05bfe.css.gz +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css +1 -0
- codex/static_root/assets/browser-CZeVqRNy.css.br +0 -0
- codex/static_root/assets/browser-CZeVqRNy.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js +1 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.br +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.49aa6df6a91d.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js +1 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js.br +0 -0
- codex/static_root/assets/change-password-dialog-C8YONrId.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.73be0c8a8278.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css +1 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.br +0 -0
- codex/static_root/assets/change-password-dialog-HvIvmmIE.css.gz +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js +1 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.e11d8efef3fb.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js +1 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js.br +0 -0
- codex/static_root/assets/confirm-dialog-Clg-o2G-.js.gz +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js +1 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.br +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.9fd36bef6fdd.js.gz +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js +1 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js.br +0 -0
- codex/static_root/assets/datetime-column-DejrPZxl.js.gz +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.1fa7c5cbead7.css.br +0 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css +1 -0
- codex/static_root/assets/datetime-column-Dg_RxTVM.css.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js +1 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.4d79a276d806.js.gz +0 -0
- codex/static_root/assets/filter-DHo57nTp.js +1 -0
- codex/static_root/assets/filter-DHo57nTp.js.br +0 -0
- codex/static_root/assets/filter-DHo57nTp.js.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.8deba6a5370d.css.gz +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css +1 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.br +0 -0
- codex/static_root/assets/flag-tab-BacZLlVi.css.gz +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js +1 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.br +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.125739911c45.js.gz +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js +1 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js.br +0 -0
- codex/static_root/assets/flag-tab-CagMLGSE.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.js → group-tab-BTAjhv32.ac196780d0a9.js} +1 -1
- codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.br +0 -0
- codex/static_root/assets/group-tab-BTAjhv32.ac196780d0a9.js.gz +0 -0
- codex/static_root/assets/{group-tab-D5vWrihT.1afb2ce453ff.js → group-tab-BTAjhv32.js} +1 -1
- codex/static_root/assets/group-tab-BTAjhv32.js.br +0 -0
- codex/static_root/assets/group-tab-BTAjhv32.js.gz +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js +1 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.br +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.55d56ce2b9b4.js.gz +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.js +1 -0
- codex/static_root/assets/http-error-DaHEsKbT.js.br +0 -0
- codex/static_root/assets/http-error-DaHEsKbT.js.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.95e42988d5d5.css.br → http-error-_wlDqEcp.5b78d3e332c9.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.5b78d3e332c9.css.gz +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css +1 -0
- codex/static_root/assets/{http-error-DKulXuTl.css.br → http-error-_wlDqEcp.css.br} +0 -0
- codex/static_root/assets/http-error-_wlDqEcp.css.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.36d50c91b2c1.js.gz +0 -0
- codex/static_root/assets/index-DUjeNg4x.js +1 -0
- codex/static_root/assets/index-DUjeNg4x.js.br +0 -0
- codex/static_root/assets/index-DUjeNg4x.js.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.954f2850a439.css.gz +0 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css +1 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.br +2 -0
- codex/static_root/assets/library-tab-CXbkxNZt.css.gz +0 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js +1 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.br +0 -0
- codex/static_root/assets/library-tab-Cud4639b.7f5a50aaabe8.js.gz +0 -0
- codex/static_root/assets/library-tab-Cud4639b.js +1 -0
- codex/static_root/assets/library-tab-Cud4639b.js.br +0 -0
- codex/static_root/assets/library-tab-Cud4639b.js.gz +0 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js +32 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.br +0 -0
- codex/static_root/assets/main-BnzL_Byv.22f98ee91990.js.gz +0 -0
- codex/static_root/assets/main-BnzL_Byv.js +32 -0
- codex/static_root/assets/main-BnzL_Byv.js.br +0 -0
- codex/static_root/assets/main-BnzL_Byv.js.gz +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css → main-sK7HC5h4.6f04ddfc5f1a.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.6f04ddfc5f1a.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.d46d718fac45.css.gz → main-sK7HC5h4.6f04ddfc5f1a.css.gz} +0 -0
- codex/static_root/assets/{main-Ztp855PW.css → main-sK7HC5h4.css} +1 -1
- codex/static_root/assets/main-sK7HC5h4.css.br +0 -0
- codex/static_root/assets/{main-Ztp855PW.css.gz → main-sK7HC5h4.css.gz} +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.7695fafdefce.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css +1 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-B3wS4_7p.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js +1 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.b71277bd7a2d.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js +1 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-Covpd-k8.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js +35 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.br +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.1cef3bf498be.js.gz +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js +35 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js.br +0 -0
- codex/static_root/assets/pdf-doc-CD2lnB32.js.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.925a6b05bfcf.css.gz +0 -0
- codex/static_root/assets/reader-Dspy46e8.css +1 -0
- codex/static_root/assets/reader-Dspy46e8.css.br +0 -0
- codex/static_root/assets/reader-Dspy46e8.css.gz +0 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js +2 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.br +0 -0
- codex/static_root/assets/reader-L85y7IXD.6de58f450421.js.gz +0 -0
- codex/static_root/assets/reader-L85y7IXD.js +2 -0
- codex/static_root/assets/reader-L85y7IXD.js.br +0 -0
- codex/static_root/assets/reader-L85y7IXD.js.gz +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js +1 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.br +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.1372d8a3e6cf.js.gz +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js +1 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js.br +0 -0
- codex/static_root/assets/relation-chips-BpuqEeqM.js.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.98e65384c3c9.css → relation-chips-D6T6FX7G.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.css.gz +0 -0
- codex/static_root/assets/{relation-chips-Bsc-gUxE.css → relation-chips-D6T6FX7G.db095b2497e4.css} +1 -1
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.br +0 -0
- codex/static_root/assets/relation-chips-D6T6FX7G.db095b2497e4.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.7bbeb1fd98f1.css.gz +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css +1 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.br +0 -0
- codex/static_root/assets/settings-drawer-BgUazs7O.css.gz +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js +2 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.br +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.b9619341ad48.js.gz +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js +2 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js.br +0 -0
- codex/static_root/assets/settings-drawer-CLMcxfVd.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js +1 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.br +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.3d7c61418528.js.gz +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js +1 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js.br +0 -0
- codex/static_root/assets/stats-tab-CQa3-W05.js.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.b93c080356fc.css.gz +0 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css +1 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.br +2 -0
- codex/static_root/assets/stats-tab-TsyG3wH_.css.gz +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js +1 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.br +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.8f31f4215114.js.gz +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js +1 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js.br +0 -0
- codex/static_root/assets/task-tab-OPhBm-O2.js.gz +0 -0
- codex/static_root/assets/to-case-kUm8NPpW.5b3369f511b8.js +1 -0
- codex/static_root/assets/to-case-kUm8NPpW.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.br +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.32a8a84d1781.js.gz +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js +1 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js.br +0 -0
- codex/static_root/assets/unauthorized-ClQNhRb5.js.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.4c2de98c2b53.css → unauthorized-DWgytUow.ca2f13dbdbf5.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.ca2f13dbdbf5.css.gz +0 -0
- codex/static_root/assets/{unauthorized-CibJYD8o.css → unauthorized-DWgytUow.css} +1 -1
- codex/static_root/assets/unauthorized-DWgytUow.css.br +0 -0
- codex/static_root/assets/unauthorized-DWgytUow.css.gz +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js +1 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.br +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.50d83951c09f.js.gz +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js +1 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js.br +0 -0
- codex/static_root/assets/user-tab-BKkxa2WL.js.gz +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.json +1 -0
- codex/static_root/js/choices-admin.248384ece3f6.json.br +0 -0
- codex/static_root/js/choices-admin.248384ece3f6.json.gz +0 -0
- codex/static_root/js/choices-admin.json +1 -1
- codex/static_root/js/choices-admin.json.br +0 -0
- codex/static_root/js/choices-admin.json.gz +0 -0
- codex/static_root/js/choices.b60817ba0a93.json +1 -0
- codex/static_root/js/choices.b60817ba0a93.json.br +0 -0
- codex/static_root/js/choices.b60817ba0a93.json.gz +0 -0
- codex/static_root/js/choices.json +1 -1
- codex/static_root/js/choices.json.br +0 -0
- codex/static_root/js/choices.json.gz +0 -0
- codex/static_root/manifest.c5fa10a7dfdc.json +641 -0
- codex/static_root/manifest.c5fa10a7dfdc.json.br +0 -0
- codex/static_root/manifest.c5fa10a7dfdc.json.gz +0 -0
- codex/static_root/manifest.json +298 -279
- 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 +2 -11
- codex/urls/api/auth.py +1 -2
- codex/urls/api/browser.py +4 -13
- codex/urls/api/reader.py +9 -3
- codex/urls/api/v3.py +0 -2
- codex/urls/opds/binary.py +1 -1
- codex/views/admin/api_key.py +8 -7
- codex/views/admin/flag.py +7 -2
- codex/views/admin/group.py +6 -2
- codex/views/admin/library.py +37 -44
- codex/views/admin/stats.py +83 -20
- codex/views/admin/tasks.py +12 -26
- codex/views/admin/user.py +9 -3
- codex/views/auth.py +54 -31
- codex/views/bookmark.py +72 -92
- codex/views/browser/base.py +90 -42
- codex/views/browser/browser.py +371 -92
- codex/views/browser/{annotations.py → browser_annotations.py} +121 -67
- codex/views/browser/browser_breadcrumbs.py +175 -0
- codex/views/browser/browser_order_by.py +63 -0
- codex/views/browser/choices.py +85 -38
- codex/views/browser/const.py +3 -0
- codex/views/browser/filters/annotations.py +19 -87
- codex/views/browser/filters/bookmark.py +12 -24
- codex/views/browser/filters/field.py +6 -8
- codex/views/browser/filters/group.py +14 -35
- codex/views/browser/filters/search.py +20 -31
- codex/views/browser/metadata.py +73 -112
- codex/views/browser/session.py +12 -0
- codex/views/const.py +0 -140
- codex/views/cover.py +77 -0
- codex/views/download.py +8 -5
- codex/views/frontend.py +2 -4
- codex/views/opds/authentication_v1.py +45 -46
- codex/views/opds/binary.py +10 -5
- codex/views/opds/const.py +0 -13
- codex/views/opds/urls.py +4 -6
- codex/views/opds/util.py +12 -4
- codex/views/opds/v1/entry/links.py +33 -18
- codex/views/opds/v1/facets.py +7 -7
- codex/views/opds/v1/feed.py +45 -55
- codex/views/opds/v1/links.py +5 -7
- codex/views/opds/v1/opensearch_v1.py +6 -2
- codex/views/opds/v2/feed.py +26 -25
- codex/views/opds/v2/links.py +2 -4
- codex/views/opds/v2/publications.py +54 -34
- codex/views/opds/v2/top_links.py +2 -2
- codex/views/reader/page.py +8 -15
- codex/views/reader/reader.py +322 -19
- codex/views/reader/{settings.py → session.py} +2 -4
- codex/views/session.py +102 -83
- codex/views/template.py +3 -3
- codex/views/version.py +2 -2
- codex/websockets/consumers.py +10 -14
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/METADATA +15 -22
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/RECORD +449 -467
- codex/librarian/importer/cache.py +0 -93
- codex/librarian/importer/create_covers.py +0 -96
- codex/librarian/importer/importer.py +0 -96
- codex/librarian/importer/init.py +0 -265
- codex/librarian/importer/link_covers.py +0 -73
- codex/librarian/importer/query_covers.py +0 -52
- codex/migrations/0027_import_order_and_covers.py +0 -280
- codex/serializers/browser/mtime.py +0 -21
- codex/serializers/fields.py +0 -149
- codex/serializers/settings.py +0 -12
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.8ec5e2094cbf.js.gz +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js +0 -1
- codex/static_root/assets/VCheckbox-DanrzdHo.js.br +0 -0
- codex/static_root/assets/VCheckbox-DanrzdHo.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.5ab34c4c47aa.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-DxNOKONG.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.11d080e8edd7.js.gz +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js +0 -1
- codex/static_root/assets/VCombobox-DDvdoPTG.js.br +0 -0
- codex/static_root/assets/VCombobox-DDvdoPTG.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.457666d7fce1.js.gz +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js +0 -1
- codex/static_root/assets/VDialog-Bd5dN_E1.js.br +0 -0
- codex/static_root/assets/VDialog-Bd5dN_E1.js.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.1c801ad756b2.css.gz +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.br +0 -0
- codex/static_root/assets/VDialog-lQ13FnRR.css.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.12673cf9f391.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js +0 -1
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-DqTuYpTR.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.3cf6bf5bf3bc.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js +0 -1
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.br +0 -0
- codex/static_root/assets/VRadioGroup-DKb8XPr-.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.08bd6b53a7ce.js.gz +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js +0 -1
- codex/static_root/assets/VSelect-C_yPbyCG.js.br +0 -0
- codex/static_root/assets/VSelect-C_yPbyCG.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.802c9b3e6e28.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js +0 -1
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.br +0 -0
- codex/static_root/assets/VSelectionControl-CmFPvGMS.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.a47491fe3e6d.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js +0 -1
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.br +0 -0
- codex/static_root/assets/VSlideGroup-D5FZWsM1.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.f0f2106b158b.js.gz +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.br +0 -0
- codex/static_root/assets/VTable-2RHFqfg6.js.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.4cc388b4aa3c.css.gz +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css +0 -1
- codex/static_root/assets/VTextField-BssVoDhB.css.br +0 -0
- codex/static_root/assets/VTextField-BssVoDhB.css.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.7fffaeff8d40.js.gz +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js +0 -1
- codex/static_root/assets/VTextField-o1N9ylIb.js.br +0 -0
- codex/static_root/assets/VTextField-o1N9ylIb.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.663fa48efea4.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js +0 -1
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.br +0 -0
- codex/static_root/assets/VWindowItem-C_zvuz9C.js.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.82c98714bcf1.css.gz +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.br +0 -0
- codex/static_root/assets/admin-B8Bp2Uih.css.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.7a8015456c0f.js.gz +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js +0 -1
- codex/static_root/assets/admin-DHyHEWZ3.js.br +0 -0
- codex/static_root/assets/admin-DHyHEWZ3.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.19507a95375b.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js +0 -30
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BXpIDQXM.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.561b3f75fe65.css.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css +0 -1
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.br +0 -0
- codex/static_root/assets/admin-drawer-panel-mne3FIj6.css.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.a1bfaf48d65f.js.gz +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js +0 -1
- codex/static_root/assets/browser-D0Vjmmp_.js.br +0 -0
- codex/static_root/assets/browser-D0Vjmmp_.js.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.155cb612ed0c.css.gz +0 -0
- codex/static_root/assets/browser-D7QQDneO.css +0 -1
- codex/static_root/assets/browser-D7QQDneO.css.br +0 -0
- codex/static_root/assets/browser-D7QQDneO.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.5f9a9aa573b7.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css +0 -1
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.br +0 -0
- codex/static_root/assets/change-password-dialog-0u6IJA87.css.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.e642f7e2b96f.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js +0 -1
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BBsJLPXZ.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.2a3277b3c147.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js +0 -1
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.br +0 -0
- codex/static_root/assets/confirm-dialog-DCKvg3ai.js.gz +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.bec27f144aca.js.br +0 -0
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js +0 -1
- codex/static_root/assets/copy-to-clipboard-DoUvDJVx.js.br +0 -0
- codex/static_root/assets/datetime-column-DeCthByU.1f3bf499e063.css +0 -1
- codex/static_root/assets/datetime-column-DeCthByU.css +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.039c7da2036f.js.gz +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js +0 -1
- codex/static_root/assets/datetime-column-jHKA_uNm.js.br +0 -0
- codex/static_root/assets/datetime-column-jHKA_uNm.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.af0fcf0888c6.js.gz +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js +0 -1
- codex/static_root/assets/filter-DgPg2ts9.js.br +0 -0
- codex/static_root/assets/filter-DgPg2ts9.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.f89bc9891671.js.gz +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js +0 -1
- codex/static_root/assets/flag-tab-9qxOALxP.js.br +0 -0
- codex/static_root/assets/flag-tab-9qxOALxP.js.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.b755a99fff7e.css.gz +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css +0 -1
- codex/static_root/assets/flag-tab-BNwbLfrN.css.br +0 -0
- codex/static_root/assets/flag-tab-BNwbLfrN.css.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.1afb2ce453ff.js.gz +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.br +0 -0
- codex/static_root/assets/group-tab-D5vWrihT.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.141fdae409bc.js.gz +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js +0 -1
- codex/static_root/assets/http-error-B4FHoKfs.js.br +0 -0
- codex/static_root/assets/http-error-B4FHoKfs.js.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.95e42988d5d5.css.gz +0 -0
- codex/static_root/assets/http-error-DKulXuTl.css +0 -1
- codex/static_root/assets/http-error-DKulXuTl.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.913e236f625e.css.gz +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css +0 -1
- codex/static_root/assets/library-tab-BN0SfPcH.css.br +0 -0
- codex/static_root/assets/library-tab-BN0SfPcH.css.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.0e3d67d0c41a.js.gz +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js +0 -1
- codex/static_root/assets/library-tab-BWo0cmzk.js.br +0 -0
- codex/static_root/assets/library-tab-BWo0cmzk.js.gz +0 -0
- codex/static_root/assets/main-Ztp855PW.css.br +0 -0
- codex/static_root/assets/main-Ztp855PW.d46d718fac45.css.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js +0 -32
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.5e60e6e8e447.js.gz +0 -0
- codex/static_root/assets/main-ipmlipkF.js +0 -32
- codex/static_root/assets/main-ipmlipkF.js.br +0 -0
- codex/static_root/assets/main-ipmlipkF.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.b4865334ee08.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css +0 -1
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BA9ifTed.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.b3341b8028df.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js +0 -1
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-kZ-Xdv8c.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.cb848834dbe1.js.gz +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js +0 -305
- codex/static_root/assets/pdf-doc-C2StaYUz.js.br +0 -0
- codex/static_root/assets/pdf-doc-C2StaYUz.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.b6b38ed5521e.js.gz +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js +0 -2
- codex/static_root/assets/reader-CCnq3j1-.js.br +0 -0
- codex/static_root/assets/reader-CCnq3j1-.js.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.78267e9de0c2.css.gz +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css +0 -1
- codex/static_root/assets/reader-CqXFa_gE.css.br +0 -0
- codex/static_root/assets/reader-CqXFa_gE.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.98e65384c3c9.css.gz +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.br +0 -0
- codex/static_root/assets/relation-chips-Bsc-gUxE.css.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.670f1d1f273f.js.gz +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js +0 -1
- codex/static_root/assets/relation-chips-C9ysW0tU.js.br +0 -0
- codex/static_root/assets/relation-chips-C9ysW0tU.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.c0def8f383cb.js.gz +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js +0 -2
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.br +0 -0
- codex/static_root/assets/settings-drawer-C0ofzAhW.js.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.01744e131806.css.gz +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css +0 -1
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.br +0 -0
- codex/static_root/assets/settings-drawer-nZ9HvKSZ.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.a5728396494b.css.gz +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css +0 -1
- codex/static_root/assets/stats-tab-B4yKuY0c.css.br +0 -0
- codex/static_root/assets/stats-tab-B4yKuY0c.css.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.38a3459d61ea.js.gz +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js +0 -1
- codex/static_root/assets/stats-tab-BqJj7j1w.js.br +0 -0
- codex/static_root/assets/stats-tab-BqJj7j1w.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.e00630a60b43.js.gz +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js +0 -1
- codex/static_root/assets/task-tab-BsPzNk8S.js.br +0 -0
- codex/static_root/assets/task-tab-BsPzNk8S.js.gz +0 -0
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.9c02da2e47c7.js.br +0 -0
- codex/static_root/assets/to-case-KvqtSug2.js +0 -1
- codex/static_root/assets/to-case-KvqtSug2.js.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.4c2de98c2b53.css.gz +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.br +0 -0
- codex/static_root/assets/unauthorized-CibJYD8o.css.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.f418e69f3afe.js.gz +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js +0 -1
- codex/static_root/assets/unauthorized-DwSdNzX2.js.br +0 -0
- codex/static_root/assets/unauthorized-DwSdNzX2.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.083d993726d8.js.gz +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js +0 -1
- codex/static_root/assets/user-tab-DrJOb9Ys.js.br +0 -0
- codex/static_root/assets/user-tab-DrJOb9Ys.js.gz +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json +0 -1
- codex/static_root/js/choices-admin.3521e2a863df.json.br +0 -0
- codex/static_root/js/choices-admin.3521e2a863df.json.gz +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json +0 -1
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.json.gz +0 -0
- codex/static_root/manifest.1c84116d9a0a.json +0 -622
- codex/static_root/manifest.1c84116d9a0a.json.br +0 -0
- codex/static_root/manifest.1c84116d9a0a.json.gz +0 -0
- codex/util.py +0 -12
- codex/views/admin/auth.py +0 -28
- codex/views/browser/breadcrumbs.py +0 -202
- codex/views/browser/cover.py +0 -165
- codex/views/browser/mtime.py +0 -61
- codex/views/browser/order_by.py +0 -65
- codex/views/browser/paginate.py +0 -106
- codex/views/browser/settings.py +0 -66
- codex/views/browser/title.py +0 -48
- codex/views/browser/validate.py +0 -161
- codex/views/opds/auth.py +0 -17
- codex/views/public.py +0 -36
- codex/views/reader/arcs.py +0 -92
- codex/views/reader/books.py +0 -181
- codex/views/reader/init.py +0 -125
- codex/views/settings.py +0 -47
- codex/views/util.py +0 -77
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/LICENSE +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/WHEEL +0 -0
- {codex-1.6.0.dist-info → codex-1.6.0a0.dist-info}/entry_points.txt +0 -0
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
"""Browser Settings Filter Serializers."""
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
from abc import ABC
|
|
4
|
+
|
|
5
|
+
from django.core.exceptions import ValidationError
|
|
6
|
+
from django.utils.translation import gettext_lazy as _
|
|
7
|
+
from rest_framework.fields import Field
|
|
8
|
+
from rest_framework.serializers import (
|
|
9
|
+
BooleanField,
|
|
10
|
+
CharField,
|
|
11
|
+
ChoiceField,
|
|
12
|
+
FloatField,
|
|
13
|
+
IntegerField,
|
|
14
|
+
ListField,
|
|
15
|
+
Serializer,
|
|
12
16
|
)
|
|
13
17
|
|
|
18
|
+
from codex.serializers.choices import CHOICES, VUETIFY_NULL_CODE
|
|
19
|
+
|
|
20
|
+
VUETIFY_NULL_CODE_STR = str(VUETIFY_NULL_CODE)
|
|
21
|
+
|
|
14
22
|
|
|
15
23
|
class BrowserFilterChoicesSerializer(Serializer):
|
|
16
24
|
"""All dynamic filters."""
|
|
@@ -39,30 +47,121 @@ class BrowserFilterChoicesSerializer(Serializer):
|
|
|
39
47
|
year = BooleanField(read_only=True)
|
|
40
48
|
|
|
41
49
|
|
|
50
|
+
def validate_decades(decades):
|
|
51
|
+
"""Validate decades."""
|
|
52
|
+
if not decades:
|
|
53
|
+
return
|
|
54
|
+
# * We don't need a whole db call just to be perfectly accurate
|
|
55
|
+
# * -1s are decoded back into None before validation
|
|
56
|
+
exists = False
|
|
57
|
+
for decade in decades:
|
|
58
|
+
if decade is None or decade % 10 == 0:
|
|
59
|
+
exists = True
|
|
60
|
+
break
|
|
61
|
+
if not exists:
|
|
62
|
+
raise ValidationError(_(f"Invalid decade in: {decades}"))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate_int_null(values):
|
|
66
|
+
"""Use a special code for null.
|
|
67
|
+
|
|
68
|
+
Because if a vuetify component has a null key it changes it to the
|
|
69
|
+
array index.
|
|
70
|
+
"""
|
|
71
|
+
for index, value in enumerate(values):
|
|
72
|
+
if value == VUETIFY_NULL_CODE:
|
|
73
|
+
values[index] = None
|
|
74
|
+
return values
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def validate_str_null(values):
|
|
78
|
+
"""Use a special code for null.
|
|
79
|
+
|
|
80
|
+
Because if a vuetify component has a null key it changes it to the
|
|
81
|
+
array index. This is the version for CharFields.
|
|
82
|
+
"""
|
|
83
|
+
for index, value in enumerate(values):
|
|
84
|
+
if value == VUETIFY_NULL_CODE_STR:
|
|
85
|
+
values[index] = None
|
|
86
|
+
return values
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class FilterListField(ListField, ABC):
|
|
90
|
+
"""Filter List field with custom arguments."""
|
|
91
|
+
|
|
92
|
+
CHILD_CLASS = Field
|
|
93
|
+
VALIDATORS = ()
|
|
94
|
+
|
|
95
|
+
def __init__(self, *args, **kwargs):
|
|
96
|
+
"""Apply the subclass's arguments."""
|
|
97
|
+
super().__init__(
|
|
98
|
+
*args,
|
|
99
|
+
child=self.CHILD_CLASS(allow_null=True, **kwargs),
|
|
100
|
+
required=False,
|
|
101
|
+
validators=self.VALIDATORS,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class FloatListField(FilterListField):
|
|
106
|
+
"""Decimal List Field with validation."""
|
|
107
|
+
|
|
108
|
+
CHILD_CLASS = FloatField # type: ignore
|
|
109
|
+
VALIDATORS = (validate_int_null,)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class IntListField(FilterListField):
|
|
113
|
+
"""Integer List Field with validation."""
|
|
114
|
+
|
|
115
|
+
CHILD_CLASS = IntegerField # type: ignore
|
|
116
|
+
VALIDATORS = (validate_int_null,)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class DecadeListField(IntListField):
|
|
120
|
+
"""Integer List Field with validation for decades."""
|
|
121
|
+
|
|
122
|
+
VALIDATORS = (validate_int_null, validate_decades)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class CharListField(FilterListField):
|
|
126
|
+
"""Char List Field with validation."""
|
|
127
|
+
|
|
128
|
+
CHILD_CLASS = CharField # type: ignore
|
|
129
|
+
VALIDATORS = (validate_str_null,)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class BooleanListField(FilterListField):
|
|
133
|
+
"""Bool List Field with validation."""
|
|
134
|
+
|
|
135
|
+
CHILD_CLASS = BooleanField # type: ignore
|
|
136
|
+
VALIDATORS = (validate_int_null,)
|
|
137
|
+
|
|
138
|
+
|
|
42
139
|
class BrowserSettingsFilterSerializer(Serializer):
|
|
43
140
|
"""Filter values for settings."""
|
|
44
141
|
|
|
45
|
-
bookmark =
|
|
142
|
+
bookmark = ChoiceField(
|
|
143
|
+
choices=tuple(CHOICES["bookmarkFilter"].keys()), required=False
|
|
144
|
+
)
|
|
46
145
|
# Dynamic filters
|
|
47
|
-
age_rating =
|
|
48
|
-
characters =
|
|
49
|
-
community_rating =
|
|
50
|
-
country =
|
|
51
|
-
contributors =
|
|
52
|
-
critical_rating =
|
|
53
|
-
decade =
|
|
54
|
-
file_type =
|
|
55
|
-
genres =
|
|
56
|
-
identifier_type =
|
|
57
|
-
language =
|
|
58
|
-
locations =
|
|
59
|
-
monochrome =
|
|
60
|
-
original_format =
|
|
61
|
-
reading_direction =
|
|
62
|
-
series_groups =
|
|
63
|
-
stories =
|
|
64
|
-
story_arcs =
|
|
65
|
-
tagger =
|
|
66
|
-
tags =
|
|
67
|
-
teams =
|
|
68
|
-
year =
|
|
146
|
+
age_rating = CharListField(allow_blank=True, required=False)
|
|
147
|
+
characters = IntListField(required=False)
|
|
148
|
+
community_rating = FloatListField(required=False)
|
|
149
|
+
country = CharListField(allow_blank=True, required=False)
|
|
150
|
+
contributors = IntListField(required=False)
|
|
151
|
+
critical_rating = FloatListField(required=False)
|
|
152
|
+
decade = DecadeListField(required=False)
|
|
153
|
+
file_type = CharListField(allow_blank=True, required=False)
|
|
154
|
+
genres = IntListField(required=False)
|
|
155
|
+
identifier_type = CharListField(allow_blank=True, required=False)
|
|
156
|
+
language = CharListField(allow_blank=True, required=False)
|
|
157
|
+
locations = IntListField()
|
|
158
|
+
monochrome = BooleanListField(required=False)
|
|
159
|
+
original_format = CharListField(required=False, allow_blank=True)
|
|
160
|
+
reading_direction = CharListField(required=False)
|
|
161
|
+
series_groups = IntListField(required=False)
|
|
162
|
+
stories = IntListField(required=False)
|
|
163
|
+
story_arcs = IntListField(required=False)
|
|
164
|
+
tagger = CharListField(required=False)
|
|
165
|
+
tags = IntListField(required=False)
|
|
166
|
+
teams = IntListField(required=False)
|
|
167
|
+
year = IntListField(required=False)
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
"""Codex Serializers for the metadata box."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.serializers import CharField, IntegerField
|
|
3
|
+
from rest_framework.serializers import CharField, IntegerField
|
|
4
4
|
|
|
5
5
|
from codex.serializers.browser.mixins import BrowserAggregateSerializerMixin
|
|
6
6
|
from codex.serializers.models.comic import ComicSerializer
|
|
7
|
-
from codex.serializers.models.named import (
|
|
8
|
-
ContributorSerializer,
|
|
9
|
-
IdentifierSeralizer,
|
|
10
|
-
StoryArcNumberSerializer,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
PREFETCH_PREFIX = "attached_"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class GroupSerializer(Serializer):
|
|
17
|
-
"""Serialize a group pk and name."""
|
|
18
|
-
|
|
19
|
-
ids = ListField(child=IntegerField(), read_only=True)
|
|
20
|
-
name = CharField(read_only=True)
|
|
21
7
|
|
|
22
8
|
|
|
23
9
|
class MetadataSerializer(BrowserAggregateSerializerMixin, ComicSerializer):
|
|
@@ -27,33 +13,3 @@ class MetadataSerializer(BrowserAggregateSerializerMixin, ComicSerializer):
|
|
|
27
13
|
parent_folder_id = IntegerField(read_only=True, required=False)
|
|
28
14
|
series_volume_count = IntegerField(read_only=True)
|
|
29
15
|
volume_issue_count = IntegerField(read_only=True)
|
|
30
|
-
|
|
31
|
-
publisher_list = GroupSerializer(many=True, required=False)
|
|
32
|
-
imprint_list = GroupSerializer(many=True, required=False)
|
|
33
|
-
series_list = GroupSerializer(many=True, required=False)
|
|
34
|
-
volume_list = GroupSerializer(many=True, required=False)
|
|
35
|
-
publisher = None
|
|
36
|
-
imprint = None
|
|
37
|
-
series = None
|
|
38
|
-
volume = None
|
|
39
|
-
|
|
40
|
-
identifiers = IdentifierSeralizer(
|
|
41
|
-
source=f"{PREFETCH_PREFIX}identifiers", many=True, allow_null=True
|
|
42
|
-
)
|
|
43
|
-
contributors = ContributorSerializer(
|
|
44
|
-
source=f"{PREFETCH_PREFIX}contributors", many=True, allow_null=True
|
|
45
|
-
)
|
|
46
|
-
stroy_arc_numbers = StoryArcNumberSerializer(
|
|
47
|
-
source=f"{PREFETCH_PREFIX}story_arc_numbers", many=True, allow_null=True
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
class Meta(ComicSerializer.Meta):
|
|
51
|
-
"""Configure the model."""
|
|
52
|
-
|
|
53
|
-
exclude = (
|
|
54
|
-
*ComicSerializer.Meta.exclude,
|
|
55
|
-
"publisher",
|
|
56
|
-
"imprint",
|
|
57
|
-
"series",
|
|
58
|
-
"volume",
|
|
59
|
-
)
|
|
@@ -1,33 +1,27 @@
|
|
|
1
1
|
"""Serializer mixins."""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime, timezone
|
|
4
|
-
|
|
5
3
|
from rest_framework.serializers import (
|
|
6
4
|
BooleanField,
|
|
7
5
|
CharField,
|
|
8
6
|
DateTimeField,
|
|
9
7
|
DecimalField,
|
|
10
8
|
IntegerField,
|
|
11
|
-
ListField,
|
|
12
9
|
Serializer,
|
|
13
|
-
SerializerMethodField,
|
|
14
10
|
)
|
|
15
11
|
|
|
16
|
-
from codex.
|
|
17
|
-
from codex.views.const import COMIC_GROUP, EPOCH_START
|
|
18
|
-
|
|
19
|
-
LOG = get_logger(__name__)
|
|
12
|
+
from codex.serializers.browser.filters import IntListField
|
|
20
13
|
|
|
21
14
|
|
|
22
15
|
class BrowserAggregateSerializerMixin(Serializer):
|
|
23
16
|
"""Mixin for browser, opds & metadata serializers."""
|
|
24
17
|
|
|
25
18
|
group = CharField(read_only=True, max_length=1)
|
|
26
|
-
ids =
|
|
19
|
+
ids = IntListField(read_only=True)
|
|
27
20
|
|
|
28
21
|
# Aggregate Annotations
|
|
29
22
|
child_count = IntegerField(read_only=True)
|
|
30
|
-
|
|
23
|
+
cover_pk = IntegerField(read_only=True)
|
|
24
|
+
cover_mtime = DateTimeField(format="%s", read_only=True)
|
|
31
25
|
|
|
32
26
|
# Bookmark annotations
|
|
33
27
|
page = IntegerField(read_only=True)
|
|
@@ -36,30 +30,3 @@ class BrowserAggregateSerializerMixin(Serializer):
|
|
|
36
30
|
progress = DecimalField(
|
|
37
31
|
max_digits=5, decimal_places=2, read_only=True, coerce_to_string=False
|
|
38
32
|
)
|
|
39
|
-
|
|
40
|
-
def get_mtime(self, obj) -> int:
|
|
41
|
-
"""Compute mtime from json array aggregates."""
|
|
42
|
-
mtime = EPOCH_START
|
|
43
|
-
for dt_str in obj.updated_ats:
|
|
44
|
-
if not dt_str:
|
|
45
|
-
continue
|
|
46
|
-
try:
|
|
47
|
-
dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S.%f").replace(
|
|
48
|
-
tzinfo=timezone.utc
|
|
49
|
-
)
|
|
50
|
-
except ValueError:
|
|
51
|
-
LOG.warning(
|
|
52
|
-
f"computing group mtime: {dt_str} is not a valid datetime string."
|
|
53
|
-
)
|
|
54
|
-
continue
|
|
55
|
-
|
|
56
|
-
if dt > mtime:
|
|
57
|
-
mtime = dt
|
|
58
|
-
|
|
59
|
-
if obj.group != COMIC_GROUP and (
|
|
60
|
-
mbua := getattr(obj, "max_bookmark_updated_at", None)
|
|
61
|
-
):
|
|
62
|
-
mtime = max(mtime, mbua)
|
|
63
|
-
|
|
64
|
-
# print(obj.group, obj.pk, obj.name, obj.updated_ats, obj.max_bookmark_updated_at, "max:", mtime)
|
|
65
|
-
return int(mtime.timestamp() * 1000)
|
|
@@ -3,15 +3,17 @@
|
|
|
3
3
|
from rest_framework.serializers import (
|
|
4
4
|
BooleanField,
|
|
5
5
|
CharField,
|
|
6
|
+
DateTimeField,
|
|
6
7
|
DecimalField,
|
|
7
8
|
IntegerField,
|
|
9
|
+
ListSerializer,
|
|
8
10
|
Serializer,
|
|
9
11
|
)
|
|
10
12
|
|
|
11
13
|
from codex.serializers.browser.mixins import (
|
|
12
14
|
BrowserAggregateSerializerMixin,
|
|
13
15
|
)
|
|
14
|
-
from codex.serializers.
|
|
16
|
+
from codex.serializers.route import RouteSerializer
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
class BrowserCardSerializer(BrowserAggregateSerializerMixin):
|
|
@@ -32,6 +34,7 @@ class BrowserCardSerializer(BrowserAggregateSerializerMixin):
|
|
|
32
34
|
order_value = CharField(read_only=True)
|
|
33
35
|
page_count = IntegerField(read_only=True)
|
|
34
36
|
reading_direction = CharField(read_only=True)
|
|
37
|
+
mtime = DateTimeField(format="%s", read_only=True)
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
class BrowserAdminFlagsSerializer(Serializer):
|
|
@@ -52,12 +55,12 @@ class BrowserPageSerializer(Serializer):
|
|
|
52
55
|
"""The main browse list."""
|
|
53
56
|
|
|
54
57
|
admin_flags = BrowserAdminFlagsSerializer(read_only=True)
|
|
55
|
-
breadcrumbs =
|
|
58
|
+
breadcrumbs = ListSerializer(child=RouteSerializer())
|
|
56
59
|
title = BrowserTitleSerializer(read_only=True)
|
|
60
|
+
covers_timestamp = IntegerField(read_only=True)
|
|
57
61
|
zero_pad = IntegerField(read_only=True)
|
|
58
62
|
libraries_exist = BooleanField(read_only=True)
|
|
59
63
|
model_group = CharField(read_only=True)
|
|
60
64
|
num_pages = IntegerField(read_only=True)
|
|
61
65
|
groups = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
62
66
|
books = BrowserCardSerializer(allow_empty=True, read_only=True, many=True)
|
|
63
|
-
mtime = TimestampField(read_only=True)
|
|
@@ -10,8 +10,6 @@ from rest_framework.serializers import (
|
|
|
10
10
|
|
|
11
11
|
from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
|
|
12
12
|
from codex.serializers.choices import CHOICES, VUETIFY_NULL_CODE
|
|
13
|
-
from codex.serializers.fields import BreadcrumbsField, TimestampField, TopGroupField
|
|
14
|
-
from codex.serializers.route import SimpleRouteSerializer
|
|
15
13
|
|
|
16
14
|
VUETIFY_NULL_CODE_STR = str(VUETIFY_NULL_CODE)
|
|
17
15
|
|
|
@@ -25,50 +23,21 @@ class BrowserSettingsShowGroupFlagsSerializer(Serializer):
|
|
|
25
23
|
v = BooleanField()
|
|
26
24
|
|
|
27
25
|
|
|
28
|
-
class
|
|
29
|
-
"""Browser Settings
|
|
30
|
-
|
|
31
|
-
filters = BrowserSettingsFilterSerializer(required=False)
|
|
32
|
-
q = CharField(allow_blank=True, required=False)
|
|
33
|
-
|
|
26
|
+
class BrowserSettingsSerializer(Serializer):
|
|
27
|
+
"""Browser Settings that the user can change.
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
This is the only browse serializer that's submitted.
|
|
30
|
+
It is also sent to the browser as part of BrowserOpenedSerializer.
|
|
31
|
+
"""
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
dynamic_covers = BooleanField(required=False)
|
|
33
|
+
filters = BrowserSettingsFilterSerializer(required=False)
|
|
40
34
|
order_by = ChoiceField(choices=tuple(CHOICES["orderBy"].keys()), required=False)
|
|
41
35
|
order_reverse = BooleanField(required=False)
|
|
36
|
+
q = CharField(allow_blank=True, required=False)
|
|
37
|
+
query = CharField(allow_blank=True, required=False) # OPDS 2.0
|
|
42
38
|
show = BrowserSettingsShowGroupFlagsSerializer(required=False)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase):
|
|
46
|
-
"""Browser Settings for the cover response."""
|
|
47
|
-
|
|
48
|
-
parent = SimpleRouteSerializer(required=False)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase):
|
|
52
|
-
"""Base Serializer for Browser & OPDS Settings."""
|
|
53
|
-
|
|
54
39
|
# search_results_limit = IntegerField(required=False)
|
|
55
|
-
top_group = TopGroupField(required=False)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
class OPDSSettingsSerializer(BrowserSettingsSerializerBase):
|
|
59
|
-
"""Browser Settings for the OPDS."""
|
|
60
|
-
|
|
61
|
-
limit = IntegerField(required=False)
|
|
62
|
-
opds_metadata = BooleanField(required=False)
|
|
63
|
-
query = CharField(allow_blank=True, required=False) # OPDS 2.0
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
class BrowserSettingsSerializer(BrowserSettingsSerializerBase):
|
|
67
|
-
"""Browser Settings that the user can change.
|
|
68
|
-
|
|
69
|
-
This is the only browse serializer that's submitted.
|
|
70
|
-
"""
|
|
71
|
-
|
|
72
|
-
breadcrumbs = BreadcrumbsField(required=False)
|
|
73
|
-
mtime = TimestampField(read_only=True)
|
|
74
40
|
twenty_four_hour_time = BooleanField(required=False)
|
|
41
|
+
top_group = ChoiceField(choices=tuple(CHOICES["topGroup"].keys()), required=False)
|
|
42
|
+
opds_metadata = BooleanField(required=False)
|
|
43
|
+
limit = IntegerField(required=False)
|
codex/serializers/choices.py
CHANGED
|
@@ -49,26 +49,13 @@ _CHOICES_FN_RE = {
|
|
|
49
49
|
_CHOICES_MODULE_NAME: create_choices_fn_regexes(_CHOICES_MODULE_NAME),
|
|
50
50
|
_ADMIN_CHOICES_MODULE_NAME: create_choices_fn_regexes(_ADMIN_CHOICES_MODULE_NAME),
|
|
51
51
|
}
|
|
52
|
-
_SINGLE_VALUE_KEYS = frozenset(
|
|
53
|
-
{
|
|
54
|
-
"customCovers",
|
|
55
|
-
"dynamicCovers",
|
|
56
|
-
"finishOnLastPage",
|
|
57
|
-
"orderReverse",
|
|
58
|
-
"q",
|
|
59
|
-
"readRtlInReverse",
|
|
60
|
-
"searchResultsLimit",
|
|
61
|
-
"twentyFourHourTime",
|
|
62
|
-
"twoPages",
|
|
63
|
-
}
|
|
64
|
-
)
|
|
52
|
+
_SINGLE_VALUE_KEYS = frozenset({"orderReverse", "q", "searchResultsLimit"})
|
|
65
53
|
|
|
66
54
|
# Exports
|
|
67
55
|
CHOICES = {}
|
|
68
56
|
DEFAULTS = {}
|
|
69
57
|
WEBSOCKET_MESSAGES = {}
|
|
70
58
|
VUETIFY_NULL_CODE = -1
|
|
71
|
-
DUMMY_NULL_NAME = "_none_"
|
|
72
59
|
|
|
73
60
|
|
|
74
61
|
def _find_filename_regex(js_root, module_name):
|
|
@@ -1,33 +1,73 @@
|
|
|
1
1
|
"""Pycountry Model Serializers."""
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import pycountry
|
|
4
|
+
from drf_spectacular.types import OpenApiTypes
|
|
5
|
+
from drf_spectacular.utils import extend_schema_field
|
|
6
|
+
from rest_framework.serializers import (
|
|
7
|
+
SerializerMethodField,
|
|
8
|
+
)
|
|
9
|
+
|
|
4
10
|
from codex.models import (
|
|
5
11
|
Country,
|
|
6
12
|
Language,
|
|
7
13
|
)
|
|
8
|
-
from codex.serializers.fields import CountryField, LanguageField
|
|
9
14
|
from codex.serializers.models.named import NamedModelSerializer
|
|
10
15
|
|
|
11
|
-
LOG = get_logger(__name__)
|
|
12
16
|
|
|
17
|
+
class PyCountrySerializer(NamedModelSerializer):
|
|
18
|
+
"""PyCountry tag serializer to include long names.
|
|
13
19
|
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
Takes a single string value and serializes the value to
|
|
21
|
+
a pk attribute and a pycountry long name lookup of that value to
|
|
22
|
+
the name.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
LOOKUP_MODULE = pycountry.countries
|
|
26
|
+
_ALPHA_2_LEN = 2
|
|
16
27
|
|
|
17
|
-
name =
|
|
28
|
+
name = SerializerMethodField()
|
|
18
29
|
|
|
19
30
|
class Meta(NamedModelSerializer.Meta):
|
|
20
|
-
"""
|
|
31
|
+
"""Abstract class."""
|
|
21
32
|
|
|
22
|
-
|
|
33
|
+
abstract = True
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def lookup_name(cls, lookup_module, obj):
|
|
37
|
+
"""Lookup the name with pycountry, just copy the key on fail."""
|
|
38
|
+
name = obj.name
|
|
39
|
+
if not name:
|
|
40
|
+
return ""
|
|
41
|
+
# fix for https://github.com/flyingcircusio/pycountry/issues/41
|
|
42
|
+
lookup_obj = (
|
|
43
|
+
lookup_module.get(alpha_2=name)
|
|
44
|
+
if len(name) == cls._ALPHA_2_LEN
|
|
45
|
+
else lookup_module.lookup(name)
|
|
46
|
+
)
|
|
47
|
+
# If lookup fails, return the key as the name
|
|
48
|
+
return lookup_obj.name if lookup_obj else name
|
|
49
|
+
|
|
50
|
+
@extend_schema_field(OpenApiTypes.STR)
|
|
51
|
+
def get_name(self, obj):
|
|
52
|
+
"""Lookup the name with pycountry, just copy the key on fail."""
|
|
53
|
+
return self.lookup_name(self.LOOKUP_MODULE, obj)
|
|
23
54
|
|
|
24
55
|
|
|
25
|
-
class LanguageSerializer(
|
|
56
|
+
class LanguageSerializer(PyCountrySerializer):
|
|
26
57
|
"""Pycountry serializer for language field."""
|
|
27
58
|
|
|
28
|
-
|
|
59
|
+
LOOKUP_MODULE = pycountry.languages
|
|
29
60
|
|
|
30
|
-
class Meta(
|
|
61
|
+
class Meta(PyCountrySerializer.Meta):
|
|
31
62
|
"""Configure model."""
|
|
32
63
|
|
|
33
64
|
model = Language
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CountrySerializer(PyCountrySerializer):
|
|
68
|
+
"""Pycountry serializer for country field."""
|
|
69
|
+
|
|
70
|
+
class Meta(PyCountrySerializer.Meta):
|
|
71
|
+
"""Configure model."""
|
|
72
|
+
|
|
73
|
+
model = Country
|
codex/serializers/opds/v2.py
CHANGED
|
@@ -12,6 +12,12 @@ from rest_framework.fields import (
|
|
|
12
12
|
from rest_framework.serializers import Serializer
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
class CharListField(ListField):
|
|
16
|
+
"""List of CharFields."""
|
|
17
|
+
|
|
18
|
+
child = CharField()
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
class OPDS2LinkPropertiesSerializer(Serializer):
|
|
16
22
|
"""Link Properties.
|
|
17
23
|
|
|
@@ -49,9 +55,9 @@ class OPDS2LinkSerializer(Serializer):
|
|
|
49
55
|
width = IntegerField(read_only=True, required=False)
|
|
50
56
|
# bitrate = IntegerField(read_only=True, required=False)
|
|
51
57
|
# duration = IntegerField(read_only=True, required=False)
|
|
52
|
-
# language =
|
|
53
|
-
alternate =
|
|
54
|
-
children =
|
|
58
|
+
# language = CharListField(read_only=True, required=False)
|
|
59
|
+
alternate = CharListField(read_only=True, required=False)
|
|
60
|
+
children = CharListField(read_only=True, required=False)
|
|
55
61
|
properties = OPDS2LinkPropertiesSerializer(read_only=True, required=False)
|
|
56
62
|
|
|
57
63
|
def get_rel(self, obj) -> str | None:
|
|
@@ -63,7 +69,7 @@ class OPDS2LinkSerializer(Serializer):
|
|
|
63
69
|
return obj.get("rel")
|
|
64
70
|
|
|
65
71
|
|
|
66
|
-
class
|
|
72
|
+
class LinkListField(ListField):
|
|
67
73
|
"""Link List."""
|
|
68
74
|
|
|
69
75
|
child = OPDS2LinkSerializer()
|
|
@@ -125,7 +131,7 @@ class OPDS2PublicationMetadataSerializer(OPDS2MetadataSerializer):
|
|
|
125
131
|
#####################
|
|
126
132
|
# Extended metadata #
|
|
127
133
|
#####################
|
|
128
|
-
subject =
|
|
134
|
+
subject = CharListField(read_only=True, required=False)
|
|
129
135
|
author = OPDS2ContributorObjectSerializer(many=True, required=False)
|
|
130
136
|
# translator = OPDS2ContributorObjectSerializer(many=True, required=False)
|
|
131
137
|
editor = OPDS2ContributorObjectSerializer(many=True, required=False)
|
|
@@ -142,7 +148,7 @@ class OPDS2FacetSerializer(Serializer):
|
|
|
142
148
|
"""Facets."""
|
|
143
149
|
|
|
144
150
|
metadata = OPDS2MetadataSerializer(read_only=True)
|
|
145
|
-
links =
|
|
151
|
+
links = LinkListField(read_only=True)
|
|
146
152
|
|
|
147
153
|
|
|
148
154
|
class OPDS2PublicationSerializer(OPDS2FacetSerializer):
|
|
@@ -152,19 +158,19 @@ class OPDS2PublicationSerializer(OPDS2FacetSerializer):
|
|
|
152
158
|
"""
|
|
153
159
|
|
|
154
160
|
metadata = OPDS2PublicationMetadataSerializer(read_only=True)
|
|
155
|
-
links =
|
|
156
|
-
images =
|
|
161
|
+
links = LinkListField(read_only=True)
|
|
162
|
+
images = LinkListField(read_only=True, required=False)
|
|
157
163
|
|
|
158
164
|
|
|
159
165
|
class OPDS2GroupSerializer(Serializer):
|
|
160
166
|
"""Group."""
|
|
161
167
|
|
|
162
168
|
metadata = OPDS2MetadataSerializer(read_only=True)
|
|
163
|
-
links =
|
|
169
|
+
links = LinkListField(read_only=True, required=False)
|
|
164
170
|
publications = ListField(
|
|
165
171
|
child=OPDS2PublicationSerializer(), read_only=True, required=False
|
|
166
172
|
)
|
|
167
|
-
navigation =
|
|
173
|
+
navigation = LinkListField(read_only=True, required=False)
|
|
168
174
|
|
|
169
175
|
|
|
170
176
|
class OPDS2FeedSerializer(OPDS2GroupSerializer):
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from rest_framework.fields import CharField, DateTimeField, DecimalField, IntegerField
|
|
4
4
|
from rest_framework.serializers import ChoiceField, Serializer
|
|
5
5
|
|
|
6
|
-
from codex.serializers.opds.v2 import
|
|
6
|
+
from codex.serializers.opds.v2 import LinkListField
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
class RecursiveField(Serializer):
|
|
@@ -15,9 +15,7 @@ class RecursiveField(Serializer):
|
|
|
15
15
|
|
|
16
16
|
def to_representation(self, instance):
|
|
17
17
|
"""Represent with own class."""
|
|
18
|
-
|
|
19
|
-
# Should not be ignored but is currently unused
|
|
20
|
-
serializer = parent.parent.__class__(instance, context=self.context) # type: ignore
|
|
18
|
+
serializer = self.parent.parent.__class__(instance, context=self.context)
|
|
21
19
|
return serializer.data
|
|
22
20
|
|
|
23
21
|
|
|
@@ -70,7 +68,7 @@ class OPDS2ProfileSerializer(Serializer):
|
|
|
70
68
|
|
|
71
69
|
name = CharField(read_only=True)
|
|
72
70
|
email = CharField(read_only=True)
|
|
73
|
-
links =
|
|
71
|
+
links = LinkListField(read_only=True, required=False)
|
|
74
72
|
loans = OPDS2CopiesSerializer(read_only=True)
|
|
75
73
|
holds = OPDS2HoldsSerializer(read_only=True)
|
|
76
74
|
|