mirror of
https://github.com/ilri/dspace-statistics-api.git
synced 2025-05-10 23:26:02 +02:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
963961354b
|
@ -1,6 +1,7 @@
|
||||
dist: bionic
|
||||
language: python
|
||||
python:
|
||||
- "3.5"
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
|
16
CHANGELOG.md
16
CHANGELOG.md
@ -4,27 +4,13 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.2.1] - 2020-03-02
|
||||
### Changed
|
||||
- Help text in API docs should reference UUIDs
|
||||
- Sample SQL file for tests should use UUIDs
|
||||
|
||||
## [1.2.0] - 2020-03-02
|
||||
### Changed
|
||||
- Remove Python 3.5 from TravisCI because black requires Python >= 3.6
|
||||
- Adapt API for DSpace 6+ UUIDs
|
||||
- This requires dropping the statistics database and re-indexing
|
||||
|
||||
### Updated
|
||||
- Run pipenv update, bringing requests 2.23.0 and pytest 5.3.5
|
||||
|
||||
## [1.1.1] - 2019-11-27
|
||||
### Added
|
||||
- Configuration for automatic sorting of imports with isort
|
||||
- Configuration for automatic code formatting with black
|
||||
|
||||
### Updated
|
||||
- Run pipenv update, bringing psycopg2 2.8.4, requests 2.22.0, pytest 5.3.1,
|
||||
- Run pipenv update, bringing psycogpg 2.8.3, requests 2.22.0, pytest 5.3.1,
|
||||
and gunicorn 20.0.4
|
||||
|
||||
### Changed
|
||||
|
3
Pipfile
3
Pipfile
@ -14,8 +14,7 @@ ipython = "*"
|
||||
"flake8" = "*"
|
||||
pytest = "*"
|
||||
isort = "*"
|
||||
black = "==19.10b0"
|
||||
pytest-clarity = "==0.3.0a0"
|
||||
black = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.8"
|
||||
|
208
Pipfile.lock
generated
208
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "be968d3927117f9ac14b9a6f60d6147b2d57ce55f694f34ed6e53abcd2197823"
|
||||
"sha256": "5e3f3e80bd92780ab66a80a83156001e50362a0d6195b1a304a15dc0c645c562"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@ -18,10 +18,10 @@
|
||||
"default": {
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||
"sha256:e4f3620cfea4f83eedc95b24abd9cd56f3c4b146dd0177e83a21b4eb49e21e50",
|
||||
"sha256:fd7c7c74727ddcf00e9acd26bba8da604ffec95bf1c2144e67aff7a8b50e6cef"
|
||||
],
|
||||
"version": "==2019.11.28"
|
||||
"version": "==2019.9.11"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
@ -60,10 +60,10 @@
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
],
|
||||
"version": "==2.9"
|
||||
"version": "==2.8"
|
||||
},
|
||||
"psycopg2-binary": {
|
||||
"hashes": [
|
||||
@ -105,18 +105,18 @@
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.23.0"
|
||||
"version": "==2.22.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
||||
"sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293",
|
||||
"sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745"
|
||||
],
|
||||
"version": "==1.25.8"
|
||||
"version": "==1.25.7"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
@ -158,10 +158,10 @@
|
||||
},
|
||||
"decorator": {
|
||||
"hashes": [
|
||||
"sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760",
|
||||
"sha256:e3a62f0520172440ca0dcc823749319382e377f37f140a0b99ef45fecb84bfe7"
|
||||
"sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce",
|
||||
"sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"
|
||||
],
|
||||
"version": "==4.4.2"
|
||||
"version": "==4.4.1"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
@ -180,11 +180,11 @@
|
||||
},
|
||||
"ipython": {
|
||||
"hashes": [
|
||||
"sha256:ca478e52ae1f88da0102360e57e528b92f3ae4316aabac80a2cd7f7ab2efb48a",
|
||||
"sha256:eb8d075de37f678424527b5ef6ea23f7b80240ca031c2dd6de5879d687a65333"
|
||||
"sha256:dfd303b270b7b5232b3d08bd30ec6fd685d8a58cabd54055e3d69d8f029f7280",
|
||||
"sha256:ed7ebe1cba899c1c3ccad6f7f1c2d2369464cc77dba8eebc65e2043e19cda995"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==7.13.0"
|
||||
"version": "==7.9.0"
|
||||
},
|
||||
"ipython-genutils": {
|
||||
"hashes": [
|
||||
@ -203,10 +203,10 @@
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2",
|
||||
"sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"
|
||||
"sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27",
|
||||
"sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e"
|
||||
],
|
||||
"version": "==0.16.0"
|
||||
"version": "==0.15.1"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
@ -217,39 +217,38 @@
|
||||
},
|
||||
"more-itertools": {
|
||||
"hashes": [
|
||||
"sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c",
|
||||
"sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"
|
||||
"sha256:409cd48d4db7052af495b09dec721011634af3753ae1ef92d2b32f73a745f832",
|
||||
"sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4"
|
||||
],
|
||||
"version": "==8.2.0"
|
||||
"version": "==7.2.0"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73",
|
||||
"sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"
|
||||
"sha256:28b924174df7a2fa32c1953825ff29c61e2f5e082343165438812f00d3a7fc47",
|
||||
"sha256:d9551545c6d761f3def1677baf08ab2a3ca17c56879e70fecba2fc4dde4ed108"
|
||||
],
|
||||
"version": "==20.1"
|
||||
"version": "==19.2"
|
||||
},
|
||||
"parso": {
|
||||
"hashes": [
|
||||
"sha256:0c5659e0c6eba20636f99a04f469798dca8da279645ce5c387315b2c23912157",
|
||||
"sha256:8515fc12cfca6ee3aa59138741fc5624d62340c97e401c74875769948d4f2995"
|
||||
"sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc",
|
||||
"sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c"
|
||||
],
|
||||
"version": "==0.6.2"
|
||||
"version": "==0.5.1"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
|
||||
"sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
|
||||
"sha256:e285ccc8b0785beadd4c18e5708b12bb8fcf529a1e61215b3feff1d1e559ea5c"
|
||||
],
|
||||
"version": "==0.7.0"
|
||||
"version": "==0.6.0"
|
||||
},
|
||||
"pexpect": {
|
||||
"hashes": [
|
||||
"sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937",
|
||||
"sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"
|
||||
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
|
||||
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
|
||||
],
|
||||
"markers": "sys_platform != 'win32'",
|
||||
"version": "==4.8.0"
|
||||
"version": "==4.7.0"
|
||||
},
|
||||
"pickleshare": {
|
||||
"hashes": [
|
||||
@ -267,10 +266,11 @@
|
||||
},
|
||||
"prompt-toolkit": {
|
||||
"hashes": [
|
||||
"sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e",
|
||||
"sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"
|
||||
"sha256:46642344ce457641f28fc9d1c9ca939b63dadf8df128b86f1b9860e59c73a5e4",
|
||||
"sha256:e7f8af9e3d70f514373bf41aa51bc33af12a6db3f71461ea47fea985defb2c31",
|
||||
"sha256:f15af68f66e664eaa559d4ac8a928111eebd5feda0c11738b5998045224829db"
|
||||
],
|
||||
"version": "==3.0.3"
|
||||
"version": "==2.0.10"
|
||||
},
|
||||
"ptyprocess": {
|
||||
"hashes": [
|
||||
@ -281,10 +281,10 @@
|
||||
},
|
||||
"py": {
|
||||
"hashes": [
|
||||
"sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
|
||||
"sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
|
||||
"sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
|
||||
"sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
|
||||
],
|
||||
"version": "==1.8.1"
|
||||
"version": "==1.8.0"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
@ -302,71 +302,50 @@
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
|
||||
"sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
|
||||
"sha256:83ec6c6133ca6b529b7ff5aa826328fd14b5bb02a58c37f4f06384e96a0f94ab",
|
||||
"sha256:b7949de3d396836085fea596998b135a22610bbcc4f2abfe9e448e44cbc58388"
|
||||
],
|
||||
"version": "==2.5.2"
|
||||
"version": "==2.5.1"
|
||||
},
|
||||
"pyparsing": {
|
||||
"hashes": [
|
||||
"sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
|
||||
"sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
|
||||
"sha256:20f995ecd72f2a1f4bf6b072b63b22e2eb457836601e76d6e5dfcd75436acc1f",
|
||||
"sha256:4ca62001be367f01bd3e92ecbb79070272a9d4964dce6a48a82ff0b8bc7e683a"
|
||||
],
|
||||
"version": "==2.4.6"
|
||||
"version": "==2.4.5"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d",
|
||||
"sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"
|
||||
"sha256:63344a2e3bce2e4d522fd62b4fdebb647c019f1f9e4ca075debbd13219db4418",
|
||||
"sha256:f67403f33b2b1d25a6756184077394167fe5e2f9d8bdaab30707d19ccec35427"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.3.5"
|
||||
},
|
||||
"pytest-clarity": {
|
||||
"hashes": [
|
||||
"sha256:5cc99e3d9b7969dfe17e5f6072d45a917c59d363b679686d3c958a1ded2e4dcf"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.3.0a0"
|
||||
"version": "==5.3.1"
|
||||
},
|
||||
"regex": {
|
||||
"hashes": [
|
||||
"sha256:01b2d70cbaed11f72e57c1cfbaca71b02e3b98f739ce33f5f26f71859ad90431",
|
||||
"sha256:046e83a8b160aff37e7034139a336b660b01dbfe58706f9d73f5cdc6b3460242",
|
||||
"sha256:113309e819634f499d0006f6200700c8209a2a8bf6bd1bdc863a4d9d6776a5d1",
|
||||
"sha256:200539b5124bc4721247a823a47d116a7a23e62cc6695744e3eb5454a8888e6d",
|
||||
"sha256:25f4ce26b68425b80a233ce7b6218743c71cf7297dbe02feab1d711a2bf90045",
|
||||
"sha256:269f0c5ff23639316b29f31df199f401e4cb87529eafff0c76828071635d417b",
|
||||
"sha256:5de40649d4f88a15c9489ed37f88f053c15400257eeb18425ac7ed0a4e119400",
|
||||
"sha256:7f78f963e62a61e294adb6ff5db901b629ef78cb2a1cfce3cf4eeba80c1c67aa",
|
||||
"sha256:82469a0c1330a4beb3d42568f82dffa32226ced006e0b063719468dcd40ffdf0",
|
||||
"sha256:8c2b7fa4d72781577ac45ab658da44c7518e6d96e2a50d04ecb0fd8f28b21d69",
|
||||
"sha256:974535648f31c2b712a6b2595969f8ab370834080e00ab24e5dbb9d19b8bfb74",
|
||||
"sha256:99272d6b6a68c7ae4391908fc15f6b8c9a6c345a46b632d7fdb7ef6c883a2bbb",
|
||||
"sha256:9b64a4cc825ec4df262050c17e18f60252cdd94742b4ba1286bcfe481f1c0f26",
|
||||
"sha256:9e9624440d754733eddbcd4614378c18713d2d9d0dc647cf9c72f64e39671be5",
|
||||
"sha256:9ff16d994309b26a1cdf666a6309c1ef51ad4f72f99d3392bcd7b7139577a1f2",
|
||||
"sha256:b33ebcd0222c1d77e61dbcd04a9fd139359bded86803063d3d2d197b796c63ce",
|
||||
"sha256:bba52d72e16a554d1894a0cc74041da50eea99a8483e591a9edf1025a66843ab",
|
||||
"sha256:bed7986547ce54d230fd8721aba6fd19459cdc6d315497b98686d0416efaff4e",
|
||||
"sha256:c7f58a0e0e13fb44623b65b01052dae8e820ed9b8b654bb6296bc9c41f571b70",
|
||||
"sha256:d58a4fa7910102500722defbde6e2816b0372a4fcc85c7e239323767c74f5cbc",
|
||||
"sha256:f1ac2dc65105a53c1c2d72b1d3e98c2464a133b4067a51a3d2477b28449709a0"
|
||||
"sha256:15454b37c5a278f46f7aa2d9339bda450c300617ca2fca6558d05d870245edc7",
|
||||
"sha256:1ad40708c255943a227e778b022c6497c129ad614bb7a2a2f916e12e8a359ee7",
|
||||
"sha256:5e00f65cc507d13ab4dfa92c1232d004fa202c1d43a32a13940ab8a5afe2fb96",
|
||||
"sha256:604dc563a02a74d70ae1f55208ddc9bfb6d9f470f6d1a5054c4bd5ae58744ab1",
|
||||
"sha256:720e34a539a76a1fedcebe4397290604cc2bdf6f81eca44adb9fb2ea071c0c69",
|
||||
"sha256:7caf47e4a9ac6ef08cabd3442cc4ca3386db141fb3c8b2a7e202d0470028e910",
|
||||
"sha256:7faf534c1841c09d8fefa60ccde7b9903c9b528853ecf41628689793290ca143",
|
||||
"sha256:b4e0406d822aa4993ac45072a584d57aa4931cf8288b5455bbf30c1d59dbad59",
|
||||
"sha256:c31eaf28c6fe75ea329add0022efeed249e37861c19681960f99bbc7db981fb2",
|
||||
"sha256:c7393597191fc2043c744db021643549061e12abe0b3ff5c429d806de7b93b66",
|
||||
"sha256:d2b302f8cdd82c8f48e9de749d1d17f85ce9a0f082880b9a4859f66b07037dc6",
|
||||
"sha256:e3d8dd0ec0ea280cf89026b0898971f5750a7bd92cb62c51af5a52abd020054a",
|
||||
"sha256:ec032cbfed59bd5a4b8eab943c310acfaaa81394e14f44454ad5c9eba4f24a74"
|
||||
],
|
||||
"version": "==2020.2.20"
|
||||
"version": "==2019.11.1"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
||||
],
|
||||
"version": "==1.14.0"
|
||||
},
|
||||
"termcolor": {
|
||||
"hashes": [
|
||||
"sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"
|
||||
],
|
||||
"version": "==1.1.0"
|
||||
"version": "==1.13.0"
|
||||
},
|
||||
"toml": {
|
||||
"hashes": [
|
||||
@ -384,36 +363,35 @@
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
|
||||
"sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
|
||||
"sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
|
||||
"sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
|
||||
"sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
|
||||
"sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
|
||||
"sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
|
||||
"sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
|
||||
"sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
|
||||
"sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
|
||||
"sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
|
||||
"sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
|
||||
"sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
|
||||
"sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
|
||||
"sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
|
||||
"sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
|
||||
"sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
|
||||
"sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
|
||||
"sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
|
||||
"sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
|
||||
"sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
|
||||
"sha256:1170afa46a3799e18b4c977777ce137bb53c7485379d9706af8a59f2ea1aa161",
|
||||
"sha256:18511a0b3e7922276346bcb47e2ef9f38fb90fd31cb9223eed42c85d1312344e",
|
||||
"sha256:262c247a82d005e43b5b7f69aff746370538e176131c32dda9cb0f324d27141e",
|
||||
"sha256:2b907eb046d049bcd9892e3076c7a6456c93a25bebfe554e931620c90e6a25b0",
|
||||
"sha256:354c16e5babd09f5cb0ee000d54cfa38401d8b8891eefa878ac772f827181a3c",
|
||||
"sha256:48e5b1e71f25cfdef98b013263a88d7145879fbb2d5185f2a0c79fa7ebbeae47",
|
||||
"sha256:4e0b70c6fc4d010f8107726af5fd37921b666f5b31d9331f0bd24ad9a088e631",
|
||||
"sha256:630968c5cdee51a11c05a30453f8cd65e0cc1d2ad0d9192819df9978984529f4",
|
||||
"sha256:66480f95b8167c9c5c5c87f32cf437d585937970f3fc24386f313a4c97b44e34",
|
||||
"sha256:71211d26ffd12d63a83e079ff258ac9d56a1376a25bc80b1cdcdf601b855b90b",
|
||||
"sha256:7954560051331d003b4e2b3eb822d9dd2e376fa4f6d98fee32f452f52dd6ebb2",
|
||||
"sha256:838997f4310012cf2e1ad3803bce2f3402e9ffb71ded61b5ee22617b3a7f6b6e",
|
||||
"sha256:95bd11af7eafc16e829af2d3df510cecfd4387f6453355188342c3e79a2ec87a",
|
||||
"sha256:bc6c7d3fa1325a0c6613512a093bc2a2a15aeec350451cbdf9e1d4bffe3e3233",
|
||||
"sha256:cc34a6f5b426748a507dd5d1de4c1978f2eb5626d51326e43280941206c209e1",
|
||||
"sha256:d755f03c1e4a51e9b24d899561fec4ccaf51f210d52abdf8c07ee2849b212a36",
|
||||
"sha256:d7c45933b1bdfaf9f36c579671fec15d25b06c8398f113dab64c18ed1adda01d",
|
||||
"sha256:d896919306dd0aa22d0132f62a1b78d11aaf4c9fc5b3410d3c666b818191630a",
|
||||
"sha256:fdc1c9bbf79510b76408840e009ed65958feba92a88833cdceecff93ae8fff66",
|
||||
"sha256:ffde2fbfad571af120fcbfbbc61c72469e72f550d676c3342492a9dfdefb8f12"
|
||||
],
|
||||
"version": "==1.4.1"
|
||||
"version": "==1.4.0"
|
||||
},
|
||||
"wcwidth": {
|
||||
"hashes": [
|
||||
"sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603",
|
||||
"sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
|
||||
"sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
|
||||
"sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
|
||||
],
|
||||
"version": "==0.1.8"
|
||||
"version": "==0.1.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
15
README.md
15
README.md
@ -1,8 +1,5 @@
|
||||
# DSpace Statistics API [](https://travis-ci.org/ilri/dspace-statistics-api) [](https://builds.sr.ht/~alanorth/dspace-statistics-api?)
|
||||
DSpace stores item view and download events in a Solr "statistics" core. This information is available for use in the various DSpace user interfaces, but is not exposed externally via any APIs. The DSpace 4/5/6 [REST API](https://wiki.duraspace.org/display/DSDOC5x/REST+API), for example, only exposes information about communities, collections, item metadata, and bitstreams.
|
||||
|
||||
- If your DSpace is version 4 or 5, use [dspace-statistics-api v1.1.1](https://github.com/ilri/dspace-statistics-api/releases/tag/v1.1.1)
|
||||
- If your DSpace is version 6+, use [dspace-statistics-api v1.2.0 or greater](https://github.com/ilri/dspace-statistics-api/releases/tag/v1.2.0)
|
||||
# DSpace Statistics API [](https://travis-ci.com/ilri/dspace-statistics-api) [](https://builds.sr.ht/~alanorth/dspace-statistics-api?)
|
||||
DSpace stores item view and download events in a Solr "statistics" core. This information is available for use in the various DSpace user interfaces, but is not exposed externally via any APIs. The DSpace 4/5 [REST API](https://wiki.duraspace.org/display/DSDOC5x/REST+API), for example, only exposes information about communities, collections, item metadata, and bitstreams.
|
||||
|
||||
This project contains an indexer and a [Falcon-based](https://falcon.readthedocs.io/) web application to make the statistics available via a simple REST API. You can read more about the Solr queries used to gather the item view and download statistics on the [DSpace wiki](https://wiki.duraspace.org/display/DSPACE/Solr).
|
||||
|
||||
@ -12,7 +9,7 @@ If you use the DSpace Statistics API please cite:
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.6+
|
||||
- Python 3.5+
|
||||
- PostgreSQL version 9.5+ (due to [`UPSERT` support](https://wiki.postgresql.org/wiki/UPSERT))
|
||||
- DSpace with [Solr usage statistics enabled](https://wiki.duraspace.org/display/DSDOC5x/SOLR+Statistics) (tested with 5.x)
|
||||
|
||||
@ -81,9 +78,9 @@ The API exposes the following endpoints:
|
||||
|
||||
- GET `/` — return a basic API documentation page.
|
||||
- GET `/items` — return views and downloads for all items that Solr knows about¹. Accepts `limit` and `page` query parameters for pagination of results (`limit` must be an integer between 1 and 100, and `page` must be an integer greater than or equal to 0).
|
||||
- GET `/item/id` — return views and downloads for a single item (`id` must be a UUID). Returns HTTP 404 if an item id is not found.
|
||||
- GET `/item/id` — return views and downloads for a single item (`id` must be a positive integer). Returns HTTP 404 if an item id is not found.
|
||||
|
||||
The item id is the *internal* uuid for an item. You can get these from the standard DSpace REST API.
|
||||
The item id is the *internal* id for an item. You can get these from the standard DSpace REST API.
|
||||
|
||||
¹ We are querying the Solr statistics core, which technically only knows about items that have either views or downloads. If an item is not present here you can assume it has zero views and zero downloads, but not necessarily that it does not exist in the repository.
|
||||
|
||||
@ -94,6 +91,8 @@ The item id is the *internal* uuid for an item. You can get these from the stand
|
||||
- Use JSON in PostgreSQL
|
||||
- Add top items endpoint, perhaps `/top/items` or `/items/top`?
|
||||
- Make community and collection stats available
|
||||
- Support [DSpace 6 UUIDs](https://jira.duraspace.org/browse/DS-1782)
|
||||
- Switch to [Python 3.6+ f-string syntax](https://realpython.com/python-f-strings/)
|
||||
- Check IDs in database to see if they are deleted...
|
||||
|
||||
## License
|
||||
|
@ -27,10 +27,11 @@ class AllItemsResource:
|
||||
cursor.execute("SELECT COUNT(id) FROM items")
|
||||
pages = round(cursor.fetchone()[0] / limit)
|
||||
|
||||
# get statistics and use limit and offset to page through results
|
||||
# get statistics, ordered by id, and use limit and offset to page through results
|
||||
cursor.execute(
|
||||
"SELECT id, views, downloads FROM items LIMIT %s OFFSET %s",
|
||||
[limit, offset],
|
||||
"SELECT id, views, downloads FROM items ORDER BY id ASC LIMIT {} OFFSET {}".format(
|
||||
limit, offset
|
||||
)
|
||||
)
|
||||
|
||||
# create a list to hold dicts of item stats
|
||||
@ -40,7 +41,7 @@ class AllItemsResource:
|
||||
for item in cursor:
|
||||
statistics.append(
|
||||
{
|
||||
"id": str(item["id"]),
|
||||
"id": item["id"],
|
||||
"views": item["views"],
|
||||
"downloads": item["downloads"],
|
||||
}
|
||||
@ -60,30 +61,26 @@ class ItemResource:
|
||||
def on_get(self, req, resp, item_id):
|
||||
"""Handles GET requests"""
|
||||
|
||||
import psycopg2.extras
|
||||
|
||||
# Adapt Python’s uuid.UUID type to PostgreSQL’s uuid
|
||||
# See: https://www.psycopg.org/docs/extras.html
|
||||
psycopg2.extras.register_uuid()
|
||||
|
||||
with DatabaseManager() as db:
|
||||
db.set_session(readonly=True)
|
||||
|
||||
with db.cursor() as cursor:
|
||||
cursor = db.cursor()
|
||||
cursor.execute(
|
||||
"SELECT views, downloads FROM items WHERE id=%s", [str(item_id)]
|
||||
"SELECT views, downloads FROM items WHERE id={}".format(item_id)
|
||||
)
|
||||
if cursor.rowcount == 0:
|
||||
raise falcon.HTTPNotFound(
|
||||
title="Item not found",
|
||||
description=f'The item with id "{str(item_id)}" was not found.',
|
||||
description='The item with id "{}" was not found.'.format(
|
||||
item_id
|
||||
),
|
||||
)
|
||||
else:
|
||||
results = cursor.fetchone()
|
||||
|
||||
statistics = {
|
||||
"id": str(item_id),
|
||||
"id": item_id,
|
||||
"views": results["views"],
|
||||
"downloads": results["downloads"],
|
||||
}
|
||||
@ -94,6 +91,6 @@ class ItemResource:
|
||||
api = application = falcon.API()
|
||||
api.add_route("/", RootResource())
|
||||
api.add_route("/items", AllItemsResource())
|
||||
api.add_route("/item/{item_id:uuid}", ItemResource())
|
||||
api.add_route("/item/{item_id:int}", ItemResource())
|
||||
|
||||
# vim: set sw=4 ts=4 expandtab:
|
||||
|
@ -15,7 +15,9 @@ class DatabaseManager:
|
||||
"""Manage database connection."""
|
||||
|
||||
def __init__(self):
|
||||
self._connection_uri = f"dbname={DATABASE_NAME} user={DATABASE_USER} password={DATABASE_PASS} host={DATABASE_HOST} port={DATABASE_PORT}"
|
||||
self._connection_uri = "dbname={} user={} password={} host={} port={}".format(
|
||||
DATABASE_NAME, DATABASE_USER, DATABASE_PASS, DATABASE_HOST, DATABASE_PORT
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
try:
|
||||
|
@ -10,10 +10,10 @@
|
||||
<ul>
|
||||
<li>GET <code>/</code> — return a basic API documentation page.</li>
|
||||
<li>GET <code>/items</code> — return views and downloads for all items that Solr knows about¹. Accepts <code>limit</code> and <code>page</code> query parameters for pagination of results (<code>limit</code> must be an integer between 1 and 100, and <code>page</code> must be an integer greater than or equal to 0).</li>
|
||||
<li>GET <code>/item/id</code> — return views and downloads for a single item (<code>id</code> must be a UUID). Returns HTTP 404 if an item id is not found.</li>
|
||||
<li>GET <code>/item/id</code> — return views and downloads for a single item (<code>id</code> must be a positive integer). Returns HTTP 404 if an item id is not found.</li>
|
||||
</ul>
|
||||
|
||||
<p>The item id is the <em>internal</em> uuid for an item. You can get these from the standard DSpace REST API.</p>
|
||||
<p>The item id is the <em>internal</em> id for an item. You can get these from the standard DSpace REST API.</p>
|
||||
|
||||
<p>¹ We are querying the Solr statistics core, which technically only knows about items that have either views or downloads. If an item is not present here you can assume it has zero views and zero downloads, but not necessarily that it does not exist in the repository.</code>
|
||||
</body>
|
||||
|
@ -70,13 +70,13 @@ def get_statistics_shards():
|
||||
|
||||
if len(statistics_core_years) > 0:
|
||||
# Begin building a string of shards starting with the default one
|
||||
shards = f"{SOLR_SERVER}/statistics"
|
||||
shards = "{}/statistics".format(SOLR_SERVER)
|
||||
|
||||
for core in statistics_core_years:
|
||||
# Create a comma-separated list of shards to pass to our Solr query
|
||||
#
|
||||
# See: https://wiki.apache.org/solr/DistributedSearch
|
||||
shards += f",{SOLR_SERVER}/{core}"
|
||||
shards += ",{}/{}".format(SOLR_SERVER, core)
|
||||
|
||||
# Return the string of shards, which may actually be empty. Solr doesn't
|
||||
# seem to mind if the shards query parameter is empty and I haven't seen
|
||||
@ -134,7 +134,9 @@ def index_views():
|
||||
while results_current_page <= results_num_pages:
|
||||
# "pages" are zero based, but one based is more human readable
|
||||
print(
|
||||
f"Indexing item views (page {results_current_page + 1} of {results_num_pages + 1})"
|
||||
"Indexing item views (page {} of {})".format(
|
||||
results_current_page + 1, results_num_pages + 1
|
||||
)
|
||||
)
|
||||
|
||||
solr_query_params = {
|
||||
@ -217,7 +219,9 @@ def index_downloads():
|
||||
while results_current_page <= results_num_pages:
|
||||
# "pages" are zero based, but one based is more human readable
|
||||
print(
|
||||
f"Indexing item downloads (page {results_current_page + 1} of {results_num_pages + 1})"
|
||||
"Indexing item downloads (page {} of {})".format(
|
||||
results_current_page + 1, results_num_pages + 1
|
||||
)
|
||||
)
|
||||
|
||||
solr_query_params = {
|
||||
@ -260,7 +264,7 @@ with DatabaseManager() as db:
|
||||
# create table to store item views and downloads
|
||||
cursor.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS items
|
||||
(id UUID PRIMARY KEY, views INT DEFAULT 0, downloads INT DEFAULT 0)"""
|
||||
(id INT PRIMARY KEY, views INT DEFAULT 0, downloads INT DEFAULT 0)"""
|
||||
)
|
||||
|
||||
# commit the table creation before closing the database connection
|
||||
|
@ -1,37 +1,27 @@
|
||||
-i https://pypi.org/simple
|
||||
appdirs==1.4.3
|
||||
attrs==19.3.0
|
||||
backcall==0.1.0
|
||||
black==19.10b0
|
||||
click==7.0
|
||||
decorator==4.4.2
|
||||
decorator==4.4.1
|
||||
entrypoints==0.3
|
||||
flake8==3.7.9
|
||||
ipython-genutils==0.2.0
|
||||
ipython==7.13.0
|
||||
isort==4.3.21
|
||||
jedi==0.16.0
|
||||
ipython==7.9.0
|
||||
jedi==0.15.1
|
||||
mccabe==0.6.1
|
||||
more-itertools==8.2.0
|
||||
packaging==20.1
|
||||
parso==0.6.2
|
||||
pathspec==0.7.0
|
||||
pexpect==4.8.0 ; sys_platform != 'win32'
|
||||
more-itertools==7.2.0
|
||||
packaging==19.2
|
||||
parso==0.5.1
|
||||
pexpect==4.7.0 ; sys_platform != 'win32'
|
||||
pickleshare==0.7.5
|
||||
pluggy==0.13.1
|
||||
prompt-toolkit==3.0.3
|
||||
prompt-toolkit==2.0.10
|
||||
ptyprocess==0.6.0
|
||||
py==1.8.1
|
||||
py==1.8.0
|
||||
pycodestyle==2.5.0
|
||||
pyflakes==2.1.1
|
||||
pygments==2.5.2
|
||||
pyparsing==2.4.6
|
||||
pytest-clarity==0.3.0a0
|
||||
pytest==5.3.5
|
||||
regex==2020.2.20
|
||||
six==1.14.0
|
||||
termcolor==1.1.0
|
||||
toml==0.10.0
|
||||
pygments==2.5.1
|
||||
pyparsing==2.4.5
|
||||
pytest==5.3.1
|
||||
six==1.13.0
|
||||
traitlets==4.3.3
|
||||
typed-ast==1.4.1
|
||||
wcwidth==0.1.8
|
||||
wcwidth==0.1.7
|
||||
|
@ -1,9 +1,9 @@
|
||||
-i https://pypi.org/simple
|
||||
certifi==2019.11.28
|
||||
certifi==2019.9.11
|
||||
chardet==3.0.4
|
||||
falcon==2.0.0
|
||||
gunicorn==20.0.4
|
||||
idna==2.9
|
||||
idna==2.8
|
||||
psycopg2-binary==2.8.4
|
||||
requests==2.23.0
|
||||
urllib3==1.25.8
|
||||
requests==2.22.0
|
||||
urllib3==1.25.7
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -11,57 +11,57 @@ def client():
|
||||
|
||||
|
||||
def test_get_docs(client):
|
||||
"""Test requesting the documentation at the root."""
|
||||
'''Test requesting the documentation at the root.'''
|
||||
|
||||
response = client.simulate_get("/")
|
||||
response = client.simulate_get('/')
|
||||
|
||||
assert isinstance(response.content, bytes)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_item(client):
|
||||
"""Test requesting a single item."""
|
||||
'''Test requesting a single item.'''
|
||||
|
||||
response = client.simulate_get("/item/c3910974-c3a5-4053-9dce-104aa7bb1621")
|
||||
response = client.simulate_get('/item/17')
|
||||
response_doc = json.loads(response.text)
|
||||
|
||||
assert isinstance(response_doc["downloads"], int)
|
||||
assert isinstance(response_doc["id"], str)
|
||||
assert isinstance(response_doc["views"], int)
|
||||
assert isinstance(response_doc['downloads'], int)
|
||||
assert isinstance(response_doc['id'], int)
|
||||
assert isinstance(response_doc['views'], int)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_missing_item(client):
|
||||
"""Test requesting a single non-existing item."""
|
||||
'''Test requesting a single non-existing item.'''
|
||||
|
||||
response = client.simulate_get("/item/c3910974-c3a5-4053-9dce-104aa7bb1620")
|
||||
response = client.simulate_get('/item/1')
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_get_items(client):
|
||||
"""Test requesting 100 items."""
|
||||
'''Test requesting 100 items.'''
|
||||
|
||||
response = client.simulate_get("/items", query_string="limit=100")
|
||||
response = client.simulate_get('/items', query_string='limit=100')
|
||||
response_doc = json.loads(response.text)
|
||||
|
||||
assert isinstance(response_doc["currentPage"], int)
|
||||
assert isinstance(response_doc["totalPages"], int)
|
||||
assert isinstance(response_doc["statistics"], list)
|
||||
assert isinstance(response_doc['currentPage'], int)
|
||||
assert isinstance(response_doc['totalPages'], int)
|
||||
assert isinstance(response_doc['statistics'], list)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_items_invalid_limit(client):
|
||||
"""Test requesting 100 items with an invalid limit parameter."""
|
||||
'''Test requesting 100 items with an invalid limit parameter.'''
|
||||
|
||||
response = client.simulate_get("/items", query_string="limit=101")
|
||||
response = client.simulate_get('/items', query_string='limit=101')
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_get_items_invalid_page(client):
|
||||
"""Test requesting 100 items with an invalid page parameter."""
|
||||
'''Test requesting 100 items with an invalid page parameter.'''
|
||||
|
||||
response = client.simulate_get("/items", query_string="page=-1")
|
||||
response = client.simulate_get('/items', query_string='page=-1')
|
||||
|
||||
assert response.status_code == 400
|
||||
|
Reference in New Issue
Block a user