codex 1.6.0a10__py3-none-any.whl → 1.6.0a12__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/librarian/covers/create.py +21 -16
- codex/serializers/browser/choices.py +23 -44
- codex/serializers/browser/filters.py +30 -36
- codex/serializers/browser/mixins.py +37 -4
- codex/serializers/browser/settings.py +10 -3
- codex/serializers/choices.py +4 -0
- codex/serializers/fields.py +75 -66
- codex/serializers/models/pycountry.py +11 -51
- codex/serializers/opds/v2.py +4 -6
- codex/serializers/reader.py +1 -0
- codex/static_root/assets/{VCheckbox-CY3rcqlS.de5154294920.js → VCheckbox-B3vCf1Vu.e1991f98ab6c.js} +1 -1
- codex/static_root/assets/VCheckbox-B3vCf1Vu.e1991f98ab6c.js.br +0 -0
- codex/static_root/assets/VCheckbox-B3vCf1Vu.e1991f98ab6c.js.gz +0 -0
- codex/static_root/assets/{VCheckbox-CY3rcqlS.js → VCheckbox-B3vCf1Vu.js} +1 -1
- codex/static_root/assets/VCheckbox-B3vCf1Vu.js.br +0 -0
- codex/static_root/assets/VCheckbox-B3vCf1Vu.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js → VCheckboxBtn-Cj6bUYmw.196cc43ca1c8.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Cj6bUYmw.196cc43ca1c8.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Cj6bUYmw.196cc43ca1c8.js.gz +0 -0
- codex/static_root/assets/{VCheckboxBtn-D2tPTt6P.js → VCheckboxBtn-Cj6bUYmw.js} +1 -1
- codex/static_root/assets/VCheckboxBtn-Cj6bUYmw.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-Cj6bUYmw.js.gz +0 -0
- codex/static_root/assets/{VCombobox-aJr--Ou7.f290ccf2ce25.js → VCombobox-wIqJY8no.3547ff73e91e.js} +1 -1
- codex/static_root/assets/VCombobox-wIqJY8no.3547ff73e91e.js.br +0 -0
- codex/static_root/assets/VCombobox-wIqJY8no.3547ff73e91e.js.gz +0 -0
- codex/static_root/assets/{VCombobox-aJr--Ou7.js → VCombobox-wIqJY8no.js} +1 -1
- codex/static_root/assets/VCombobox-wIqJY8no.js.br +0 -0
- codex/static_root/assets/VCombobox-wIqJY8no.js.gz +0 -0
- codex/static_root/assets/{VDialog-61pzfUB2.33f73beb5224.js → VDialog-D6HUFV6a.ac71da510b3f.js} +1 -1
- codex/static_root/assets/VDialog-D6HUFV6a.ac71da510b3f.js.br +0 -0
- codex/static_root/assets/VDialog-D6HUFV6a.ac71da510b3f.js.gz +0 -0
- codex/static_root/assets/{VDialog-61pzfUB2.js → VDialog-D6HUFV6a.js} +1 -1
- codex/static_root/assets/VDialog-D6HUFV6a.js.br +0 -0
- codex/static_root/assets/VDialog-D6HUFV6a.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-rewm0P4z.5c7680f815b6.js → VExpansionPanels-BUJaYmzY.44e8a00a2ebe.js} +1 -1
- codex/static_root/assets/VExpansionPanels-BUJaYmzY.44e8a00a2ebe.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BUJaYmzY.44e8a00a2ebe.js.gz +0 -0
- codex/static_root/assets/{VExpansionPanels-rewm0P4z.js → VExpansionPanels-BUJaYmzY.js} +1 -1
- codex/static_root/assets/VExpansionPanels-BUJaYmzY.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-BUJaYmzY.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-QFQbK1qo.750a42b1b814.js → VRadioGroup-CayclHra.0a5b1c9a7b71.js} +1 -1
- codex/static_root/assets/VRadioGroup-CayclHra.0a5b1c9a7b71.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CayclHra.0a5b1c9a7b71.js.gz +0 -0
- codex/static_root/assets/{VRadioGroup-QFQbK1qo.js → VRadioGroup-CayclHra.js} +1 -1
- codex/static_root/assets/VRadioGroup-CayclHra.js.br +0 -0
- codex/static_root/assets/VRadioGroup-CayclHra.js.gz +0 -0
- codex/static_root/assets/{VSelect-Dixy6okt.5d53830daa8a.js → VSelect-ou2Q9UaN.8ddd219784e1.js} +1 -1
- codex/static_root/assets/VSelect-ou2Q9UaN.8ddd219784e1.js.br +0 -0
- codex/static_root/assets/VSelect-ou2Q9UaN.8ddd219784e1.js.gz +0 -0
- codex/static_root/assets/{VSelect-Dixy6okt.js → VSelect-ou2Q9UaN.js} +1 -1
- codex/static_root/assets/VSelect-ou2Q9UaN.js.br +0 -0
- codex/static_root/assets/VSelect-ou2Q9UaN.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-MNmxKc3J.js → VSelectionControl-BOKIwNZb.16e0cdde978c.js} +1 -1
- codex/static_root/assets/VSelectionControl-BOKIwNZb.16e0cdde978c.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BOKIwNZb.16e0cdde978c.js.gz +0 -0
- codex/static_root/assets/{VSelectionControl-MNmxKc3J.08055d00917e.js → VSelectionControl-BOKIwNZb.js} +1 -1
- codex/static_root/assets/VSelectionControl-BOKIwNZb.js.br +0 -0
- codex/static_root/assets/VSelectionControl-BOKIwNZb.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-DZEV0FoV.5f996daf0e98.js → VSlideGroup-C3XgVg2T.d2b0ee4f596c.js} +1 -1
- codex/static_root/assets/VSlideGroup-C3XgVg2T.d2b0ee4f596c.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C3XgVg2T.d2b0ee4f596c.js.gz +0 -0
- codex/static_root/assets/{VSlideGroup-DZEV0FoV.js → VSlideGroup-C3XgVg2T.js} +1 -1
- codex/static_root/assets/VSlideGroup-C3XgVg2T.js.br +0 -0
- codex/static_root/assets/VSlideGroup-C3XgVg2T.js.gz +0 -0
- codex/static_root/assets/{VTable-lcnYg9Ee.d72927b13c2e.js → VTable-CrIiBr9q.b4669247af45.js} +1 -1
- codex/static_root/assets/VTable-CrIiBr9q.b4669247af45.js.br +0 -0
- codex/static_root/assets/VTable-CrIiBr9q.b4669247af45.js.gz +0 -0
- codex/static_root/assets/{VTable-lcnYg9Ee.js → VTable-CrIiBr9q.js} +1 -1
- codex/static_root/assets/VTable-CrIiBr9q.js.br +0 -0
- codex/static_root/assets/VTable-CrIiBr9q.js.gz +0 -0
- codex/static_root/assets/{VTextField-BkFrjhdV.js → VTextField-HAAxrpNi.6f6dcb50079a.js} +1 -1
- codex/static_root/assets/VTextField-HAAxrpNi.6f6dcb50079a.js.br +0 -0
- codex/static_root/assets/VTextField-HAAxrpNi.6f6dcb50079a.js.gz +0 -0
- codex/static_root/assets/{VTextField-BkFrjhdV.b02653a9b3b7.js → VTextField-HAAxrpNi.js} +1 -1
- codex/static_root/assets/VTextField-HAAxrpNi.js.br +0 -0
- codex/static_root/assets/VTextField-HAAxrpNi.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C__tAqdc.9791c128c3b7.js → VWindowItem-aal6-Uv4.d178e2af20a7.js} +1 -1
- codex/static_root/assets/VWindowItem-aal6-Uv4.d178e2af20a7.js.br +0 -0
- codex/static_root/assets/VWindowItem-aal6-Uv4.d178e2af20a7.js.gz +0 -0
- codex/static_root/assets/{VWindowItem-C__tAqdc.js → VWindowItem-aal6-Uv4.js} +1 -1
- codex/static_root/assets/VWindowItem-aal6-Uv4.js.br +0 -0
- codex/static_root/assets/VWindowItem-aal6-Uv4.js.gz +0 -0
- codex/static_root/assets/admin-D5JDmLDy.e4df3dd3a3fe.js +1 -0
- codex/static_root/assets/admin-D5JDmLDy.e4df3dd3a3fe.js.br +0 -0
- codex/static_root/assets/admin-D5JDmLDy.e4df3dd3a3fe.js.gz +0 -0
- codex/static_root/assets/admin-D5JDmLDy.js +1 -0
- codex/static_root/assets/admin-D5JDmLDy.js.br +0 -0
- codex/static_root/assets/admin-D5JDmLDy.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-rrNKi_GE.e2b034db7934.js → admin-drawer-panel-BF-h3Wm0.1f372db8d08f.js} +2 -2
- codex/static_root/assets/admin-drawer-panel-BF-h3Wm0.1f372db8d08f.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF-h3Wm0.1f372db8d08f.js.gz +0 -0
- codex/static_root/assets/{admin-drawer-panel-rrNKi_GE.js → admin-drawer-panel-BF-h3Wm0.js} +2 -2
- codex/static_root/assets/admin-drawer-panel-BF-h3Wm0.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-BF-h3Wm0.js.gz +0 -0
- codex/static_root/assets/browser-B1-dPld9.0b3fa4fdbe22.css +1 -0
- codex/static_root/assets/browser-B1-dPld9.0b3fa4fdbe22.css.br +0 -0
- codex/static_root/assets/browser-B1-dPld9.0b3fa4fdbe22.css.gz +0 -0
- codex/static_root/assets/browser-B1-dPld9.css +1 -0
- codex/static_root/assets/browser-B1-dPld9.css.br +0 -0
- codex/static_root/assets/browser-B1-dPld9.css.gz +0 -0
- codex/static_root/assets/browser-VhFkImB_.9c3b2c746614.js +1 -0
- codex/static_root/assets/browser-VhFkImB_.9c3b2c746614.js.br +0 -0
- codex/static_root/assets/browser-VhFkImB_.9c3b2c746614.js.gz +0 -0
- codex/static_root/assets/browser-VhFkImB_.js +1 -0
- codex/static_root/assets/browser-VhFkImB_.js.br +0 -0
- codex/static_root/assets/browser-VhFkImB_.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-Bn20jYzD.593c5af1732b.js → change-password-dialog-BCYw5K7E.a8bae0450f20.js} +1 -1
- codex/static_root/assets/change-password-dialog-BCYw5K7E.a8bae0450f20.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BCYw5K7E.a8bae0450f20.js.gz +0 -0
- codex/static_root/assets/{change-password-dialog-Bn20jYzD.js → change-password-dialog-BCYw5K7E.js} +1 -1
- codex/static_root/assets/change-password-dialog-BCYw5K7E.js.br +0 -0
- codex/static_root/assets/change-password-dialog-BCYw5K7E.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-CWszioQE.859051c35dd9.js → confirm-dialog-c9JOSGSy.3c5788f95f47.js} +1 -1
- codex/static_root/assets/confirm-dialog-c9JOSGSy.3c5788f95f47.js.br +3 -0
- codex/static_root/assets/confirm-dialog-c9JOSGSy.3c5788f95f47.js.gz +0 -0
- codex/static_root/assets/{confirm-dialog-CWszioQE.js → confirm-dialog-c9JOSGSy.js} +1 -1
- codex/static_root/assets/confirm-dialog-c9JOSGSy.js.br +3 -0
- codex/static_root/assets/confirm-dialog-c9JOSGSy.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BSdlLt-4.27ec2df67a0b.js → datetime-column-D-6LPKdI.d46bbd8134df.js} +1 -1
- codex/static_root/assets/datetime-column-D-6LPKdI.d46bbd8134df.js.br +0 -0
- codex/static_root/assets/datetime-column-D-6LPKdI.d46bbd8134df.js.gz +0 -0
- codex/static_root/assets/{datetime-column-BSdlLt-4.js → datetime-column-D-6LPKdI.js} +1 -1
- codex/static_root/assets/datetime-column-D-6LPKdI.js.br +0 -0
- codex/static_root/assets/datetime-column-D-6LPKdI.js.gz +0 -0
- codex/static_root/assets/{filter-D3gaGf87.1f14dbfd9929.js → filter-BOnVoHTU.93c3d0d0d3f6.js} +1 -1
- codex/static_root/assets/filter-BOnVoHTU.93c3d0d0d3f6.js.br +0 -0
- codex/static_root/assets/filter-BOnVoHTU.93c3d0d0d3f6.js.gz +0 -0
- codex/static_root/assets/{filter-D3gaGf87.js → filter-BOnVoHTU.js} +1 -1
- codex/static_root/assets/filter-BOnVoHTU.js.br +0 -0
- codex/static_root/assets/filter-BOnVoHTU.js.gz +0 -0
- codex/static_root/assets/{flag-tab-8vzpdySX.a982c7f0776a.js → flag-tab-IniE-dLy.a02733e359aa.js} +1 -1
- codex/static_root/assets/flag-tab-IniE-dLy.a02733e359aa.js.br +0 -0
- codex/static_root/assets/flag-tab-IniE-dLy.a02733e359aa.js.gz +0 -0
- codex/static_root/assets/{flag-tab-8vzpdySX.js → flag-tab-IniE-dLy.js} +1 -1
- codex/static_root/assets/flag-tab-IniE-dLy.js.br +0 -0
- codex/static_root/assets/flag-tab-IniE-dLy.js.gz +0 -0
- codex/static_root/assets/{group-tab-Cz9t_ECi.a9e8aea79254.js → group-tab-RPa8XC0J.3d483919ea92.js} +1 -1
- codex/static_root/assets/group-tab-RPa8XC0J.3d483919ea92.js.br +0 -0
- codex/static_root/assets/group-tab-RPa8XC0J.3d483919ea92.js.gz +0 -0
- codex/static_root/assets/{group-tab-Cz9t_ECi.js → group-tab-RPa8XC0J.js} +1 -1
- codex/static_root/assets/group-tab-RPa8XC0J.js.br +0 -0
- codex/static_root/assets/group-tab-RPa8XC0J.js.gz +0 -0
- codex/static_root/assets/{http-error-Cqtszzf1.b31df3d9c29f.js → http-error-DBQycf8i.561cde2b4598.js} +1 -1
- codex/static_root/assets/http-error-DBQycf8i.561cde2b4598.js.br +0 -0
- codex/static_root/assets/http-error-DBQycf8i.561cde2b4598.js.gz +0 -0
- codex/static_root/assets/{http-error-Cqtszzf1.js → http-error-DBQycf8i.js} +1 -1
- codex/static_root/assets/http-error-DBQycf8i.js.br +0 -0
- codex/static_root/assets/http-error-DBQycf8i.js.gz +0 -0
- codex/static_root/assets/{library-tab-DMlnswUU.js → library-tab-C5trLvkq.35c7a1b79c71.js} +1 -1
- codex/static_root/assets/library-tab-C5trLvkq.35c7a1b79c71.js.br +0 -0
- codex/static_root/assets/library-tab-C5trLvkq.35c7a1b79c71.js.gz +0 -0
- codex/static_root/assets/{library-tab-DMlnswUU.30f454e84184.js → library-tab-C5trLvkq.js} +1 -1
- codex/static_root/assets/library-tab-C5trLvkq.js.br +0 -0
- codex/static_root/assets/library-tab-C5trLvkq.js.gz +0 -0
- codex/static_root/assets/main-x0Pcl23A.364bfe447c9c.js +32 -0
- codex/static_root/assets/main-x0Pcl23A.364bfe447c9c.js.br +0 -0
- codex/static_root/assets/main-x0Pcl23A.364bfe447c9c.js.gz +0 -0
- codex/static_root/assets/main-x0Pcl23A.js +32 -0
- codex/static_root/assets/main-x0Pcl23A.js.br +0 -0
- codex/static_root/assets/main-x0Pcl23A.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.50051431acf1.css +1 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.50051431acf1.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.50051431acf1.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.css +1 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-BLUoVz2y.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.71aa03f525e4.js +1 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.71aa03f525e4.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.71aa03f525e4.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.js +1 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-DEgDOzzp.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BiamrjRZ.4c9be1ade43c.js → pdf-doc-CkV-xfvG.35c1a6a19a41.js} +1 -1
- codex/static_root/assets/pdf-doc-CkV-xfvG.35c1a6a19a41.js.br +0 -0
- codex/static_root/assets/pdf-doc-CkV-xfvG.35c1a6a19a41.js.gz +0 -0
- codex/static_root/assets/{pdf-doc-BiamrjRZ.js → pdf-doc-CkV-xfvG.js} +1 -1
- codex/static_root/assets/pdf-doc-CkV-xfvG.js.br +0 -0
- codex/static_root/assets/pdf-doc-CkV-xfvG.js.gz +0 -0
- codex/static_root/assets/reader-ClcgSG93.6afe950e70ac.js +2 -0
- codex/static_root/assets/reader-ClcgSG93.6afe950e70ac.js.br +0 -0
- codex/static_root/assets/reader-ClcgSG93.6afe950e70ac.js.gz +0 -0
- codex/static_root/assets/reader-ClcgSG93.js +2 -0
- codex/static_root/assets/reader-ClcgSG93.js.br +0 -0
- codex/static_root/assets/reader-ClcgSG93.js.gz +0 -0
- codex/static_root/assets/{reader-BpsctRsY.5d72e393911a.css → reader-CqXFa_gE.78267e9de0c2.css} +1 -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-BpsctRsY.css → reader-CqXFa_gE.css} +1 -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-C5acBjpy.dc67d4528868.js → relation-chips-2vga5ApB.d94c344f909b.js} +1 -1
- codex/static_root/assets/relation-chips-2vga5ApB.d94c344f909b.js.br +0 -0
- codex/static_root/assets/relation-chips-2vga5ApB.d94c344f909b.js.gz +0 -0
- codex/static_root/assets/{relation-chips-C5acBjpy.js → relation-chips-2vga5ApB.js} +1 -1
- codex/static_root/assets/relation-chips-2vga5ApB.js.br +0 -0
- codex/static_root/assets/relation-chips-2vga5ApB.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-D_JuRi7F.f02f3f981f8f.js → settings-drawer-BOTI_tHr.03748f801e08.js} +2 -2
- codex/static_root/assets/settings-drawer-BOTI_tHr.03748f801e08.js.br +0 -0
- codex/static_root/assets/settings-drawer-BOTI_tHr.03748f801e08.js.gz +0 -0
- codex/static_root/assets/{settings-drawer-D_JuRi7F.js → settings-drawer-BOTI_tHr.js} +2 -2
- codex/static_root/assets/settings-drawer-BOTI_tHr.js.br +0 -0
- codex/static_root/assets/settings-drawer-BOTI_tHr.js.gz +0 -0
- codex/static_root/assets/{stats-tab-ClqRTk1z.c759ace65a87.js → stats-tab-CBz_jhmH.9bdc41225f06.js} +1 -1
- codex/static_root/assets/stats-tab-CBz_jhmH.9bdc41225f06.js.br +0 -0
- codex/static_root/assets/stats-tab-CBz_jhmH.9bdc41225f06.js.gz +0 -0
- codex/static_root/assets/{stats-tab-ClqRTk1z.js → stats-tab-CBz_jhmH.js} +1 -1
- codex/static_root/assets/stats-tab-CBz_jhmH.js.br +0 -0
- codex/static_root/assets/stats-tab-CBz_jhmH.js.gz +0 -0
- codex/static_root/assets/{task-tab-DF9qa6LO.0b32cafde4ad.js → task-tab-B8sYV6Ia.90a8deec0214.js} +1 -1
- codex/static_root/assets/task-tab-B8sYV6Ia.90a8deec0214.js.br +0 -0
- codex/static_root/assets/task-tab-B8sYV6Ia.90a8deec0214.js.gz +0 -0
- codex/static_root/assets/{task-tab-DF9qa6LO.js → task-tab-B8sYV6Ia.js} +1 -1
- codex/static_root/assets/task-tab-B8sYV6Ia.js.br +0 -0
- codex/static_root/assets/task-tab-B8sYV6Ia.js.gz +0 -0
- codex/static_root/assets/to-case-CnnzURtN.f0e4b42ddb89.js +1 -0
- codex/static_root/assets/to-case-CnnzURtN.js +1 -0
- codex/static_root/assets/{unauthorized-BjKuLH6k.2ae00ddd79fb.js → unauthorized-D5-FvA2_.61b1cd239b47.js} +1 -1
- codex/static_root/assets/unauthorized-D5-FvA2_.61b1cd239b47.js.br +0 -0
- codex/static_root/assets/unauthorized-D5-FvA2_.61b1cd239b47.js.gz +0 -0
- codex/static_root/assets/{unauthorized-BjKuLH6k.js → unauthorized-D5-FvA2_.js} +1 -1
- codex/static_root/assets/unauthorized-D5-FvA2_.js.br +0 -0
- codex/static_root/assets/unauthorized-D5-FvA2_.js.gz +0 -0
- codex/static_root/assets/{user-tab-Dpxe3ygA.7d90174631f0.js → user-tab-pzFjrggM.97c02ebee729.js} +1 -1
- codex/static_root/assets/user-tab-pzFjrggM.97c02ebee729.js.br +0 -0
- codex/static_root/assets/user-tab-pzFjrggM.97c02ebee729.js.gz +0 -0
- codex/static_root/assets/{user-tab-Dpxe3ygA.js → user-tab-pzFjrggM.js} +1 -1
- codex/static_root/assets/user-tab-pzFjrggM.js.br +0 -0
- codex/static_root/assets/user-tab-pzFjrggM.js.gz +0 -0
- codex/static_root/js/{choices.a0aaaa6c7ef7.json → choices.2f6b0359e1ef.json} +1 -1
- codex/static_root/js/choices.2f6b0359e1ef.json.br +0 -0
- codex/static_root/js/choices.2f6b0359e1ef.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.35e46a3ec8fa.json → manifest.a18726680d5f.json} +223 -223
- codex/static_root/manifest.a18726680d5f.json.br +0 -0
- codex/static_root/manifest.a18726680d5f.json.gz +0 -0
- codex/static_root/manifest.json +223 -223
- codex/static_root/manifest.json.br +0 -0
- codex/static_root/manifest.json.gz +0 -0
- codex/static_root/staticfiles.json +1 -1
- codex/util.py +8 -6
- codex/views/bookmark.py +29 -23
- codex/views/browser/annotations.py +9 -45
- codex/views/browser/browser.py +26 -6
- codex/views/browser/choices.py +4 -22
- codex/views/browser/cover.py +35 -11
- codex/views/browser/filters/annotations.py +23 -8
- codex/views/browser/metadata.py +22 -12
- codex/views/const.py +1 -1
- codex/views/reader/init.py +16 -12
- codex/views/reader/page.py +13 -2
- codex/views/session.py +3 -1
- codex/views/util.py +13 -0
- codex/websockets/consumers.py +1 -1
- {codex-1.6.0a10.dist-info → codex-1.6.0a12.dist-info}/METADATA +1 -1
- {codex-1.6.0a10.dist-info → codex-1.6.0a12.dist-info}/RECORD +260 -262
- codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.br +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.de5154294920.js.gz +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.js.br +0 -0
- codex/static_root/assets/VCheckbox-CY3rcqlS.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.9a90d6818bf6.js.gz +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.br +0 -0
- codex/static_root/assets/VCheckboxBtn-D2tPTt6P.js.gz +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.br +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.f290ccf2ce25.js.gz +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.js.br +0 -0
- codex/static_root/assets/VCombobox-aJr--Ou7.js.gz +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.br +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.33f73beb5224.js.gz +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.js.br +0 -0
- codex/static_root/assets/VDialog-61pzfUB2.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.5c7680f815b6.js.gz +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.js.br +0 -0
- codex/static_root/assets/VExpansionPanels-rewm0P4z.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.br +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.750a42b1b814.js.gz +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.js.br +0 -0
- codex/static_root/assets/VRadioGroup-QFQbK1qo.js.gz +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.br +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.5d53830daa8a.js.gz +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.js.br +0 -0
- codex/static_root/assets/VSelect-Dixy6okt.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.br +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.08055d00917e.js.gz +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.js.br +0 -0
- codex/static_root/assets/VSelectionControl-MNmxKc3J.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.5f996daf0e98.js.gz +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.js.br +0 -0
- codex/static_root/assets/VSlideGroup-DZEV0FoV.js.gz +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.br +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.d72927b13c2e.js.gz +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.js.br +0 -0
- codex/static_root/assets/VTable-lcnYg9Ee.js.gz +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.br +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.b02653a9b3b7.js.gz +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.js.br +0 -0
- codex/static_root/assets/VTextField-BkFrjhdV.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.br +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.9791c128c3b7.js.gz +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.js.br +0 -0
- codex/static_root/assets/VWindowItem-C__tAqdc.js.gz +0 -0
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js +0 -1
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.br +0 -0
- codex/static_root/assets/admin-CXES2HXe.9e48a9bf84e0.js.gz +0 -0
- codex/static_root/assets/admin-CXES2HXe.js +0 -1
- codex/static_root/assets/admin-CXES2HXe.js.br +0 -0
- codex/static_root/assets/admin-CXES2HXe.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.e2b034db7934.js.gz +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.br +0 -0
- codex/static_root/assets/admin-drawer-panel-rrNKi_GE.js.gz +0 -0
- codex/static_root/assets/browser-BuDeXGEe.css +0 -1
- codex/static_root/assets/browser-BuDeXGEe.css.br +0 -0
- codex/static_root/assets/browser-BuDeXGEe.css.gz +0 -0
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css +0 -1
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.br +0 -0
- codex/static_root/assets/browser-BuDeXGEe.f60854183b45.css.gz +0 -0
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js +0 -1
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.br +0 -0
- codex/static_root/assets/browser-s6mFzrK9.0d2200b451af.js.gz +0 -0
- codex/static_root/assets/browser-s6mFzrK9.js +0 -1
- codex/static_root/assets/browser-s6mFzrK9.js.br +0 -0
- codex/static_root/assets/browser-s6mFzrK9.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.593c5af1732b.js.gz +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.js.br +0 -0
- codex/static_root/assets/change-password-dialog-Bn20jYzD.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.859051c35dd9.js.gz +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.js.br +0 -0
- codex/static_root/assets/confirm-dialog-CWszioQE.js.gz +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.br +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.27ec2df67a0b.js.gz +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.js.br +0 -0
- codex/static_root/assets/datetime-column-BSdlLt-4.js.gz +0 -0
- codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.br +0 -0
- codex/static_root/assets/filter-D3gaGf87.1f14dbfd9929.js.gz +0 -0
- codex/static_root/assets/filter-D3gaGf87.js.br +0 -0
- codex/static_root/assets/filter-D3gaGf87.js.gz +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.br +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.a982c7f0776a.js.gz +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.js.br +0 -0
- codex/static_root/assets/flag-tab-8vzpdySX.js.gz +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.br +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.a9e8aea79254.js.gz +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.js.br +0 -0
- codex/static_root/assets/group-tab-Cz9t_ECi.js.gz +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.br +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.b31df3d9c29f.js.gz +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.js.br +0 -0
- codex/static_root/assets/http-error-Cqtszzf1.js.gz +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.br +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.30f454e84184.js.gz +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.js.br +0 -0
- codex/static_root/assets/library-tab-DMlnswUU.js.gz +0 -0
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js +0 -32
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.br +0 -0
- codex/static_root/assets/main-vjXu_QYL.548bd4c38799.js.gz +0 -0
- codex/static_root/assets/main-vjXu_QYL.js +0 -32
- codex/static_root/assets/main-vjXu_QYL.js.br +0 -0
- codex/static_root/assets/main-vjXu_QYL.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css +0 -1
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.5829b01b8a2f.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css +0 -1
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.br +0 -0
- codex/static_root/assets/pagination-toolbar-DGdMiBRs.css.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js +0 -1
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.91024effdab9.js.gz +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js +0 -1
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js.br +0 -0
- codex/static_root/assets/pagination-toolbar-JW78pa0q.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.br +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.4c9be1ade43c.js.gz +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.js.br +0 -0
- codex/static_root/assets/pdf-doc-BiamrjRZ.js.gz +0 -0
- codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.br +0 -0
- codex/static_root/assets/reader-BpsctRsY.5d72e393911a.css.gz +0 -0
- codex/static_root/assets/reader-BpsctRsY.css.br +0 -0
- codex/static_root/assets/reader-BpsctRsY.css.gz +0 -0
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js +0 -2
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.br +0 -0
- codex/static_root/assets/reader-CDk8oZzB.ad62eebda920.js.gz +0 -0
- codex/static_root/assets/reader-CDk8oZzB.js +0 -2
- codex/static_root/assets/reader-CDk8oZzB.js.br +0 -0
- codex/static_root/assets/reader-CDk8oZzB.js.gz +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.br +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.dc67d4528868.js.gz +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.js.br +0 -0
- codex/static_root/assets/relation-chips-C5acBjpy.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.br +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.f02f3f981f8f.js.gz +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.js.br +0 -0
- codex/static_root/assets/settings-drawer-D_JuRi7F.js.gz +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.br +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.c759ace65a87.js.gz +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.js.br +0 -0
- codex/static_root/assets/stats-tab-ClqRTk1z.js.gz +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.br +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.0b32cafde4ad.js.gz +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.js.br +0 -0
- codex/static_root/assets/task-tab-DF9qa6LO.js.gz +0 -0
- codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js +0 -1
- codex/static_root/assets/to-case-CGbULRaE.4c80644f460d.js.br +0 -0
- codex/static_root/assets/to-case-CGbULRaE.js +0 -1
- codex/static_root/assets/to-case-CGbULRaE.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.2ae00ddd79fb.js.gz +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.js.br +0 -0
- codex/static_root/assets/unauthorized-BjKuLH6k.js.gz +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.br +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.7d90174631f0.js.gz +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.js.br +0 -0
- codex/static_root/assets/user-tab-Dpxe3ygA.js.gz +0 -0
- codex/static_root/js/choices.a0aaaa6c7ef7.json.br +0 -0
- codex/static_root/js/choices.a0aaaa6c7ef7.json.gz +0 -0
- codex/static_root/manifest.35e46a3ec8fa.json.br +0 -0
- codex/static_root/manifest.35e46a3ec8fa.json.gz +0 -0
- {codex-1.6.0a10.dist-info → codex-1.6.0a12.dist-info}/LICENSE +0 -0
- {codex-1.6.0a10.dist-info → codex-1.6.0a12.dist-info}/WHEEL +0 -0
- {codex-1.6.0a10.dist-info → codex-1.6.0a12.dist-info}/entry_points.txt +0 -0
codex/librarian/covers/create.py
CHANGED
|
@@ -26,17 +26,17 @@ class CoverCreateMixin(CoverPathMixin):
|
|
|
26
26
|
@classmethod
|
|
27
27
|
def _create_cover_thumbnail(cls, cover_image_data):
|
|
28
28
|
"""Isolate the save thumbnail function for leak detection."""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
cover_thumb_buffer = BytesIO()
|
|
30
|
+
with BytesIO(cover_image_data) as image_io:
|
|
31
|
+
with Image.open(image_io) as cover_image:
|
|
32
|
+
cover_image.thumbnail(
|
|
33
|
+
cls._THUMBNAIL_SIZE,
|
|
34
|
+
Image.Resampling.LANCZOS, # type: ignore
|
|
35
|
+
reducing_gap=3.0,
|
|
36
|
+
)
|
|
37
|
+
cover_image.save(cover_thumb_buffer, "WEBP", method=6)
|
|
38
|
+
cover_image.close() # extra close for animated sequences
|
|
39
|
+
return cover_thumb_buffer
|
|
40
40
|
|
|
41
41
|
@classmethod
|
|
42
42
|
def _get_comic_cover_image(cls, comic_path):
|
|
@@ -71,15 +71,18 @@ class CoverCreateMixin(CoverPathMixin):
|
|
|
71
71
|
cover_image = cls._get_custom_cover_image(db_path)
|
|
72
72
|
else:
|
|
73
73
|
cover_image = cls._get_comic_cover_image(db_path)
|
|
74
|
-
|
|
74
|
+
thumb_buffer = cls._create_cover_thumbnail(cover_image)
|
|
75
|
+
thumb_bytes = thumb_buffer.getvalue()
|
|
76
|
+
thumb_buffer.seek(0)
|
|
75
77
|
except Exception as exc:
|
|
76
|
-
|
|
78
|
+
thumb_bytes = b""
|
|
79
|
+
thumb_buffer = None
|
|
77
80
|
cover_str = db_path if db_path else f"{pk=}"
|
|
78
81
|
log.warning(f"Could not create cover thumbnail for {cover_str}: {exc}")
|
|
79
82
|
|
|
80
|
-
task = CoverSaveToCache(cover_path,
|
|
83
|
+
task = CoverSaveToCache(cover_path, thumb_bytes)
|
|
81
84
|
librarian_queue.put(task)
|
|
82
|
-
return
|
|
85
|
+
return thumb_buffer
|
|
83
86
|
|
|
84
87
|
def save_cover_to_cache(self, cover_path, data):
|
|
85
88
|
"""Save cover thumb image to the disk cache."""
|
|
@@ -110,9 +113,11 @@ class CoverCreateMixin(CoverPathMixin):
|
|
|
110
113
|
status.decrement_total()
|
|
111
114
|
else:
|
|
112
115
|
# bulk contributor creates covers inline
|
|
113
|
-
self.create_cover_from_path(
|
|
116
|
+
data = self.create_cover_from_path(
|
|
114
117
|
pk, cover_path, self.log, self.librarian_queue
|
|
115
118
|
)
|
|
119
|
+
if data:
|
|
120
|
+
data.close()
|
|
116
121
|
status.increment_complete()
|
|
117
122
|
self.status_controller.update(status)
|
|
118
123
|
|
|
@@ -4,66 +4,40 @@ from types import MappingProxyType
|
|
|
4
4
|
|
|
5
5
|
from rest_framework.fields import SerializerMethodField
|
|
6
6
|
from rest_framework.serializers import (
|
|
7
|
-
BooleanField,
|
|
8
7
|
CharField,
|
|
9
|
-
ChoiceField,
|
|
10
|
-
FloatField,
|
|
11
|
-
IntegerField,
|
|
12
8
|
Serializer,
|
|
13
9
|
)
|
|
14
10
|
from rest_framework.utils.serializer_helpers import ReturnList
|
|
15
11
|
|
|
16
12
|
from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
|
|
17
13
|
from codex.serializers.fields import (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
DecadeListField,
|
|
21
|
-
FloatListField,
|
|
22
|
-
IntListField,
|
|
14
|
+
VuetifyCharField,
|
|
15
|
+
VuetifyIntegerField,
|
|
23
16
|
)
|
|
17
|
+
from codex.serializers.models.pycountry import CountrySerializer, LanguageSerializer
|
|
24
18
|
|
|
25
19
|
|
|
26
20
|
class BrowserChoicesCharPkSerializer(Serializer):
|
|
27
|
-
"""Named Model
|
|
21
|
+
"""Named Model Serializer."""
|
|
28
22
|
|
|
29
|
-
pk =
|
|
23
|
+
pk = VuetifyCharField(read_only=True)
|
|
30
24
|
name = CharField(read_only=True)
|
|
31
25
|
|
|
32
26
|
|
|
33
|
-
class
|
|
34
|
-
"""Named Model Serailizer with pk = char hack for languages & countries."""
|
|
35
|
-
|
|
36
|
-
pk = BooleanField(read_only=True)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class BrowserChoicesIntPkSerializer(BrowserChoicesCharPkSerializer):
|
|
40
|
-
"""Named Model Serailizer with pk = char hack for languages & countries."""
|
|
41
|
-
|
|
42
|
-
pk = IntegerField(read_only=True)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class BrowserChoicesFloatPkSerializer(BrowserChoicesCharPkSerializer):
|
|
27
|
+
class BrowserChoicesIntegerPkSerializer(BrowserChoicesCharPkSerializer):
|
|
46
28
|
"""Named Model Serailizer with pk = char hack for languages & countries."""
|
|
47
29
|
|
|
48
|
-
pk =
|
|
30
|
+
pk = VuetifyIntegerField(read_only=True)
|
|
49
31
|
|
|
50
32
|
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
BooleanListField: BrowserChoicesBoolPkSerializer,
|
|
54
|
-
CharListField: BrowserChoicesCharPkSerializer,
|
|
55
|
-
ChoiceField: BrowserChoicesCharPkSerializer,
|
|
56
|
-
DecadeListField: BrowserChoicesIntPkSerializer,
|
|
57
|
-
FloatListField: BrowserChoicesFloatPkSerializer,
|
|
58
|
-
IntListField: BrowserChoicesIntPkSerializer,
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
_CHOICES_SERIALIZER_CLASS_MAP = MappingProxyType(
|
|
33
|
+
_CHOICES_NAME_SERIALIZER_MAP = MappingProxyType(
|
|
62
34
|
{
|
|
63
|
-
|
|
64
|
-
|
|
35
|
+
"bookmark": BrowserChoicesCharPkSerializer,
|
|
36
|
+
"country": CountrySerializer,
|
|
37
|
+
"language": LanguageSerializer,
|
|
65
38
|
}
|
|
66
39
|
)
|
|
40
|
+
_LIST_FIELDS = frozenset({"decade", "monochrome", "reading_direction", "year"})
|
|
67
41
|
|
|
68
42
|
|
|
69
43
|
class BrowserChoicesFilterSerializer(Serializer):
|
|
@@ -73,10 +47,15 @@ class BrowserChoicesFilterSerializer(Serializer):
|
|
|
73
47
|
|
|
74
48
|
def get_choices(self, obj) -> ReturnList:
|
|
75
49
|
"""Dynamic Serializer response by field type."""
|
|
76
|
-
|
|
50
|
+
field_name = obj.get("field_name", "")
|
|
77
51
|
choices = obj.get("choices", [])
|
|
78
|
-
serializer_class =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
52
|
+
serializer_class = _CHOICES_NAME_SERIALIZER_MAP.get(field_name)
|
|
53
|
+
value: ReturnList
|
|
54
|
+
if serializer_class:
|
|
55
|
+
value = serializer_class(choices, many=True).data # type: ignore
|
|
56
|
+
elif not serializer_class and field_name in _LIST_FIELDS:
|
|
57
|
+
field = BrowserSettingsFilterSerializer().get_fields().get(field_name) # type: ignore
|
|
58
|
+
value = field.to_representation(choices)
|
|
59
|
+
else:
|
|
60
|
+
value = BrowserChoicesIntegerPkSerializer(choices, many=True).data # type: ignore
|
|
61
|
+
return value
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
"""Browser Settings Filter Serializers."""
|
|
2
2
|
|
|
3
|
-
from rest_framework.serializers import
|
|
4
|
-
BooleanField,
|
|
5
|
-
ChoiceField,
|
|
6
|
-
Serializer,
|
|
7
|
-
)
|
|
3
|
+
from rest_framework.serializers import BooleanField, ListField, Serializer
|
|
8
4
|
|
|
9
|
-
from codex.serializers.choices import CHOICES
|
|
10
5
|
from codex.serializers.fields import (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
BookmarkFilterField,
|
|
7
|
+
VuetifyBooleanField,
|
|
8
|
+
VuetifyCharField,
|
|
9
|
+
VuetifyDecadeField,
|
|
10
|
+
VuetifyFloatField,
|
|
11
|
+
VuetifyIntegerField,
|
|
16
12
|
)
|
|
17
13
|
|
|
18
14
|
|
|
@@ -46,29 +42,27 @@ class BrowserFilterChoicesSerializer(Serializer):
|
|
|
46
42
|
class BrowserSettingsFilterSerializer(Serializer):
|
|
47
43
|
"""Filter values for settings."""
|
|
48
44
|
|
|
49
|
-
bookmark =
|
|
50
|
-
choices=tuple(CHOICES["bookmarkFilter"].keys()), required=False
|
|
51
|
-
)
|
|
45
|
+
bookmark = BookmarkFilterField(required=False)
|
|
52
46
|
# Dynamic filters
|
|
53
|
-
age_rating =
|
|
54
|
-
characters =
|
|
55
|
-
community_rating =
|
|
56
|
-
country =
|
|
57
|
-
contributors =
|
|
58
|
-
critical_rating =
|
|
59
|
-
decade =
|
|
60
|
-
file_type =
|
|
61
|
-
genres =
|
|
62
|
-
identifier_type =
|
|
63
|
-
language =
|
|
64
|
-
locations =
|
|
65
|
-
monochrome =
|
|
66
|
-
original_format =
|
|
67
|
-
reading_direction =
|
|
68
|
-
series_groups =
|
|
69
|
-
stories =
|
|
70
|
-
story_arcs =
|
|
71
|
-
tagger =
|
|
72
|
-
tags =
|
|
73
|
-
teams =
|
|
74
|
-
year =
|
|
47
|
+
age_rating = ListField(child=VuetifyCharField(), required=False)
|
|
48
|
+
characters = ListField(child=VuetifyIntegerField(), required=False)
|
|
49
|
+
community_rating = ListField(child=VuetifyFloatField(), required=False)
|
|
50
|
+
country = ListField(child=VuetifyIntegerField(), required=False)
|
|
51
|
+
contributors = ListField(child=VuetifyIntegerField(), required=False)
|
|
52
|
+
critical_rating = ListField(child=VuetifyFloatField(), required=False)
|
|
53
|
+
decade = ListField(child=VuetifyDecadeField(), required=False)
|
|
54
|
+
file_type = ListField(child=VuetifyCharField(), required=False)
|
|
55
|
+
genres = ListField(child=VuetifyIntegerField(), required=False)
|
|
56
|
+
identifier_type = ListField(child=VuetifyCharField(), required=False)
|
|
57
|
+
language = ListField(child=VuetifyIntegerField(), required=False)
|
|
58
|
+
locations = ListField(child=VuetifyIntegerField(), required=False)
|
|
59
|
+
monochrome = ListField(child=VuetifyBooleanField(), required=False)
|
|
60
|
+
original_format = ListField(child=VuetifyCharField(), required=False)
|
|
61
|
+
reading_direction = ListField(child=VuetifyCharField(), required=False)
|
|
62
|
+
series_groups = ListField(child=VuetifyIntegerField(), required=False)
|
|
63
|
+
stories = ListField(child=VuetifyIntegerField(), required=False)
|
|
64
|
+
story_arcs = ListField(child=VuetifyIntegerField(), required=False)
|
|
65
|
+
tagger = ListField(child=VuetifyCharField(), required=False)
|
|
66
|
+
tags = ListField(child=VuetifyIntegerField(), required=False)
|
|
67
|
+
teams = ListField(child=VuetifyIntegerField(), required=False)
|
|
68
|
+
year = ListField(child=VuetifyIntegerField(), required=False)
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
"""Serializer mixins."""
|
|
2
2
|
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
|
|
3
5
|
from rest_framework.serializers import (
|
|
4
6
|
BooleanField,
|
|
5
7
|
CharField,
|
|
6
8
|
DateTimeField,
|
|
7
9
|
DecimalField,
|
|
8
10
|
IntegerField,
|
|
11
|
+
ListField,
|
|
9
12
|
Serializer,
|
|
13
|
+
SerializerMethodField,
|
|
10
14
|
)
|
|
11
15
|
|
|
12
|
-
from codex.
|
|
13
|
-
from codex.
|
|
16
|
+
from codex.logger.logging import get_logger
|
|
17
|
+
from codex.views.const import COMIC_GROUP, EPOCH_START
|
|
18
|
+
|
|
19
|
+
LOG = get_logger(__name__)
|
|
14
20
|
|
|
15
21
|
|
|
16
22
|
class BrowserAggregateSerializerMixin(Serializer):
|
|
17
23
|
"""Mixin for browser, opds & metadata serializers."""
|
|
18
24
|
|
|
19
25
|
group = CharField(read_only=True, max_length=1)
|
|
20
|
-
ids =
|
|
26
|
+
ids = ListField(child=IntegerField(), read_only=True)
|
|
21
27
|
|
|
22
28
|
# Aggregate Annotations
|
|
23
29
|
child_count = IntegerField(read_only=True)
|
|
24
|
-
mtime =
|
|
30
|
+
mtime = SerializerMethodField(read_only=True)
|
|
25
31
|
|
|
26
32
|
# Bookmark annotations
|
|
27
33
|
page = IntegerField(read_only=True)
|
|
@@ -30,3 +36,30 @@ class BrowserAggregateSerializerMixin(Serializer):
|
|
|
30
36
|
progress = DecimalField(
|
|
31
37
|
max_digits=5, decimal_places=2, read_only=True, coerce_to_string=False
|
|
32
38
|
)
|
|
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)
|
|
@@ -11,6 +11,7 @@ from rest_framework.serializers import (
|
|
|
11
11
|
from codex.serializers.browser.filters import BrowserSettingsFilterSerializer
|
|
12
12
|
from codex.serializers.choices import CHOICES, VUETIFY_NULL_CODE
|
|
13
13
|
from codex.serializers.fields import BreadcrumbsField, TimestampField, TopGroupField
|
|
14
|
+
from codex.serializers.route import SimpleRouteSerializer
|
|
14
15
|
|
|
15
16
|
VUETIFY_NULL_CODE_STR = str(VUETIFY_NULL_CODE)
|
|
16
17
|
|
|
@@ -31,8 +32,8 @@ class BrowserFilterChoicesInputSerilalizer(Serializer):
|
|
|
31
32
|
q = CharField(allow_blank=True, required=False)
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
class
|
|
35
|
-
"""
|
|
35
|
+
class BrowserCoverInputSerializerBase(BrowserFilterChoicesInputSerilalizer):
|
|
36
|
+
"""Base Serializer for Cover and Settings."""
|
|
36
37
|
|
|
37
38
|
custom_covers = BooleanField(required=False)
|
|
38
39
|
dynamic_covers = BooleanField(required=False)
|
|
@@ -41,7 +42,13 @@ class BrowserCoverInputSerializer(BrowserFilterChoicesInputSerilalizer):
|
|
|
41
42
|
show = BrowserSettingsShowGroupFlagsSerializer(required=False)
|
|
42
43
|
|
|
43
44
|
|
|
44
|
-
class
|
|
45
|
+
class BrowserCoverInputSerializer(BrowserCoverInputSerializerBase):
|
|
46
|
+
"""Browser Settings for the cover response."""
|
|
47
|
+
|
|
48
|
+
parent = SimpleRouteSerializer(required=False)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class BrowserSettingsSerializerBase(BrowserCoverInputSerializerBase):
|
|
45
52
|
"""Base Serializer for Browser & OPDS Settings."""
|
|
46
53
|
|
|
47
54
|
# search_results_limit = IntegerField(required=False)
|
codex/serializers/choices.py
CHANGED
|
@@ -53,10 +53,13 @@ _SINGLE_VALUE_KEYS = frozenset(
|
|
|
53
53
|
{
|
|
54
54
|
"customCovers",
|
|
55
55
|
"dynamicCovers",
|
|
56
|
+
"finishOnLastPage",
|
|
56
57
|
"orderReverse",
|
|
57
58
|
"q",
|
|
59
|
+
"readRtlInReverse",
|
|
58
60
|
"searchResultsLimit",
|
|
59
61
|
"twentyFourHourTime",
|
|
62
|
+
"twoPages",
|
|
60
63
|
}
|
|
61
64
|
)
|
|
62
65
|
|
|
@@ -65,6 +68,7 @@ CHOICES = {}
|
|
|
65
68
|
DEFAULTS = {}
|
|
66
69
|
WEBSOCKET_MESSAGES = {}
|
|
67
70
|
VUETIFY_NULL_CODE = -1
|
|
71
|
+
DUMMY_NULL_NAME = "_none_"
|
|
68
72
|
|
|
69
73
|
|
|
70
74
|
def _find_filename_regex(js_root, module_name):
|
codex/serializers/fields.py
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
from abc import ABC
|
|
4
4
|
from datetime import datetime, timezone
|
|
5
5
|
|
|
6
|
+
import pycountry
|
|
6
7
|
from django.core.exceptions import ValidationError
|
|
7
8
|
from django.utils.translation import gettext_lazy as _
|
|
8
|
-
from rest_framework.fields import Field
|
|
9
9
|
from rest_framework.serializers import (
|
|
10
10
|
BooleanField,
|
|
11
11
|
CharField,
|
|
@@ -15,10 +15,12 @@ from rest_framework.serializers import (
|
|
|
15
15
|
ListField,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
-
from codex.
|
|
18
|
+
from codex.logger.logging import get_logger
|
|
19
|
+
from codex.serializers.choices import CHOICES, DUMMY_NULL_NAME, VUETIFY_NULL_CODE
|
|
19
20
|
from codex.serializers.route import RouteSerializer
|
|
20
21
|
|
|
21
22
|
VUETIFY_NULL_CODE_STR = str(VUETIFY_NULL_CODE)
|
|
23
|
+
LOG = get_logger(__name__)
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
class TimestampField(IntegerField):
|
|
@@ -35,93 +37,61 @@ class TimestampField(IntegerField):
|
|
|
35
37
|
return datetime.fromtimestamp(float(data) / 1000, tz=timezone.utc)
|
|
36
38
|
|
|
37
39
|
|
|
38
|
-
def
|
|
40
|
+
def validate_decade(decade):
|
|
39
41
|
"""Validate decades."""
|
|
40
|
-
if not decades:
|
|
41
|
-
return
|
|
42
42
|
# * We don't need a whole db call just to be perfectly accurate
|
|
43
43
|
# * -1s are decoded back into None before validation
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
exists = True
|
|
48
|
-
break
|
|
49
|
-
if not exists:
|
|
50
|
-
raise ValidationError(_(f"Invalid decade in: {decades}"))
|
|
44
|
+
if decade is not None and decade % 10 != 0:
|
|
45
|
+
raise ValidationError(_(f"Invalid decade: {decade}"))
|
|
46
|
+
return True
|
|
51
47
|
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
"""
|
|
49
|
+
class VuetifyNullCodeFieldMixin:
|
|
50
|
+
"""Convert Vuetify null codes to None."""
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
array index.
|
|
58
|
-
"""
|
|
59
|
-
for index, value in enumerate(values):
|
|
60
|
-
if value == VUETIFY_NULL_CODE:
|
|
61
|
-
values[index] = None
|
|
62
|
-
return values
|
|
52
|
+
NULL_CODE = VUETIFY_NULL_CODE
|
|
63
53
|
|
|
54
|
+
def to_internal_value(self, data):
|
|
55
|
+
"""Convert numeric null code to None."""
|
|
56
|
+
return None if data == self.NULL_CODE else data
|
|
64
57
|
|
|
65
|
-
def validate_str_null(values):
|
|
66
|
-
"""Use a special code for null.
|
|
67
58
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
"""
|
|
71
|
-
for index, value in enumerate(values):
|
|
72
|
-
if value == VUETIFY_NULL_CODE_STR:
|
|
73
|
-
values[index] = None
|
|
74
|
-
return values
|
|
59
|
+
class VuetifyFloatField(VuetifyNullCodeFieldMixin, FloatField): # type: ignore
|
|
60
|
+
"""Float Field with null code conversion."""
|
|
75
61
|
|
|
76
62
|
|
|
77
|
-
class
|
|
78
|
-
"""
|
|
63
|
+
class VuetifyIntegerField(VuetifyNullCodeFieldMixin, IntegerField): # type: ignore
|
|
64
|
+
"""Integer Field with null code conversion."""
|
|
79
65
|
|
|
80
|
-
CHILD_CLASS = Field
|
|
81
|
-
VALIDATORS = ()
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
super().__init__(
|
|
86
|
-
*args,
|
|
87
|
-
child=self.CHILD_CLASS(allow_null=True, **kwargs),
|
|
88
|
-
required=False,
|
|
89
|
-
validators=self.VALIDATORS,
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class FloatListField(FilterListField):
|
|
94
|
-
"""Decimal List Field with validation."""
|
|
95
|
-
|
|
96
|
-
CHILD_CLASS = FloatField # type: ignore
|
|
97
|
-
VALIDATORS = (validate_int_null,)
|
|
67
|
+
class VuetifyCharField(VuetifyNullCodeFieldMixin, CharField): # type: ignore
|
|
68
|
+
"""Char Field with null code conversion."""
|
|
98
69
|
|
|
70
|
+
NULL_CODE = VUETIFY_NULL_CODE_STR
|
|
99
71
|
|
|
100
|
-
class IntListField(FilterListField):
|
|
101
|
-
"""Integer List Field with validation."""
|
|
102
72
|
|
|
103
|
-
|
|
104
|
-
|
|
73
|
+
class VuetifyBooleanField(VuetifyNullCodeFieldMixin, BooleanField): # type: ignore
|
|
74
|
+
"""Boolean Field with null code conversion."""
|
|
105
75
|
|
|
106
76
|
|
|
107
|
-
class
|
|
108
|
-
"""
|
|
77
|
+
class BookmarkFilterField(ChoiceField):
|
|
78
|
+
"""Bookmark Choice Field."""
|
|
109
79
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
80
|
+
def __init__(self, *args, **kwargs):
|
|
81
|
+
"""Use bookmark filter choices."""
|
|
82
|
+
super().__init__(
|
|
83
|
+
*args, choices=tuple(CHOICES["bookmarkFilter"].keys()), **kwargs
|
|
84
|
+
)
|
|
115
85
|
|
|
116
|
-
CHILD_CLASS = CharField # type: ignore
|
|
117
|
-
VALIDATORS = (validate_str_null,)
|
|
118
86
|
|
|
87
|
+
class VuetifyDecadeField(VuetifyIntegerField):
|
|
88
|
+
"""Integer field with null code conversion and decade validation."""
|
|
119
89
|
|
|
120
|
-
|
|
121
|
-
"""Bool List Field with validation."""
|
|
90
|
+
VALIDATORS = (validate_decade,)
|
|
122
91
|
|
|
123
|
-
|
|
124
|
-
|
|
92
|
+
def __init__(self, *args, **kwargs):
|
|
93
|
+
"""Use decade validator."""
|
|
94
|
+
super().__init__(*args, validators=self.VALIDATORS, **kwargs)
|
|
125
95
|
|
|
126
96
|
|
|
127
97
|
class BreadcrumbsField(ListField):
|
|
@@ -138,3 +108,42 @@ class TopGroupField(ChoiceField):
|
|
|
138
108
|
def __init__(self, *args, **kwargs):
|
|
139
109
|
"""Initialize with choices."""
|
|
140
110
|
super().__init__(*args, choices=self.class_choices, **kwargs)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class PyCountryField(CharField, ABC):
|
|
114
|
+
"""Serialize to a long pycountry name."""
|
|
115
|
+
|
|
116
|
+
LOOKUP_MODULE = pycountry.countries
|
|
117
|
+
_ALPHA_2_LEN = 2
|
|
118
|
+
|
|
119
|
+
def to_representation(self, value):
|
|
120
|
+
"""Lookup the name with pycountry, just copy the value on fail."""
|
|
121
|
+
if not value:
|
|
122
|
+
return ""
|
|
123
|
+
if value == DUMMY_NULL_NAME:
|
|
124
|
+
return value
|
|
125
|
+
try:
|
|
126
|
+
# fix for https://github.com/flyingcircusio/pycountry/issues/41
|
|
127
|
+
lookup_obj = (
|
|
128
|
+
self.LOOKUP_MODULE.get(alpha_2=value)
|
|
129
|
+
if len(value) == self._ALPHA_2_LEN
|
|
130
|
+
else self.LOOKUP_MODULE.lookup(value)
|
|
131
|
+
)
|
|
132
|
+
# If lookup fails, return the key as the name
|
|
133
|
+
except Exception:
|
|
134
|
+
LOG.warning(f"Could not serialize name with pycountry {value}")
|
|
135
|
+
return value
|
|
136
|
+
else:
|
|
137
|
+
return lookup_obj.name if lookup_obj else value
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class CountryField(PyCountryField):
|
|
141
|
+
"""Serializer to long country name."""
|
|
142
|
+
|
|
143
|
+
LOOKUP_MODULE = pycountry.countries
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class LanguageField(PyCountryField):
|
|
147
|
+
"""Serializer to long language name."""
|
|
148
|
+
|
|
149
|
+
LOOKUP_MODULE = pycountry.languages
|
|
@@ -1,73 +1,33 @@
|
|
|
1
1
|
"""Pycountry Model Serializers."""
|
|
2
2
|
|
|
3
|
-
import
|
|
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
|
-
|
|
3
|
+
from codex.logger.logging import get_logger
|
|
10
4
|
from codex.models import (
|
|
11
5
|
Country,
|
|
12
6
|
Language,
|
|
13
7
|
)
|
|
8
|
+
from codex.serializers.fields import CountryField, LanguageField
|
|
14
9
|
from codex.serializers.models.named import NamedModelSerializer
|
|
15
10
|
|
|
11
|
+
LOG = get_logger(__name__)
|
|
16
12
|
|
|
17
|
-
class PyCountrySerializer(NamedModelSerializer):
|
|
18
|
-
"""PyCountry tag serializer to include long names.
|
|
19
13
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
the name.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
LOOKUP_MODULE = pycountry.countries
|
|
26
|
-
_ALPHA_2_LEN = 2
|
|
14
|
+
class CountrySerializer(NamedModelSerializer):
|
|
15
|
+
"""Pycountry serializer for country field."""
|
|
27
16
|
|
|
28
|
-
name =
|
|
17
|
+
name = CountryField(read_only=True)
|
|
29
18
|
|
|
30
19
|
class Meta(NamedModelSerializer.Meta):
|
|
31
|
-
"""
|
|
32
|
-
|
|
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
|
|
20
|
+
"""Configure model."""
|
|
49
21
|
|
|
50
|
-
|
|
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)
|
|
22
|
+
model = Country
|
|
54
23
|
|
|
55
24
|
|
|
56
|
-
class LanguageSerializer(
|
|
25
|
+
class LanguageSerializer(NamedModelSerializer):
|
|
57
26
|
"""Pycountry serializer for language field."""
|
|
58
27
|
|
|
59
|
-
|
|
28
|
+
name = LanguageField(read_only=True)
|
|
60
29
|
|
|
61
|
-
class Meta(
|
|
30
|
+
class Meta(NamedModelSerializer.Meta):
|
|
62
31
|
"""Configure model."""
|
|
63
32
|
|
|
64
33
|
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
|