diff --git a/.gitignore b/.gitignore index e7701474f..586353ce7 100644 --- a/.gitignore +++ b/.gitignore @@ -123,3 +123,5 @@ private_vars.sh .pycheckers .pyre legacy_comparison.org + +tests/data/browse.db \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index c9ed5ccba..760766c19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ python: script: - pip install pipenv - pipenv sync --dev + - FLASK_APP=app.py pipenv run python populate_test_database.py --drop_and_create - pipenv run nose2 --with-coverage --coverage-config .coveragerc - tests/lint.sh - tests/docstyle.sh diff --git a/Pipfile b/Pipfile index 85eb2159a..1dbc29f4c 100644 --- a/Pipfile +++ b/Pipfile @@ -22,7 +22,7 @@ arxiv-auth = "==0.4.2rc1" mypy = "*" jinja2 = "==2.10.1" flask-s3 = "*" -arxiv-base = "==0.16.6" +arxiv-base = "==0.16.7" retry = "==0.9.2" [dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock index 8f18403ca..bbd62b429 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ed2d009eb3ccbeaa75ed049efbcdccdf4a705bc5cba77559cab412016dafb24d" + "sha256": "20cb4768ee76da08023a7dbf7778316848fe864cd054bc84c151c07fe48d72af" }, "pipfile-spec": 6, "requires": { @@ -43,10 +43,10 @@ }, "arxiv-base": { "hashes": [ - "sha256:f7a5ff38c91bc3c8f40db500179394cbca1a3dd00d9c89c8e9e2ad63a6829664" + "sha256:353e021c60330d8c8a5a879ab513a89f5372e01616feb899641a86d5eab10521" ], "index": "pypi", - "version": "==0.16.6" + "version": "==0.16.7" }, "async-timeout": { "hashes": [ @@ -77,17 +77,17 @@ }, "boto3": { "hashes": [ - "sha256:68e32e2d1c911b0e8408278c7603f0f46c31780b46c44d23346ccef71b3f10dc", - "sha256:967c7a5ac484fe627706e241dfc9294a6220c863ceb53a4f34e3fe9e11a71d7a" + "sha256:970bd7b332e73d7b51077ed36772c634811b38c81b0cc6ed0f910e50d7ebadf8", + "sha256:cdd79a3a7bbe1f33a365f0acfcc75c4405b482b3eb9ce3f4e6b16c418e201ac3" ], - "version": "==1.12.2" + "version": "==1.12.39" }, "botocore": { "hashes": [ - "sha256:00bff61d899c4f12abe020527452e08cf49b3b60400c5d0d9f83c00b7d18c642", - "sha256:5ffdf30746dbfca59d31d2059789168255e96bd98a17a65f8edb3b6de0a96b3e" + "sha256:94232b44e1540b7e043e220bd43f855400d0a243e926b26b3fb72994e971d518", + "sha256:e20ba56476b1031ce5ac8e22b59dabc75bd0e03231f124ed6b9ff99fe0b0c96b" ], - "version": "==1.15.2" + "version": "==1.15.39" }, "chardet": { "hashes": [ @@ -98,10 +98,10 @@ }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", + "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" ], - "version": "==7.0" + "version": "==7.1.1" }, "dataclasses": { "hashes": [ @@ -113,10 +113,10 @@ }, "decorator": { "hashes": [ - "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce", - "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d" + "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760", + "sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7" ], - "version": "==4.4.1" + "version": "==4.4.2" }, "docutils": { "hashes": [ @@ -128,11 +128,11 @@ }, "flask": { "hashes": [ - "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", - "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" + "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", + "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" ], "index": "pypi", - "version": "==1.1.1" + "version": "==1.1.2" }, "flask-api": { "hashes": [ @@ -182,11 +182,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302", - "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b" + "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", + "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" ], "markers": "python_version < '3.8'", - "version": "==1.5.0" + "version": "==1.6.0" }, "itsdangerous": { "hashes": [ @@ -205,10 +205,10 @@ }, "jmespath": { "hashes": [ - "sha256:3720a4b1bd659dd2eecad0666459b9788813e032b83e7ba58578e48254e0a0e6", - "sha256:bde2aef6f44302dfb30320115b17d030798de8c4110e28d5cf6cf91a7a31074c" + "sha256:695cb76fa78a10663425d5b73ddc5714eb711157e52704d69be03b1a02ba4fec", + "sha256:cca55c8d153173e21baa59983015ad0daf603f9cb799904ff057bfb8ff8dc2d9" ], - "version": "==0.9.4" + "version": "==0.9.5" }, "jsonschema": { "hashes": [ @@ -219,42 +219,31 @@ }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + "sha256:06358015a4dee8ee23ae426bf885616ab3963622defd829eb45b44e3dee3515f", + "sha256:0b0c4fc852c5f02c6277ef3b33d23fcbe89b1b227460423e3335374da046b6db", + "sha256:267677fc42afed5094fc5ea1c4236bbe4b6a00fe4b08e93451e65ae9048139c7", + "sha256:303cb70893e2c345588fb5d5b86e0ca369f9bb56942f03064c5e3e75fa7a238a", + "sha256:3c9b624a0d9ed5a5093ac4edc4e823e6b125441e60ef35d36e6f4a6fdacd5054", + "sha256:42033e14cae1f6c86fc0c3e90d04d08ce73ac8e46ba420a0d22d545c2abd4977", + "sha256:4e4a99b6af7bdc0856b50020c095848ec050356a001e1f751510aef6ab14d0e0", + "sha256:4eb07faad54bb07427d848f31030a65a49ebb0cec0b30674f91cf1ddd456bfe4", + "sha256:63a7161cd8c2bc563feeda45df62f42c860dd0675e2b8da2667f25bb3c95eaba", + "sha256:68e0fd039b68d2945b4beb947d4023ca7f8e95b708031c345762efba214ea761", + "sha256:8092a63397025c2f655acd42784b2a1528339b90b987beb9253f22e8cdbb36c3", + "sha256:841218860683c0f2223e24756843d84cc49cccdae6765e04962607754a52d3e0", + "sha256:94076b2314bd2f6cfae508ad65b4d493e3a58a50112b7a2cbb6287bdbc404ae8", + "sha256:9d22aff1c5322e402adfb3ce40839a5056c353e711c033798cf4f02eb9f5124d", + "sha256:b0e4584f62b3e5f5c1a7bcefd2b52f236505e6ef032cc508caa4f4c8dc8d3af1", + "sha256:b1163ffc1384d242964426a8164da12dbcdbc0de18ea36e2c34b898ed38c3b45", + "sha256:beac28ed60c8e838301226a7a85841d0af2068eba2dcb1a58c2d32d6c05e440e", + "sha256:c29f096ce79c03054a1101d6e5fe6bf04b0bb489165d5e0e9653fb4fe8048ee1", + "sha256:c58779966d53e5f14ba393d64e2402a7926601d1ac8adeb4e83893def79d0428", + "sha256:cfe14b37908eaf7d5506302987228bff69e1b8e7071ccd4e70fd0283b1b47f0b", + "sha256:e834249c45aa9837d0753351cdca61a4b8b383cc9ad0ff2325c97ff7b69e72a6", + "sha256:eed1b234c4499811ee85bcefa22ef5e466e75d132502226ed29740d593316c1f" ], "index": "pypi", - "version": "==1.1.1" + "version": "==2.0.0a1" }, "mimesis": { "hashes": [ @@ -272,45 +261,45 @@ }, "multidict": { "hashes": [ - "sha256:13f3ebdb5693944f52faa7b2065b751cb7e578b8dd0a5bb8e4ab05ad0188b85e", - "sha256:26502cefa86d79b86752e96639352c7247846515c864d7c2eb85d036752b643c", - "sha256:4fba5204d32d5c52439f88437d33ad14b5f228e25072a192453f658bddfe45a7", - "sha256:527124ef435f39a37b279653ad0238ff606b58328ca7989a6df372fd75d7fe26", - "sha256:5414f388ffd78c57e77bd253cf829373721f450613de53dc85a08e34d806e8eb", - "sha256:5eee66f882ab35674944dfa0d28b57fa51e160b4dce0ce19e47f495fdae70703", - "sha256:63810343ea07f5cd86ba66ab66706243a6f5af075eea50c01e39b4ad6bc3c57a", - "sha256:6bd10adf9f0d6a98ccc792ab6f83d18674775986ba9bacd376b643fe35633357", - "sha256:83c6ddf0add57c6b8a7de0bc7e2d656be3eefeff7c922af9a9aae7e49f225625", - "sha256:93166e0f5379cf6cd29746989f8a594fa7204dcae2e9335ddba39c870a287e1c", - "sha256:9a7b115ee0b9b92d10ebc246811d8f55d0c57e82dbb6a26b23c9a9a6ad40ce0c", - "sha256:a38baa3046cce174a07a59952c9f876ae8875ef3559709639c17fdf21f7b30dd", - "sha256:a6d219f49821f4b2c85c6d426346a5d84dab6daa6f85ca3da6c00ed05b54022d", - "sha256:a8ed33e8f9b67e3b592c56567135bb42e7e0e97417a4b6a771e60898dfd5182b", - "sha256:d7d428488c67b09b26928950a395e41cc72bb9c3d5abfe9f0521940ee4f796d4", - "sha256:dcfed56aa085b89d644af17442cdc2debaa73388feba4b8026446d168ca8dad7", - "sha256:f29b885e4903bd57a7789f09fe9d60b6475a6c1a4c0eca874d8558f00f9d4b51" - ], - "version": "==4.7.4" + "sha256:317f96bc0950d249e96d8d29ab556d01dd38888fbe68324f46fd834b430169f1", + "sha256:42f56542166040b4474c0c608ed051732033cd821126493cf25b6c276df7dd35", + "sha256:4b7df040fb5fe826d689204f9b544af469593fb3ff3a069a6ad3409f742f5928", + "sha256:544fae9261232a97102e27a926019100a9db75bec7b37feedd74b3aa82f29969", + "sha256:620b37c3fea181dab09267cd5a84b0f23fa043beb8bc50d8474dd9694de1fa6e", + "sha256:6e6fef114741c4d7ca46da8449038ec8b1e880bbe68674c01ceeb1ac8a648e78", + "sha256:7774e9f6c9af3f12f296131453f7b81dabb7ebdb948483362f5afcaac8a826f1", + "sha256:85cb26c38c96f76b7ff38b86c9d560dea10cf3459bb5f4caf72fc1bb932c7136", + "sha256:a326f4240123a2ac66bb163eeba99578e9d63a8654a59f4688a79198f9aa10f8", + "sha256:ae402f43604e3b2bc41e8ea8b8526c7fa7139ed76b0d64fc48e28125925275b2", + "sha256:aee283c49601fa4c13adc64c09c978838a7e812f85377ae130a24d7198c0331e", + "sha256:b51249fdd2923739cd3efc95a3d6c363b67bbf779208e9f37fd5e68540d1a4d4", + "sha256:bb519becc46275c594410c6c28a8a0adc66fe24fef154a9addea54c1adb006f5", + "sha256:c2c37185fb0af79d5c117b8d2764f4321eeb12ba8c141a95d0aa8c2c1d0a11dd", + "sha256:dc561313279f9d05a3d0ffa89cd15ae477528ea37aa9795c4654588a3287a9ab", + "sha256:e439c9a10a95cb32abd708bb8be83b2134fa93790a4fb0535ca36db3dda94d20", + "sha256:fc3b4adc2ee8474cb3cd2a155305d5f8eda0a9c91320f83e55748e1fcb68f8e3" + ], + "version": "==4.7.5" }, "mypy": { "hashes": [ - "sha256:0a9a45157e532da06fe56adcfef8a74629566b607fa2c1ac0122d1ff995c748a", - "sha256:2c35cae79ceb20d47facfad51f952df16c2ae9f45db6cb38405a3da1cf8fc0a7", - "sha256:4b9365ade157794cef9685791032521233729cb00ce76b0ddc78749abea463d2", - "sha256:53ea810ae3f83f9c9b452582261ea859828a9ed666f2e1ca840300b69322c474", - "sha256:634aef60b4ff0f650d3e59d4374626ca6153fcaff96ec075b215b568e6ee3cb0", - "sha256:7e396ce53cacd5596ff6d191b47ab0ea18f8e0ec04e15d69728d530e86d4c217", - "sha256:7eadc91af8270455e0d73565b8964da1642fe226665dd5c9560067cd64d56749", - "sha256:7f672d02fffcbace4db2b05369142e0506cdcde20cea0e07c7c2171c4fd11dd6", - "sha256:85baab8d74ec601e86134afe2bcccd87820f79d2f8d5798c889507d1088287bf", - "sha256:87c556fb85d709dacd4b4cb6167eecc5bbb4f0a9864b69136a0d4640fdc76a36", - "sha256:a6bd44efee4dc8c3324c13785a9dc3519b3ee3a92cada42d2b57762b7053b49b", - "sha256:c6d27bd20c3ba60d5b02f20bd28e20091d6286a699174dfad515636cb09b5a72", - "sha256:e2bb577d10d09a2d8822a042a23b8d62bc3b269667c9eb8e60a6edfa000211b1", - "sha256:f97a605d7c8bc2c6d1172c2f0d5a65b24142e11a58de689046e62c2d632ca8c1" + "sha256:15b948e1302682e3682f11f50208b726a246ab4e6c1b39f9264a8796bb416aa2", + "sha256:219a3116ecd015f8dca7b5d2c366c973509dfb9a8fc97ef044a36e3da66144a1", + "sha256:3b1fc683fb204c6b4403a1ef23f0b1fac8e4477091585e0c8c54cbdf7d7bb164", + "sha256:3beff56b453b6ef94ecb2996bea101a08f1f8a9771d3cbf4988a61e4d9973761", + "sha256:7687f6455ec3ed7649d1ae574136835a4272b65b3ddcf01ab8704ac65616c5ce", + "sha256:7ec45a70d40ede1ec7ad7f95b3c94c9cf4c186a32f6bacb1795b60abd2f9ef27", + "sha256:86c857510a9b7c3104cf4cde1568f4921762c8f9842e987bc03ed4f160925754", + "sha256:8a627507ef9b307b46a1fea9513d5c98680ba09591253082b4c48697ba05a4ae", + "sha256:8dfb69fbf9f3aeed18afffb15e319ca7f8da9642336348ddd6cab2713ddcf8f9", + "sha256:a34b577cdf6313bf24755f7a0e3f3c326d5c1f4fe7422d1d06498eb25ad0c600", + "sha256:a8ffcd53cb5dfc131850851cc09f1c44689c2812d0beb954d8138d4f5fc17f65", + "sha256:b90928f2d9eb2f33162405f32dde9f6dcead63a0971ca8a1b50eb4ca3e35ceb8", + "sha256:c56ffe22faa2e51054c5f7a3bc70a370939c2ed4de308c690e7949230c995913", + "sha256:f91c7ae919bbc3f96cd5e5b2e786b2b108343d1d7972ea130f7de27fdd547cf3" ], "index": "pypi", - "version": "==0.761" + "version": "==0.770" }, "mypy-extensions": { "hashes": [ @@ -351,9 +340,9 @@ }, "pyrsistent": { "hashes": [ - "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280" + "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3" ], - "version": "==0.15.7" + "version": "==0.16.0" }, "python-dateutil": { "hashes": [ @@ -408,10 +397,28 @@ }, "sqlalchemy": { "hashes": [ - "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb" + "sha256:083e383a1dca8384d0ea6378bd182d83c600ed4ff4ec8247d3b2442cf70db1ad", + "sha256:0a690a6486658d03cc6a73536d46e796b6570ac1f8a7ec133f9e28c448b69828", + "sha256:114b6ace30001f056e944cebd46daef38fdb41ebb98f5e5940241a03ed6cad43", + "sha256:128f6179325f7597a46403dde0bf148478f868df44841348dfc8d158e00db1f9", + "sha256:13d48cd8b925b6893a4e59b2dfb3e59a5204fd8c98289aad353af78bd214db49", + "sha256:211a1ce7e825f7142121144bac76f53ac28b12172716a710f4bf3eab477e730b", + "sha256:2dc57ee80b76813759cccd1a7affedf9c4dbe5b065a91fb6092c9d8151d66078", + "sha256:3e625e283eecc15aee5b1ef77203bfb542563fa4a9aa622c7643c7b55438ff49", + "sha256:43078c7ec0457387c79b8d52fff90a7ad352ca4c7aa841c366238c3e2cf52fdf", + "sha256:5b1bf3c2c2dca738235ce08079783ef04f1a7fc5b21cf24adaae77f2da4e73c3", + "sha256:6056b671aeda3fc451382e52ab8a753c0d5f66ef2a5ccc8fa5ba7abd20988b4d", + "sha256:68d78cf4a9dfade2e6cf57c4be19f7b82ed66e67dacf93b32bb390c9bed12749", + "sha256:7025c639ce7e170db845e94006cf5f404e243e6fc00d6c86fa19e8ad8d411880", + "sha256:7224e126c00b8178dfd227bc337ba5e754b197a3867d33b9f30dc0208f773d70", + "sha256:7d98e0785c4cd7ae30b4a451416db71f5724a1839025544b4edbd92e00b91f0f", + "sha256:8d8c21e9d4efef01351bf28513648ceb988031be4159745a7ad1b3e28c8ff68a", + "sha256:bbb545da054e6297242a1bb1ba88e7a8ffb679f518258d66798ec712b82e4e07", + "sha256:d00b393f05dbd4ecd65c989b7f5a81110eae4baea7a6a4cdd94c20a908d1456e", + "sha256:e18752cecaef61031252ca72031d4d6247b3212ebb84748fc5d1a0d2029c23ea" ], "index": "pypi", - "version": "==1.3.13" + "version": "==1.3.16" }, "typed-ast": { "hashes": [ @@ -441,12 +448,12 @@ }, "typing-extensions": { "hashes": [ - "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", - "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", - "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575" + "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5", + "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae", + "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392" ], "markers": "python_version < '3.7'", - "version": "==3.7.4.1" + "version": "==3.7.4.2" }, "urllib3": { "hashes": [ @@ -464,10 +471,10 @@ }, "validators": { "hashes": [ - "sha256:b192e6bde7d617811d59f50584ed240b580375648cd032d106edeb3164099508" + "sha256:6a0d9502219aee486f1ee12d8a9635e4a56f3dbcfa204b4e0de3a038ae35f34f" ], "index": "pypi", - "version": "==0.14.2" + "version": "==0.14.3" }, "webencodings": { "hashes": [ @@ -478,10 +485,10 @@ }, "werkzeug": { "hashes": [ - "sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096", - "sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16" + "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" ], - "version": "==1.0.0" + "version": "==1.0.1" }, "wtforms": { "hashes": [ @@ -514,10 +521,10 @@ }, "zipp": { "hashes": [ - "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2", - "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a" + "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", + "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" ], - "version": "==3.0.0" + "version": "==3.1.0" } }, "develop": { @@ -558,19 +565,19 @@ }, "beautifulsoup4": { "hashes": [ - "sha256:05fd825eb01c290877657a56df4c6e4c311b3965bda790c613a3d6fb01a5462a", - "sha256:9fbb4d6e48ecd30bcacc5b63b94088192dcda178513b2ae3c394229f8911b887", - "sha256:e1505eeed31b0f4ce2dbb3bc8eb256c04cc2b3b72af7d551a4ab6efd5cbe5dae" + "sha256:594ca51a10d2b3443cbac41214e12dbb2a1cd57e1a7344659849e2e20ba6a8d8", + "sha256:a4bbe77fd30670455c5296242967a123ec28c37e9702a8a81bd2f20a4baf0368", + "sha256:d4e96ac9b0c3a6d3f0caae2e4124e6055c5dcafde8e2f831ff194c104f0775a0" ], "index": "pypi", - "version": "==4.8.2" + "version": "==4.9.0" }, "certifi": { "hashes": [ - "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", - "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" + "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", + "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" ], - "version": "==2019.11.28" + "version": "==2020.4.5.1" }, "chardet": { "hashes": [ @@ -581,55 +588,55 @@ }, "click": { "hashes": [ - "sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", - "sha256:5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7" + "sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", + "sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" ], - "version": "==7.0" + "version": "==7.1.1" }, "coverage": { "hashes": [ - "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3", - "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c", - "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0", - "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477", - "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a", - "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf", - "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691", - "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73", - "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987", - "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894", - "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e", - "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef", - "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf", - "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68", - "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8", - "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954", - "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2", - "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40", - "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc", - "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc", - "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e", - "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d", - "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f", - "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc", - "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301", - "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea", - "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb", - "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af", - "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52", - "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37", - "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0" + "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" ], "index": "pypi", - "version": "==5.0.3" + "version": "==5.1" }, "coveralls": { "hashes": [ - "sha256:4b6bfc2a2a77b890f556bc631e35ba1ac21193c356393b66c84465c06218e135", - "sha256:67188c7ec630c5f708c31552f2bcdac4580e172219897c4136504f14b823132f" + "sha256:41bd57b60321dfd5b56e990ab3f7ed876090691c21a9e3b005e1f6e42e6ba4b9", + "sha256:d213f5edd49053d03f0db316ccabfe17725f2758147afc9a37eaca9d8e8602b5" ], "index": "pypi", - "version": "==1.11.1" + "version": "==2.0.0" }, "docopt": { "hashes": [ @@ -647,26 +654,26 @@ }, "flask": { "hashes": [ - "sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", - "sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" + "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060", + "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557" ], "index": "pypi", - "version": "==1.1.1" + "version": "==1.1.2" }, "flask-testing": { "hashes": [ - "sha256:dc076623d7d850653a018cb64f500948334c8aeb6b10a5a842bf1bcfb98122bc" + "sha256:d849bf53eb1ceb09dff6611888588cb60f20238058fb1ebcd917d69febc373e6" ], "index": "pypi", - "version": "==0.7.1" + "version": "==0.8.0" }, "hypothesis": { "hashes": [ - "sha256:2e5382dea626072f12a17c10d2b3c3f13bb7cb35ba40d0978bc29b0ade589b63", - "sha256:c82209a75ffb17556b2c396bbd55832f1525938809bd8bb6651a86e7b5ded154" + "sha256:5ed71ef64680c2beeb935e0a334b2393b05b4862eb67c221f6c8930a16060bc8", + "sha256:91de2487a40102bd8ecc961bc3fd0cc49ef35485add222fe5be09836710c17f0" ], "index": "pypi", - "version": "==5.5.4" + "version": "==5.9.0" }, "idna": { "hashes": [ @@ -684,11 +691,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302", - "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b" + "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", + "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" ], "markers": "python_version < '3.8'", - "version": "==1.5.0" + "version": "==1.6.0" }, "inflect": { "hashes": [ @@ -747,42 +754,31 @@ }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" + "sha256:06358015a4dee8ee23ae426bf885616ab3963622defd829eb45b44e3dee3515f", + "sha256:0b0c4fc852c5f02c6277ef3b33d23fcbe89b1b227460423e3335374da046b6db", + "sha256:267677fc42afed5094fc5ea1c4236bbe4b6a00fe4b08e93451e65ae9048139c7", + "sha256:303cb70893e2c345588fb5d5b86e0ca369f9bb56942f03064c5e3e75fa7a238a", + "sha256:3c9b624a0d9ed5a5093ac4edc4e823e6b125441e60ef35d36e6f4a6fdacd5054", + "sha256:42033e14cae1f6c86fc0c3e90d04d08ce73ac8e46ba420a0d22d545c2abd4977", + "sha256:4e4a99b6af7bdc0856b50020c095848ec050356a001e1f751510aef6ab14d0e0", + "sha256:4eb07faad54bb07427d848f31030a65a49ebb0cec0b30674f91cf1ddd456bfe4", + "sha256:63a7161cd8c2bc563feeda45df62f42c860dd0675e2b8da2667f25bb3c95eaba", + "sha256:68e0fd039b68d2945b4beb947d4023ca7f8e95b708031c345762efba214ea761", + "sha256:8092a63397025c2f655acd42784b2a1528339b90b987beb9253f22e8cdbb36c3", + "sha256:841218860683c0f2223e24756843d84cc49cccdae6765e04962607754a52d3e0", + "sha256:94076b2314bd2f6cfae508ad65b4d493e3a58a50112b7a2cbb6287bdbc404ae8", + "sha256:9d22aff1c5322e402adfb3ce40839a5056c353e711c033798cf4f02eb9f5124d", + "sha256:b0e4584f62b3e5f5c1a7bcefd2b52f236505e6ef032cc508caa4f4c8dc8d3af1", + "sha256:b1163ffc1384d242964426a8164da12dbcdbc0de18ea36e2c34b898ed38c3b45", + "sha256:beac28ed60c8e838301226a7a85841d0af2068eba2dcb1a58c2d32d6c05e440e", + "sha256:c29f096ce79c03054a1101d6e5fe6bf04b0bb489165d5e0e9653fb4fe8048ee1", + "sha256:c58779966d53e5f14ba393d64e2402a7926601d1ac8adeb4e83893def79d0428", + "sha256:cfe14b37908eaf7d5506302987228bff69e1b8e7071ccd4e70fd0283b1b47f0b", + "sha256:e834249c45aa9837d0753351cdca61a4b8b383cc9ad0ff2325c97ff7b69e72a6", + "sha256:eed1b234c4499811ee85bcefa22ef5e466e75d132502226ed29740d593316c1f" ], "index": "pypi", - "version": "==1.1.1" + "version": "==2.0.0a1" }, "mccabe": { "hashes": [ @@ -808,10 +804,10 @@ }, "packaging": { "hashes": [ - "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73", - "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334" + "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3", + "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752" ], - "version": "==20.1" + "version": "==20.3" }, "pluggy": { "hashes": [ @@ -845,25 +841,25 @@ }, "pyflakes": { "hashes": [ - "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0", - "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2" + "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", + "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" ], - "version": "==2.1.1" + "version": "==2.2.0" }, "pygments": { "hashes": [ - "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b", - "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe" + "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", + "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" ], - "version": "==2.5.2" + "version": "==2.6.1" }, "pyhamcrest": { "hashes": [ - "sha256:36cda5e849e3ee9ce54628667471e5e769f596259bb3c13c3596f6059a615ef8", - "sha256:5959cb4ab465b303522d2e0d270a6ee581c3ad9ba419e304bb6ebe50a60ea37a" + "sha256:412e00137858f04bde0729913874a48485665f2d36fe9ee449f26be864af9316", + "sha256:7ead136e03655af85069b6f47b23eb7c3e5c221aa9f022a4fbb499f5b7308f29" ], "index": "pypi", - "version": "==2.0.0" + "version": "==2.0.2" }, "pylama": { "hashes": [ @@ -883,18 +879,18 @@ }, "pyparsing": { "hashes": [ - "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f", - "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec" + "sha256:67199f0c41a9c702154efb0e7a8cc08accf830eb003b4d9fa42c4059002e2492", + "sha256:700d17888d441604b0bd51535908dcb297561b040819cccde647a92439db5a2a" ], - "version": "==2.4.6" + "version": "==3.0.0a1" }, "pytest": { "hashes": [ - "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d", - "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6" + "sha256:0e5b30f5cb04e887b91b1ee519fa3d89049595f428c1db76e73bd7f17b09b172", + "sha256:84dde37075b8805f3d1f392cc47e38a0e59518fb46a431cfdaf7cf1ce805f970" ], "index": "pypi", - "version": "==5.3.5" + "version": "==5.4.1" }, "pytest-easyread": { "hashes": [ @@ -905,11 +901,11 @@ }, "pytest-html": { "hashes": [ - "sha256:933da7a5e71e5eace9e475441ed88a684f20f6198aa36516cb947ac05edd9921", - "sha256:bc40553ca2a1835479c2caf7d48604502cd66d0c5db58ddbca53d74946ee71bd" + "sha256:6a4ac391e105e391208e3eb9bd294a60dd336447fd8e1acddff3a6de7f4e57c5", + "sha256:9e4817e8be8ddde62e8653c8934d0f296b605da3d2277a052f762c56a8b32df2" ], "index": "pypi", - "version": "==2.0.1" + "version": "==2.1.1" }, "pytest-metadata": { "hashes": [ @@ -928,10 +924,10 @@ }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", + "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" ], - "version": "==2.22.0" + "version": "==2.23.0" }, "six": { "hashes": [ @@ -956,18 +952,18 @@ }, "soupsieve": { "hashes": [ - "sha256:bdb0d917b03a1369ce964056fc195cfdff8819c40de04695a80bc813c3cfa1f5", - "sha256:e2c1c5dee4a1c36bcb790e0fabd5492d874b8ebd4617622c4f6a731701060dda" + "sha256:e914534802d7ffd233242b785229d5ba0766a7f487385e3f714446a07bf540ae", + "sha256:fcd71e08c0aee99aca1b73f45478549ee7e7fc006d51b37bec9e9def7dc22b69" ], - "version": "==1.9.5" + "version": "==2.0" }, "sphinx": { "hashes": [ - "sha256:525527074f2e0c2585f68f73c99b4dc257c34bbe308b27f5f8c7a6e20642742f", - "sha256:543d39db5f82d83a5c1aa0c10c88f2b6cff2da3e711aa849b2c627b4b403bbd9" + "sha256:50972d83b78990fd61d0d3fe8620814cae53db29443e92c13661bc43dff46ec8", + "sha256:8411878f4768ec2a8896b844d68070204f9354a831b37937989c2e559d29dffc" ], "index": "pypi", - "version": "==2.4.2" + "version": "==3.0.1" }, "sphinx-autodoc-typehints": { "hashes": [ @@ -979,24 +975,24 @@ }, "sphinxcontrib-applehelp": { "hashes": [ - "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897", - "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d" + "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a", + "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58" ], - "version": "==1.0.1" + "version": "==1.0.2" }, "sphinxcontrib-devhelp": { "hashes": [ - "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34", - "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981" + "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", + "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" ], - "version": "==1.0.1" + "version": "==1.0.2" }, "sphinxcontrib-htmlhelp": { "hashes": [ - "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", - "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7" + "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", + "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" ], - "version": "==1.0.2" + "version": "==1.0.3" }, "sphinxcontrib-jsmath": { "hashes": [ @@ -1007,17 +1003,17 @@ }, "sphinxcontrib-qthelp": { "hashes": [ - "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20", - "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f" + "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", + "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" ], - "version": "==1.0.2" + "version": "==1.0.3" }, "sphinxcontrib-serializinghtml": { "hashes": [ - "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227", - "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768" + "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", + "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" ], - "version": "==1.1.3" + "version": "==1.1.4" }, "sqlacodegen": { "hashes": [ @@ -1029,10 +1025,28 @@ }, "sqlalchemy": { "hashes": [ - "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb" + "sha256:083e383a1dca8384d0ea6378bd182d83c600ed4ff4ec8247d3b2442cf70db1ad", + "sha256:0a690a6486658d03cc6a73536d46e796b6570ac1f8a7ec133f9e28c448b69828", + "sha256:114b6ace30001f056e944cebd46daef38fdb41ebb98f5e5940241a03ed6cad43", + "sha256:128f6179325f7597a46403dde0bf148478f868df44841348dfc8d158e00db1f9", + "sha256:13d48cd8b925b6893a4e59b2dfb3e59a5204fd8c98289aad353af78bd214db49", + "sha256:211a1ce7e825f7142121144bac76f53ac28b12172716a710f4bf3eab477e730b", + "sha256:2dc57ee80b76813759cccd1a7affedf9c4dbe5b065a91fb6092c9d8151d66078", + "sha256:3e625e283eecc15aee5b1ef77203bfb542563fa4a9aa622c7643c7b55438ff49", + "sha256:43078c7ec0457387c79b8d52fff90a7ad352ca4c7aa841c366238c3e2cf52fdf", + "sha256:5b1bf3c2c2dca738235ce08079783ef04f1a7fc5b21cf24adaae77f2da4e73c3", + "sha256:6056b671aeda3fc451382e52ab8a753c0d5f66ef2a5ccc8fa5ba7abd20988b4d", + "sha256:68d78cf4a9dfade2e6cf57c4be19f7b82ed66e67dacf93b32bb390c9bed12749", + "sha256:7025c639ce7e170db845e94006cf5f404e243e6fc00d6c86fa19e8ad8d411880", + "sha256:7224e126c00b8178dfd227bc337ba5e754b197a3867d33b9f30dc0208f773d70", + "sha256:7d98e0785c4cd7ae30b4a451416db71f5724a1839025544b4edbd92e00b91f0f", + "sha256:8d8c21e9d4efef01351bf28513648ceb988031be4159745a7ad1b3e28c8ff68a", + "sha256:bbb545da054e6297242a1bb1ba88e7a8ffb679f518258d66798ec712b82e4e07", + "sha256:d00b393f05dbd4ecd65c989b7f5a81110eae4baea7a6a4cdd94c20a908d1456e", + "sha256:e18752cecaef61031252ca72031d4d6247b3212ebb84748fc5d1a0d2029c23ea" ], "index": "pypi", - "version": "==1.3.13" + "version": "==1.3.16" }, "typed-ast": { "hashes": [ @@ -1070,10 +1084,10 @@ }, "wcwidth": { "hashes": [ - "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603", - "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8" + "sha256:cafe2186b3c009a04067022ce1dcd79cb38d8d65ee4f4791b8888d6599d1bbe1", + "sha256:ee73862862a156bf77ff92b09034fc4825dd3af9cf81bc5b360668d425f3c5f1" ], - "version": "==0.1.8" + "version": "==0.1.9" }, "weighted-levenshtein": { "hashes": [ @@ -1084,10 +1098,10 @@ }, "werkzeug": { "hashes": [ - "sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096", - "sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16" + "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", + "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" ], - "version": "==1.0.0" + "version": "==1.0.1" }, "wrapt": { "hashes": [ @@ -1097,10 +1111,10 @@ }, "zipp": { "hashes": [ - "sha256:12248a63bbdf7548f89cb4c7cda4681e537031eda29c02ea29674bc6854460c2", - "sha256:7c0f8e91abc0dc07a5068f315c52cb30c66bfbc581e5b50704c8a2f6ebae794a" + "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", + "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" ], - "version": "==3.0.0" + "version": "==3.1.0" } } } diff --git a/README.md b/README.md index f75121607..0a3a6af53 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,9 @@ If all goes well, http://127.0.0.1:5000/abs/0906.5132 should render the basic abs page. By default, the application will use the directory trees in -`tests/data/abs_files` and `tests/data/cache` and when looking for the document metadata and cache files, respectively. These paths can be overridden via -environment variables (see `browse/config.py`). +`tests/data/abs_files` and `tests/data/cache` and when looking for the +document metadata and cache files, respectively. These paths can be +overridden via environment variables (see `browse/config.py`). ### Rebuilding the test database diff --git a/browse/config.py b/browse/config.py index ba198518a..878a76a9c 100644 --- a/browse/config.py +++ b/browse/config.py @@ -8,7 +8,7 @@ import dateutil.parser from datetime import datetime, timedelta -APP_VERSION = '0.2.9' +APP_VERSION = '0.3.0' """The application version """ ON = 'yes' @@ -236,20 +236,15 @@ SQLALCHEMY_ECHO = False SQLALCHEMY_RECORD_QUERIES = False -SQLALCHEMY_POOL_SIZE = int(os.environ.get('BROWSE_SQLALCHEMY_POOL_SIZE', '10')) -"""SQLALCHEMY_POOL_SIZE is set from BROWSE_SQLALCHEMY_POOL_SIZE. - -Ignored under sqlite.""" - -SQLALCHEMY_MAX_OVERFLOW = int(os.environ.get('BROWSE_SQLALCHEMY_MAX_OVERFLOW', '0')) -"""SQLALCHEMY_MAX_OVERFLOW is set from BROWSE_SQLALCHEMY_MAX_OVERFLOW. - -Ignored under sqlite.""" - # SQLALCHEMY_POOL_SIZE and SQLALCHEMY_MAX_OVERFLOW will not work with sqlite -if 'sqlite' in SQLALCHEMY_DATABASE_URI: - SQLALCHEMY_POOL_SIZE = None - SQLALCHEMY_MAX_OVERFLOW = None +if 'sqlite' not in SQLALCHEMY_DATABASE_URI: + SQLALCHEMY_POOL_SIZE = int(os.environ.get('BROWSE_SQLALCHEMY_POOL_SIZE', '10')) + """SQLALCHEMY_POOL_SIZE is set from BROWSE_SQLALCHEMY_POOL_SIZE. + Ignored under sqlite.""" + + SQLALCHEMY_MAX_OVERFLOW = int(os.environ.get('BROWSE_SQLALCHEMY_MAX_OVERFLOW', '0')) + """SQLALCHEMY_MAX_OVERFLOW is set from BROWSE_SQLALCHEMY_MAX_OVERFLOW. + Ignored under sqlite.""" BROWSE_DAILY_STATS_PATH = os.environ.get( 'BROWSE_DAILY_STATS_PATH', 'tests/data/daily_stats') @@ -283,23 +278,41 @@ 'BROWSE_USER_BANNER_ENABLED', '0'))) """Enable/disable user banner.""" try: - BROWSE_USER_BANNER_START_DATE = dateutil.parser.parse( - os.environ.get('BROWSE_USER_BANNER_START_DATE') - ).replace(hour=0, minute=0, second=0) + if os.environ.get('BROWSE_USER_BANNER_START_DATE',None): + BROWSE_USER_BANNER_START_DATE = dateutil.parser.parse( + os.environ.get('BROWSE_USER_BANNER_START_DATE', 'nodate') + ).replace(hour=0, minute=0, second=0) + else: + raise ValueError except Exception: if BROWSE_USER_BANNER_ENABLED: warnings.warn("Bad value for BROWSE_USER_BANNER_START_DATE") BROWSE_USER_BANNER_START_DATE = datetime.now() - timedelta(days=1) try: - BROWSE_USER_BANNER_END_DATE = dateutil.parser.parse( - os.environ.get('BROWSE_USER_BANNER_END_DATE') - ).replace(hour=23, minute=59, second=59) + if os.environ.get('BROWSE_USER_BANNER_END_DATE', None): + BROWSE_USER_BANNER_END_DATE = dateutil.parser.parse( + os.environ.get('BROWSE_USER_BANNER_END_DATE', 'noate') + ).replace(hour=23, minute=59, second=59) + else: + raise ValueError except Exception: if BROWSE_USER_BANNER_ENABLED: warnings.warn("Bad value for BROWSE_USER_BANNER_END_DATE") BROWSE_USER_BANNER_END_DATE = datetime.now() + timedelta(days=1) +BROWSE_STATUS_BANNER_ENABLED = bool(int(os.environ.get( + 'BROWSE_STATUS_BANNER_ENABLED', '0'))) +"""Enable/disable status service banner.""" + +BROWSE_STATUS_BANNER_SCRIPT_URL = os.environ.get( + 'BROWSE_STATUS_BANNER_SCRIPT_URL', + 'https://code.sorryapp.com/status-bar/4.latest/status-bar.min.js') + +BROWSE_STATUS_BANNER_SITE_ID = os.environ.get( + 'BROWSE_STATUS_BANNER_SITE_ID', 'foo') +"""Enable/disable status service banner.""" + DOCUMENT_LATEST_VERSIONS_PATH = os.environ.get( 'DOCUMENT_LATEST_VERSIONS_PATH', 'tests/data/abs_files/ftp') """Paths to .abs and source files.""" @@ -341,10 +354,7 @@ CLASSIC_DATABASE_URI = os.environ.get('CLASSIC_DATABASE_URI', os.environ.get( 'BROWSE_SQLALCHEMY_DATABASE_URI', default=None)) """If not set, legacy database integrations for auth will not be available.""" -if not CLASSIC_DATABASE_URI: - warnings.warn("No value set for CLASSIC_DATABASE_URI") -elif 'sqlite' in CLASSIC_DATABASE_URI: - warnings.warn("Using sqlite in CLASSIC_DATABASE_URI") + CLASSIC_SESSION_HASH = os.environ.get('CLASSIC_SESSION_HASH', 'foosecret') SESSION_DURATION = os.environ.get( diff --git a/browse/controllers/archive_page/__init__.py b/browse/controllers/archive_page/__init__.py index 6f15b247b..f93bec529 100644 --- a/browse/controllers/archive_page/__init__.py +++ b/browse/controllers/archive_page/__init__.py @@ -14,7 +14,7 @@ -def get_archive(archive_id: str) -> Response: +def get_archive(archive_id: str) -> Tuple[Dict[str, Any], int, Dict[str, Any]]: """Gets archive page.""" data: Dict[str, Any] = {} response_headers: Dict[str, Any] = {} @@ -59,10 +59,10 @@ def get_archive(archive_id: str) -> Response: data["catchup_to"] = datetime.date.today() - datetime.timedelta(days=7) data["template"] = "archive/single_archive.html" - return data, status.HTTP_200_OK, response_headers # type: ignore + return data, status.HTTP_200_OK, response_headers -def archive_index(archive_id: str, status: int) -> Response: +def archive_index(archive_id: str, status: int) -> Tuple[Dict[str, Any], int, Dict[str, Any]]: """Landing page for when there is no archive specified.""" data: Dict[str, Any] = {} data["bad_archive"] = archive_id @@ -84,7 +84,7 @@ def archive_index(archive_id: str, status: int) -> Response: data["defunct"] = defunct data["template"] = "archive/archive_list_all.html" - return data, status, {} # type: ignore + return data, status, {} def subsumed_msg(archive: Dict[str, str], subsumed_by: str) -> Dict[str, str]: diff --git a/browse/controllers/list_page/__init__.py b/browse/controllers/list_page/__init__.py index 42f8b14fa..01d2c7b18 100644 --- a/browse/controllers/list_page/__init__.py +++ b/browse/controllers/list_page/__init__.py @@ -100,8 +100,8 @@ def get_listing(subject_or_category: str, Number of articles to show """ # TODO make sure to handle POST too - skip = skip or request.args.get('skip', None) - show = show or request.args.get('show', None) + skip = skip or request.args.get('skip', '0') + show = show or request.args.get('show', '') if request.args.get('archive', None) is not None: subject_or_category = request.args.get('archive') # type: ignore if request.args.get('year', None): @@ -421,8 +421,7 @@ def sub_sections_for_types( }) for sec in secs: - typ = {'new': 'New', 'cross': 'Cross', 'rep': 'Replacement'}[ # type: ignore - sec['type']] + typ = {'new': 'New', 'cross': 'Cross', 'rep': 'Replacement'}[sec['type']] # type: ignore date = resp['announced'].strftime('%A, %-d %B %Y') showing = 'showing ' @@ -441,7 +440,8 @@ def sub_sections_for_types( def _not_modified(response: Union[ListingResponse, NewResponse, NotModifiedResponse]) -> bool: - return bool(response and response.get('not_modified', False)) + return bool ( response and 'not_modified' in response) +# return bool(response and response.get('not_modified', False)) def _expires_headers(listing_resp: diff --git a/browse/controllers/prevnext/__init__.py b/browse/controllers/prevnext/__init__.py index c400bcdb7..eff894da8 100644 --- a/browse/controllers/prevnext/__init__.py +++ b/browse/controllers/prevnext/__init__.py @@ -1,9 +1,8 @@ """Handle requests to support sequential navigation between arXiv IDs.""" -from flask import url_for +from flask import url_for, escape from typing import Tuple, Dict, Any -from werkzeug.datastructures import MultiDict -from werkzeug.exceptions import InternalServerError, BadRequest +from werkzeug.exceptions import BadRequest from browse.domain.identifier import Identifier, IdentifierException from browse.services.database import get_sequential_id @@ -16,21 +15,25 @@ logger = logging.getLogger(__name__) -def get_prevnext(request_params: MultiDict) -> Response: +def get_prevnext(id: str, function: str, context: str) -> Response: """ Get the next or previous arXiv ID in the browse context. - The 'id', 'function', and 'context' request parameters are required. The - 'site' parameter from the classic prevnext is no longer supported. + The 'site' parameter from the classic prevnext is no longer supported. Parameters ---------- - request_params : dict + id + arxiv id + function + prev or next + context + which archive or category to browse Returns ------- dict - Search result response data. + Result response data. int HTTP status code. dict @@ -38,48 +41,33 @@ def get_prevnext(request_params: MultiDict) -> Response: Raises ------ - InternalServerError - Raised when there was an unexpected problem executing the query. BadRequest Raised when request parameters are missing, invalid, or when an ID redirect cannot be returned even when the request parameters are valid. """ - if 'id' not in request_params: + if id is None or not id: raise BadRequest('Missing article identifier') - try: - arxiv_id = Identifier(request_params['id']) - except IdentifierException: - raise BadRequest(f"Invalid article identifier {request_params['id']}") - - if not ('function' in request_params - and request_params['function'] in ['prev', 'next']): + if function not in ['prev', 'next']: raise BadRequest('Missing or invalid function request') - - if 'context' not in request_params: + if context is None or not context: raise BadRequest('Missing context') - context = request_params['context'] - if not (context in CATEGORIES_ACTIVE or context in ARCHIVES or context == 'all'): raise BadRequest('Invalid context') - is_next = request_params['function'] == 'next' try: - seq_id = get_sequential_id(paper_id=arxiv_id, - is_next=is_next, - context=context) - except Exception as ex: - logger.warning(f'Error getting sequential ID: {ex}') - raise InternalServerError from ex + arxiv_id = Identifier(id) + except IdentifierException: + raise BadRequest(escape(f"Invalid article identifier {id}")) + seq_id = get_sequential_id(paper_id=arxiv_id, + is_next=function == 'next', + context=context) if not seq_id: raise BadRequest( - f'No {"next" if is_next else "previous"} article found for ' - f'{arxiv_id.id} in {context}' - ) + escape(f'No {function} article found for ' + f'{arxiv_id.id} in {context}')) - redirect_url = url_for('browse.abstract', - arxiv_id=seq_id, - context=context) + redirect_url = url_for('browse.abstract', arxiv_id=seq_id, context=context) return {}, status.HTTP_301_MOVED_PERMANENTLY, {'Location': redirect_url} diff --git a/browse/controllers/prevnext/tests.py b/browse/controllers/prevnext/tests.py index 7a79bcd60..6ebf42eba 100644 --- a/browse/controllers/prevnext/tests.py +++ b/browse/controllers/prevnext/tests.py @@ -1,5 +1,7 @@ """Tests for prevnext controller, :mod:`browse.controllers.prevnext`.""" +# mypy: ignore-errors + from unittest import TestCase, mock from werkzeug.datastructures import MultiDict from werkzeug.exceptions import BadRequest @@ -11,93 +13,57 @@ class TestPrevNextController(TestCase): def test_missing_parameters(self) -> None: """Test request with missing parameters.""" - request_data = MultiDict() - with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - request_data = MultiDict({ - 'id': '1801.00001' - }) with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - - request_data = MultiDict({ - 'id': '1801.00001', - 'function': 'next' - }) + prevnext.get_prevnext('','','') with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - - request_data = MultiDict({ - 'id': '1801.00001', - 'context': 'cs' - }) + prevnext.get_prevnext('1801.00001','','') with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - - request_data = MultiDict({ - 'function': 'prev', - 'context': 'cs' - }) + prevnext.get_prevnext('1801.00001', 'next','') with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) + prevnext.get_prevnext('1801.00001', '','cs') + with self.assertRaises(BadRequest): + prevnext.get_prevnext('','prev','cs') def test_bad_parameters(self) -> None: """Test parameters with bad values.""" - request_data = MultiDict({ - 'id': 'foo', # invalid - 'function': 'prev', # valid - 'context': 'cs.AI' # valid - }) - with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - request_data = MultiDict({ - 'id': 'cs/0001001', # valid - 'function': 'bar', # invalid - 'context': 'cs' # valid - }) with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) - - request_data = MultiDict({ - 'id': 'cs/0001001', # valid - 'function': 'next', # valid - 'context': 'baz' # invalid - }) + prevnext.get_prevnext('foo', # invalid + 'prev', # valid + 'cs.AI') # valid + with self.assertRaises(BadRequest): + prevnext.get_prevnext('cs/0001001', # valid + 'bar', # invalid + 'cs' ) # valid with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) + prevnext.get_prevnext('cs/0001001', # valid + 'next', # valid + 'baz') # invalid @mock.patch('browse.controllers.prevnext.get_sequential_id') @mock.patch('browse.controllers.prevnext.url_for') def test_good_parameters(self, mock_url_for, mock_get_sequential_id) -> None: # type: ignore """Test parameters with good values.""" - request_data = MultiDict({ - 'id': '1801.00001', - 'function': 'next', - 'context': 'all' - }) mock_get_sequential_id.return_value = '1801.00002' - _, status, headers = prevnext.get_prevnext(request_data) + _, status, headers = prevnext.get_prevnext('1801.00001', + 'next', + 'all') self.assertEqual(status, 301) - request_data = MultiDict({ - 'id': '1801.00002', - 'function': 'prev', - 'context': 'cs.AI' - }) mock_get_sequential_id.return_value = '1801.00001' - _, status, headers = prevnext.get_prevnext(request_data) + _, status, headers = prevnext.get_prevnext('1801.00002', + 'prev', + 'cs.AI') self.assertEqual(status, 301) - request_data = MultiDict({ - 'id': '1701.00002', - 'function': 'next', - 'context': 'physics.gen-ph' - }) mock_get_sequential_id.return_value = None with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) + prevnext.get_prevnext('1701.00002', + 'next', + 'physics.gen-ph') mock_get_sequential_id.return_value = '' with self.assertRaises(BadRequest): - prevnext.get_prevnext(request_data) + prevnext.get_prevnext('1701.00002', + 'next', + 'physics.gen-ph') diff --git a/browse/controllers/stats_page/__init__.py b/browse/controllers/stats_page/__init__.py index 828b2842a..5d5cf379e 100644 --- a/browse/controllers/stats_page/__init__.py +++ b/browse/controllers/stats_page/__init__.py @@ -17,6 +17,10 @@ Response = Tuple[Dict[str, Any], int, Dict[str, Any]] logger = logging.getLogger(__name__) +def get_main_stats_page() -> Response: + """Minimal rendering of the main stats page.""" + response_data: Dict[str, Any] = {} + return response_data, status.HTTP_200_OK, {} def get_hourly_stats_page(requested_date_str: Optional[str] = None) -> Response: """Get data for the /stats/today page.""" diff --git a/browse/controllers/stats_page/tests.py b/browse/controllers/stats_page/tests.py index df5e87b6b..cca6b91bd 100644 --- a/browse/controllers/stats_page/tests.py +++ b/browse/controllers/stats_page/tests.py @@ -1,5 +1,5 @@ """Tests for stats page controllers, :mod:`browse.controllers.stats_page`.""" - +# mypy: ignore-errors from unittest import TestCase, mock from datetime import date, datetime from werkzeug.exceptions import BadRequest diff --git a/browse/controllers/tb_page/tests.py b/browse/controllers/tb_page/tests.py index e70d1cddd..369fa783f 100644 --- a/browse/controllers/tb_page/tests.py +++ b/browse/controllers/tb_page/tests.py @@ -1,5 +1,7 @@ """Tests for tb controllers, :mod:`browse.controllers.tb_page`.""" +# mypy: ignore-errors + from unittest import TestCase, mock from werkzeug.datastructures import MultiDict from werkzeug.exceptions import BadRequest diff --git a/browse/domain/identifier.py b/browse/domain/identifier.py index 08e315db7..ccfc99a6e 100644 --- a/browse/domain/identifier.py +++ b/browse/domain/identifier.py @@ -57,7 +57,7 @@ def __init__(self, arxiv_id: str) -> None: self.archive: Optional[str] = None self.filename: Optional[str] = None self.year: Optional[int] = None - self.month = None + self.month: Optional[int] = None self.is_old_id: Optional[bool] = None if self.ids in taxonomy.definitions.ARCHIVES: diff --git a/browse/factory.py b/browse/factory.py index f9deec073..3b7c2291d 100644 --- a/browse/factory.py +++ b/browse/factory.py @@ -20,7 +20,7 @@ def create_web_app() -> Flask: """Initialize an instance of the browse web application.""" app = Flask('browse', static_url_path=f'/static/browse/{APP_VERSION}') - app.config.from_pyfile('config.py') # type: ignore + app.config.from_pyfile('config.py') # TODO Only needed until this route is added to arxiv-base if 'URLS' not in app.config: diff --git a/browse/routes/ui.py b/browse/routes/ui.py index d4e62ce41..fde725b2a 100644 --- a/browse/routes/ui.py +++ b/browse/routes/ui.py @@ -1,7 +1,7 @@ """Provides the user intefaces for browse.""" import re from datetime import datetime -from typing import Callable, Dict, Mapping, Union +from typing import Callable, Dict, Mapping, Union, Tuple, Any from flask import Blueprint, render_template, request, Response, session, \ current_app, url_for, redirect from werkzeug.exceptions import InternalServerError, BadRequest, NotFound @@ -23,7 +23,7 @@ @blueprint.context_processor -def inject_now() -> None: +def inject_now() -> Dict: """Inject current datetime into request context.""" return dict(request_datetime=datetime.now()) @@ -56,23 +56,21 @@ def home() -> Response: raise InternalServerError('Unexpected error') - @blueprint.route('abs', methods=['GET']) -def bare_abs() -> Response: +def bare_abs() -> Any: """Check several legacy request parameters.""" if request.args: if 'id' in request.args: - return abstract(request.args['id']) # type: ignore + return abstract(request.args['id']) elif 'archive' in request.args and 'papernum' in request.args: - return abstract( # type: ignore - f"{request.args['archive']}/{request.args['papernum']}") + return abstract(f"{request.args['archive']}/{request.args['papernum']}") else: for param in request.args: # singleton case, where the parameter is the value # e.g. /abs?/\d{7} if not request.args[param] \ and re.match(r'^[a-z\-]+(\.[A-Z]{2})?\/\d{7}$', param): - return abstract(param) # type: ignore + return abstract(param) """Return abs-specific 404.""" raise AbsNotFound @@ -80,7 +78,7 @@ def bare_abs() -> Response: @blueprint.route('abs/', methods=['GET'], defaults={'arxiv_id': ''}) @blueprint.route('abs/', methods=['GET']) -def abstract(arxiv_id: str) -> Response: +def abstract(arxiv_id: str) -> Any: """Abstract (abs) page view.""" response, code, headers = abs_page.get_abs_page(arxiv_id) @@ -91,17 +89,16 @@ def abstract(arxiv_id: str) -> Response: return Response( response['abs_meta'].raw_safe, mimetype='text/plain') - return render_template('abs/abs.html', **response), code, headers # type: ignore + return render_template('abs/abs.html', **response), code, headers elif code == status.HTTP_301_MOVED_PERMANENTLY: - return redirect(headers['Location'], code=code) # type: ignore + return redirect(headers['Location'], code=code) elif code == status.HTTP_304_NOT_MODIFIED: - return '', code, headers # type: ignore - - raise InternalServerError('Unexpected error') + return '', code, headers + raise InternalServerError('Unexpected error') @blueprint.route('category_taxonomy', methods=['GET']) -def category_taxonomy() -> Response: +def category_taxonomy() -> Any: """Display the arXiv category taxonomy.""" response = { 'groups': taxonomy.definitions.GROUPS, @@ -109,7 +106,7 @@ def category_taxonomy() -> Response: 'categories': taxonomy.definitions.CATEGORIES_ACTIVE } return render_template('category_taxonomy.html', **response), \ - status.HTTP_200_OK, None # type: ignore + status.HTTP_200_OK, None @blueprint.route('tb/', defaults={'arxiv_id': ''}, methods=['GET']) @@ -150,14 +147,11 @@ def tb_redirect(trackback_id: str, hashed_document_id: str) -> Response: @blueprint.route('prevnext', methods=['GET', 'POST']) -def previous_next() -> Union[str, Response]: +def previous_next() -> Tuple[Dict[str, Any], int, Dict[str, Any]]: """Previous/Next navigation used on /abs page.""" - if not request.args: - raise BadRequest - response, code, headers = prevnext.get_prevnext(request.args) - if code == status.HTTP_301_MOVED_PERMANENTLY: - return redirect(headers['Location'], code=code) # type: ignore - raise InternalServerError('Unexpected error') + return prevnext.get_prevnext(request.args.get('id', default=''), + request.args.get('function', default=''), + request.args.get('context', default='')) @blueprint.route('trackback/', methods=['GET'], defaults={'arxiv_id': ''}) @@ -194,7 +188,7 @@ def list_articles(context: str, subcontext: str) -> Response: 'recent', 'new' or a string of format YYMM. """ response, code, headers = \ - list_page.get_listing(context, subcontext) # type: ignore + list_page.get_listing(context, subcontext) if code == status.HTTP_200_OK: # TODO if it is a HEAD request we don't want to render the template return render_template(response['template'], **response), code, headers # type: ignore @@ -205,6 +199,12 @@ def list_articles(context: str, subcontext: str) -> Response: return response, code, headers # type: ignore +@blueprint.route('stats/main', methods=['GET']) +def main() -> Response: + """Display the stats main page.""" + response, code, headers = stats_page.get_main_stats_page() + return render_template('stats/main.html', **response), code, headers # type: ignore + @blueprint.route('stats/', methods=['GET']) def stats(command: str) -> Response: @@ -245,7 +245,6 @@ def stats(command: str) -> Response: raise NotFound raise InternalServerError('Unexpected error') - @blueprint.route('format/') def format(arxiv_id: str) -> Response: """Get formats article.""" @@ -327,7 +326,7 @@ def form(arxiv_id: str) -> Response: @blueprint.route('archive/', strict_slashes=False) def archive(archive: str): # type: ignore """Landing page for an archive.""" - response, code, headers = archive_page.get_archive(archive) # type: ignore + response, code, headers = archive_page.get_archive(archive) if code == status.HTTP_200_OK or code == status.HTTP_404_NOT_FOUND: return render_template(response['template'], **response), code, headers elif code == status.HTTP_301_MOVED_PERMANENTLY: diff --git a/browse/services/database/__init__.py b/browse/services/database/__init__.py index c63bb1e8a..ad8cdb612 100644 --- a/browse/services/database/__init__.py +++ b/browse/services/database/__init__.py @@ -171,7 +171,7 @@ def count_trackback_pings(paper_id: str) -> int: .filter(Document.paper_id == paper_id) \ .filter(TrackbackPing.status == 'accepted').first() - return row.num_pings + return int(row.num_pings) @db_handle_error(logger=logger, default_return_val=0) @@ -217,7 +217,7 @@ def get_document_count() -> Optional[int]: row = db.session.query( func.count(Document.document_id).label('num_documents') ).filter(not_(Document.paper_id.like('test%'))).first() - return row.num_documents + return int(row.num_documents) @db_handle_error(logger=logger, default_return_val=0) @@ -233,7 +233,7 @@ def get_document_count_by_yymm(paper_date: Optional[date] = None) -> int: func.count(Document.document_id).label('num_documents') ).filter(Document.paper_id.like(yymm_like))\ .filter(not_(Document.paper_id.like('test%'))).first() - return row.num_documents + return int(row.num_documents) @db_handle_error(logger=logger, default_return_val=None) @@ -241,16 +241,36 @@ def get_sequential_id(paper_id: Identifier, context: str = 'all', is_next: bool = True) -> Optional[str]: """Get the next or previous paper ID in sequence.""" + + if not isinstance(paper_id, Identifier) or not paper_id.month or not paper_id.year: + return None + + # In case we go over month or year boundry + inc = 1 if is_next else -1 + nxtmonth = int(paper_id.month) + inc + if nxtmonth > 12 or nxtmonth < 1: + nxyear = int(paper_id.year) + inc + nxtmonth = 1 if is_next else 12 + else: + nxyear = paper_id.year + + nextyymm = "{}{:02d}".format(str(nxyear)[2:],nxtmonth) + query = db.session.query(Document.paper_id) if paper_id.is_old_id: # NB: classic did not support old identifiers in prevnext if context == 'all': like_id = f'{paper_id.archive}/{paper_id.yymm}%' + next_q = f'{paper_id.archive}/{nextyymm}%' else: like_id = f'%/{paper_id.yymm}%' + next_q = f'%/{nextyymm}%' else: like_id = f'{paper_id.yymm}.%' - query = query.filter(Document.paper_id.like(like_id)) + next_q = f'{nextyymm}.%' + + query = query.filter((Document.paper_id.like(like_id) | + Document.paper_id.like(next_q))) if is_next: query = query.filter(Document.paper_id > paper_id.id). \ @@ -270,7 +290,6 @@ def get_sequential_id(paper_id: Identifier, in_category.c.subject_class == subject_class) result = query.first() - if result: return f'{result.paper_id}' return None @@ -281,7 +300,7 @@ def __all_hourly_stats_query() -> Query: @db_handle_error(logger=logger, default_return_val=(0, 0, 0)) -def get_hourly_stats_count(stats_date: Optional[date]) -> Tuple[int, int]: +def get_hourly_stats_count(stats_date: Optional[date]) -> Tuple[int, int, int]: """Get sum of normal/admin connections and nodes for a given date.""" stats_date = date.today() if not isinstance(stats_date, date) \ else stats_date diff --git a/browse/services/listing/__init__.py b/browse/services/listing/__init__.py index b815b397e..904491e9b 100644 --- a/browse/services/listing/__init__.py +++ b/browse/services/listing/__init__.py @@ -99,6 +99,6 @@ def get_listing_service() -> ListingService: # importing at runtime to avoid cyclic imports that kill python import importlib fl = importlib.import_module("browse.services.listing.fake_listings") - g.listing_service = fl.FakeListingFilesService() + g.listing_service = fl.FakeListingFilesService() # type: ignore return cast(ListingService, g.listing_service) diff --git a/browse/static/css/arXiv.css b/browse/static/css/arXiv.css index 17a65c8b8..fb53452aa 100644 --- a/browse/static/css/arXiv.css +++ b/browse/static/css/arXiv.css @@ -5,7 +5,6 @@ /**************************************** * General rules *****************************************/ - body { margin: 0; padding: 0; @@ -430,19 +429,9 @@ footer .sorry-app-links .help { @media screen and (min-width: 990px) { .endorsers .help { top: 5rem; } /* adjusts for endorsers and feedback being a single line instead of double, uses desktop breakpoint */ } - @media screen and (min-width: 769px) { .columns { display: flex; flex-direction: row; } } - -.column { - display: block; - flex-basis: 0; - flex-grow: 1; - flex-shrink: 1; - padding: 0.75rem; -} - .icon { height:.9rem; width:.9rem; @@ -669,39 +658,6 @@ p.tagline { #abs-outer .extra-services .bib-sidebar { border-left: .35em solid #ddd !important; } -@media screen and (max-width: 768px) { - #abs-outer a.mobile-submission-download { - display: flex; - justify-content: center; - margin: 1em .25em; - border-radius: 10px; - border: 2px solid #cecece; - background-color: #f2f2f2; - padding: 1em; - font-weight: 800; - color:black; - text-decoration: none; - } - #abs-outer .extra-services h2 { - font-size:15px; - } - #abs-outer .extra-services h3 { - font-size:14px; - } - #abs-outer .leftcolumn, #abs-outer .extra-services { - width:100%; - float:left; - } - #abs-outer .extra-services, #abs-outer .extra-services .full-text, #abs-outer .extra-services .ancillary, #abs-outer .extra-services .browse, #abs-outer .extra-services .extra-ref-cite, #abs-outer .extra-services .bookmarks { - border:0px; - font-size:12px; - } - #abs-outer .extra-services { - border-top: .35em solid #ddd; - border-bottom: .35em solid #ddd; - margin:.5em 0 1em 0; - } -} /* arXiv.org archive/subjclass info for abs pages... */ #abs-outer .subheader { @@ -825,11 +781,413 @@ div#long-author-list { font-style: italic; clear:both; } + + +/*** mobile friendly abs page styles ***/ +#abs-outer .header-breadcrumbs-mobile { + display: none; +} +#abs-outer #abs .dateline { + margin-top: 15px; + margin-bottom: 0; +} +#abs-outer #abs h1.title { + margin-top: .25em; +} +.endorsers .help { + display: inline-block; +} @media screen and (max-width: 768px) { + #abs-outer .header-breadcrumbs-mobile { + display: block; + color: black; + font-size: .85em; + margin: .25em 0 .5em 1em; + } + #abs-outer #abs a.mobile-submission-download { + display: flex; + justify-content: center; + margin: .7em .25em; + border-radius: 10px; + background-color: #408bd0; + padding: .25em 1em; + font-weight: 800; + color: white; + text-decoration: none; + font-size: 20px; + text-align: center; + } + #abs-outer #abs a.mobile-download-grey { + background-color: #eeeeee; + color: #565656; + } + #abs-outer .extra-services h2 { + font-size:15px; + margin-bottom: .5em; + } + #abs-outer .extra-services h3 { + font-size:14px; + } + #abs-outer .leftcolumn, #abs-outer .extra-services { + width:100%; + float:left; + } + #abs-outer .extra-services, #abs-outer .extra-services .full-text, #abs-outer .extra-services .ancillary, #abs-outer .extra-services .browse, #abs-outer .extra-services .extra-ref-cite { + border:0px; + font-size:12px; + } + #abs-outer .extra-services { + -webkit-box-shadow: inset 0px 8px 15px 0px rgba(173,173,173,1); + -moz-box-shadow: inset 0px 8px 15px 0px rgba(173,173,173,1); + box-shadow: inset 0px 8px 15px 0px rgba(173,173,173,1); + background-color: #E6E6E6; + margin: 0; + padding: 1em 0; + } + #abs-outer .extra-ref-cite ul li { + float: left; + height: 50px; + } + #abs-outer .extra-ref-cite ul li .inspire-links { + font-size: 9px; + } + #abs-outer .extra-ref-cite ul li .inspire-links a { + color: #046baf; + text-decoration: underline; + } + #abs-outer .extra-services .full-text ul, #abs-outer .extra-services .extra-ref-cite ul { + list-style: none; + margin: 0; + padding: 0; + } + #abs-outer .extra-services .full-text ul li, #abs-outer .extra-services .extra-ref-cite ul li { + display: inline-block; + margin: 0 0 .25em 0; + padding: 0; + } + #abs-outer .extra-services .bookmarks { + margin: 1em 0 0 0; + border-left: 0; + padding: .25em .5em 0 1em; + border-top: 2px solid #cccccc; + font-size: 1em; + } + #abs-outer .extra-services .bookmarks .abs-button-small { + margin-top: .25em; + } + #abs-outer .extra-services .browse { + margin-top: .5em; + border-top: 2px solid #cccccc; + padding-top: 1em; + } + #abs-outer .extra-services .prevnext { + margin-top: .5em; + } + #abs-outer .extra-services .browse .current { + color: #AB4B02; + display:inline; + } + .abs-switch-cat { + margin: 0 0 1em 0; + } + .browse .abs-switch-cat .switch { + display: inline; + } + .browse .abs-switch-cat .switch a { + font-weight: bold; + } + .browse .abs-switch-cat .switch .subclass { + padding: 0; + margin-left: -3px; + } + .abs-switch-cat .subclass:before { + content: ", "; + } + .abs-button { + display: inline-block; + border-radius: 5px; + border: 1px solid #046BAF; + font-size: 1.25em; + color: #046BAF !important; + padding: .5em; + background: #E6E6E6; + margin-right: .3em; + } + .abs-button-small { + font-size: 1em; + padding: .25em 1em; + margin: .75em .5em 0 0; + } + .abs-button-grey { + border: 1px solid #666666; + color: #666666 !important; + } + #abs-outer .extra-services .bib-sidebar { + border: 0 !important; + } + #abs-outer .extra-services .bib-sidebar a { + display: inline-block; + border-radius: 5px; + background: #E6E6E6; + border: 1px solid #666666; + color: #666666 !important; + font-size: 1em !important; + padding: .25em 1em; + margin: 0 .5em 0 0; + } + #abs-outer .subheader { + background-color: #fefefe; + padding: .25em 0; + border-bottom: 1px solid #ccc; + } + #abs-outer .subheader h1 { + margin: 0; + font-size: .75em; + padding: .2em 0 .2em 1em; + font-weight: normal; + font-style: normal; + color: #b55c06; + } + #abs-outer #abs .dateline { + color: #767676; + font-size: .85em; + font-style: normal; + margin: 2em 0 0 1em; + } + #abs-outer .submission-history { + padding: 1em; + margin: 0; + background-color: #f5f5f5; + -webkit-box-shadow: inset 0px -6px 15px 0px rgba(219,219,219,1); + -moz-box-shadow: inset 0px -6px 15px 0px rgba(219,219,219,1); + box-shadow: inset 0px -6px 15px 0px rgba(219,219,219,1); + } + #abs-outer #abs h1.title { + margin: 0 .25em 0 .5em; + font-size: 1.5em; + } + #abs-outer #abs .authors { + margin: 1em .25em 0 1em; + font-size: .9em; + line-height: 1.5em; + } + #abs-outer #abs .authors a { + font-size: inherit; + } + #abs-outer #abs blockquote.abstract { + margin: 0 1em; + } + #abs-outer #abs .metatable { + margin: .75em 0 1.5em 1.5em; + } + #abs-outer #abs a, #abs-outer a, #abs-outer .endorsers a { + color: #1777bc; + } + #abs-outer .abs-license a, #abs-outer .submission-history a, #abs-outer .extra_services a, #abs-outer .extra_services .browse a, #abs-outer .abs-switch-cat .switch a, #abs-outer .extra-services .full-text .abs-license a { + color: #046BAF; + } #abs-outer .endorsers { - margin: 1em 0 .5em 20px; + display: block; + float: left; + border: 1px solid #eee; + padding: 1em; + margin: 1em; + } + #abs-outer .endorsers .help { + display:none; + } +} + + + +/*** styles for mobile-friendly header ***/ +.mobile-header .toggle-target { + max-height: 0; + visibility: hidden; +} +.mobile-header .toggle-target.open { + max-height: 500px; + visibility: visible; +} +.mobile-header { + background-color: #b31b1b; +} +.mobile-header .columns { + height: 65px; + align-items: center; +} +.mobile-header .column { + border-left: 1px solid #fc5554; + border-right: 1px solid #731515; + height: 65px; + padding: 0 1em; + display: flex; + align-items: center; +} +.mobile-header .column:first-child { + border-left: 0; +} +.mobile-header .column:last-child { + border-right: 0; +} +.mobile-header .column.logo-arxiv { + width: 100px; + flex: none; +} +.mobile-header .column.logo-arxiv img { + height: 25px; +} +.mobile-header .column.logo-cornell { + display: flex; +} +.mobile-header .column.logo-cornell img { + height:45px; +} +.mobile-header .column.nav { + justify-content: flex-end; + align-self: flex-end; +} +.mobile-header #toggle-container button.toggle-control { + background-color: transparent; + border-radius: 0; + border: 0; + font-size: 25px; + padding: 3px; + margin-left: .5em; +} +.mobile-header #toggle-container button.toggle-control svg.icon { + width: 1.25rem; + margin: 0; +} +.mobile-header .mobile-toggle-block { + margin: 0; + width: 100%; + flex: none; + transition: max-height 3s; + position: absolute; + top:64px; + left:0; + background-color: #b31a1a; + overflow: hidden; +} +.mobile-header .mobile-toggle-block form.mobile-search-form { + margin: .5em 1em 1em 1em; +} +.mobile-header .mobile-toggle-block form.mobile-search-form input::-webkit-input-placeholder { /* Edge */ + color: #94908c; +} +.mobile-header .mobile-toggle-block form.mobile-search-form input:-ms-input-placeholder { /* Internet Explorer 10-11 */ + color: #94908c; +} +.mobile-header .mobile-toggle-block form.mobile-search-form input::placeholder { + color: #94908c; +} +.mobile-header .mobile-toggle-block form.mobile-search-form input { + width: calc(100% - 50px); + background: #fef6f6; + border-radius: 10px 0 0 10px; + border: 0; + padding: 5px; + color: black; + font-size: 1.5em; +} +.mobile-header .mobile-toggle-block form.mobile-search-form button.button { + width: 50px; + background: #fc5554; + border: 0; + border-left: 1px solid #b31b1b; + border-radius: 0 10px 10px 0; + padding: 3px; + font-size: 20px; + font-weight: bold; + color: #fffaf4; +} +.mobile-header .mobile-toggle-block nav.mobile-menu { + padding: .75em 1em; +} +.mobile-header .mobile-toggle-block nav.mobile-menu h2 { + margin: 0; + font-size: 12px; + color: #fcd7d7; + text-transform: uppercase; +} +.mobile-header .mobile-toggle-block nav.mobile-menu ul { + list-style: none; + margin: .75em 0 0 0; + padding: 0; +} +.mobile-header .mobile-toggle-block nav.mobile-menu ul li { + display: inline-flex; + align-items: stretch; + padding:.1em .2em; + margin:0 0 .5em 0; + border-top: 2px solid #fc5554; + margin-right: .75em; +} +.mobile-header .mobile-toggle-block nav.mobile-menu ul li a { + color: #fef6f6; + font-weight: normal; + font-size: 12px; +} +@media screen and (min-width: 769px) { + .mobile-header { + display: none; + } +} +@media screen and (max-width: 425px) { + .mobile-header .column.logo-cornell { + /* display:none; */ + } +} +@media screen and (min-width: 426px) { + .mobile-header .column.nav { + flex: none; + width: 100px; } } +/*special styles for phone header only */ +@media screen and (max-width: 500px) { + .mobile-header .columns { + height: 80px; + } + .mobile-header .column { + height: 80px; + padding: 0 .5em; + } + .mobile-header .column.logo-arxiv { + border-right: 0 !important; + } + .mobile-header .column.logo-cornell { + justify-content: flex-end; + border-left: 0 !important; + } + .mobile-header .column.logo-cornell img { + height: 73px; + } + .mobile-header .column.nav { + width: 65px; + flex: none; + } + .mobile-header .mobile-toggle-block { + top: 80px; + } +} +@media screen and (min-width: 501px) { + .mobile-header .column { + height: 65px; + } + .mobile-header .column.logo-cornell img { + height: 45px; + } + .mobile-header .column.nav { + width: 65px; + } +} + + + + + /* Floating box on right for links to extra services */ .extra-services { @@ -979,6 +1337,7 @@ div#long-dc-list { } .bookmarks { + clear: both; margin: 0; padding: 0 1em .5em 1em; font-size: 90%; @@ -1397,6 +1756,22 @@ body#front.with-cu-identity #header #search { display: flex; } } +@media screen and (max-width: 1023px) { + .is-hidden-touch { + display: none !important; + } +} +@media screen and (min-width: 1024px) { + .is-hidden-desktop { + display: none !important; + } +} +.is-pulled-left { + float: left !important; +} +.is-pulled-right { + float: right !important; +} .is-sr-only { border: none !important; clip: rect(0, 0, 0, 0) !important; @@ -1407,6 +1782,375 @@ body#front.with-cu-identity #header #search { white-space: nowrap !important; width: 0.01em !important; } +.button { + border-width: 1px; + cursor: pointer; + justify-content: center; + padding-bottom: calc(0.5em - 1px); + padding-left: 1em; + padding-right: 1em; + padding-top: calc(0.5em - 1px); + text-align: center; + white-space: nowrap; +} +/*** limited bulma tab styles so browse can use them ***/ +.tabs { + -webkit-overflow-scrolling: touch; + align-items: stretch; + display: flex; + font-size: 1rem; + justify-content: space-between; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; +} +.tabs a { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + color: #4a4a4a; + display: flex; + justify-content: center; + margin-bottom: -1px; + padding: 0.5em 1em; + vertical-align: top; +} +.tabs a:hover { + border-bottom-color: #363636; + color: #363636; +} +.tabs li { + display: block; +} +.tabs li.is-active a { + border-bottom-color: #3273dc; + color: #3273dc; +} +.tabs ul { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; +} +.tabs ul.is-left { + padding-right: 0.75em; +} +.tabs ul.is-center { + flex: none; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; +} +.tabs ul.is-right { + justify-content: flex-end; + padding-left: 0.75em; +} +.tabs .icon:first-child { + margin-right: 0.5em; +} +.tabs .icon:last-child { + margin-left: 0.5em; +} +.tabs.is-centered ul { + justify-content: center; +} +.tabs.is-right ul { + justify-content: flex-end; +} +.tabs.is-boxed a { + border: 1px solid transparent; + border-radius: 4px 4px 0 0; +} +.tabs.is-boxed a:hover { + background-color: whitesmoke; + border-bottom-color: #dbdbdb; +} +.tabs.is-boxed li.is-active a { + background-color: white; + border-color: #dbdbdb; + border-bottom-color: transparent !important; +} +/*** limited column styles copied from base so that browse can use bulma columns too ***/ +.column { + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + padding: 0.75rem; +} +.columns.is-mobile > .column.is-full { + flex: none; + width: 100%; +} +.columns.is-mobile > .column.is-three-quarters { + flex: none; + width: 75%; +} +.columns.is-mobile > .column.is-two-thirds { + flex: none; + width: 66.6666%; +} +.columns.is-mobile > .column.is-half { + flex: none; + width: 50%; +} +.columns.is-mobile > .column.is-one-third { + flex: none; + width: 33.3333%; +} +.columns.is-mobile > .column.is-one-quarter { + flex: none; + width: 25%; +} +.columns.is-mobile > .column.is-one-fifth { + flex: none; + width: 20%; +} +.columns.is-mobile > .column.is-two-fifths { + flex: none; + width: 40%; +} +.columns.is-mobile > .column.is-three-fifths { + flex: none; + width: 60%; +} +.columns.is-mobile > .column.is-four-fifths { + flex: none; + width: 80%; +} +.columns. .column.is-full { + flex: none; + width: 100%; +} +.columns .column.is-three-quarters { + flex: none; + width: 75%; +} +.columns .column.is-two-thirds { + flex: none; + width: 66.6666%; +} +.columns .column.is-half { + flex: none; + width: 50%; +} +.columns .column.is-one-third { + flex: none; + width: 33.3333%; +} +.columns .column.is-one-quarter { + flex: none; + width: 25%; +} +.columns .column.is-one-fifth { + flex: none; + width: 20%; +} +.columns .column.is-two-fifths { + flex: none; + width: 40%; +} +.columns .column.is-three-fifths { + flex: none; + width: 60%; +} +.columns .column.is-four-fifths { + flex: none; + width: 80%; +} + +/*** base styles for vanilla accordion. Move to base for wider use? ***/ +.accordion-body { + display: none; +} +.accordion-body.open { + display: block; +} +/*** styles for the accordion large-data-list ***/ +.large-data-list .accordion-body { + margin-top: 1em; +} +.large-data-list .accordion-head:after { + content: "\02C5"; + padding-left: 1em; + position: relative; + top: .25em; + font-size:1.25em; + font-weight:100; +} +.large-data-list .accordion-head.open:after { + content: "\02C4"; + color: #b7b8b9; +} +/*** styles for general large-data-list ***/ +.large-data-list { + border-bottom: 1px solid #b7b8b9; +} +.large-data-list a { + color: #1777bc; +} +.large-data-list p { + margin:1em 1em 1em 0; + font-size:.85em; + color: #000000; +} +.large-data-list .column { + padding: 0; +} +.large-data-list h2 { + border-top: 1px solid #e3e5e6; + padding: .75em 0; + margin: 0; + font-size: 1em; + cursor: pointer; +} +.large-data-list h2:first-child { + border-top: 1px solid #b7b8b9; +} +.large-data-list h3 { + margin:1em 1em 1em 0; + font-size: .9em; + font-weight: normal; + text-transform: uppercase; +} +.large-data-list h3 span { + font-size: .7em; +} +.large-data-list h4 { + margin:1em 1em 1em 0; + font-size: .9em; +} +.large-data-list h4 span { + font-size: .8em; + font-weight: normal; +} +.large-data-list .physics { + border-bottom: .5px dotted #ccc; +} +.large-data-list .physics:last-child { + border-bottom: 0px; +} +.large-data-list .columns.divided { + margin-left: .3em; + border-bottom: .5px dotted #ccc; +} +.large-data-list .columns.divided:last-child { + margin-bottom: 1.5em; + border-bottom: 0px; +} +.guide { + display:none; +} +.guide .columns.divided .column { + border-top: .5px dotted black; +} +.form-trackbacks { + padding: .5em 0; + border-top: 1px solid #cccccc; +} +.form-trackbacks .button { + background-color: #d3e1e6; +} +.trackback-styles { + border-top: 1px solid #eae9e7 !important; + padding: 1em !important; +} +.trackback-styles blockquote { + background-color: #f7f5f3; + margin: 1em 0; + padding: 2em 1em; +} +.trackback-styles h2 { + margin-bottom: 1em; +} +.trackback-styles h2, .trackback-styles p { + margin-left:0 !important; + padding-left:0 !important; + border: 0 !important; +} +/*** styles for bold-divided-list ***/ +.bold-divided-list { + margin: 1em .5em; + padding: 0 0 .5em 0; +} +.bold-divided-list p { + margin-top: 0; +} +.bold-divided-list .bold-divided-column { + padding-left: 1.5em; + padding-top: 1em; + padding-bottom: 1.75em; + border-top: .5px dotted #ccc; +} +.bold-divided-list .bold-divided-column ul { + margin: 0; + padding: 0; + font-size: .85em; + list-style: none; +} +.bold-divided-list .bold-divided-column ul li { + color: black; +} +.bold-divided-list .bold-divided-column ul li a { + font-weight: bold; +} +.bold-divided-list .bold-divided-column ul li:before { + color: #000; + content: "\002D"; + margin: 0 6px 0 0; + font-weight: bold; +} +.bold-divided-list .bold-divided-header { + margin-top: 0; + padding: 1em 0 0 0; + border-top: 1px solid black; +} + +@media screen and (min-width: 769px) { + .trackback-styles { + padding: 2em !important; + } + .large-data-list { + margin: 0 4em; + } + .large-data-list .accordion-body { + margin-left:2em; + } + .large-data-list h2 { + padding-left: 2em; + } + .guide { + display: block; + background: #eef5f9; + border-radius: 10px 0 0 0; + margin-bottom: 2em; + margin-top:2em; + margin-right: 4em; + padding-bottom: 1em; + color: black; + box-shadow: 0 0 3px 0 rgba(210, 210, 210, .5); + } + .guide p em { + color: #994D04; + font-style: italic; + } + .guide a { + color: #046BAF; + } + .form-trackbacks { + padding: 1em 0 2em 0; + text-align: right; + } + .bold-divided-list { + margin-top: 4em; + padding-bottom: 2em; + } +} /*icon styles*/ svg.icon { @@ -1418,6 +2162,9 @@ svg.icon { .icon.filter-black { fill: #000000; } +.icon.filter-light_grey { + fill: #bfbfbf; +} .icon.filter-grey { fill: #f5f5f5; } diff --git a/browse/static/images/arxiv-logo.png b/browse/static/images/arxiv-logo.png new file mode 100644 index 000000000..4e44db12b Binary files /dev/null and b/browse/static/images/arxiv-logo.png differ diff --git a/browse/static/images/icons/cu/.DS_Store b/browse/static/images/icons/cu/.DS_Store new file mode 100644 index 000000000..5008ddfcf Binary files /dev/null and b/browse/static/images/icons/cu/.DS_Store differ diff --git a/browse/static/images/icons/cu/cornell-reduced-white-SEAL.png b/browse/static/images/icons/cu/cornell-reduced-white-SEAL.png new file mode 100644 index 000000000..ea26f3126 Binary files /dev/null and b/browse/static/images/icons/cu/cornell-reduced-white-SEAL.png differ diff --git a/browse/static/images/icons/cu/cornell_seal_simple_black.svg b/browse/static/images/icons/cu/cornell_seal_simple_black.svg new file mode 100644 index 000000000..71212a069 --- /dev/null +++ b/browse/static/images/icons/cu/cornell_seal_simple_black.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/browse/static/images/icons/cu/cornell_seal_simple_white.svg b/browse/static/images/icons/cu/cornell_seal_simple_white.svg new file mode 100644 index 000000000..e93d6c1cb --- /dev/null +++ b/browse/static/images/icons/cu/cornell_seal_simple_white.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/browse/static/images/stat-thumb-downloads.jpg b/browse/static/images/stat-thumb-downloads.jpg new file mode 100644 index 000000000..e2c7a0a3a Binary files /dev/null and b/browse/static/images/stat-thumb-downloads.jpg differ diff --git a/browse/static/images/stat-thumb-hourly.jpg b/browse/static/images/stat-thumb-hourly.jpg new file mode 100644 index 000000000..6cf53e641 Binary files /dev/null and b/browse/static/images/stat-thumb-hourly.jpg differ diff --git a/browse/static/images/stat-thumb-institutional.jpg b/browse/static/images/stat-thumb-institutional.jpg new file mode 100644 index 000000000..d01b6a7cd Binary files /dev/null and b/browse/static/images/stat-thumb-institutional.jpg differ diff --git a/browse/static/images/stat-thumb-subject.jpg b/browse/static/images/stat-thumb-subject.jpg new file mode 100644 index 000000000..b7dcec0bc Binary files /dev/null and b/browse/static/images/stat-thumb-subject.jpg differ diff --git a/browse/static/images/stat-thumb-uploads.jpg b/browse/static/images/stat-thumb-uploads.jpg new file mode 100644 index 000000000..c59f7c06d Binary files /dev/null and b/browse/static/images/stat-thumb-uploads.jpg differ diff --git a/browse/static/js/accordion.js b/browse/static/js/accordion.js new file mode 100644 index 000000000..e461b6889 --- /dev/null +++ b/browse/static/js/accordion.js @@ -0,0 +1,21 @@ +// vanilla accordion script. no jquery, dependencies. +var jsaccordion = { + init : function (target) { + // initialize the accordion + var headers = document.querySelectorAll("#" + target + " .toggle-control"); + if (headers.length > 0) { for (var head of headers) { + head.addEventListener("click", jsaccordion.select); + }} + }, + select : function () { + // fired when user clicks on a header + var contents = this.nextElementSibling; + contents.classList.toggle("open"); + var contents = this; + contents.classList.toggle("open"); + } +}; +// runs on page load +window.addEventListener('load', function(){ + jsaccordion.init("toggle-container"); +}); diff --git a/browse/templates/abs/abs.html b/browse/templates/abs/abs.html index bbca550c3..309c6c1fe 100644 --- a/browse/templates/abs/abs.html +++ b/browse/templates/abs/abs.html @@ -2,7 +2,7 @@ {% from 'abs/author_links.html' import display_authors_with_links %} {% import 'base/macros.html' as base_macros %} - {% block title %}[{{ requested_id }}] {{ abs_meta.title|tex2utf }}{% endblock %} +{% block title %}[{{ requested_id }}] {{ abs_meta.title|tex2utf }}{% endblock %} {% block head %} {{ super() -}} @@ -17,20 +17,27 @@ {% block body_id %}{% endblock %} -{% block header_class %}{% endblock %} {% block header_h1 %} - + {% endblock header_h1 %} {%- block content %} {%- include "abs/trackback_rdf.html" %}
- {{ generate_download_button() }} + + {% set download_button %} + {{ generate_download_button() }} + {% endset %} +

{% if abs_meta.primary_archive.id != abs_meta.primary_category.id %}{{ abs_meta.primary_archive.name }} > {% endif %}{{ abs_meta.primary_category.name }}

+
+ arXiv:{{ requested_id }} ({{ abs_meta.primary_archive.id }}) +
+ {{ base_macros.abs( abs_meta.arxiv_identifier.id, @@ -48,7 +55,7 @@

{% if abs_meta.primary_archive.id != abs_meta.primary_category.id %}{{ abs_m version = abs_meta.version, submission_history = abs_meta.version_history, secondary_categories = abs_meta.get_secondaries(), - include_stylesheet = 0) + download_button_markup = download_button ) }}
@@ -66,8 +73,10 @@

Submission history

From: {{ abs_meta.submitter.name|tex2utf if abs_meta {% include "abs/extra_services.html" %} -
Which authors of this paper are endorsers? | Disable MathJax (What is MathJax?) - {% include "feedback_collector.html" %} +
+ Which authors of this paper are endorsers? | + Disable MathJax (What is MathJax?) + {% include "feedback_collector.html" %}
{#- The following supports the arXiv Labs Bibliographic Explorer project: https://labs.arxiv.org/projects/bibexplorer -#} @@ -145,6 +154,6 @@

Submission history

From: {{ abs_meta.submitter.name|tex2utf if abs_meta {% if formats|length > 0 and ('pdf' in formats or 'pdfonly' in formats) %} Download PDF {% else %} - No PDF available, click for other formats + No PDF available, click to view other formats {% endif %} {% endmacro %} diff --git a/browse/templates/abs/bookmarking.html b/browse/templates/abs/bookmarking.html index e4d040cf3..49addcbd7 100644 --- a/browse/templates/abs/bookmarking.html +++ b/browse/templates/abs/bookmarking.html @@ -2,22 +2,22 @@

Bookmark

{%- set absUrl = canonical_url( abs_meta.arxiv_id ) -%} {%- set title = abs_meta.title -%} - BibSonomy logo - Mendeley logo - Reddit logo - ScienceWISE logo diff --git a/browse/templates/abs/extra_services.html b/browse/templates/abs/extra_services.html index fd51dfb3c..dbda1df5f 100644 --- a/browse/templates/abs/extra_services.html +++ b/browse/templates/abs/extra_services.html @@ -25,24 +25,24 @@ {%- for format in format_list if not (format == 'src' and not current) %}
  • {%- if format == 'src' -%} - Source + Source {%- elif format.startswith('ps') -%} - PostScript{% if format.endswith('(400)') %} (400 dpi){% elif format.endswith('(600)') %} (600 dpi){% elif format.endswith('(cm)') %} (Type I cm){% elif format.endswith('(CM)') %} (Type I CM){% endif %} + PostScript{% if format.endswith('(400)') %} (400 dpi){% elif format.endswith('(600)') %} (600 dpi){% elif format.endswith('(cm)') %} (Type I cm){% elif format.endswith('(CM)') %} (Type I CM){% endif %} {%- elif format.startswith('pdf') -%} - PDF{% if format.endswith('only') %} only{% endif %} + PDF{% if format.endswith('only') %} only{% endif %} {%- elif format == 'dvi' -%} - {{ format.upper() }} + {{ format.upper() }} {%- elif format == 'html' -%} - {{ format.upper() }} + {{ format.upper() }} {%- elif format == 'other' -%} - Other formats + Other formats {%- elif format == 'nops' -%} - (PostScript unavailable) + (PostScript unavailable) {%- elif format == 'sciencewise_pdf' -%} {#- TODO: consider this format for deprecation -#} {#- TODO: create clickthrough link from sciencewise link -#} - Tagged PDF - + Tagged PDF + ScienceWISE {%- endif -%} @@ -53,56 +53,56 @@ {%- endmacro -%} {%- macro generate_browse_context() -%} -

    Current browse context:

    -
    {{ browse_context }}
    + Current browse context:
    {{ browse_context }}
    - - {% if browse_context_previous_url -%} - - < prev - - {%- else -%} - < previous - {% endif -%} - -  |  - - {% if browse_context_next_url %} - - next > - - {%- else -%} - next > - {%- endif -%} - -
    + {% if browse_context_previous_url -%} + + < prev + +   |   + {%- else -%} + < previous article +   |   + {% endif -%} + + {% if browse_context_next_url %} + + next > + + {%- else -%} + next article > + {%- endif -%} +
    {#end div.prevnext#} {%- if browse_context != 'arxiv' -%} {#- This fixes a bug in the classic UI logic -#} {%- endif -%} {%- if not abs_meta.arxiv_identifier.is_old_id and (abs_meta.get_browse_context_list()|length > 1) -%} -

    Change to browse by:

    -
    - {% for category in abs_meta.get_browse_context_list() if not (browse_context==category) %} - {%- set switch_url = url_for('browse.abstract', arxiv_id=abs_meta.arxiv_identifier.id, context=category) -%} - {% if '.' in category %} - {{ category }} - {% else %} - {{ category }} - {% endif %} -
    - {% endfor %} +
    + Change to browse by: +
    + {% for category in abs_meta.get_browse_context_list() if not (browse_context==category) %} + {%- set switch_url = url_for('browse.abstract', arxiv_id=abs_meta.arxiv_identifier.id, context=category) -%} + {% if '.' in category %} + {{ category }}
    + {% else %} + {{ category }}
    + {% endif %} + {% endfor %} +
    {% endif %} {%- endmacro -%} @@ -192,21 +192,22 @@

    References & Citations

    +
    {% if trackback_ping_count and trackback_ping_count > 0 %} {% endif %} diff --git a/browse/templates/base.html b/browse/templates/base.html index cafd1a5b2..4c056cb2e 100644 --- a/browse/templates/base.html +++ b/browse/templates/base.html @@ -11,6 +11,7 @@ + {%- if config['BROWSE_ANALYTICS_ENABLED'] %} + {%- endif %} diff --git a/browse/templates/category_taxonomy.html b/browse/templates/category_taxonomy.html index 41c2e6542..4a04c561e 100644 --- a/browse/templates/category_taxonomy.html +++ b/browse/templates/category_taxonomy.html @@ -3,39 +3,90 @@ {% block header_h1 %}

    {{ config['BROWSE_SITE_LABEL'] }} > category taxonomy

    {% endblock %} {# Disable login on this page for now, per classic #} {% block login_link %}{% endblock %} +{% set list_name = 'category_taxonomy' %} {%- block content %} -

    arXiv Category Taxonomy

    -
    -

    Group Name

    -
      -
    • Archive Name (Archive ID) - omitted if group consists of a single archive with the same name as the group
    • -
        -
      • Category Name (Category ID)
        Category description if available
      • -
      -
    +
    +
    +

    arXiv
    Category Taxonomy

    +
    +
    +

    Classification guide

    +
    +

    Group Name

    +
    +
    +

    Archive Name (Archive ID)

    +

    omitted if group consists of a single archive with the same name as the group

    +
    +
    +

    Category Name
    + (Category ID) +

    +
    +
    +

    Category description if available

    +
    +
    +
    +
    -{%- for group_key, group_details in groups.items()|sort if not group_details.is_test -%} -

    {{ groups[group_key].name }}

    -
      - {%- for archive_key, archive_details in archives.items()|sort(attribute='1.name') if archives[archive_key].in_group == group_key %} - {%- set archive_url = url_for('browse.archive', archive=archive_key) if archive_key != 'cs' else 'https://arxiv.org/corr' -%} - {%- set archive_name = archives[archive_key].name -%} - {%- if not (loop.first and loop.last) -%} -
    • {{ archive_name }} ({{ archive_key }})
    • - {%- endif -%} -
        - {% for category_key, category_details in categories.items()|sort(attribute='1.name') if categories[category_key].in_archive == archive_key %} -
      • {{ categories[category_key].name }} ({{ category_key }}) - {%- if 'description' in categories[category_key] -%} -
        {{ categories[category_key].description }} - {%- endif -%} -
      • - {%- endfor -%} -
      +
      + {%- for group_key, group_details in groups.items()|sort if not group_details.is_test -%} +

      {{ groups[group_key].name }}

      +
      + {%- for archive_key, archive_details in archives.items()|sort(attribute='1.name') if archives[archive_key].in_group == group_key %} +
      + {%- set archive_url = url_for('browse.archive', archive=archive_key) if archive_key != 'cs' else 'https://arxiv.org/corr' -%} + {%- set archive_name = archives[archive_key].name -%} + {%- if not (loop.first and loop.last) -%} +
      +

      {{ archive_name }}
      ({{ archive_key }})

      +
      + {%- endif -%} +
      + {% for category_key, category_details in categories.items()|sort(attribute='1.name') if categories[category_key].in_archive == archive_key %} +
      +
      +

      {{ categories[category_key].name }} ({{ category_key }})

      +
      +
      + {%- if 'description' in categories[category_key] -%} +

      {{ categories[category_key].description }}

      + {%- else -%} +

      Description coming soon

      + {%- endif -%} +
      +
      + {%- endfor -%} +
      +
      + {%- endfor -%} +
      {%- endfor -%} -
    -{%- endfor -%} - +
    + {% endblock content %} diff --git a/browse/templates/feedback_collector.html b/browse/templates/feedback_collector.html index a119d5305..7a049f62a 100644 --- a/browse/templates/feedback_collector.html +++ b/browse/templates/feedback_collector.html @@ -1,2 +1,2 @@ -Browse v0.2.9 released 2020-02-21 +Browse v0.3.0 released 2020-04-15    diff --git a/browse/templates/feedback_collector_js.html b/browse/templates/feedback_collector_js.html index 8d131dd9c..1115abb3d 100644 --- a/browse/templates/feedback_collector_js.html +++ b/browse/templates/feedback_collector_js.html @@ -9,7 +9,7 @@ }, fieldValues: { "components": ["15700"], // Jira ID for browse component - "versions": ["14311"], // Jira ID for browse-0.2.9 release + "versions": ["14312"], // Jira ID for browse-0.3.0 release "customfield_11401": window.location.href } }; diff --git a/browse/templates/mobile-menu.html b/browse/templates/mobile-menu.html new file mode 100644 index 000000000..d20fb4373 --- /dev/null +++ b/browse/templates/mobile-menu.html @@ -0,0 +1,82 @@ + + + + diff --git a/browse/templates/stats/base.html b/browse/templates/stats/base.html index e835ba930..14f94042b 100644 --- a/browse/templates/stats/base.html +++ b/browse/templates/stats/base.html @@ -8,6 +8,25 @@ {% block header_h1 %}

    {{ config['BROWSE_SITE_LABEL'] }} > stats > server usage

    {% endblock %} +{% block content_stats %} +
    +
    +

    arXiv
    {{ pagename }}

    +
    + +
    +{% endblock content_stats %} + {%- block content %} -

    See also other arXiv usage statistics.

    {% endblock content %} diff --git a/browse/templates/stats/main.html b/browse/templates/stats/main.html new file mode 100644 index 000000000..a301e4fb7 --- /dev/null +++ b/browse/templates/stats/main.html @@ -0,0 +1,47 @@ +{%- extends "stats/base.html" -%} +{% set pagename = 'Statistics' %} +{% block title %}arXiv Statistics{% endblock %} + +{% block content_stats %} + {{ super() }} +
    +

    arXiv tracks and publishes the following statistical breakdowns: arXiv's hourly usage for any given day, monthly submissions or downloads going back to the beggining of arXiv, a breakdown of submissions by subject area over time, and institutional download rankings. Click on a chart below to view in more detail.

    +
    + +
    +

    Caveats: While we have taken considerable effort to extract reliable data for all charts there are many factors which affect results. + These may include our counting methodology, excluding legitimate robot activity, and excluding data from the arXiv mirrors, among other factors. + For more information and detailed caveats view each chart page.

    + For best viewing results view charts on a tablet size or larger screen.

    +
    + + + +{% endblock content_stats %} + +{% block content %} +{{ super() }} +{% endblock content %} diff --git a/browse/templates/stats/monthly_downloads.html b/browse/templates/stats/monthly_downloads.html index 222fd5ec9..c9730bfd8 100644 --- a/browse/templates/stats/monthly_downloads.html +++ b/browse/templates/stats/monthly_downloads.html @@ -1,21 +1,35 @@ {%- extends "stats/base.html" -%} - +{% set pagename = 'Monthly Downloads' %} {% block title %}Monthly Download Rates{% endblock %} {% block header_h1 %}

    {{ config['BROWSE_SITE_LABEL'] }} > stats > monthly downloads

    {% endblock %} -{% block content %} -

    arXiv Monthly Download Rates [CSV]

    - -{% include "stats/monthly_downloads_js.html" %} +{% block content_stats %} + {{ super() }} +
    +

    The charts below display the number of downloads per month. Click and drag in the small graph to pan or zoom in on a section.

    +

    +
    +
    +
    + Download CSV +

    Total number of downloads {% if most_recent_dt %}through {{ most_recent_dt.strftime('%B %Y') }}{% endif %} = {{ "{:,}".format(total_downloads) }}

    +
    + {% include "stats/monthly_downloads_js.html" %} +
    +
    +

    Caveats: While we have attempted to extract download data representing unique full-text downloads by real users, there are many factors which affect accuracy. + These factors include:

    +
      +
    1. the data is from the main arXiv site and the arXiv mirrors, though some mirror data is incomplete.
    2. +
    3. only web downloads are included (and not FTP or email "downloads" that were formerly supported).
    4. +
    5. we have counted downloads according to the COUNTER algorithm which excludes rapid repeat downloads.
    6. +
    7. we have attempted to identify and remove robot or automated downloads from the count (false positives lead to undercounting, failing to identify robots leads to overcounting).
    8. + +
    +
    +{% endblock content_stats %} -

    Blue: Number of downloads per month.
    Click and drag in the small graph below to pan or zoom.

    -

    Total number of downloads {% if most_recent_dt %}through {{ most_recent_dt.strftime('%B %Y') }}{% endif %} = {{ "{:,}".format(total_downloads) }}

    -

    - Caveats: While we have attempted to extract download data representing unique full-text downloads by real users, there are many factors which affect accuracy. These factors include: 1) the data is from the main arXiv site and the - arXiv mirrors, though some mirror data is incomplete; 2) only web downloads are included (and not FTP or email "downloads" that were formerly supported); 3) we have counted downloads according to the COUNTER algorithm which excludes - rapid repeat downloads; 4) we have attempted to identify and remove robot or automated downloads from the count (false positives lead to undercounting, failing to identify robots leads to overcounting); 5) data prior to 2009 has not been cleaned - with as much care as later data, it shows trends nonetheless. -

    +{% block content %} {{ super() }} {% endblock content %} diff --git a/browse/templates/stats/monthly_downloads_js.html b/browse/templates/stats/monthly_downloads_js.html index 84833c98d..160a033a2 100644 --- a/browse/templates/stats/monthly_downloads_js.html +++ b/browse/templates/stats/monthly_downloads_js.html @@ -1,57 +1,80 @@ diff --git a/browse/templates/stats/monthly_submissions.html b/browse/templates/stats/monthly_submissions.html index 216f26fb8..cf56360b1 100644 --- a/browse/templates/stats/monthly_submissions.html +++ b/browse/templates/stats/monthly_submissions.html @@ -1,16 +1,26 @@ {%- extends "stats/base.html" -%} - +{% set pagename = 'Monthly Submissions' %} {% block title %}Monthly Submissions{% endblock %} {% block header_h1 %}

    {{ config['BROWSE_SITE_LABEL'] }} > stats > monthly submission rates

    {% endblock %} +{% block content_stats %} + {{ super() }} +
    +

    This chart displays the number of new submissions received during each month since {{ arxiv_start_dt.strftime('%B %Y') }} (after {{ "%.1f"|format(arxiv_age_years|float) }} years). Hover over the graph to see the exact count for a given month.

    +
    +
    +
    + Download CSV +

    Total number of submissions as of {{ current_dt.strftime('%B %-d, %Y') }} = {{ "{:,}".format(num_submissions) }}.

    +
    + {% include "stats/monthly_submissions_js.html" %} +
    +
    +

    The total number of submissions excludes {{ "{:,}".format(num_migrated) }} articles that were migrated to arXiv rather than being submitted directly, + and includes {{ "{:,}".format(num_deleted) }} articles that have been deleted. The total number of articles available is {{ "{:,}".format(num_submissions_adjusted) }}

    +
    +{% endblock content_stats %} + {% block content %} -

    arXiv Monthly Submission Rates [CSV]

    -{% include "stats/monthly_submissions_js.html" %} -

    Blue: Number of new submissions received during each month since {{ arxiv_start_dt.strftime('%B %Y') }}.
    - Hover over the graph to see the exact count for a given month.

    -

    Total number of submissions shown in graph as of {{ current_dt.strftime('%B %-d, %Y') }} (after {{ "%.1f"|format(arxiv_age_years|float) }} years) = {{ "{:,}".format(num_submissions) }}

    -

    - The total number of submissions excludes {{ "{:,}".format(num_migrated) }} articles that were migrated to arXiv rather than being submitted directly, and includes {{ "{:,}".format(num_deleted) }} articles that have been deleted. The total number of articles available is {{ "{:,}".format(num_submissions_adjusted) }}. -

    {{ super() }} {% endblock content %} diff --git a/browse/templates/stats/monthly_submissions_js.html b/browse/templates/stats/monthly_submissions_js.html index b8f9bd946..8d4d3d876 100644 --- a/browse/templates/stats/monthly_submissions_js.html +++ b/browse/templates/stats/monthly_submissions_js.html @@ -2,68 +2,82 @@ svg { font: 10px sans-serif; } - .axis path { fill: none; } - .axis line { stroke: #000; } - .axis .minor line { stroke: #777; stroke-dasharray: 2, 2; } - .x.axis line { stroke: lightgrey; } - .x.axis .minor { stroke-opacity: .5; } - .y.axis line { stroke: lightgrey; stroke-dasharray: 2, 2; } - .y.axis .minor { stroke-opacity: .5; } - .line { fill: none; - stroke: steelblue; + stroke: #d9af0c;/*medium yellow*/ stroke-width: 1.5px; } - .overlay { fill: none; pointer-events: all; } - .focus circle { fill: none; - stroke: red; + stroke: #994d04;/*darkest orange*/ + } + .focus { + color: red; } - .area { - fill: lightsteelblue; stroke-width: 0; } diff --git a/browse/templates/stats/today.html b/browse/templates/stats/today.html index 3f882af2c..70b6d6c14 100644 --- a/browse/templates/stats/today.html +++ b/browse/templates/stats/today.html @@ -1,16 +1,31 @@ {%- extends "stats/base.html" -%} - +{% set pagename = 'Hourly Usage Stats' %} {% block title %}hourly usage{% endblock %} -{% block content %} +{% block content_stats %} + {{ super() }} +
    +

    arXiv Web Server Usage from + {{ requested_dt.strftime('%A, %B %-d, %Y') }} + ({{ config['BROWSE_SITE_LABEL'] }} site only) + Current local time is {{ current_dt.strftime('%A, %B, %-d, %Y %H:%M:%S') }} US/Eastern

    +
    +
    +
    + Download CSV + {% if normal_count > 0 %} +

    Total number of connections = {{ "{:,}".format(normal_count) }} (+{{ "{:,}".format(admin_count) }} local & administrative connections)

    + {% else %} +

    No data currently available.

    + {% endif %} +
    + {% include "stats/today_js.html" %} +
    +
    +

    Current local time is {{ current_dt.strftime('%A, %B, %-d, %Y %H:%M:%S') }} US/Eastern

    +
    +{% endblock content_stats %} -

    arXiv Web Server Usage from {{ requested_dt.strftime('%A, %B %-d, %Y') }} ({{ config['BROWSE_SITE_LABEL'] }} site only) [CSV]

    -{% if normal_count > 0 %} -{% include "stats/today_js.html" %} -

    Total number of connections = {{ "{:,}".format(normal_count) }} (+{{ "{:,}".format(admin_count) }} local & administrative connections) -{% else %} -

    No data currently available. -{% endif %} -
    Current local time is {{ current_dt.strftime('%A, %B, %-d, %Y %H:%M:%S') }} US/Eastern

    +{% block content %} {{ super() }} {% endblock content %} diff --git a/browse/templates/stats/today_js.html b/browse/templates/stats/today_js.html index 0cbc2c60f..bb897a071 100644 --- a/browse/templates/stats/today_js.html +++ b/browse/templates/stats/today_js.html @@ -1,20 +1,42 @@