aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbnewbold <bnewbold@archive.org>2020-08-03 18:00:41 +0000
committerbnewbold <bnewbold@archive.org>2020-08-03 18:00:41 +0000
commit4702bee24dde8bae64df76ad411a6d8329cc9bdf (patch)
tree221ec7bd8d77bddb2dec344c19253cca65156911
parent5037642d7775c638d035c2faed8094537dfaf94d (diff)
parent31f59b4b0ba7ff95b685c8826a7d019fb142f65c (diff)
downloadfatcat-4702bee24dde8bae64df76ad411a6d8329cc9bdf.tar.gz
fatcat-4702bee24dde8bae64df76ad411a6d8329cc9bdf.zip
Merge branch 'bnewbold-editing' into 'master'
editing improvements See merge request webgroup/fatcat!73
-rw-r--r--README.md2
-rw-r--r--proposals/20200729_toml_editing.md43
-rw-r--r--python/Pipfile5
-rw-r--r--python/Pipfile.lock598
-rw-r--r--python/fatcat_tools/transforms/__init__.py2
-rw-r--r--python/fatcat_tools/transforms/entities.py22
-rw-r--r--python/fatcat_web/editing_routes.py571
-rw-r--r--python/fatcat_web/entity_helpers.py27
-rw-r--r--python/fatcat_web/forms.py85
-rw-r--r--python/fatcat_web/routes.py2
-rw-r--r--python/fatcat_web/templates/base.html4
-rw-r--r--python/fatcat_web/templates/container_create.html4
-rw-r--r--python/fatcat_web/templates/container_edit.html8
-rw-r--r--python/fatcat_web/templates/deleted_entity.html2
-rw-r--r--python/fatcat_web/templates/edit_macros.html19
-rw-r--r--python/fatcat_web/templates/editgroup_view.html2
-rw-r--r--python/fatcat_web/templates/entity_create_toml.html20
-rw-r--r--python/fatcat_web/templates/entity_delete.html49
-rw-r--r--python/fatcat_web/templates/entity_edit.html8
-rw-r--r--python/fatcat_web/templates/entity_edit_toml.html55
-rw-r--r--python/fatcat_web/templates/entity_macros.html12
-rw-r--r--python/fatcat_web/templates/file_create.html4
-rw-r--r--python/fatcat_web/templates/file_edit.html8
-rw-r--r--python/fatcat_web/templates/home.html12
-rw-r--r--python/fatcat_web/templates/release_create.html4
-rw-r--r--python/fatcat_web/templates/release_edit.html35
-rw-r--r--python/tests/transform_toml.py22
-rw-r--r--python/tests/web_editing.py131
-rw-r--r--python/tests/web_entity_views.py16
29 files changed, 1368 insertions, 404 deletions
diff --git a/README.md b/README.md
index c06fe6e5..dcad54c3 100644
--- a/README.md
+++ b/README.md
@@ -59,7 +59,7 @@ schema, lives under `./python_openapi_client/`.
- [x] Authorization (aka, roles)
- Web Interface
- [x] Migrate Python codebase
- - [ ] Creation and editing of all entities
+ - [x] Creation and editing of all entities
- Other
- [x] Elasticsearch schema
- [x] Basic logging
diff --git a/proposals/20200729_toml_editing.md b/proposals/20200729_toml_editing.md
new file mode 100644
index 00000000..bdb8c12f
--- /dev/null
+++ b/proposals/20200729_toml_editing.md
@@ -0,0 +1,43 @@
+
+status: implemented
+
+TOML Editing of Entity Metadata
+===============================
+
+Goal is to enable full-power editing through the web interface, of the raw
+entity schema and "extra" metadata, for all entity types.
+
+A side-effect of this should be enabling redirect editing between entities, as
+well as "undeleting" or other state transitions (other than deletion).
+
+Plan:
+
+- find and add a toml transform library to pipenv deps. preferably with good
+ human-readable parsing errors
+- TOML/JSON transform helpers, with tests
+- implement generic TOML entity editing view (HTML)
+- implement generic TOML entity editing endpoints
+
+Some metadata fields are removed before displaying TOML to edit. For example,
+the ident, revision, and redirect fields for 'active' entities. It should still
+be possible to do redirects by entering only the redirect field in the TOML
+form.
+
+## UI Integration
+
+For existing edit forms, add a link to the "advanced" editing option.
+
+For endpoints without a form-based option (yet), do an HTTP redirect to the
+TOML editing option.
+
+## New Webface Views
+
+`/<entity>/create/toml`
+ GET: display template to be filled in
+ POST: form submit for creation
+`/<entity>/<ident>/edit/toml`
+ GET: transform entity for editing
+ POST: form submit for edit
+`/editgroup/<editgroup_id>/<entity>/<ident>/edit/toml`
+ GET: transform entity for editing
+ POST: form submit for edit
diff --git a/python/Pipfile b/python/Pipfile
index 1c7ebf3a..4c0977a1 100644
--- a/python/Pipfile
+++ b/python/Pipfile
@@ -30,7 +30,7 @@ flask-wtf = "*"
Flask-Misaka = "*"
flask-mwoauth = "*"
WTForms = "*"
-loginpass = ">=0.4"
+loginpass = "==0.4"
# loginpass 0.4 is not actually compatible with newer authlib
authlib = "<0.13"
requests = ">=2"
@@ -53,8 +53,9 @@ elasticsearch = ">=6.0.0,<7.0.0"
dateparser = ">=0.7"
langdetect = "*"
pathlib2 = "*"
-pycountry = "*"
+pycountry = "==19.8.18"
tldextract = "*"
+toml = ">=0.10"
[requires]
# We install Python 3.7 using a PPA (deadsnakes) on Internet Archive cluster
diff --git a/python/Pipfile.lock b/python/Pipfile.lock
index 2f428b1c..9b1a5d15 100644
--- a/python/Pipfile.lock
+++ b/python/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "94b31d68f1fdf85834841d90948ba599ba1a69723b0dd899f8ba9e709bc5307c"
+ "sha256": "b2e4ed259ddec6c4016e6857ec856146c0fb279b3d6a91d138c1611e9a591c3a"
},
"pipfile-spec": 6,
"requires": {
@@ -47,43 +47,43 @@
},
"certifi": {
"hashes": [
- "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
- "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
+ "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
+ "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
],
- "version": "==2020.4.5.1"
+ "version": "==2020.6.20"
},
"cffi": {
"hashes": [
- "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff",
- "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b",
- "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac",
- "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0",
- "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384",
- "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26",
- "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6",
- "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b",
- "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e",
- "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd",
- "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2",
- "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66",
- "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc",
- "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8",
- "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55",
- "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4",
- "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5",
- "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d",
- "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78",
- "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa",
- "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793",
- "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f",
- "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a",
- "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f",
- "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30",
- "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f",
- "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3",
- "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c"
- ],
- "version": "==1.14.0"
+ "sha256:267adcf6e68d77ba154334a3e4fc921b8e63cbb38ca00d33d40655d4228502bc",
+ "sha256:26f33e8f6a70c255767e3c3f957ccafc7f1f706b966e110b855bfe944511f1f9",
+ "sha256:3cd2c044517f38d1b577f05927fb9729d3396f1d44d0c659a445599e79519792",
+ "sha256:4a03416915b82b81af5502459a8a9dd62a3c299b295dcdf470877cb948d655f2",
+ "sha256:4ce1e995aeecf7cc32380bc11598bfdfa017d592259d5da00fc7ded11e61d022",
+ "sha256:4f53e4128c81ca3212ff4cf097c797ab44646a40b42ec02a891155cd7a2ba4d8",
+ "sha256:4fa72a52a906425416f41738728268072d5acfd48cbe7796af07a923236bcf96",
+ "sha256:66dd45eb9530e3dde8f7c009f84568bc7cac489b93d04ac86e3111fb46e470c2",
+ "sha256:6923d077d9ae9e8bacbdb1c07ae78405a9306c8fd1af13bfa06ca891095eb995",
+ "sha256:833401b15de1bb92791d7b6fb353d4af60dc688eaa521bd97203dcd2d124a7c1",
+ "sha256:8416ed88ddc057bab0526d4e4e9f3660f614ac2394b5e019a628cdfff3733849",
+ "sha256:892daa86384994fdf4856cb43c93f40cbe80f7f95bb5da94971b39c7f54b3a9c",
+ "sha256:98be759efdb5e5fa161e46d404f4e0ce388e72fbf7d9baf010aff16689e22abe",
+ "sha256:a6d28e7f14ecf3b2ad67c4f106841218c8ab12a0683b1528534a6c87d2307af3",
+ "sha256:b1d6ebc891607e71fd9da71688fcf332a6630b7f5b7f5549e6e631821c0e5d90",
+ "sha256:b2a2b0d276a136146e012154baefaea2758ef1f56ae9f4e01c612b0831e0bd2f",
+ "sha256:b87dfa9f10a470eee7f24234a37d1d5f51e5f5fa9eeffda7c282e2b8f5162eb1",
+ "sha256:bac0d6f7728a9cc3c1e06d4fcbac12aaa70e9379b3025b27ec1226f0e2d404cf",
+ "sha256:c991112622baee0ae4d55c008380c32ecfd0ad417bcd0417ba432e6ba7328caa",
+ "sha256:cda422d54ee7905bfc53ee6915ab68fe7b230cacf581110df4272ee10462aadc",
+ "sha256:d3148b6ba3923c5850ea197a91a42683f946dba7e8eb82dfa211ab7e708de939",
+ "sha256:d6033b4ffa34ef70f0b8086fd4c3df4bf801fee485a8a7d4519399818351aa8e",
+ "sha256:ddff0b2bd7edcc8c82d1adde6dbbf5e60d57ce985402541cd2985c27f7bec2a0",
+ "sha256:e23cb7f1d8e0f93addf0cae3c5b6f00324cccb4a7949ee558d7b6ca973ab8ae9",
+ "sha256:effd2ba52cee4ceff1a77f20d2a9f9bf8d50353c854a282b8760ac15b9833168",
+ "sha256:f90c2267101010de42f7273c94a1f026e56cbc043f9330acd8a80e64300aba33",
+ "sha256:f960375e9823ae6a07072ff7f8a85954e5a6434f97869f50d0e41649a1c8144f",
+ "sha256:fcf32bf76dc25e30ed793145a57426064520890d7c02866eb93d3e4abe516948"
+ ],
+ "version": "==1.14.1"
},
"chardet": {
"hashes": [
@@ -113,71 +113,72 @@
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==7.1.2"
},
"confluent-kafka": {
"hashes": [
- "sha256:1b10a9e4ede8c7ee382c16075b55275963d3fe9b8eec3fc511d0868847cc6eed",
- "sha256:1c46cbc2eb0876f0cdbd33ed7ea684ed1b009a25b65cf87736d3506d2f4ae57e",
- "sha256:2500a78334d642e49b98710722e548c0e3d5dc4c6eae63f02d66448678ed2922",
- "sha256:2515771b18d190df2182881abcf02fe8fde0aab567402ff36295b35cd495de65",
- "sha256:3150c8875511e2cea4086206f3c10448f744c9c35f9033fd0874c8c55f7b87e2",
- "sha256:4b0a3c47f9183570e9ee77ae8c36080fbc1996045251e25772944e4dadf1db21",
- "sha256:4f875798bbc766767b9c6ed95b084fde851e0bf074527ab0daffa87f4e750635",
- "sha256:515049659b045b99e0464d5ff5def4785478490563bc5ac1341a4f29dc335e82",
- "sha256:52088adf1abdf3a384a54ec7a3bfaa0b61e5da8cc03a2e26a8351bbbf49f72a9",
- "sha256:5342d3ff348b8082eaa4c63f4c82a72f3bf0ef8efa12a8580c890fa6e160f761",
- "sha256:55734905c5a8642e596cf1e60ec4d86f05d31a185cbc71d1c73430bb0c08db19",
- "sha256:624349587e97135996383c58edd8d53b38c57d653e6536c1f816049fc75faea3",
- "sha256:804a7d71b3cb61444930af67986064c9555b8c33f05a27003ea314d6c847e522",
- "sha256:931231853cec933addfafa27772177dcfab899d82e2e39fe7485c0602088daf7",
- "sha256:a4f5edc1d7958bbf5f12ba83c1f83e22a66daa9c4318c7f28c5bb1db9289fe09",
- "sha256:a591936a90095144451f041315239b2c823b7a15fa820cf45e45c422591345d6",
- "sha256:a6eb8f3f553e98a6ef0d00f9cf8e4e8dde73c914a43a00fecef97330de80bcea",
- "sha256:aa48215edcf16071d44ba29951c82c5f541d5ec915590aff0b4240e8e13f3ba3",
- "sha256:bfacb9fa0e3a5e31a5ac9a5da15de656e95e7153e022ec5620095b76a6098ec0",
- "sha256:bfbcbe7068690369ac2de3fe953854de34ad5e901157e96bcb990ca8b86d1d93",
- "sha256:c2660807e5c1ecd723e280f76918794c3fd84595000c1e8de1f254f5d89a785c",
- "sha256:c42ff838ee5e248f95f65b5adca4e2fdd4a2817fa26cede36d83a426e0f1370c",
- "sha256:c5b741764d8ea2b8334fdaf4b56297c5bab780142f1c0cad0bd642cac30cb89e",
- "sha256:dac33a04f73093de275953867a05de244560aa9842def6316cbb52bc0f02eff3",
- "sha256:f1695a00789795f9f798588bb62688b563baf471a76ca20fa01c957844938d7d",
- "sha256:f25836e03559a381ba74b9a6940b716e61ba8ae2db2d5d3a40accbc60617e1af"
+ "sha256:00acc73f7d49961bf427f5e4fd6c0a220a6bfa5ccc91e0ad1f9ffa1751a169b0",
+ "sha256:0a59afbb90bdd22b9acdd3bb134f5ee1dff3cc5df55eaf52bf97b2f8d0d00de3",
+ "sha256:13b0e2011560f461ff39daf38089dd7f91404b3e66dba0456ccce0700f93c4f2",
+ "sha256:175c7064c8f19975616974558c45f42c147a202d4b1c0b0a83afefb920367696",
+ "sha256:22d7201d1aa89f1c5546749e781492925ed3eb0d7bd8f781fc57294cd45ddde3",
+ "sha256:3034cacc3b0d03eb3ce39cc5a64c1070d223870246f5d90c9113996be9db7df8",
+ "sha256:3e2d4f55ca952aeada3831d6615dc13a8a42c8e97175855ca08bbc6e6091b080",
+ "sha256:5a1c47320d6afc5b2599f8f8e143aed6845a2d903facde984606e02f10f11221",
+ "sha256:7b03bd9cc7b5e4df0a27eed359762c61a35313d4981ef1d9b418069eee454e66",
+ "sha256:85ff4823770ce2efaabb46d88e5ae26a840e0051fd481abaa805f21a5a84d003",
+ "sha256:9534cd2c0313df75b70eb4cf729382998970d97bbdda5cf3aef7081b855ccebe",
+ "sha256:99b13d0957a5967c85aee6138ef5f9acec90294267a549c5683744f20cf5d7b4",
+ "sha256:9a1c77291c1ac4b991aa0358f2f44636686eb8f52fb628502d30c312160a14e9",
+ "sha256:9c47b8aacfe347bffd86bf75b98626718912b63df87f256dff1abc06a0355410",
+ "sha256:a116382ae67e0d6a54684bab4ee9b1be54e789d031a6e5e74c3edc657c79d23c",
+ "sha256:b1c89f3653385acc5da71570e03281f35ac6960367f2b2a426ae431deb1a1a35",
+ "sha256:bb77276d569f511abe4a5b32a53f8a30285bc7be68219e5711a44720bf356ac2",
+ "sha256:bbd9633552840ab9367fb762ea21272759db8caec2c34ff16ee28be177644cdf",
+ "sha256:bfdfa81e4e72d2c24e408a5e199aae0a477499ae40647dfa6906d002d9b07f38",
+ "sha256:c7461d6db081c23a6d38ceba348e7c178d7e974cf22c45ba8a4918ecb8855a44",
+ "sha256:d6a5d4c72360a75e875e88f7cce42b66a786d037ca2002303ab1c580d49caf53",
+ "sha256:dabed41cc60d1fc6d3cb44a90fe02e5192c9bf0f73c7b35761981e62ecabc592",
+ "sha256:dd544847c713eeeb525031348ff6ffea4ecdd11c13590893e599a9d4676a9bd4",
+ "sha256:eba169a9de8c978c9f33c763857c5279eceac46a4fd55a381c2528b9d4b3359e",
+ "sha256:f2d1ee0bfdf618017bbfaa42406546155c1a86263e4f286295318578c723803b"
],
"index": "pypi",
- "version": "==1.4.2"
+ "version": "==1.5.0"
},
"cryptography": {
"hashes": [
- "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6",
- "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b",
- "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5",
- "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf",
- "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e",
- "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b",
- "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae",
- "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b",
- "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0",
- "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b",
- "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d",
- "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229",
- "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3",
- "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365",
- "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55",
- "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270",
- "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e",
- "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785",
- "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"
- ],
- "version": "==2.9.2"
+ "sha256:0c608ff4d4adad9e39b5057de43657515c7da1ccb1807c3a27d4cf31fc923b4b",
+ "sha256:0cbfed8ea74631fe4de00630f4bb592dad564d57f73150d6f6796a24e76c76cd",
+ "sha256:124af7255ffc8e964d9ff26971b3a6153e1a8a220b9a685dc407976ecb27a06a",
+ "sha256:384d7c681b1ab904fff3400a6909261cae1d0939cc483a68bdedab282fb89a07",
+ "sha256:45741f5499150593178fc98d2c1a9c6722df88b99c821ad6ae298eff0ba1ae71",
+ "sha256:4b9303507254ccb1181d1803a2080a798910ba89b1a3c9f53639885c90f7a756",
+ "sha256:4d355f2aee4a29063c10164b032d9fa8a82e2c30768737a2fd56d256146ad559",
+ "sha256:51e40123083d2f946794f9fe4adeeee2922b581fa3602128ce85ff813d85b81f",
+ "sha256:8713ddb888119b0d2a1462357d5946b8911be01ddbf31451e1d07eaa5077a261",
+ "sha256:8e924dbc025206e97756e8903039662aa58aa9ba357d8e1d8fc29e3092322053",
+ "sha256:8ecef21ac982aa78309bb6f092d1677812927e8b5ef204a10c326fc29f1367e2",
+ "sha256:8ecf9400d0893836ff41b6f977a33972145a855b6efeb605b49ee273c5e6469f",
+ "sha256:9367d00e14dee8d02134c6c9524bb4bd39d4c162456343d07191e2a0b5ec8b3b",
+ "sha256:a09fd9c1cca9a46b6ad4bea0a1f86ab1de3c0c932364dbcf9a6c2a5eeb44fa77",
+ "sha256:ab49edd5bea8d8b39a44b3db618e4783ef84c19c8b47286bf05dfdb3efb01c83",
+ "sha256:bea0b0468f89cdea625bb3f692cd7a4222d80a6bdafd6fb923963f2b9da0e15f",
+ "sha256:bec7568c6970b865f2bcebbe84d547c52bb2abadf74cefce396ba07571109c67",
+ "sha256:ce82cc06588e5cbc2a7df3c8a9c778f2cb722f56835a23a68b5a7264726bb00c",
+ "sha256:dea0ba7fe6f9461d244679efa968d215ea1f989b9c1957d7f10c21e5c7c09ad6"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==3.0"
},
"dateparser": {
"hashes": [
- "sha256:1b1f0e3034f82d1f92b45fa445826da6a36d67af8a1169e04869685594276011",
- "sha256:fb5bfde4795fa4b179fe05c2c25b3981f785de26bec37e247dee1079c63d5689"
+ "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b",
+ "sha256:e875efd8c57c85c2d02b238239878db59ff1971f5a823457fcc69e493bf6ebfa"
],
"index": "pypi",
- "version": "==0.7.4"
+ "version": "==0.7.6"
},
"elasticsearch": {
"hashes": [
@@ -196,7 +197,8 @@
"version": "==6.4.0"
},
"fatcat-openapi-client": {
- "path": "./../python_openapi_client"
+ "path": "./../python_openapi_client",
+ "version": "==0.3.2"
},
"flask": {
"hashes": [
@@ -255,29 +257,32 @@
},
"ftfy": {
"hashes": [
- "sha256:67f9c8b33a4b742376a3eda11b0e3bd5c0cbe719d95ea0bfd3736a7bdd1c24c8"
+ "sha256:51c7767f8c4b47d291fcef30b9625fb5341c06a31e6a3b627039c706c42f3720"
],
"index": "pypi",
- "version": "==5.7"
+ "version": "==5.8"
},
"future": {
"hashes": [
"sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.18.2"
},
"idna": {
"hashes": [
- "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
- "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "version": "==2.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.0"
},
"jinja2": {
@@ -285,6 +290,7 @@
"sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==2.11.2"
},
"kazoo": {
@@ -312,35 +318,40 @@
},
"lxml": {
"hashes": [
- "sha256:06748c7192eab0f48e3d35a7adae609a329c6257495d5e53878003660dc0fec6",
- "sha256:0790ddca3f825dd914978c94c2545dbea5f56f008b050e835403714babe62a5f",
- "sha256:1aa7a6197c1cdd65d974f3e4953764eee3d9c7b67e3966616b41fab7f8f516b7",
- "sha256:22c6d34fdb0e65d5f782a4d1a1edb52e0a8365858dafb1c08cb1d16546cf0786",
- "sha256:2754d4406438c83144f9ffd3628bbe2dcc6d62b20dbc5c1ec4bc4385e5d44b42",
- "sha256:27ee0faf8077c7c1a589573b1450743011117f1aa1a91d5ae776bbc5ca6070f2",
- "sha256:2b02c106709466a93ed424454ce4c970791c486d5fcdf52b0d822a7e29789626",
- "sha256:2d1ddce96cf15f1254a68dba6935e6e0f1fe39247de631c115e84dd404a6f031",
- "sha256:4f282737d187ae723b2633856085c31ae5d4d432968b7f3f478a48a54835f5c4",
- "sha256:51bb4edeb36d24ec97eb3e6a6007be128b720114f9a875d6b370317d62ac80b9",
- "sha256:7eee37c1b9815e6505847aa5e68f192e8a1b730c5c7ead39ff317fde9ce29448",
- "sha256:7fd88cb91a470b383aafad554c3fe1ccf6dfb2456ff0e84b95335d582a799804",
- "sha256:9144ce36ca0824b29ebc2e02ca186e54040ebb224292072250467190fb613b96",
- "sha256:925baf6ff1ef2c45169f548cc85204433e061360bfa7d01e1be7ae38bef73194",
- "sha256:a636346c6c0e1092ffc202d97ec1843a75937d8c98aaf6771348ad6422e44bb0",
- "sha256:a87dbee7ad9dce3aaefada2081843caf08a44a8f52e03e0a4cc5819f8398f2f4",
- "sha256:a9e3b8011388e7e373565daa5e92f6c9cb844790dc18e43073212bb3e76f7007",
- "sha256:afb53edf1046599991fb4a7d03e601ab5f5422a5435c47ee6ba91ec3b61416a6",
- "sha256:b26719890c79a1dae7d53acac5f089d66fd8cc68a81f4e4bd355e45470dc25e1",
- "sha256:b7462cdab6fffcda853338e1741ce99706cdf880d921b5a769202ea7b94e8528",
- "sha256:b77975465234ff49fdad871c08aa747aae06f5e5be62866595057c43f8d2f62c",
- "sha256:c47a8a5d00060122ca5908909478abce7bbf62d812e3fc35c6c802df8fb01fe7",
- "sha256:c79e5debbe092e3c93ca4aee44c9a7631bdd407b2871cb541b979fd350bbbc29",
- "sha256:d8d40e0121ca1606aa9e78c28a3a7d88a05c06b3ca61630242cded87d8ce55fa",
- "sha256:ee2be8b8f72a2772e72ab926a3bccebf47bb727bda41ae070dc91d1fb759b726",
- "sha256:f95d28193c3863132b1f55c1056036bf580b5a488d908f7d22a04ace8935a3a9",
- "sha256:fadd2a63a2bfd7fb604508e553d1cf68eca250b2fbdbd81213b5f6f2fbf23529"
- ],
- "version": "==4.5.1"
+ "sha256:05a444b207901a68a6526948c7cc8f9fe6d6f24c70781488e32fd74ff5996e3f",
+ "sha256:08fc93257dcfe9542c0a6883a25ba4971d78297f63d7a5a26ffa34861ca78730",
+ "sha256:107781b213cf7201ec3806555657ccda67b1fccc4261fb889ef7fc56976db81f",
+ "sha256:121b665b04083a1e85ff1f5243d4a93aa1aaba281bc12ea334d5a187278ceaf1",
+ "sha256:1fa21263c3aba2b76fd7c45713d4428dbcc7644d73dcf0650e9d344e433741b3",
+ "sha256:2b30aa2bcff8e958cd85d907d5109820b01ac511eae5b460803430a7404e34d7",
+ "sha256:4b4a111bcf4b9c948e020fd207f915c24a6de3f1adc7682a2d92660eb4e84f1a",
+ "sha256:5591c4164755778e29e69b86e425880f852464a21c7bb53c7ea453bbe2633bbe",
+ "sha256:59daa84aef650b11bccd18f99f64bfe44b9f14a08a28259959d33676554065a1",
+ "sha256:5a9c8d11aa2c8f8b6043d845927a51eb9102eb558e3f936df494e96393f5fd3e",
+ "sha256:5dd20538a60c4cc9a077d3b715bb42307239fcd25ef1ca7286775f95e9e9a46d",
+ "sha256:74f48ec98430e06c1fa8949b49ebdd8d27ceb9df8d3d1c92e1fdc2773f003f20",
+ "sha256:786aad2aa20de3dbff21aab86b2fb6a7be68064cbbc0219bde414d3a30aa47ae",
+ "sha256:7ad7906e098ccd30d8f7068030a0b16668ab8aa5cda6fcd5146d8d20cbaa71b5",
+ "sha256:80a38b188d20c0524fe8959c8ce770a8fdf0e617c6912d23fc97c68301bb9aba",
+ "sha256:8f0ec6b9b3832e0bd1d57af41f9238ea7709bbd7271f639024f2fc9d3bb01293",
+ "sha256:92282c83547a9add85ad658143c76a64a8d339028926d7dc1998ca029c88ea6a",
+ "sha256:94150231f1e90c9595ccc80d7d2006c61f90a5995db82bccbca7944fd457f0f6",
+ "sha256:9dc9006dcc47e00a8a6a029eb035c8f696ad38e40a27d073a003d7d1443f5d88",
+ "sha256:a76979f728dd845655026ab991df25d26379a1a8fc1e9e68e25c7eda43004bed",
+ "sha256:aa8eba3db3d8761db161003e2d0586608092e217151d7458206e243be5a43843",
+ "sha256:bea760a63ce9bba566c23f726d72b3c0250e2fa2569909e2d83cda1534c79443",
+ "sha256:c3f511a3c58676147c277eff0224c061dd5a6a8e1373572ac817ac6324f1b1e0",
+ "sha256:c9d317efde4bafbc1561509bfa8a23c5cab66c44d49ab5b63ff690f5159b2304",
+ "sha256:cc411ad324a4486b142c41d9b2b6a722c534096963688d879ea6fa8a35028258",
+ "sha256:cdc13a1682b2a6241080745b1953719e7fe0850b40a5c71ca574f090a1391df6",
+ "sha256:cfd7c5dd3c35c19cec59c63df9571c67c6d6e5c92e0fe63517920e97f61106d1",
+ "sha256:e1cacf4796b20865789083252186ce9dc6cc59eca0c2e79cca332bdff24ac481",
+ "sha256:e70d4e467e243455492f5de463b72151cc400710ac03a0678206a5f27e79ddef",
+ "sha256:ecc930ae559ea8a43377e8b60ca6f8d61ac532fc57efb915d899de4a67928efd",
+ "sha256:f161af26f596131b63b236372e4ce40f3167c1b5b5d459b29d2514bd8c9dc9ee"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==4.5.2"
},
"markupsafe": {
"hashes": [
@@ -378,6 +389,7 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
"misaka": {
@@ -399,6 +411,7 @@
"sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889",
"sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==3.1.0"
},
"pathlib2": {
@@ -421,6 +434,7 @@
"sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
"sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.20"
},
"pygal": {
@@ -448,10 +462,10 @@
},
"pylatexenc": {
"hashes": [
- "sha256:bf15f2de2159501c9aa753929b5e0bec9aede4bfecb3b355f0bba8868eba0d1a"
+ "sha256:cd51da5ed908251c600b3f27fc1a8690727ba1e5f0b65f613aea1aca91c2530b"
],
"index": "pypi",
- "version": "==2.4"
+ "version": "==2.6"
},
"pymacaroons": {
"hashes": [
@@ -463,29 +477,25 @@
},
"pynacl": {
"hashes": [
- "sha256:05c26f93964373fc0abe332676cb6735f0ecad27711035b9472751faa8521255",
- "sha256:0c6100edd16fefd1557da078c7a31e7b7d7a52ce39fdca2bec29d4f7b6e7600c",
- "sha256:0d0a8171a68edf51add1e73d2159c4bc19fc0718e79dec51166e940856c2f28e",
- "sha256:1c780712b206317a746ace34c209b8c29dbfd841dfbc02aa27f2084dd3db77ae",
- "sha256:2424c8b9f41aa65bbdbd7a64e73a7450ebb4aa9ddedc6a081e7afcc4c97f7621",
- "sha256:2d23c04e8d709444220557ae48ed01f3f1086439f12dbf11976e849a4926db56",
- "sha256:30f36a9c70450c7878053fa1344aca0145fd47d845270b43a7ee9192a051bf39",
- "sha256:37aa336a317209f1bb099ad177fef0da45be36a2aa664507c5d72015f956c310",
- "sha256:4943decfc5b905748f0756fdd99d4f9498d7064815c4cf3643820c9028b711d1",
- "sha256:53126cd91356342dcae7e209f840212a58dcf1177ad52c1d938d428eebc9fee5",
- "sha256:57ef38a65056e7800859e5ba9e6091053cd06e1038983016effaffe0efcd594a",
- "sha256:5bd61e9b44c543016ce1f6aef48606280e45f892a928ca7068fba30021e9b786",
- "sha256:6482d3017a0c0327a49dddc8bd1074cc730d45db2ccb09c3bac1f8f32d1eb61b",
- "sha256:7d3ce02c0784b7cbcc771a2da6ea51f87e8716004512493a2b69016326301c3b",
- "sha256:a14e499c0f5955dcc3991f785f3f8e2130ed504fa3a7f44009ff458ad6bdd17f",
- "sha256:a39f54ccbcd2757d1d63b0ec00a00980c0b382c62865b61a505163943624ab20",
- "sha256:aabb0c5232910a20eec8563503c153a8e78bbf5459490c49ab31f6adf3f3a415",
- "sha256:bd4ecb473a96ad0f90c20acba4f0bf0df91a4e03a1f4dd6a4bdc9ca75aa3a715",
- "sha256:bf459128feb543cfca16a95f8da31e2e65e4c5257d2f3dfa8c0c1031139c9c92",
- "sha256:e2da3c13307eac601f3de04887624939aca8ee3c9488a0bb0eca4fb9401fc6b1",
- "sha256:f67814c38162f4deb31f68d590771a29d5ae3b1bd64b75cf232308e5c74777e0"
- ],
- "version": "==1.3.0"
+ "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4",
+ "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4",
+ "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574",
+ "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d",
+ "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25",
+ "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f",
+ "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505",
+ "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122",
+ "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7",
+ "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420",
+ "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f",
+ "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96",
+ "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6",
+ "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514",
+ "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff",
+ "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.4.0"
},
"python-dateutil": {
"hashes": [
@@ -497,11 +507,11 @@
},
"python-dotenv": {
"hashes": [
- "sha256:25c0ff1a3e12f4bde8d592cc254ab075cfe734fc5dd989036716fd17ee7e5ec7",
- "sha256:3b9909bc96b0edc6b01586e1eed05e71174ef4e04c71da5786370cebea53ad74"
+ "sha256:8c10c99a1b25d9a68058a1ad6f90381a62ba68230ca93966882a4dbc3bc9c33d",
+ "sha256:c10863aee750ad720f4f43436565e4c1698798d763b63234fb5021b6c616e423"
],
"index": "pypi",
- "version": "==0.13.0"
+ "version": "==0.14.0"
},
"python-magic": {
"hashes": [
@@ -527,6 +537,9 @@
"version": "==2020.1"
},
"raven": {
+ "extras": [
+ "flask"
+ ],
"hashes": [
"sha256:3fa6de6efa2493a7c827472e984ce9b020797d0da16f1db67197bcc23c8fae54",
"sha256:44a13f87670836e153951af9a3c80405d36b43097db869a36e92809673692ce4"
@@ -536,37 +549,37 @@
},
"regex": {
"hashes": [
- "sha256:1386e75c9d1574f6aa2e4eb5355374c8e55f9aac97e224a8a5a6abded0f9c927",
- "sha256:27ff7325b297fb6e5ebb70d10437592433601c423f5acf86e5bc1ee2919b9561",
- "sha256:329ba35d711e3428db6b45a53b1b13a0a8ba07cbbcf10bbed291a7da45f106c3",
- "sha256:3a9394197664e35566242686d84dfd264c07b20f93514e2e09d3c2b3ffdf78fe",
- "sha256:51f17abbe973c7673a61863516bdc9c0ef467407a940f39501e786a07406699c",
- "sha256:579ea215c81d18da550b62ff97ee187b99f1b135fd894a13451e00986a080cad",
- "sha256:70c14743320a68c5dac7fc5a0f685be63bc2024b062fe2aaccc4acc3d01b14a1",
- "sha256:7e61be8a2900897803c293247ef87366d5df86bf701083b6c43119c7c6c99108",
- "sha256:8044d1c085d49673aadb3d7dc20ef5cb5b030c7a4fa253a593dda2eab3059929",
- "sha256:89d76ce33d3266173f5be80bd4efcbd5196cafc34100fdab814f9b228dee0fa4",
- "sha256:99568f00f7bf820c620f01721485cad230f3fb28f57d8fbf4a7967ec2e446994",
- "sha256:a7c37f048ec3920783abab99f8f4036561a174f1314302ccfa4e9ad31cb00eb4",
- "sha256:c2062c7d470751b648f1cacc3f54460aebfc261285f14bc6da49c6943bd48bdd",
- "sha256:c9bce6e006fbe771a02bda468ec40ffccbf954803b470a0345ad39c603402577",
- "sha256:ce367d21f33e23a84fb83a641b3834dd7dd8e9318ad8ff677fbfae5915a239f7",
- "sha256:ce450ffbfec93821ab1fea94779a8440e10cf63819be6e176eb1973a6017aff5",
- "sha256:ce5cc53aa9fbbf6712e92c7cf268274eaff30f6bd12a0754e8133d85a8fb0f5f",
- "sha256:d466967ac8e45244b9dfe302bbe5e3337f8dc4dec8d7d10f5e950d83b140d33a",
- "sha256:d881c2e657c51d89f02ae4c21d9adbef76b8325fe4d5cf0e9ad62f850f3a98fd",
- "sha256:e565569fc28e3ba3e475ec344d87ed3cd8ba2d575335359749298a0899fe122e",
- "sha256:ea55b80eb0d1c3f1d8d784264a6764f931e172480a2f1868f2536444c5f01e01"
- ],
- "version": "==2020.5.14"
+ "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204",
+ "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162",
+ "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f",
+ "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb",
+ "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6",
+ "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7",
+ "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88",
+ "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99",
+ "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644",
+ "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a",
+ "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840",
+ "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067",
+ "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd",
+ "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4",
+ "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e",
+ "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89",
+ "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e",
+ "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc",
+ "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf",
+ "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341",
+ "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"
+ ],
+ "version": "==2020.7.14"
},
"requests": {
"hashes": [
- "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
- "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
+ "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
+ "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"index": "pypi",
- "version": "==2.23.0"
+ "version": "==2.24.0"
},
"requests-file": {
"hashes": [
@@ -596,6 +609,7 @@
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"soupsieve": {
@@ -603,6 +617,7 @@
"sha256:1634eea42ab371d3d346309b93df7870a88610f0725d47528be902a0d95ecc55",
"sha256:a59dc181727e95d25f781f0eb4fd1825ff45590ec8ff49eadfd7f1a537cc0232"
],
+ "markers": "python_version >= '3.5'",
"version": "==2.0.1"
},
"tabulate": {
@@ -620,6 +635,14 @@
"index": "pypi",
"version": "==2.2.2"
},
+ "toml": {
+ "hashes": [
+ "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+ "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+ ],
+ "index": "pypi",
+ "version": "==0.10.1"
+ },
"tzlocal": {
"hashes": [
"sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44",
@@ -629,62 +652,66 @@
},
"urllib3": {
"hashes": [
- "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
- "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
+ "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a",
+ "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
],
- "version": "==1.25.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.25.10"
},
"wcwidth": {
"hashes": [
- "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1",
- "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"
+ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
+ "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
],
- "version": "==0.1.9"
+ "version": "==0.2.5"
},
"werkzeug": {
"hashes": [
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==1.0.1"
},
"wtforms": {
"hashes": [
- "sha256:6ff8635f4caeed9f38641d48cfe019d0d3896f41910ab04494143fc027866e1b",
- "sha256:861a13b3ae521d6700dac3b2771970bd354a63ba7043ecc3a82b5288596a1972"
+ "sha256:7b504fc724d0d1d4d5d5c114e778ec88c37ea53144683e084215eed5155ada4c",
+ "sha256:81195de0ac94fbc8368abbaf9197b88c4f3ffd6c2719b5bf5fc9da744f3d829c"
],
"index": "pypi",
- "version": "==2.3.1"
+ "version": "==2.3.3"
}
},
"develop": {
"astroid": {
"hashes": [
- "sha256:4c17cea3e592c21b6e222f673868961bad77e1f985cb1694ed077475a89229c1",
- "sha256:d8506842a3faf734b81599c8b98dcc423de863adcc1999248480b18bd31a0f38"
+ "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703",
+ "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"
],
- "version": "==2.4.1"
+ "markers": "python_version >= '3.5'",
+ "version": "==2.4.2"
},
"attrs": {
"hashes": [
"sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
"sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==19.3.0"
},
"backcall": {
"hashes": [
- "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
- "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
+ "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e",
+ "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"
],
- "version": "==0.1.0"
+ "version": "==0.2.0"
},
"certifi": {
"hashes": [
- "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
- "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
+ "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3",
+ "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"
],
- "version": "==2020.4.5.1"
+ "version": "==2020.6.20"
},
"chardet": {
"hashes": [
@@ -695,39 +722,43 @@
},
"coverage": {
"hashes": [
- "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a",
- "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355",
- "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65",
- "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7",
- "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9",
- "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1",
- "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0",
- "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55",
- "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c",
- "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6",
- "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef",
- "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019",
- "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e",
- "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0",
- "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf",
- "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24",
- "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2",
- "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c",
- "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4",
- "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0",
- "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd",
- "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04",
- "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e",
- "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730",
- "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2",
- "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768",
- "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796",
- "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7",
- "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a",
- "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489",
- "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"
- ],
- "version": "==5.1"
+ "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb",
+ "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3",
+ "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716",
+ "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034",
+ "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3",
+ "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8",
+ "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0",
+ "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f",
+ "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4",
+ "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962",
+ "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d",
+ "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b",
+ "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4",
+ "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3",
+ "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258",
+ "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59",
+ "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01",
+ "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd",
+ "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b",
+ "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d",
+ "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89",
+ "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd",
+ "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b",
+ "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d",
+ "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46",
+ "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546",
+ "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082",
+ "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b",
+ "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4",
+ "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8",
+ "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811",
+ "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd",
+ "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651",
+ "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==5.2.1"
},
"decorator": {
"hashes": [
@@ -738,34 +769,35 @@
},
"flake8": {
"hashes": [
- "sha256:c69ac1668e434d37a2d2880b3ca9aafd54b3a10a3ac1ab101d22f29e29cf8634",
- "sha256:ccaa799ef9893cebe69fdfefed76865aeaefbb94cb8545617b2298786a4de9a5"
+ "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
+ "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
],
"index": "pypi",
- "version": "==3.8.2"
+ "version": "==3.8.3"
},
"idna": {
"hashes": [
- "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
- "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "version": "==2.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
},
"importlib-metadata": {
"hashes": [
- "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
- "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
+ "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83",
+ "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
],
"markers": "python_version < '3.8'",
- "version": "==1.6.0"
+ "version": "==1.7.0"
},
"ipython": {
"hashes": [
- "sha256:5b241b84bbf0eb085d43ae9d46adf38a13b45929ca7774a740990c2c242534bb",
- "sha256:f0126781d0f959da852fb3089e170ed807388e986a8dd4e6ac44855845b0fb1c"
+ "sha256:2dbcc8c27ca7d3cfe4fcdff7f45b27f9a8d3edfa70ff8024a71c7a8eb5f09d64",
+ "sha256:9f4fcb31d3b2c533333893b9172264e4821c1ac91839500f31bd43f2c59b3ccf"
],
"index": "pypi",
- "version": "==7.14.0"
+ "version": "==7.16.1"
},
"ipython-genutils": {
"hashes": [
@@ -779,14 +811,16 @@
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
"sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.3.21"
},
"jedi": {
"hashes": [
- "sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798",
- "sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030"
+ "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20",
+ "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"
],
- "version": "==0.17.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.17.2"
},
"lazy-object-proxy": {
"hashes": [
@@ -812,6 +846,7 @@
"sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
"sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.4.3"
},
"mccabe": {
@@ -823,24 +858,27 @@
},
"more-itertools": {
"hashes": [
- "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be",
- "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"
+ "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5",
+ "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"
],
- "version": "==8.3.0"
+ "markers": "python_version >= '3.5'",
+ "version": "==8.4.0"
},
"packaging": {
"hashes": [
"sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
"sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==20.4"
},
"parso": {
"hashes": [
- "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0",
- "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"
+ "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea",
+ "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"
],
- "version": "==0.7.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.7.1"
},
"pexpect": {
"hashes": [
@@ -869,6 +907,7 @@
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
"prompt-toolkit": {
@@ -876,6 +915,7 @@
"sha256:563d1a4140b63ff9dd587bda9557cffb2fe73650205ab6f4383092fb882e7dc8",
"sha256:df7e9e63aea609b1da3a65641ceaf5bc7d05e0a04de5bd45d05dbeffbabf9e04"
],
+ "markers": "python_full_version >= '3.6.1'",
"version": "==3.0.5"
},
"psycopg2": {
@@ -894,6 +934,7 @@
"sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055",
"sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8.5"
},
"ptyprocess": {
@@ -905,16 +946,18 @@
},
"py": {
"hashes": [
- "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
- "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
+ "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
+ "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
- "version": "==1.8.1"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.9.0"
},
"pycodestyle": {
"hashes": [
"sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
"sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.6.0"
},
"pyflakes": {
@@ -922,6 +965,7 @@
"sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
"sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.2.0"
},
"pygments": {
@@ -929,46 +973,48 @@
"sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44",
"sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"
],
+ "markers": "python_version >= '3.5'",
"version": "==2.6.1"
},
"pylint": {
"hashes": [
- "sha256:b95e31850f3af163c2283ed40432f053acbc8fc6eba6a069cb518d9dbf71848c",
- "sha256:dd506acce0427e9e08fb87274bcaa953d38b50a58207170dbf5b36cf3e16957b"
+ "sha256:7dd78437f2d8d019717dbf287772d0b2dbdfd13fc016aa7faa08d67bccc46adc",
+ "sha256:d0ece7d223fe422088b0e8f13fa0a1e8eb745ebffcb8ed53d3e95394b6101a1c"
],
"index": "pypi",
- "version": "==2.5.2"
+ "version": "==2.5.3"
},
"pyparsing": {
"hashes": [
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.4.7"
},
"pytest": {
"hashes": [
- "sha256:95c710d0a72d91c13fae35dce195633c929c3792f54125919847fdcdf7caa0d3",
- "sha256:eb2b5e935f6a019317e455b6da83dd8650ac9ffd2ee73a7b657a30873d67a698"
+ "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1",
+ "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"
],
"index": "pypi",
- "version": "==5.4.2"
+ "version": "==5.4.3"
},
"pytest-cov": {
"hashes": [
- "sha256:b6a814b8ed6247bd81ff47f038511b57fe1ce7f4cc25b9106f1a4b106f1d9322",
- "sha256:c87dfd8465d865655a8213859f1b4749b43448b5fae465cb981e16d52a811424"
+ "sha256:1a629dc9f48e53512fcbfda6b07de490c374b0c83c55ff7a1720b3fccff0ac87",
+ "sha256:6e6d18092dce6fad667cd7020deed816f858ad3b49d5b5e2b1cc1c97a4dba65c"
],
"index": "pypi",
- "version": "==2.9.0"
+ "version": "==2.10.0"
},
"pytest-mock": {
"hashes": [
- "sha256:997729451dfc36b851a9accf675488c7020beccda15e11c75632ee3d1b1ccd71",
- "sha256:ce610831cedeff5331f4e2fc453a5dd65384303f680ab34bee2c6533855b431c"
+ "sha256:5564c7cd2569b603f8451ec77928083054d8896046830ca763ed68f4112d17c7",
+ "sha256:7122d55505d5ed5a6f3df940ad174b3f606ecae5e9bc379569cdcbd4cd9d2b83"
],
"index": "pypi",
- "version": "==3.1.0"
+ "version": "==3.2.0"
},
"pytest-pylint": {
"hashes": [
@@ -987,25 +1033,26 @@
},
"requests": {
"hashes": [
- "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
- "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
+ "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b",
+ "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"
],
"index": "pypi",
- "version": "==2.23.0"
+ "version": "==2.24.0"
},
"responses": {
"hashes": [
- "sha256:1a78bc010b20a5022a2c0cb76b8ee6dc1e34d887972615ebd725ab9a166a4960",
- "sha256:3d596d0be06151330cb230a2d630717ab20f7a81f205019481e206eb5db79915"
+ "sha256:7bb697a5fedeb41d81e8b87f152d453d5cab42dcd1691b6a7d6097e94d33f373",
+ "sha256:af94d28cdfb48ded0ad82a5216616631543650f440334a693479b8991a6594a2"
],
"index": "pypi",
- "version": "==0.10.14"
+ "version": "==0.10.15"
},
"six": {
"hashes": [
"sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.15.0"
},
"toml": {
@@ -1013,6 +1060,7 @@
"sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
"sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
],
+ "index": "pypi",
"version": "==0.10.1"
},
"traitlets": {
@@ -1046,22 +1094,23 @@
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
],
- "markers": "implementation_name == 'cpython' and python_version < '3.8'",
+ "markers": "python_version < '3.8' and implementation_name == 'cpython'",
"version": "==1.4.1"
},
"urllib3": {
"hashes": [
- "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
- "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
+ "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a",
+ "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"
],
- "version": "==1.25.9"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.25.10"
},
"wcwidth": {
"hashes": [
- "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1",
- "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1"
+ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
+ "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
],
- "version": "==0.1.9"
+ "version": "==0.2.5"
},
"wrapt": {
"hashes": [
@@ -1074,6 +1123,7 @@
"sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
"sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
],
+ "markers": "python_version >= '3.6'",
"version": "==3.1.0"
}
}
diff --git a/python/fatcat_tools/transforms/__init__.py b/python/fatcat_tools/transforms/__init__.py
index 3f4700ff..7cc8f699 100644
--- a/python/fatcat_tools/transforms/__init__.py
+++ b/python/fatcat_tools/transforms/__init__.py
@@ -1,5 +1,5 @@
-from .entities import entity_to_dict, entity_from_json, entity_from_dict
+from .entities import entity_to_dict, entity_from_json, entity_from_dict, entity_from_toml, entity_to_toml
from .elasticsearch import release_to_elasticsearch, container_to_elasticsearch, changelog_to_elasticsearch, file_to_elasticsearch
from .csl import release_to_csl, citeproc_csl
from .ingest import release_ingest_request
diff --git a/python/fatcat_tools/transforms/entities.py b/python/fatcat_tools/transforms/entities.py
index 53455e85..3892d54a 100644
--- a/python/fatcat_tools/transforms/entities.py
+++ b/python/fatcat_tools/transforms/entities.py
@@ -1,9 +1,10 @@
import json
+import toml
import collections
from fatcat_openapi_client import ApiClient
-def entity_to_dict(entity, api_client=None):
+def entity_to_dict(entity, api_client=None) -> dict:
"""
Hack to take advantage of the code-generated serialization code.
@@ -17,7 +18,7 @@ def entity_to_dict(entity, api_client=None):
api_client = ApiClient()
return api_client.sanitize_for_serialization(entity)
-def entity_from_json(json_str, entity_type, api_client=None):
+def entity_from_json(json_str: str, entity_type, api_client=None):
"""
Hack to take advantage of the code-generated deserialization code
@@ -29,6 +30,21 @@ def entity_from_json(json_str, entity_type, api_client=None):
thing.data = json_str
return api_client.deserialize(thing, entity_type)
-def entity_from_dict(obj, entity_type, api_client=None):
+def entity_from_dict(obj: dict, entity_type, api_client=None):
json_str = json.dumps(obj)
return entity_from_json(json_str, entity_type, api_client=api_client)
+
+def entity_to_toml(entity, api_client=None, pop_fields=None) -> str:
+ """
+ pop_fields parameter can be used to strip out some fields from the resulting
+ TOML. Eg, for fields which should not be edited, like the ident.
+ """
+ obj = entity_to_dict(entity, api_client=api_client)
+ pop_fields = pop_fields or []
+ for k in pop_fields:
+ obj.pop(k, None)
+ return toml.dumps(obj)
+
+def entity_from_toml(toml_str: str, entity_type, api_client=None):
+ obj = toml.loads(toml_str)
+ return entity_from_dict(obj, entity_type, api_client=api_client)
diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py
index ffce35f3..8e3b03b0 100644
--- a/python/fatcat_web/editing_routes.py
+++ b/python/fatcat_web/editing_routes.py
@@ -1,8 +1,10 @@
+from typing import Optional
+
from flask import render_template, abort, redirect, session, flash
from flask_login import login_required
-from fatcat_openapi_client import Editgroup
+from fatcat_openapi_client import *
from fatcat_openapi_client.rest import ApiException
from fatcat_tools.transforms import *
from fatcat_web import app, api, auth_api
@@ -13,6 +15,104 @@ from fatcat_web.entity_helpers import *
### Helper Methods ##########################################################
+def generic_entity_create_from_toml(user_api, entity_type: str, editgroup_id: str, toml_str: str) -> EntityEdit:
+ if entity_type == 'container':
+ entity = entity_from_toml(toml_str, ContainerEntity)
+ edit = user_api.create_container(editgroup_id, entity)
+ elif entity_type == 'creator':
+ entity = entity_from_toml(toml_str, CreatorEntity)
+ edit = user_api.create_creator(editgroup_id, entity)
+ elif entity_type == 'file':
+ entity = entity_from_toml(toml_str, FileEntity)
+ edit = user_api.create_file(editgroup_id, entity)
+ elif entity_type == 'fileset':
+ entity = entity_from_toml(toml_str, FilesetEntity)
+ edit = user_api.create_fileset(editgroup_id, entity)
+ elif entity_type == 'webcapture':
+ entity = entity_from_toml(toml_str, WebcaptureEntity)
+ edit = user_api.create_webcapture(editgroup_id, entity)
+ elif entity_type == 'release':
+ entity = entity_from_toml(toml_str, ReleaseEntity)
+ edit = user_api.create_release(editgroup_id, entity)
+ elif entity_type == 'work':
+ entity = entity_from_toml(toml_str, WorkEntity)
+ edit = user_api.create_work(editgroup_id, entity)
+ else:
+ raise NotImplementedError
+ return edit
+
+def generic_entity_delete_edit(user_api, entity_type: str, editgroup_id: str, edit_id: str) -> None:
+ try:
+ if entity_type == 'container':
+ user_api.delete_container_edit(editgroup_id, edit_id)
+ elif entity_type == 'creator':
+ user_api.delete_creator_edit(editgroup_id, edit_id)
+ elif entity_type == 'file':
+ user_api.delete_file_edit(editgroup_id, edit_id)
+ elif entity_type == 'fileset':
+ user_api.delete_fileset_edit(editgroup_id, edit_id)
+ elif entity_type == 'webcapture':
+ user_api.delete_webcapture_edit(editgroup_id, edit_id)
+ elif entity_type == 'release':
+ user_api.delete_release_edit(editgroup_id, edit_id)
+ elif entity_type == 'work':
+ user_api.delete_work_edit(editgroup_id, edit_id)
+ else:
+ raise NotImplementedError
+ except ApiException as ae:
+ if ae.status == 404:
+ pass
+ else:
+ raise ae
+
+def generic_entity_delete_entity(user_api, entity_type: str, editgroup_id: str, entity_ident: str) -> None:
+ try:
+ if entity_type == 'container':
+ edit = user_api.delete_container(editgroup_id, entity_ident)
+ elif entity_type == 'creator':
+ edit = user_api.delete_creator(editgroup_id, entity_ident)
+ elif entity_type == 'file':
+ edit = user_api.delete_file(editgroup_id, entity_ident)
+ elif entity_type == 'fileset':
+ edit = user_api.delete_fileset(editgroup_id, entity_ident)
+ elif entity_type == 'webcapture':
+ edit = user_api.delete_webcapture(editgroup_id, entity_ident)
+ elif entity_type == 'release':
+ edit = user_api.delete_release(editgroup_id, entity_ident)
+ elif entity_type == 'work':
+ edit = user_api.delete_work(editgroup_id, entity_ident)
+ else:
+ raise NotImplementedError
+ except ApiException as ae:
+ raise ae
+ return edit
+
+def generic_entity_update_from_toml(user_api, entity_type: str, editgroup_id: str, existing_ident, toml_str: str) -> EntityEdit:
+ if entity_type == 'container':
+ entity = entity_from_toml(toml_str, ContainerEntity)
+ edit = user_api.update_container(editgroup_id, existing_ident, entity)
+ elif entity_type == 'creator':
+ entity = entity_from_toml(toml_str, CreatorEntity)
+ edit = user_api.update_creator(editgroup_id, existing_ident, entity)
+ elif entity_type == 'file':
+ entity = entity_from_toml(toml_str, FileEntity)
+ edit = user_api.update_file(editgroup_id, existing_ident, entity)
+ elif entity_type == 'fileset':
+ entity = entity_from_toml(toml_str, FilesetEntity)
+ edit = user_api.update_fileset(editgroup_id, existing_ident, entity)
+ elif entity_type == 'webcapture':
+ entity = entity_from_toml(toml_str, WebcaptureEntity)
+ edit = user_api.update_webcapture(editgroup_id, existing_ident, entity)
+ elif entity_type == 'release':
+ entity = entity_from_toml(toml_str, ReleaseEntity)
+ edit = user_api.update_release(editgroup_id, existing_ident, entity)
+ elif entity_type == 'work':
+ entity = entity_from_toml(toml_str, WorkEntity)
+ edit = user_api.update_work(editgroup_id, existing_ident, entity)
+ else:
+ raise NotImplementedError
+ return edit
+
def form_editgroup_get_or_create(api, edit_form):
"""
This function expects a submitted, validated edit form
@@ -75,7 +175,7 @@ def generic_entity_edit(editgroup_id, entity_type, existing_ident, edit_template
try:
editgroup = api.get_editgroup(editgroup_id)
except ApiException as ae:
- abort(ae.status)
+ raise ae
# check that editgroup is edit-able
if editgroup.changelog_index != None:
@@ -138,14 +238,7 @@ def generic_entity_edit(editgroup_id, entity_type, existing_ident, edit_template
# a "update pointer" edit
existing.revision = None
try:
- if entity_type == 'container':
- user_api.delete_container_edit(editgroup.editgroup_id, existing_edit.edit_id)
- elif entity_type == 'file':
- user_api.delete_file_edit(editgroup.editgroup_id, existing_edit.edit_id)
- elif entity_type == 'release':
- user_api.delete_release_edit(editgroup.editgroup_id, existing_edit.edit_id)
- else:
- raise NotImplementedError
+ generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, existing_edit.edit_id)
except ApiException as ae:
if ae.status == 404:
pass
@@ -194,7 +287,13 @@ def generic_entity_edit(editgroup_id, entity_type, existing_ident, edit_template
existing_ident=existing_ident, editgroup=editgroup,
potential_editgroups=potential_editgroups), status
-def generic_edit_delete(editgroup_id, entity_type, edit_id):
+def generic_entity_toml_edit(editgroup_id, entity_type, existing_ident, edit_template):
+ """
+ Similar to generic_entity_edit(), but for TOML editing mode.
+
+ Handles both creation and update/edit paths.
+ """
+
# fetch editgroup (if set) or 404
editgroup = None
if editgroup_id:
@@ -205,23 +304,185 @@ def generic_edit_delete(editgroup_id, entity_type, edit_id):
# check that editgroup is edit-able
if editgroup.changelog_index != None:
- abort(400, "Editgroup already merged")
+ flash("Editgroup already merged")
+ abort(400)
+
+ # fetch entity (if set) or 404
+ existing = None
+ existing_edit = None
+ if editgroup and existing_ident:
+ existing, existing_edit = generic_get_editgroup_entity(editgroup, entity_type, existing_ident)
+ elif existing_ident:
+ existing = generic_get_entity(entity_type, existing_ident)
+
+ # parse form (if submitted)
+ status = 200
+ form = EntityTomlForm()
+
+ if form.is_submitted():
+ if form.validate_on_submit():
+ # API on behalf of user
+ user_api = auth_api(session['api_token'])
+ if not editgroup:
+ editgroup = form_editgroup_get_or_create(user_api, form)
+
+ if editgroup:
+
+ if not existing_ident: # it's a create
+ try:
+ edit = generic_entity_create_from_toml(user_api, entity_type, editgroup.editgroup_id, form.toml.data)
+ except ValueError as ve:
+ form.toml.errors = [ve]
+ status = 400
+ except ApiException as ae:
+ app.log.warning(ae)
+ raise ae
+ if status == 200:
+ return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident))
+ else: # it's an update
+ # TODO: some danger of wiping database state here is
+ # "updated edit" causes, eg, a 4xx error. Better to allow
+ # this in the API itself. For now, form validation *should*
+ # catch most errors, and if not editor can hit back and try
+ # again. This means, need to allow failure of deletion.
+ if existing_edit:
+ # need to clear revision on object or this becomes just
+ # a "update pointer" edit
+ existing.revision = None
+ generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, existing_edit.edit_id)
+ try:
+ edit = generic_entity_update_from_toml(user_api, entity_type, editgroup.editgroup_id, existing.ident, form.toml.data)
+ except ValueError as ve:
+ form.toml.errors = [ve]
+ status = 400
+ except ApiException as ae:
+ app.log.warning(ae)
+ raise ae
+ if status == 200:
+ return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident))
+ else:
+ status = 400
+ elif form.errors:
+ status = 400
+ app.log.info("form errors (did not validate): {}".format(form.errors))
+
+ else: # form is not submitted
+ if existing:
+ form = EntityTomlForm.from_entity(existing)
+
+ editor_editgroups = api.get_editor_editgroups(session['editor']['editor_id'], limit=20)
+ potential_editgroups = [e for e in editor_editgroups if e.changelog_index == None and e.submitted == None]
+
+ if not form.is_submitted():
+ # default to most recent not submitted, fallback to "create new"
+ form.editgroup_id.data = ""
+ if potential_editgroups:
+ form.editgroup_id.data = potential_editgroups[0].editgroup_id
+
+ return render_template(edit_template, form=form, entity_type=entity_type,
+ existing_ident=existing_ident, editgroup=editgroup,
+ potential_editgroups=potential_editgroups), status
+
+def generic_entity_delete(editgroup_id: Optional[str], entity_type: str, existing_ident: str):
+ """
+ Similar to generic_entity_edit(), but for deleting entities. This is a bit
+ simpler!
+
+ Handles both creation and update/edit paths.
+ """
+
+ # fetch editgroup (if set) or 404
+ editgroup = None
+ if editgroup_id:
+ try:
+ editgroup = api.get_editgroup(editgroup_id)
+ except ApiException as ae:
+ raise ae
+
+ # check that editgroup is edit-able
+ if editgroup.changelog_index != None:
+ flash("Editgroup already merged")
+ abort(400)
+
+ # fetch entity (if set) or 404
+ existing = None
+ existing_edit = None
+ if editgroup and existing_ident:
+ existing, existing_edit = generic_get_editgroup_entity(editgroup, entity_type, existing_ident)
+ elif existing_ident:
+ existing = generic_get_entity(entity_type, existing_ident)
+
+ # parse form (if submitted)
+ status = 200
+ form = EntityEditForm()
+
+ if form.is_submitted():
+ if form.validate_on_submit():
+ # API on behalf of user
+ user_api = auth_api(session['api_token'])
+ if not editgroup:
+ editgroup = form_editgroup_get_or_create(user_api, form)
+
+ if editgroup:
+ # TODO: some danger of wiping database state here is
+ # "updated edit" causes, eg, a 4xx error. Better to allow
+ # this in the API itself. For now, form validation *should*
+ # catch most errors, and if not editor can hit back and try
+ # again. This means, need to allow failure of deletion.
+ if existing_edit:
+ # need to clear revision on object or this becomes just
+ # a "update pointer" edit
+ existing.revision = None
+ generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, existing_edit.edit_id)
+ try:
+ edit = generic_entity_delete_entity(user_api, entity_type, editgroup.editgroup_id, existing.ident)
+ except ApiException as ae:
+ app.log.warning(ae)
+ raise ae
+ if status == 200:
+ return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident))
+ else:
+ status = 400
+ elif form.errors:
+ status = 400
+ app.log.info("form errors (did not validate): {}".format(form.errors))
+
+ else: # form is not submitted
+ if existing:
+ form = EntityTomlForm.from_entity(existing)
+
+ editor_editgroups = api.get_editor_editgroups(session['editor']['editor_id'], limit=20)
+ potential_editgroups = [e for e in editor_editgroups if e.changelog_index == None and e.submitted == None]
+
+ if not form.is_submitted():
+ # default to most recent not submitted, fallback to "create new"
+ form.editgroup_id.data = ""
+ if potential_editgroups:
+ form.editgroup_id.data = potential_editgroups[0].editgroup_id
+
+ return render_template("entity_delete.html", form=form, entity_type=entity_type,
+ existing_ident=existing_ident, editgroup=editgroup,
+ potential_editgroups=potential_editgroups), status
+
+def generic_edit_delete(editgroup_id, entity_type, edit_id):
+ # fetch editgroup (if set) or 404
+ editgroup = None
+ if editgroup_id:
+ try:
+ editgroup = api.get_editgroup(editgroup_id)
+ except ApiException as ae:
+ abort(ae.status)
+
+ # check that editgroup is edit-able
+ if editgroup.changelog_index != None:
+ flash("Editgroup already merged")
+ abort(400)
# API on behalf of user
user_api = auth_api(session['api_token'])
# do the deletion
- try:
- if entity_type == 'container':
- user_api.delete_container_edit(editgroup.editgroup_id, edit_id)
- elif entity_type == 'file':
- user_api.delete_file_edit(editgroup.editgroup_id, edit_id)
- elif entity_type == 'release':
- user_api.delete_release_edit(editgroup.editgroup_id, edit_id)
- else:
- raise NotImplementedError
- except ApiException as ae:
- raise ae
+ generic_entity_delete_edit(user_api, entity_type, editgroup.editgroup_id, edit_id)
return redirect("/editgroup/{}".format(editgroup_id))
@@ -234,19 +495,43 @@ def container_create_view():
@app.route('/container/<ident>/edit', methods=['GET', 'POST'])
@login_required
-def container_edit(ident):
+def container_edit_view(ident):
return generic_entity_edit(None, 'container', ident, 'container_edit.html')
+@app.route('/container/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def container_delete_view(ident):
+ return generic_entity_delete(None, 'container', ident)
+
@app.route('/editgroup/<editgroup_id>/container/<ident>/edit', methods=['GET', 'POST'])
@login_required
-def container_editgroup_edit(editgroup_id, ident):
+def container_editgroup_edit_view(editgroup_id, ident):
return generic_entity_edit(editgroup_id, 'container', ident, 'container_edit.html')
+@app.route('/editgroup/<editgroup_id>/container/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def container_editgroup_delete_view(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'container', ident)
+
@app.route('/editgroup/<editgroup_id>/container/edit/<edit_id>/delete', methods=['POST'])
@login_required
def container_edit_delete(editgroup_id, edit_id):
return generic_edit_delete(editgroup_id, 'container', edit_id)
+@app.route('/creator/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def creator_delete_view(ident):
+ return generic_entity_delete(None, 'creator', ident)
+
+@app.route('/editgroup/<editgroup_id>/creator/edit/<edit_id>/delete', methods=['POST'])
+def creator_edit_delete(editgroup_id, edit_id):
+ return generic_edit_delete(editgroup_id, 'creator', edit_id)
+
+@app.route('/editgroup/<editgroup_id>/creator/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def creator_editgroup_delete(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'creator', ident)
+
@app.route('/file/create', methods=['GET', 'POST'])
@login_required
def file_create_view():
@@ -254,19 +539,57 @@ def file_create_view():
@app.route('/file/<ident>/edit', methods=['GET', 'POST'])
@login_required
-def file_edit(ident):
+def file_edit_view(ident):
return generic_entity_edit(None, 'file', ident, 'file_edit.html')
+@app.route('/file/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def file_delete_view(ident):
+ return generic_entity_delete(None, 'file', ident)
+
@app.route('/editgroup/<editgroup_id>/file/<ident>/edit', methods=['GET', 'POST'])
@login_required
-def file_editgroup_edit(editgroup_id, ident):
+def file_editgroup_edit_view(editgroup_id, ident):
return generic_entity_edit(editgroup_id, 'file', ident, 'file_edit.html')
+@app.route('/editgroup/<editgroup_id>/file/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def file_editgroup_delete_view(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'file', ident)
+
@app.route('/editgroup/<editgroup_id>/file/edit/<edit_id>/delete', methods=['POST'])
@login_required
def file_edit_delete(editgroup_id, edit_id):
return generic_edit_delete(editgroup_id, 'file', edit_id)
+@app.route('/fileset/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def fileset_delete_view(ident):
+ return generic_entity_delete(None, 'fileset', ident)
+
+@app.route('/editgroup/<editgroup_id>/fileset/edit/<edit_id>/delete', methods=['POST'])
+def fileset_edit_delete(editgroup_id, edit_id):
+ return generic_edit_delete(editgroup_id, 'fileset', edit_id)
+
+@app.route('/editgroup/<editgroup_id>/fileset/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def fileset_editgroup_delete(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'fileset', ident)
+
+@app.route('/webcapture/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def webcapture_delete_view(ident):
+ return generic_entity_delete(None, 'webcapture', ident)
+
+@app.route('/editgroup/<editgroup_id>/webcapture/edit/<edit_id>/delete', methods=['POST'])
+def webcapture_edit_delete(editgroup_id, edit_id):
+ return generic_edit_delete(editgroup_id, 'webcapture', edit_id)
+
+@app.route('/editgroup/<editgroup_id>/webcapture/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def webcapture_editgroup_delete(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'webcapture', ident)
+
@app.route('/release/create', methods=['GET', 'POST'])
@login_required
def release_create_view():
@@ -274,82 +597,208 @@ def release_create_view():
@app.route('/release/<ident>/edit', methods=['GET', 'POST'])
@login_required
-def release_edit(ident):
+def release_edit_view(ident):
return generic_entity_edit(None, 'release', ident, 'release_edit.html')
+@app.route('/release/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def release_delete_view(ident):
+ return generic_entity_delete(None, 'release', ident)
+
@app.route('/editgroup/<editgroup_id>/release/<ident>/edit', methods=['GET', 'POST'])
@login_required
def release_editgroup_edit(editgroup_id, ident):
return generic_entity_edit(editgroup_id, 'release', ident, 'release_edit.html')
+@app.route('/editgroup/<editgroup_id>/release/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def release_editgroup_delete(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'release', ident)
+
@app.route('/editgroup/<editgroup_id>/release/edit/<edit_id>/delete', methods=['POST'])
@login_required
def release_edit_delete(editgroup_id, edit_id):
return generic_edit_delete(editgroup_id, 'release', edit_id)
+@app.route('/work/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def work_delete_view(ident):
+ return generic_entity_delete(None, 'work', ident)
-### Not-Implemented Views ###################################################
+@app.route('/editgroup/<editgroup_id>/work/edit/<edit_id>/delete', methods=['POST'])
+def work_edit_delete(editgroup_id, edit_id):
+ return generic_edit_delete(editgroup_id, 'work', edit_id)
+
+@app.route('/editgroup/<editgroup_id>/work/<ident>/delete', methods=['GET', 'POST'])
+@login_required
+def work_editgroup_delete(editgroup_id, ident):
+ return generic_entity_delete(editgroup_id, 'work', ident)
+
+### TOML Views ##############################################################
+
+@app.route('/container/create/toml', methods=['GET', 'POST'])
+@login_required
+def container_create_toml_view():
+ return generic_entity_toml_edit(None, 'container', None, 'entity_create_toml.html')
+
+@app.route('/container/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def container_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'container', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/container/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def container_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'container', ident, 'entity_edit_toml.html')
+
+@app.route('/creator/create/toml', methods=['GET', 'POST'])
+@login_required
+def creator_create_toml_view():
+ return generic_entity_toml_edit(None, 'creator', None, 'entity_create_toml.html')
+
+@app.route('/creator/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def creator_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'creator', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/creator/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def creator_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'creator', ident, 'entity_edit_toml.html')
+
+@app.route('/file/create/toml', methods=['GET', 'POST'])
+@login_required
+def file_create_toml_view():
+ return generic_entity_toml_edit(None, 'file', None, 'entity_create_toml.html')
+
+@app.route('/file/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def file_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'file', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/file/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def file_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'file', ident, 'entity_edit_toml.html')
+
+@app.route('/fileset/create/toml', methods=['GET', 'POST'])
+@login_required
+def fileset_create_toml_view():
+ return generic_entity_toml_edit(None, 'fileset', None, 'entity_create_toml.html')
+
+@app.route('/fileset/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def fileset_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'fileset', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/fileset/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def fileset_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'fileset', ident, 'entity_edit_toml.html')
+
+@app.route('/webcapture/create/toml', methods=['GET', 'POST'])
+@login_required
+def webcapture_create_toml_view():
+ return generic_entity_toml_edit(None, 'webcapture', None, 'entity_create_toml.html')
+
+@app.route('/webcapture/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def webcapture_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'webcapture', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/webcapture/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def webcapture_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'webcapture', ident, 'entity_edit_toml.html')
+
+@app.route('/release/create/toml', methods=['GET', 'POST'])
+@login_required
+def release_create_toml_view():
+ return generic_entity_toml_edit(None, 'release', None, 'entity_create_toml.html')
+
+@app.route('/release/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def release_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'release', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/release/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def release_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'release', ident, 'entity_edit_toml.html')
+
+@app.route('/work/create/toml', methods=['GET', 'POST'])
+@login_required
+def work_create_toml_view():
+ return generic_entity_toml_edit(None, 'work', None, 'entity_create_toml.html')
+
+@app.route('/work/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def work_edit_toml_view(ident):
+ return generic_entity_toml_edit(None, 'work', ident, 'entity_edit_toml.html')
+
+@app.route('/editgroup/<editgroup_id>/work/<ident>/edit/toml', methods=['GET', 'POST'])
+@login_required
+def work_editgroup_edit_toml(editgroup_id, ident):
+ return generic_entity_toml_edit(editgroup_id, 'work', ident, 'entity_edit_toml.html')
+
+### TOML-Only Editing Redirects ################################################
@app.route('/creator/create', methods=['GET'])
+@login_required
def creator_create_view():
- return abort(404)
+ return redirect('/creator/create/toml')
@app.route('/creator/<ident>/edit', methods=['GET'])
-def creator_edit(ident):
- return render_template('entity_edit.html'), 404
+@login_required
+def creator_edit_view(ident):
+ return redirect(f'/creator/{ident}/edit/toml')
@app.route('/editgroup/<editgroup_id>/creator/<ident>/edit', methods=['GET', 'POST'])
+@login_required
def creator_editgroup_edit(editgroup_id, ident):
- return abort(404)
-
-@app.route('/editgroup/<editgroup_id>/creator/edit/<edit_id>/delete', methods=['POST'])
-def creator_edit_delete(editgroup_id, edit_id):
- return abort(404)
+ return redirect(f'/editgroup/{editgroup_id}/creator/{ident}/edit/toml')
@app.route('/fileset/create', methods=['GET'])
+@login_required
def fileset_create_view():
- return abort(404)
+ return redirect('/fileset/create/toml')
@app.route('/fileset/<ident>/edit', methods=['GET'])
-def fileset_edit(ident):
- return render_template('entity_edit.html'), 404
+@login_required
+def fileset_edit_view(ident):
+ return redirect(f'/fileset/{ident}/edit/toml')
@app.route('/editgroup/<editgroup_id>/fileset/<ident>/edit', methods=['GET', 'POST'])
+@login_required
def fileset_editgroup_edit(editgroup_id, ident):
- return abort(404)
-
-@app.route('/editgroup/<editgroup_id>/fileset/edit/<edit_id>/delete', methods=['POST'])
-def fileset_edit_delete(editgroup_id, edit_id):
- return abort(404)
+ return redirect(f'/editgroup/{editgroup_id}/fileset/{ident}/edit/toml')
@app.route('/webcapture/create', methods=['GET'])
+@login_required
def webcapture_create_view():
- return abort(404)
+ return redirect('/webcapture/create/toml')
@app.route('/webcapture/<ident>/edit', methods=['GET'])
-def webcapture_edit(ident):
- return render_template('entity_edit.html'), 404
+@login_required
+def webcapture_edit_view(ident):
+ return redirect(f'/webcapture/{ident}/edit/toml')
@app.route('/editgroup/<editgroup_id>/webcapture/<ident>/edit', methods=['GET', 'POST'])
+@login_required
def webcapture_editgroup_edit(editgroup_id, ident):
- return abort(404)
-
-@app.route('/editgroup/<editgroup_id>/webcapture/edit/<edit_id>/delete', methods=['POST'])
-def webcapture_edit_delete(editgroup_id, edit_id):
- return abort(404)
+ return redirect(f'/editgroup/{editgroup_id}/webcapture/{ident}/edit/toml')
@app.route('/work/create', methods=['GET'])
+@login_required
def work_create_view():
- return abort(404)
+ return redirect('/work/create/toml')
@app.route('/work/<ident>/edit', methods=['GET'])
-def work_edit(ident):
- return render_template('entity_edit.html'), 404
+@login_required
+def work_edit_view(ident):
+ return redirect(f'/work/{ident}/edit/toml')
@app.route('/editgroup/<editgroup_id>/work/<ident>/edit', methods=['GET', 'POST'])
+@login_required
def work_editgroup_edit(editgroup_id, ident):
- return abort(404)
-
-@app.route('/editgroup/<editgroup_id>/work/edit/<edit_id>/delete', methods=['POST'])
-def work_edit_delete(editgroup_id, edit_id):
- return abort(404)
+ return redirect(f'/editgroup/{editgroup_id}/work/{ident}/edit/toml')
diff --git a/python/fatcat_web/entity_helpers.py b/python/fatcat_web/entity_helpers.py
index c9a57290..7156b9be 100644
--- a/python/fatcat_web/entity_helpers.py
+++ b/python/fatcat_web/entity_helpers.py
@@ -1,5 +1,6 @@
from flask import abort
+from fatcat_openapi_client import *
from fatcat_openapi_client.rest import ApiException, ApiValueError
from fatcat_tools.transforms import *
from fatcat_web import api
@@ -148,6 +149,26 @@ def generic_get_entity_revision(entity_type, revision_id):
except ApiValueError:
abort(400)
+def generic_deleted_entity(entity_type, ident):
+ if entity_type == 'container':
+ entity = ContainerEntity()
+ elif entity_type == 'creator':
+ entity = CreatorEntity()
+ elif entity_type == 'file':
+ entity = FileEntity()
+ elif entity_type == 'fileset':
+ entity = FilesetEntity()
+ elif entity_type == 'webcapture':
+ entity = WebcaptureEntity()
+ elif entity_type == 'release':
+ entity = ReleaseEntity()
+ elif entity_type == 'work':
+ entity = WorkEntity()
+ else:
+ raise NotImplementedError
+ entity.ident = ident
+ return entity
+
def generic_get_editgroup_entity(editgroup, entity_type, ident):
if entity_type == 'container':
edits = editgroup.edits.containers
@@ -166,14 +187,18 @@ def generic_get_editgroup_entity(editgroup, entity_type, ident):
else:
raise NotImplementedError
revision_id = None
+ edit = None
for e in edits:
if e.ident == ident:
revision_id = e.revision
edit = e
break
- if not revision_id:
+ if not edit:
# couldn't find relevant edit in this editgroup
abort(404)
+ if not revision_id:
+ # deletion, presumably
+ return generic_deleted_entity(entity_type, ident), edit
try:
entity = generic_get_entity_revision(entity_type, revision_id)
diff --git a/python/fatcat_web/forms.py b/python/fatcat_web/forms.py
index 15585bf6..ff885fcb 100644
--- a/python/fatcat_web/forms.py
+++ b/python/fatcat_web/forms.py
@@ -4,10 +4,14 @@ Note: in thoery could use, eg, https://github.com/christabor/swagger_wtforms,
but can't find one that is actually maintained.
"""
+import datetime
+
+import toml
from flask_wtf import FlaskForm
from wtforms import SelectField, DateField, StringField, IntegerField, \
- HiddenField, FormField, FieldList, validators
+ HiddenField, FormField, FieldList, validators, ValidationError, TextAreaField
+from fatcat_tools import entity_to_toml
from fatcat_openapi_client import ContainerEntity, FileEntity, \
ReleaseEntity, ReleaseContrib, FileUrl, ReleaseExtIds
@@ -29,6 +33,16 @@ release_stage_options = [
('published', 'Published'),
('updated', 'Updated'),
]
+withdrawn_status_options = [
+ ('', 'Not Withdrawn (blank)'),
+ ('retracted', 'Retracted'),
+ ('withdrawn', 'Withdrawn'),
+ ('concern', 'Concern Noted'),
+ ('spam', 'Spam'),
+ ('legal', 'Legal Taketown'),
+ ('safety', 'Public Safety'),
+ ('national-security', 'National Security'),
+]
role_type_options = [
('author', 'Author'),
('editor', 'Editor'),
@@ -62,11 +76,25 @@ class ReleaseContribForm(FlaskForm):
default='author')
RELEASE_SIMPLE_ATTRS = ['title', 'original_title', 'work_id', 'container_id',
- 'release_type', 'release_stage', 'release_date', 'volume', 'issue',
- 'pages', 'publisher', 'language', 'license_slug']
+ 'release_type', 'release_stage', 'withdrawn_status', 'release_date',
+ 'release_year', 'volume', 'issue', 'pages', 'publisher', 'language',
+ 'license_slug']
RELEASE_EXTID_ATTRS = ['doi', 'wikidata_qid', 'isbn13', 'pmid', 'pmcid']
+def valid_year(form, field):
+ if field.data > datetime.date.today().year + 5:
+ raise ValidationError(
+ f"Year is too far in the future: {field.data}")
+ if field.data < 10:
+ raise ValidationError(
+ f"Year is too far in the past: {field.data}")
+
+def valid_2char_ascii(form, field):
+ if len(field.data) != 2 or len(field.data.encode('utf-8')) != 2 or not field.data.isalpha() or field.data != field.data.lower():
+ raise ValidationError(
+ f"Must be 2-character ISO format, lower case: {field.data}")
+
class ReleaseEntityForm(EntityEditForm):
"""
TODO:
@@ -75,7 +103,7 @@ class ReleaseEntityForm(EntityEditForm):
"""
title = StringField('Title',
[validators.DataRequired()])
- original_title = StringField('Original Title')
+ original_title = StringField('Title in Original Language (if different)')
work_id = StringField('Work FCID',
[validators.Optional(True),
validators.Length(min=26, max=26)])
@@ -87,9 +115,14 @@ class ReleaseEntityForm(EntityEditForm):
choices=release_type_options,
default='')
release_stage = SelectField(choices=release_stage_options)
+ withdrawn_status = SelectField("Withdrawn Status",
+ [validators.Optional(True)],
+ choices=withdrawn_status_options,
+ default='')
release_date = DateField('Release Date',
[validators.Optional(True)])
- #release_year
+ release_year = IntegerField('Release Year',
+ [validators.Optional(True), valid_year])
doi = StringField('DOI',
[validators.Regexp(r'^10\..*\/.*', message="DOI must be valid"),
validators.Optional(True)])
@@ -104,7 +137,8 @@ class ReleaseEntityForm(EntityEditForm):
issue = StringField('Issue')
pages = StringField('Pages')
publisher = StringField('Publisher (optional)')
- language = StringField('Language (code)')
+ language = StringField('Language (code)',
+ [validators.Optional(True), valid_2char_ascii])
license_slug = StringField('License (slug)')
contribs = FieldList(FormField(ReleaseContribForm))
#refs
@@ -150,11 +184,13 @@ class ReleaseEntityForm(EntityEditForm):
a = None
setattr(re, simple_attr, a)
for extid_attr in RELEASE_EXTID_ATTRS:
- a = getattr(self, simple_attr).data
+ a = getattr(self, extid_attr).data
# special case blank strings
if a == '':
a = None
- setattr(re.ext_ids, simple_attr, a)
+ setattr(re.ext_ids, extid_attr, a)
+ if self.release_date.data:
+ re.release_year = self.release_date.data.year
# bunch of complexity here to preserve old contrib metadata (eg,
# affiliation and extra) not included in current forms
# TODO: this may be broken; either way needs tests
@@ -204,8 +240,9 @@ class ContainerEntityForm(EntityEditForm):
issnl = StringField("ISSN-L (linking)")
issne = StringField("ISSN (electronic)")
issnp = StringField("ISSN (print)")
- original_name = StringField("Original Name (native language)")
- country = StringField("Country of Publication (ISO code)")
+ original_name = StringField("Name in Original Language (if different)")
+ country = StringField("Country of Publication (ISO code)",
+ [validators.Optional(True), valid_2char_ascii])
wikidata_qid = StringField('Wikidata QID')
urls = FieldList(
StringField("Container URLs",
@@ -413,3 +450,31 @@ class SavePaperNowForm(FlaskForm):
ingest_request['link_source'] = 'arxiv'
ingest_request['link_source_id'] = release.ext_ids.arxiv
return ingest_request
+
+def valid_toml(form, field):
+ try:
+ toml.loads(field.data)
+ except toml.TomlDecodeError as tpe:
+ raise ValidationError(tpe)
+
+class EntityTomlForm(EntityEditForm):
+
+ toml = TextAreaField(
+ "TOML",
+ [validators.DataRequired(),
+ valid_toml,
+ ],
+ )
+
+ @staticmethod
+ def from_entity(entity):
+ """
+ Initializes form with TOML version of existing entity
+ """
+ etf = EntityTomlForm()
+ if entity.state == 'active':
+ pop_fields = ['ident', 'state', 'revision', 'redirect']
+ else:
+ pop_fields = ['ident', 'state']
+ etf.toml.data = entity_to_toml(entity, pop_fields=pop_fields)
+ return etf
diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py
index 74805c69..da2bb6cf 100644
--- a/python/fatcat_web/routes.py
+++ b/python/fatcat_web/routes.py
@@ -227,7 +227,7 @@ def generic_editgroup_entity_view(editgroup_id, entity_type, ident, view_templat
entity, edit = generic_get_editgroup_entity(editgroup, entity_type, ident)
- if entity.state == "deleted":
+ if entity.revision is None or entity.state == "deleted":
return render_template('deleted_entity.html', entity=entity,
entity_type=entity_type, editgroup=editgroup)
diff --git a/python/fatcat_web/templates/base.html b/python/fatcat_web/templates/base.html
index 18c66077..2c18ec44 100644
--- a/python/fatcat_web/templates/base.html
+++ b/python/fatcat_web/templates/base.html
@@ -25,6 +25,10 @@
@media only screen and (max-width: 479px) {
.mobile-hide{ display: none !important; }
}
+
+ .field textarea#toml {
+ font-family: monospace;
+ }
</style>
{% block extra_head %}{% endblock %}
</head>
diff --git a/python/fatcat_web/templates/container_create.html b/python/fatcat_web/templates/container_create.html
index 5786d05d..be8c5671 100644
--- a/python/fatcat_web/templates/container_create.html
+++ b/python/fatcat_web/templates/container_create.html
@@ -9,13 +9,15 @@ a journal (eg, "New England Journal of Medicine"), conference proceedings, a
book series, or a blog. Not all publications are in a container.
<form class="ui form" id="create_container_form" method="POST" action="/container/create">
+ <p>Experienced users can also use the <a href="/container/create/toml">TOML
+ creation form</a> to access all metadata fields in a raw format.
{% endblock %}
{% block edit_form_suffix %}
<br><br>
<input class="ui primary submit button" type="submit" value="Create Container!">
<p>
- <i>New entity will be part of the current editgroup, which needs to be
+ <i>New container entity will be part of the current editgroup, which needs to be
submited and approved before the entity will formally be included in the
catalog.</i>
</form>
diff --git a/python/fatcat_web/templates/container_edit.html b/python/fatcat_web/templates/container_edit.html
index 5188ce0d..99f77d53 100644
--- a/python/fatcat_web/templates/container_edit.html
+++ b/python/fatcat_web/templates/container_edit.html
@@ -7,6 +7,14 @@
<h1 class="ui header">Edit Container Entity</h1>
<form class="ui form" id="edit_container_form" method="POST" action="{% if editgroup %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/container/{{ existing_ident }}/edit">
+
+ <p>Experienced users can also use the <a href="{% if editgroup
+ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/container/{{
+ existing_ident }}/edit/toml">TOML editing form</a> to access all metadata
+ fields in a raw format.
+ {% if not editgroup %}
+ You can also <a href="/container/{{ existing_ident }}/delete">delete this entity</a>.
+ {% endif %}
{% endblock %}
<p>See <a href="https://guide.fatcat.wiki/entity_container.html">the catalog
diff --git a/python/fatcat_web/templates/deleted_entity.html b/python/fatcat_web/templates/deleted_entity.html
index eefc87cf..4c6b14b6 100644
--- a/python/fatcat_web/templates/deleted_entity.html
+++ b/python/fatcat_web/templates/deleted_entity.html
@@ -30,7 +30,7 @@
<b>Entity Type:</b> <code>{{ entity_type }}</code>
</div>
-{{ entity_macros.fatcat_bits(entity, entity_type, "") }}
+{{ entity_macros.fatcat_bits(entity, entity_type, "", editgroup=editgroup) }}
</div>
</div>
diff --git a/python/fatcat_web/templates/edit_macros.html b/python/fatcat_web/templates/edit_macros.html
index a207e51e..d4839373 100644
--- a/python/fatcat_web/templates/edit_macros.html
+++ b/python/fatcat_web/templates/edit_macros.html
@@ -17,6 +17,23 @@
</div>
{%- endmacro %}
+{% macro form_toml_field(field, div_classes="") -%}
+<h3 class="ui dividing header">Raw Metadata (TOML)</h3>
+<div class="field {{ div_classes }} {% if field.errors %}error{% endif %}">
+ <p><a href="https://toml.io/en/">TOML</a> is a markup language, similar to
+ YAML or Wikitext. The schema here is the same as the Fatcat API JSON schema, but
+ with a syntax that is easier to read and edit by hand. All nested metadata
+ fields are available here; refer to the fatcat guide for metadata
+ documentation and style guide, or TOML documentation for syntax notes (eg,
+ what those double brackets mean, and how to represent lists of authors or
+ references).
+ <br>
+ <br>
+ {{ field(rows=24) }}
+ {{ form_field_errors(field) }}
+</div>
+{%- endmacro %}
+
{% macro form_field_inline(field, div_classes="") -%}
<div class="ui grid">
<div class="three wide column middle aligned right aligned" {# style="padding-right: 0.5rem;" #}>
@@ -38,7 +55,7 @@
{% macro editgroup_dropdown(form, editgroup=None, potential_editgroups=None) -%}
{% if editgroup %}
<p>You are updating an existing un-merged editgroup: <a href="/editgroup/{{ editgroup.editgroup_id}}">{{ editgroup.editgroup_id }}</a>.
- <p><b>Description:</b> {{ editgroup.description }}
+ <p><b>Description:</b> {{ editgroup.description or "" }}
{% else %}
{% if not potential_editgroups %}
<p>You have no un-submitted editgroups in progress; a new one will be
diff --git a/python/fatcat_web/templates/editgroup_view.html b/python/fatcat_web/templates/editgroup_view.html
index e8146d19..a36dc3e5 100644
--- a/python/fatcat_web/templates/editgroup_view.html
+++ b/python/fatcat_web/templates/editgroup_view.html
@@ -25,7 +25,7 @@
updated
{% endif %}
<a href="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/{{ edit.ident }}">[view edit]</a>
- {% if auth_to.edit and not editgroup.changelog_index and not editgroup.submitted and entity_type in ('release', 'file', 'container') %}
+ {% if auth_to.edit and not editgroup.changelog_index and not editgroup.submitted %}
<a href="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/{{ edit.ident }}/edit" style="color: green;">[re-edit]</a>
<form id="submit_edit_delete" method="POST" action="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/edit/{{ edit.edit_id }}/delete" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
diff --git a/python/fatcat_web/templates/entity_create_toml.html b/python/fatcat_web/templates/entity_create_toml.html
new file mode 100644
index 00000000..ec5bc4a2
--- /dev/null
+++ b/python/fatcat_web/templates/entity_create_toml.html
@@ -0,0 +1,20 @@
+{% extends "entity_edit_toml.html" %}
+
+{% block edit_form_prefix %}
+<div class="ui segment">
+<h1 class="ui header">Create New {{ entity_type }} Entity (TOML mode)</h1>
+
+<form class="ui form" id="create_entity_form" method="POST" action="/{{ entity_type }}/create/toml">
+{% endblock %}
+
+{% block edit_form_suffix %}
+ <br><br>
+ <input class="ui primary submit button" type="submit" value="Create {{ entity_type }}!">
+ <p>
+ <i>New {{ entity_type }} entity will be part of the current editgroup, which
+ needs to be submited and approved before the entity will formally be included
+ in the catalog.</i>
+</form>
+</div>
+{% endblock %}
+
diff --git a/python/fatcat_web/templates/entity_delete.html b/python/fatcat_web/templates/entity_delete.html
new file mode 100644
index 00000000..b2e13af4
--- /dev/null
+++ b/python/fatcat_web/templates/entity_delete.html
@@ -0,0 +1,49 @@
+{% import "edit_macros.html" as edit_macros %}
+{% extends "base.html" %}
+
+{% block body %}
+{% block edit_form_prefix %}
+ <div class="ui segment">
+ <h1 class="ui header">Delete Entity</h1>
+
+ <form class="ui form" id="delete_form" method="POST" action="{% if editgroup and editgroup.editgroup_id %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/{{ entity_type }}/{{ existing_ident }}/delete">
+{% endblock %}
+
+ <p>See <a href="https://guide.fatcat.wiki/entity_release.html">the catalog
+ style guide</a> for schema notes, and <a
+ href="https://guide.fatcat.wiki/editing_quickstart.html">the editing
+ tutorial</a> if this is your first time making an edit.
+
+ {{ form.hidden_tag() }}
+
+ <h3 class="ui dividing header">Editgroup Metadata</h3>
+ {{ edit_macros.editgroup_dropdown(form, editgroup, potential_editgroups) }}
+
+ <h3 class="ui dividing header">Submit</h3>
+ {{ edit_macros.form_field_basic(form.edit_description) }}
+ This description will be attached to the individual edit, not to the
+ editgroup as a whole.
+
+{% block edit_form_suffix %}
+ <br><br>
+ <input class="ui primary submit button" type="submit" value="Update Release!">
+ <p>
+ <i>Deletion will be part of the current editgroup, which needs to be submited and
+ approved before the change is included in the catalog.</i>
+</form>
+</div>
+{% endblock %}
+{% endblock %}
+
+{% block postscript %}
+<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
+<script>
+<!-- Form code -->
+$(document).ready(function() {
+
+ // these javascript dropdowns hide the original <input>, which breaks browser
+ // form focusing (eg, for required fields) :(
+ $('.ui.dropdown') .dropdown();
+});
+</script>
+{% endblock %}
diff --git a/python/fatcat_web/templates/entity_edit.html b/python/fatcat_web/templates/entity_edit.html
deleted file mode 100644
index 97f7bf46..00000000
--- a/python/fatcat_web/templates/entity_edit.html
+++ /dev/null
@@ -1,8 +0,0 @@
-{% extends "base.html" %}
-{% block body %}
-
-<h1>Not Implemented</h1>
-
-<p>Entity editing via the web interface isn't implemented yet. Sorry!
-
-{% endblock %}
diff --git a/python/fatcat_web/templates/entity_edit_toml.html b/python/fatcat_web/templates/entity_edit_toml.html
new file mode 100644
index 00000000..64768d6e
--- /dev/null
+++ b/python/fatcat_web/templates/entity_edit_toml.html
@@ -0,0 +1,55 @@
+{% import "edit_macros.html" as edit_macros %}
+{% extends "base.html" %}
+
+{% block body %}
+{% block edit_form_prefix %}
+ <div class="ui segment">
+ <h1 class="ui header">Edit Entity (TOML mode)</h1>
+
+ <form class="ui form" id="edit_toml_form" method="POST" action="{% if editgroup and editgroup.editgroup_id %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/{{ entity_type }}/{{ existing_ident }}/edit/toml">
+
+ {% if not editgroup %}
+ <p>You can also <a href="/{{ entity_type }}/{{ existing_ident }}/delete">delete this entity</a>.
+ {% endif %}
+{% endblock %}
+
+ <p>See <a href="https://guide.fatcat.wiki/entity_release.html">the catalog
+ style guide</a> for schema notes, and <a
+ href="https://guide.fatcat.wiki/editing_quickstart.html">the editing
+ tutorial</a> if this is your first time making an edit.
+
+ {{ form.hidden_tag() }}
+
+ <h3 class="ui dividing header">Editgroup Metadata</h3>
+ {{ edit_macros.editgroup_dropdown(form, editgroup, potential_editgroups) }}
+
+ {{ edit_macros.form_toml_field(form.toml, "required") }}
+
+ <h3 class="ui dividing header">Submit</h3>
+ {{ edit_macros.form_field_basic(form.edit_description) }}
+ This description will be attached to the individual edit, not to the
+ editgroup as a whole.
+
+{% block edit_form_suffix %}
+ <br><br>
+ <input class="ui primary submit button" type="submit" value="Update Release!">
+ <p>
+ <i>Edit will be part of the current editgroup, which needs to be submited and
+ approved before the change is included in the catalog.</i>
+</form>
+</div>
+{% endblock %}
+{% endblock %}
+
+{% block postscript %}
+<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
+<script>
+<!-- Form code -->
+$(document).ready(function() {
+
+ // these javascript dropdowns hide the original <input>, which breaks browser
+ // form focusing (eg, for required fields) :(
+ $('.ui.dropdown') .dropdown();
+});
+</script>
+{% endblock %}
diff --git a/python/fatcat_web/templates/entity_macros.html b/python/fatcat_web/templates/entity_macros.html
index 718c071c..0ce646bf 100644
--- a/python/fatcat_web/templates/entity_macros.html
+++ b/python/fatcat_web/templates/entity_macros.html
@@ -33,7 +33,7 @@
{% if entity.state %}
State is "{{ entity.state }}".
{% endif %}
- {% if entity.state != "deleted" %}
+ {% if entity.revision %}
Revision:
<br><small><code><a href="/{{ entity_type }}/rev/{{ entity.revision }}">{{ entity.revision }}</a></code></small>
{% endif %}
@@ -43,11 +43,13 @@
{%- else -%}
https://api.{{ config.FATCAT_DOMAIN }}
{%- endif -%}
- /v0/{{ entity_type }}
- {%- if entity.ident -%}
- /{{ entity.ident }}
+ /v0
+ {%- if editgroup and entity.ident -%}
+ /editgroup/{{ editgroup.editgroup_id }}{# /{{ entity_type }}/{{ entity.ident }} #}
+ {%- elif entity.ident -%}
+ /{{ entity_type }}/{{ entity.ident }}
{%- elif entity.revision -%}
- /rev/{{ entity.revision }}
+ /{{ entity_type }}/rev/{{ entity.revision }}
{% endif %}
{% if expand %}?expand={{ expand}}{% endif %}">
As JSON object via API
diff --git a/python/fatcat_web/templates/file_create.html b/python/fatcat_web/templates/file_create.html
index a7c99b96..affcfb6e 100644
--- a/python/fatcat_web/templates/file_create.html
+++ b/python/fatcat_web/templates/file_create.html
@@ -5,13 +5,15 @@
<h1 class="ui header">Create New File Entity</h1>
<form class="ui form" id="create_file_form" method="POST" action="/file/create">
+ <p>Experienced users can also use the <a href="/file/create/toml">TOML
+ creation form</a> to access all metadata fields in a raw format.
{% endblock %}
{% block edit_form_suffix %}
<br><br>
<input class="ui primary submit button" type="submit" value="Create File!">
<p>
- <i>New entity will be part of the current editgroup, which needs to be
+ <i>New file entity will be part of the current editgroup, which needs to be
submited and approved before the entity will formally be included in the
catalog.</i>
</form>
diff --git a/python/fatcat_web/templates/file_edit.html b/python/fatcat_web/templates/file_edit.html
index e8a421b3..745b0c41 100644
--- a/python/fatcat_web/templates/file_edit.html
+++ b/python/fatcat_web/templates/file_edit.html
@@ -7,6 +7,14 @@
<h1 class="ui header">Edit File Entity</h1>
<form class="ui form" id="edit_file_form" method="POST" action="{% if editgroup %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/file/{{ existing_ident }}/edit">
+
+ <p>Experienced users can also use the <a href="{% if editgroup
+ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/file/{{
+ existing_ident }}/edit/toml">TOML editing form</a> to access all metadata
+ fields in a raw format.
+ {% if not editgroup %}
+ You can also <a href="/file/{{ existing_ident }}/delete">delete this entity</a>.
+ {% endif %}
{% endblock %}
<p>See <a href="https://guide.fatcat.wiki/entity_file.html">the catalog
diff --git a/python/fatcat_web/templates/home.html b/python/fatcat_web/templates/home.html
index 4557e212..de32d6a4 100644
--- a/python/fatcat_web/templates/home.html
+++ b/python/fatcat_web/templates/home.html
@@ -171,11 +171,10 @@
<tr><td><b>Creator</b>
<br>authors, editors, translators
+ <td><a href="/creator/create">Create</a>
{% if config.FATCAT_DOMAIN == 'fatcat.wiki' %}
- <td>
<td><a href="/creator/iimvc523xbhqlav6j3sbthuehu">Author</a>
{% else %}
- <td><!-- <a href="/creator/create">Create</a> -->
<td><a href="/creator/iimvc523xbhqlav6j3sbthuehu">Author</a> (prod)
<br><a href="/creator/aaaaaaaaaaaaaircaaaaaaaaai">Dummy</a>
<br><a href="/creator/aaaaaaaaaaaaaircaaaaaaaaam">Realistic</a>
@@ -206,11 +205,10 @@
</form>
<tr><td><b>File Set</b>
<br>datasets, suplementary materials
+ <td><a href="/fileset/create">Create</a>
{% if config.FATCAT_DOMAIN == 'fatcat.wiki' %}
- <td>
<td><a href="/fileset/ho376wmdanckpp66iwfs7g22ne">Dataset</a>
{% else %}
- <td>
<td><a href="/fileset/ho376wmdanckpp66iwfs7g22ne">Dataset</a> (prod)
<br><a href="/fileset/aaaaaaaaaaaaaztgaaaaaaaaai">Dummy</a>
<br><a href="/fileset/aaaaaaaaaaaaaztgaaaaaaaaam">Realistic</a>
@@ -218,12 +216,11 @@
<td>
<tr><td><b>Web Capture</b>
<br>HTML and interactive articles, blog posts
+ <td><a href="/webcapture/create">Create</a>
{% if config.FATCAT_DOMAIN == 'fatcat.wiki' %}
- <td>
<td><a href="/webcapture/z7uaeatyvfgwdpuxtrdu4okqii">D-Lib</a>
<br><a href="/webcapture/5l2pubtfefbmdnqws2izccqlpm">Blog Post</a>
{% else %}
- <td>
<td><a href="/webcapture/z7uaeatyvfgwdpuxtrdu4okqii">D-Lib</a> (prod)
<br><a href="/webcapture/aaaaaaaaaaaaa53xaaaaaaaaai">Dummy</a>
<br><a href="/webcapture/aaaaaaaaaaaaa53xaaaaaaaaam">Realistic</a>
@@ -231,11 +228,10 @@
<td>
<tr><td><b>Work</b>
<br>for grouping Releases
+ <td><a href="/work/create">Create</a>
{% if config.FATCAT_DOMAIN == 'fatcat.wiki' %}
- <td>
<td><a href="/work/ftl6xv267vb6xfech3khri3nwa">Paper</a>
{% else %}
- <td>
<td><a href="/work/ftl6xv267vb6xfech3khri3nwa">Paper</a> (prod)
<br><a href="/work/aaaaaaaaaaaaavkvaaaaaaaaai">Dummy</a>
<br><a href="/work/aaaaaaaaaaaaavkvaaaaaaaaam">Realistic</a>
diff --git a/python/fatcat_web/templates/release_create.html b/python/fatcat_web/templates/release_create.html
index 5ec2efe5..4f5dabd7 100644
--- a/python/fatcat_web/templates/release_create.html
+++ b/python/fatcat_web/templates/release_create.html
@@ -5,13 +5,15 @@
<h1 class="ui header">Create New Release Entity</h1>
<form class="ui form" id="create_release_form" method="POST" action="/release/create">
+ <p>Experienced users can also use the <a href="/release/create/toml">TOML
+ creation form</a> to access all metadata fields in a raw format.
{% endblock %}
{% block edit_form_suffix %}
<br><br>
<input class="ui primary submit button" type="submit" value="Create Release!">
<p>
- <i>New entity will be part of the current editgroup, which needs to be
+ <i>New release entity will be part of the current editgroup, which needs to be
submited and approved before the entity will formally be included in the
catalog.</i>
</form>
diff --git a/python/fatcat_web/templates/release_edit.html b/python/fatcat_web/templates/release_edit.html
index a4a7e56f..c26c9850 100644
--- a/python/fatcat_web/templates/release_edit.html
+++ b/python/fatcat_web/templates/release_edit.html
@@ -7,6 +7,14 @@
<h1 class="ui header">Edit Release Entity</h1>
<form class="ui form" id="edit_release_form" method="POST" action="{% if editgroup %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/release/{{ existing_ident }}/edit">
+
+ <p>Experienced users can also use the <a href="{% if editgroup
+ %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/release/{{
+ existing_ident }}/edit/toml">TOML editing form</a> to access all metadata
+ fields in a raw format.
+ {% if not editgroup %}
+ You can also <a href="/release/{{ existing_ident }}/delete">delete this entity</a>.
+ {% endif %}
{% endblock %}
<p>See <a href="https://guide.fatcat.wiki/entity_release.html">the catalog
@@ -14,6 +22,7 @@
href="https://guide.fatcat.wiki/editing_quickstart.html">the editing
tutorial</a> if this is your first time making an edit.
+
{{ form.hidden_tag() }}
<h3 class="ui dividing header">Editgroup Metadata</h3>
@@ -21,21 +30,22 @@
<br>
<h3 class="ui dividing header">The Basics</h3>
+
+ {{ edit_macros.form_field_inline(form.release_type, "required") }}
+ {{ edit_macros.form_field_inline(form.title, "required") }}
+ {{ edit_macros.form_field_inline(form.original_title) }}
+
<div class="ui grid">
<div class="three wide column" style="padding-bottom: 0px;"></div>
<div class="twelve wide column" style="padding-bottom: 0px;">
<div class="ui equal width fields">
- {{ edit_macros.form_field_basic(form.release_type, "required") }}
- {{ edit_macros.form_field_basic(form.release_stage) }}
+ {{ edit_macros.form_field_basic(form.release_year) }}
+ {{ edit_macros.form_field_basic(form.release_date) }}
</div>
</div>
<div class="one wide column" style="padding-bottom: 0px;"></div>
</div>
- {{ edit_macros.form_field_inline(form.title, "required") }}
- {{ edit_macros.form_field_inline(form.original_title) }}
- {{ edit_macros.form_field_inline(form.work_id) }}
- {{ edit_macros.form_field_inline(form.release_date) }}
<div class="ui grid">
<div class="three wide column" style="padding-bottom: 0px;"></div>
<div class="twelve wide column" style="padding-bottom: 0px;">
@@ -47,6 +57,19 @@
<div class="one wide column" style="padding-bottom: 0px;"></div>
</div>
+ <div class="ui grid">
+ <div class="three wide column" style="padding-bottom: 0px;"></div>
+ <div class="twelve wide column" style="padding-bottom: 0px;">
+ <div class="ui equal width fields">
+ {{ edit_macros.form_field_basic(form.release_stage) }}
+ {{ edit_macros.form_field_basic(form.withdrawn_status) }}
+ </div>
+ </div>
+ <div class="one wide column" style="padding-bottom: 0px;"></div>
+ </div>
+
+ {{ edit_macros.form_field_inline(form.work_id) }}
+
<br>
<h3 class="ui dividing header">Contributors</h3>
<div class="list-group" id="contrib_list" name="contrib_list">
diff --git a/python/tests/transform_toml.py b/python/tests/transform_toml.py
new file mode 100644
index 00000000..d12ba027
--- /dev/null
+++ b/python/tests/transform_toml.py
@@ -0,0 +1,22 @@
+
+import json
+
+from fatcat_tools import *
+from fatcat_openapi_client import *
+from import_crossref import crossref_importer
+from fixtures import *
+
+
+def test_basic_toml(crossref_importer):
+ with open('tests/files/crossref-works.single.json', 'r') as f:
+ # not a single line
+ raw = json.loads(f.read())
+ r = crossref_importer.parse_record(raw)
+ r.state = 'active'
+ toml_str = entity_to_toml(r)
+ r2 = entity_from_toml(toml_str, ReleaseEntity)
+ assert r == r2
+
+ toml_str = entity_to_toml(r, pop_fields=['ident', 'revision', 'blah', 'extra'])
+ r3 = entity_from_toml(toml_str, ReleaseEntity)
+ assert r != r3
diff --git a/python/tests/web_editing.py b/python/tests/web_editing.py
index 17f4f5ae..fb8b3f93 100644
--- a/python/tests/web_editing.py
+++ b/python/tests/web_editing.py
@@ -2,7 +2,7 @@
from fixtures import *
-def test_web_release_create_merge(app_admin, api):
+def test_web_release_create_accept(app_admin, api):
eg = quick_eg(api)
@@ -24,6 +24,18 @@ def test_web_release_create_merge(app_admin, api):
#assert b'badmojo' in rv.data
assert b'Not a valid choice' in rv.data
+ # bad wikidata QID
+ rv = app_admin.post('/release/create',
+ data={
+ 'editgroup_id': eg.editgroup_id,
+ 'release_type': 'article-journal',
+ 'release_stage': 'published',
+ 'title': 'something bogus',
+ 'wikidata_qid': '884',
+ },
+ follow_redirects=True)
+ assert rv.status_code == 400
+
# ok/valid submit
rv = app_admin.post('/release/create',
data={
@@ -31,9 +43,11 @@ def test_web_release_create_merge(app_admin, api):
'release_type': 'article-journal',
'release_stage': 'published',
'title': 'something bogus',
+ 'doi': '10.1234/999999',
},
follow_redirects=True)
assert rv.status_code == 200
+ assert b'10.1234/999999' in rv.data
rv = app_admin.get('/editgroup/{}'.format(eg.editgroup_id))
assert rv.status_code == 200
@@ -129,18 +143,117 @@ def test_web_file_create(app_admin, api):
follow_redirects=True)
assert rv.status_code == 200
+def test_web_file_toml_create(app_admin, api):
-def test_web_edit_get(app_admin):
+ eg = quick_eg(api)
- # these are all existing entities
- rv = app_admin.get('/release/aaaaaaaaaaaaarceaaaaaaaaai/edit')
+ # bogus/bad submit
+ rv = app_admin.post('/file/create/toml',
+ data={
+ 'editgroup_id': eg.editgroup_id,
+ },
+ follow_redirects=True)
+ assert rv.status_code == 400
+
+ # ok/valid submit
+ rv = app_admin.post('/file/create/toml',
+ data={
+ 'editgroup_id': eg.editgroup_id,
+ 'toml': """
+size = 12345
+sha1 = "45be56a396c4d03faaa41e055170c23534dec736"
+ """,
+ },
+ follow_redirects=True)
assert rv.status_code == 200
- assert b'A bigger example' in rv.data
- rv = app_admin.get('/file/aaaaaaaaaaaaamztaaaaaaaaam/edit')
+ # upper-case SHA-1
+ rv = app_admin.post('/file/create/toml',
+ data={
+ 'editgroup_id': eg.editgroup_id,
+ 'toml': """
+size = 12345
+sha1 = "45BE56A396C4D03FAAA41E055170C23534DEC736"
+ """,
+ },
+ follow_redirects=True)
+ assert rv.status_code == 400
+
+def test_web_file_delete(app_admin, api):
+
+ eg = quick_eg(api)
+
+ rv = app_admin.get('/file/aaaaaaaaaaaaamztaaaaaaaaam/delete')
assert rv.status_code == 200
- assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data
- rv = app_admin.get('/container/aaaaaaaaaaaaaeiraaaaaaaaam/edit')
+ rv = app_admin.post('/file/aaaaaaaaaaaaamztaaaaaaaaam/delete',
+ data={
+ 'editgroup_id': eg.editgroup_id,
+ },
+ follow_redirects=True)
assert rv.status_code == 200
- assert b'1549-1277' in rv.data
+ # NOTE: did not *accept* the deletion edit
+
+DUMMY_DEMO_ENTITIES = {
+ 'container': 'aaaaaaaaaaaaaeiraaaaaaaaam',
+ 'creator': 'aaaaaaaaaaaaaircaaaaaaaaaq',
+ 'file': 'aaaaaaaaaaaaamztaaaaaaaaam',
+ 'fileset': 'aaaaaaaaaaaaaztgaaaaaaaaai',
+ 'webcapture': 'aaaaaaaaaaaaa53xaaaaaaaaai',
+ 'release': 'aaaaaaaaaaaaarceaaaaaaaaai',
+ 'work': 'aaaaaaaaaaaaavkvaaaaaaaaai',
+}
+
+def test_web_edit_get(app_admin):
+
+ # these are all existing entities
+ for entity_type in ['release', 'file', 'container']:
+ rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit')
+ assert rv.status_code == 200
+ if entity_type == 'release':
+ assert b'A bigger example' in rv.data
+ elif entity_type == 'file':
+ assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data
+ elif entity_type == 'container':
+ assert b'1549-1277' in rv.data
+
+ rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit/toml')
+ assert rv.status_code == 200
+ if entity_type == 'release':
+ assert b'A bigger example' in rv.data
+ elif entity_type == 'file':
+ assert b'ffc1005680cb620eec4c913437dfabbf311b535cfe16cbaeb2faec1f92afc362' in rv.data
+ elif entity_type == 'container':
+ assert b'1549-1277' in rv.data
+
+ # TOML-only endpoints
+ for entity_type in ['creator', 'fileset', 'webcapture', 'work']:
+ rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit')
+ assert rv.status_code == 302
+
+ rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/edit/toml')
+ assert rv.status_code == 200
+
+
+def test_web_create_get(app_admin):
+
+ for entity_type in ['release', 'file', 'container']:
+ rv = app_admin.get(f'/{entity_type}/create')
+ assert rv.status_code == 200
+
+ rv = app_admin.get(f'/{entity_type}/create/toml')
+ assert rv.status_code == 200
+
+ # these are TOML only
+ for entity_type in ['creator', 'fileset', 'webcapture', 'work']:
+ rv = app_admin.get(f'/{entity_type}/create')
+ assert rv.status_code == 302
+
+ rv = app_admin.get(f'/{entity_type}/create/toml')
+ assert rv.status_code == 200
+
+def test_web_edit_delete(app_admin):
+
+ for entity_type in DUMMY_DEMO_ENTITIES.keys():
+ rv = app_admin.get(f'/{entity_type}/{DUMMY_DEMO_ENTITIES[entity_type]}/delete')
+ assert rv.status_code == 200
diff --git a/python/tests/web_entity_views.py b/python/tests/web_entity_views.py
index b01bd815..7b973ef2 100644
--- a/python/tests/web_entity_views.py
+++ b/python/tests/web_entity_views.py
@@ -210,9 +210,9 @@ def test_web_creator(app):
rv = app.get('/creator/aaaaaaaaaaaaaircaaaaaaaaai')
assert rv.status_code == 200
rv = app.get('/creator/aaaaaaaaaaaaaircaaaaaaaaai/edit')
- assert rv.status_code == 404
+ assert rv.status_code == 302
rv = app.get('/creator/create')
- assert rv.status_code == 404
+ assert rv.status_code == 302
def test_web_file(app):
@@ -266,9 +266,9 @@ def test_web_fileset(app):
rv = app.get('/fileset/aaaaaaaaaaaaaztgaaaaaaaaai')
assert rv.status_code == 200
rv = app.get('/fileset/aaaaaaaaaaaaaztgaaaaaaaaai/edit')
- assert rv.status_code == 404
+ assert rv.status_code == 302
rv = app.get('/fileset/create')
- assert rv.status_code == 404
+ assert rv.status_code == 302
def test_web_webcatpure(app):
@@ -277,9 +277,9 @@ def test_web_webcatpure(app):
rv = app.get('/webcapture/aaaaaaaaaaaaa53xaaaaaaaaai')
assert rv.status_code == 200
rv = app.get('/webcapture/aaaaaaaaaaaaa53xaaaaaaaaai/edit')
- assert rv.status_code == 404
+ assert rv.status_code == 302
rv = app.get('/webcapture/create')
- assert rv.status_code == 404
+ assert rv.status_code == 302
def test_web_release(app):
@@ -376,6 +376,6 @@ def test_web_work(app):
rv = app.get('/work/aaaaaaaaaaaaavkvaaaaaaaaai')
assert rv.status_code == 200
rv = app.get('/work/aaaaaaaaaaaaavkvaaaaaaaaai/edit')
- assert rv.status_code == 404
+ assert rv.status_code == 302
rv = app.get('/work/create')
- assert rv.status_code == 404
+ assert rv.status_code == 302