From dd64130df856df81c0ad53fd1af38cbf6b6cd5fd Mon Sep 17 00:00:00 2001 From: Luke Murphy <lukewm@riseup.net> Date: Wed, 24 Oct 2018 14:07:15 +0200 Subject: [PATCH] First stab at some location coverage. --- Pipfile | 1 + Pipfile.lock | 155 +++++++++++++----- internewshid/rest_api/serializers.py | 26 +++ .../rest_api/tests/location_coverage_tests.py | 46 ++++++ internewshid/rest_api/urls.py | 15 +- internewshid/rest_api/views.py | 10 +- 6 files changed, 204 insertions(+), 49 deletions(-) create mode 100644 internewshid/rest_api/tests/location_coverage_tests.py diff --git a/Pipfile b/Pipfile index 73192b29..5fdb2388 100644 --- a/Pipfile +++ b/Pipfile @@ -30,6 +30,7 @@ openpyxl = "*" python-dateutil = "*" pytz = "*" django-constance = {version = "*", extras = ["database"]} +rest-pandas = "*" [dev-packages] factory_boy = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 4650e0fe..9e4d6d2f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2aa92a301153dd63c052458b6505a6e5e864ea6ab28df9aa9b93d34d9f869ee5" + "sha256": "179591b179de506537efcf057cc9b1fd3fb4e25ac6a67a124d268c38d1a4ab69" }, "pipfile-spec": 6, "requires": { @@ -99,10 +99,10 @@ }, "django-tables2": { "hashes": [ - "sha256:e601af8a63d053c0b12c1b9f883f14f081b9cbc313cbaa3633a96b6e136e05e5" + "sha256:0f39ef59a03a2bba1e8fc2fa5af5ebf45f98a078f1035197957965be2d76faea" ], "index": "pypi", - "version": "==2.0.1" + "version": "==2.0.2" }, "django-widget-tweaks": { "hashes": [ @@ -119,11 +119,11 @@ }, "djangorestframework": { "hashes": [ - "sha256:b6714c3e4b0f8d524f193c91ecf5f5450092c2145439ac2769711f7eba89a9d9", - "sha256:c375e4f95a3a64fccac412e36fb42ba36881e52313ec021ef410b40f67cddca4" + "sha256:607865b0bb1598b153793892101d881466bd5a991de12bd6229abb18b1c86136", + "sha256:63f76cbe1e7d12b94c357d7e54401103b2e52aef0f7c1650d6c820ad708776e5" ], "index": "pypi", - "version": "==3.8.2" + "version": "==3.9.0" }, "djangorestframework-bulk": { "hashes": [ @@ -175,12 +175,71 @@ "index": "pypi", "version": "==1.2.5" }, + "numpy": { + "hashes": [ + "sha256:032df9b6571c5f1d41ea6f6a189223208cb488990373aa686aca55570fcccb42", + "sha256:094f8a83e5bd0a44a7557fa24a46db6ba7d5299c389ddbc9e0e18722f567fb63", + "sha256:1c0c80e74759fa4942298044274f2c11b08c86230b25b8b819e55e644f5ff2b6", + "sha256:2aa0910eaeb603b1a5598193cc3bc8eacf1baf6c95cbc3955eb8e15fa380c133", + "sha256:2f5ebc7a04885c7d69e5daa05208faef4db7f1ae6a99f4d36962df8cd54cdc76", + "sha256:32a07241cb624e104b88b08dea2851bf4ec5d65a1f599d7735041ced7171fd7a", + "sha256:3c7959f750b54b445f14962a3ddc41b9eadbab00b86da55fbb1967b2b79aad10", + "sha256:3d8f9273c763a139a99e65c2a3c10f1109df30bedae7f011b10d95c538364704", + "sha256:63bca71691339d2d6f8a7c970821f2b12098a53afccc0190d4e1555e75e5223a", + "sha256:7ae9c3baff3b989859c88e0168ad10902118595b996bf781eaf011bb72428798", + "sha256:866a7c8774ccc7d603667fad95456b4cf56d79a2bb5a7648ac9f0082e0b9416e", + "sha256:8bc4b92a273659e44ca3f3a2f8786cfa39d8302223bcfe7df794429c63d5f5a1", + "sha256:919f65e0732195474897b1cafefb4d4e7c2bb8174a725e506b62e9096e4df28d", + "sha256:9d1598573d310104acb90377f0a8c2319f737084689f5eb18012becaf345cda5", + "sha256:9fff90c88bfaad2901be50453d5cd7897a826c1d901f0654ee1d73ab3a48cd18", + "sha256:a245464ddf6d90e2d6287e9cef6bcfda2a99467fdcf1b677b99cd0b6c7b43de2", + "sha256:a988db28f54e104a01e8573ceb6f28202b4c15635b1450b2e3b2b822c6564f9b", + "sha256:b12fe6f31babb9477aa0f9692730654b3ee0e71f33b4568170dfafd439caf0a2", + "sha256:b7599ff4acd23f5de983e3aec772153b1043e131487a5c6ad0f94b41a828877a", + "sha256:c9f4dafd6065c4c782be84cd67ceeb9b1d4380af60a7af32be10ebecd723385e", + "sha256:ce3622b73ccd844ba301c1aea65d36cf9d8331e7c25c16b1725d0f14db99aaf4", + "sha256:d0f36a24cf8061a2c03e151be3418146717505b9b4ec17502fa3bbdb04ec1431", + "sha256:d263f8f14f2da0c079c0297e829e550d8f2c4e0ffef215506bd1d0ddd2bff3de", + "sha256:d8837ff272800668aabdfe70b966631914b0d6513aed4fc1b1428446f771834d", + "sha256:ef694fe72a3995aa778a5095bda946e0d31f7efabd5e8063ad8c6238ab7d3f78", + "sha256:f1fd1a6f40a501ba4035f5ed2c1f4faa68245d1407bf97d2ee401e4f23d1720b", + "sha256:fa337b6bd5fe2b8c4e705f4102186feb9985de9bb8536d32d5129a658f1789e0", + "sha256:febd31cd0d2fd2509ca2ec53cb339f8bf593c1bd245b9fc55c1917a68532a0af" + ], + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==1.15.3" + }, "openpyxl": { "hashes": [ - "sha256:22904d7bdfaaab33d65d50a0915a65eeb2f29c85d9ec53081563850678a29927" + "sha256:022c0f3fa1e873cc0ba20651c54dd5e6276fc4ff150b4060723add4fc448645e" ], "index": "pypi", - "version": "==2.5.8" + "version": "==2.5.9" + }, + "pandas": { + "hashes": [ + "sha256:11975fad9edbdb55f1a560d96f91830e83e29bed6ad5ebf506abda09818eaf60", + "sha256:12e13d127ca1b585dd6f6840d3fe3fa6e46c36a6afe2dbc5cb0b57032c902e31", + "sha256:1c87fcb201e1e06f66e23a61a5fea9eeebfe7204a66d99df24600e3f05168051", + "sha256:242e9900de758e137304ad4b5663c2eff0d798c2c3b891250bd0bd97144579da", + "sha256:26c903d0ae1542890cb9abadb4adcb18f356b14c2df46e4ff657ae640e3ac9e7", + "sha256:2e1e88f9d3e5f107b65b59cd29f141995597b035d17cc5537e58142038942e1a", + "sha256:31b7a48b344c14691a8e92765d4023f88902ba3e96e2e4d0364d3453cdfd50db", + "sha256:4fd07a932b4352f8a8973761ab4e84f965bf81cc750fb38e04f01088ab901cb8", + "sha256:5b24ca47acf69222e82530e89111dd9d14f9b970ab2cd3a1c2c78f0c4fbba4f4", + "sha256:647b3b916cc8f6aeba240c8171be3ab799c3c1b2ea179a3be0bd2712c4237553", + "sha256:66b060946046ca27c0e03e9bec9bba3e0b918bafff84c425ca2cc2e157ce121e", + "sha256:6efa9fa6e1434141df8872d0fa4226fc301b17aacf37429193f9d70b426ea28f", + "sha256:be4715c9d8367e51dbe6bc6d05e205b1ae234f0dc5465931014aa1c4af44c1ba", + "sha256:bea90da782d8e945fccfc958585210d23de374fa9294a9481ed2abcef637ebfc", + "sha256:d318d77ab96f66a59e792a481e2701fba879e1a453aefeebdb17444fe204d1ed", + "sha256:d785fc08d6f4207437e900ffead930a61e634c5e4f980ba6d3dc03c9581748c7", + "sha256:de9559287c4fe8da56e8c3878d2374abc19d1ba2b807bfa7553e912a8e5ba87c", + "sha256:f4f98b190bb918ac0bc0e3dd2ab74ff3573da9f43106f6dba6385406912ec00f", + "sha256:f71f1a7e2d03758f6e957896ed696254e2bc83110ddbc6942018f1a232dd9dad", + "sha256:fb944c8f0b0ab5c1f7846c686bc4cdf8cde7224655c12edcd59d5212cd57bec0" + ], + "version": "==0.23.4" }, "pillow": { "hashes": [ @@ -221,19 +280,26 @@ }, "python-dateutil": { "hashes": [ - "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", - "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" + "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", + "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02" ], "index": "pypi", - "version": "==2.7.3" + "version": "==2.7.5" }, "pytz": { "hashes": [ - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" + "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca", + "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6" + ], + "index": "pypi", + "version": "==2018.7" + }, + "rest-pandas": { + "hashes": [ + "sha256:cdfbc1baa5927f833e986b586f751b677a38d0360d827291937ee68a0ed7e105" ], "index": "pypi", - "version": "==2018.5" + "version": "==1.0.0" }, "six": { "hashes": [ @@ -274,6 +340,7 @@ "hashes": [ "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", + "sha256:0bf8cbbd71adfff0ef1f3a1531e6402d13b7b01ac50a79c97ca15f030dba6306", "sha256:10a46017fef60e16694a30627319f38a2b9b52e90182dddb6e37dcdab0f4bf95", "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", "sha256:23d341cdd4a0371820eb2b0bd6b88f5003a7438bbedb33688cd33b8eae59affd", @@ -302,6 +369,7 @@ "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", + "sha256:f05a636b4564104120111800021a92e43397bc12a5c72fed7036be8556e0029e", "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80" ], "version": "==4.5.1" @@ -339,17 +407,17 @@ }, "faker": { "hashes": [ - "sha256:74b32991f8e08e4f2f84858b919eca253becfaec4b3fa5fcff7fdbd70d5d78b1", - "sha256:c2ce42dd8361e6d392276006d757532562463c8642b1086709584200b7fd7758" + "sha256:2621643b80a10b91999925cfd20f64d2b36f20bf22136bbdc749bb57d6ffe124", + "sha256:5ed822d31bd2d6edf10944d176d30dc9c886afdd381eefb7ba8b7aad86171646" ], - "version": "==0.9.1" + "version": "==0.9.2" }, "funcsigs": { "hashes": [ "sha256:330cc27ccbf7f1e992e69fef78261dc7c6569012cf397db8d3de0234e6c937ca", "sha256:a7bb0f2cf3a3fd1ab2732cb49eba4252c2af4240442415b4abce3b87022a8f50" ], - "markers": "python_version < '3.3'", + "markers": "python_version < '3.0'", "version": "==1.0.2" }, "futures": { @@ -409,25 +477,26 @@ }, "pbr": { "hashes": [ - "sha256:1be135151a0da949af8c5d0ee9013d9eafada71237eb80b3ba8896b4f12ec5dc", - "sha256:cf36765bf2218654ae824ec8e14257259ba44e43b117fd573c8d07a9895adbdd" + "sha256:8fc938b1123902f5610b06756a31b1e6febf0d105ae393695b0c9d4244ed2910", + "sha256:f20ec0abbf132471b68963bb34d9c78e603a5cf9e24473f14358e66551d47475" ], - "version": "==4.3.0" + "version": "==5.1.0" }, "pluggy": { "hashes": [ - "sha256:6e3836e39f4d36ae72840833db137f7b7d35105079aee6ec4a62d9f80d594dd1", - "sha256:95eb8364a4708392bae89035f45341871286a333f749c3141c20573d2b3876e1" + "sha256:447ba94990e8014ee25ec853339faf7b0fc8050cdc3289d4d71f7f410fb90095", + "sha256:bde19360a8ec4dfd8a20dcb811780a30998101f078fc7ded6162f0076f50508f" ], - "version": "==0.7.1" + "markers": "python_version >= '2.7' and python_version != '3.0.*' and python_version != '3.1.*' and python_version != '3.2.*' and python_version != '3.3.*'", + "version": "==0.8.0" }, "py": { "hashes": [ - "sha256:06a30435d058473046be836d3fc4f27167fd84c45b99704f2fb5509ef61f9af1", - "sha256:50402e9d1c9005d759426988a492e0edaadb7f4e68bcddfea586bc7432d009c6" + "sha256:bf92637198836372b520efcba9e020c330123be8ce527e535d185ed4b6f45694", + "sha256:e76826342cefe3c3d5f7e8ee4316b80d1dd8a300781612ddbc765c17ba25a6c6" ], "index": "pypi", - "version": "==1.6.0" + "version": "==1.7.0" }, "pycodestyle": { "hashes": [ @@ -438,11 +507,11 @@ }, "pydocstyle": { "hashes": [ - "sha256:08a870edc94508264ed90510db466c6357c7192e0e866561d740624a8fc7d90c", - "sha256:4d5bcde961107873bae621f3d580c3e35a426d3687ffc6f8fb356f6628da5a97", - "sha256:af9fcccb303899b83bec82dc9a1d56c60fc369973223a5e80c3dfa9bdf984405" + "sha256:2258f9b0df68b97bf3a6c29003edc5238ff8879f1efb6f1999988d934e432bd8", + "sha256:5741c85e408f9e0ddf873611085e819b809fca90b619f5fd7f34bd4959da3dd4", + "sha256:ed79d4ec5e92655eccc21eb0c6cf512e69512b4a97d215ace46d17e4990f2039" ], - "version": "==2.1.1" + "version": "==3.0.0" }, "pyflakes": { "hashes": [ @@ -453,18 +522,18 @@ }, "pylava": { "hashes": [ - "sha256:20d12927f866c2d31c7d16cc4201a02c36ad26212f36f13e6725e0c40502520e", - "sha256:667320f3a9eab9f3528b6a83e4e7e92eb8b4791a654402b2f48fd927f01a81a1" + "sha256:b2fc881e9112a36f5a8a97bdbbedfa1704bbb65c34afa630e6df31532ae20994", + "sha256:dcdecb6668cb8c4a38e0ee4fdb5f4d9488793af57bc040d89b0d1142f13b8622" ], "index": "pypi", - "version": "==0.2.1" + "version": "==0.2.2" }, "pytest": { "hashes": [ - "sha256:7e258ee50338f4e46957f9e09a0f10fb1c2d05493fa901d113a8dafd0790de4e", - "sha256:9332147e9af2dcf46cd7ceb14d5acadb6564744ddff1fe8c17f0ce60ece7d9a2" + "sha256:a9e5e8d7ab9d5b0747f37740276eb362e6a76275d76cebbb52c6049d93b475db", + "sha256:bf47e8ed20d03764f963f0070ff1c8fda6e2671fc5dd562a4d3b7148ad60f5ca" ], - "version": "==3.8.2" + "version": "==3.9.3" }, "pytest-cov": { "hashes": [ @@ -498,19 +567,19 @@ }, "python-dateutil": { "hashes": [ - "sha256:1adb80e7a782c12e52ef9a8182bebeb73f1d7e24e374397af06fb4956c8dc5c0", - "sha256:e27001de32f627c22380a688bcc43ce83504a7bc5da472209b4c70f02829f0b8" + "sha256:063df5763652e21de43de7d9e00ccf239f953a832941e37be541614732cdfc93", + "sha256:88f9287c0174266bb0d8cedd395cfba9c58e87e5ad86b2ce58859bc11be3cf02" ], "index": "pypi", - "version": "==2.7.3" + "version": "==2.7.5" }, "pytz": { "hashes": [ - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" + "sha256:31cb35c89bd7d333cd32c5f278fca91b523b0834369e757f4c5641ea252236ca", + "sha256:8e0f8568c118d3077b46be7d654cc8167fa916092e28320cde048e54bfc9f1e6" ], "index": "pypi", - "version": "==2018.5" + "version": "==2018.7" }, "scandir": { "hashes": [ diff --git a/internewshid/rest_api/serializers.py b/internewshid/rest_api/serializers.py index c9ad5d11..7963643b 100644 --- a/internewshid/rest_api/serializers.py +++ b/internewshid/rest_api/serializers.py @@ -36,6 +36,17 @@ class TermSerializer(serializers.ModelSerializer): ) +class LocationCoverageTermSerializer(TermSerializer): + class Meta: + model = Term + fields = ( + 'long_name', + ) + + def to_representation(self, instance): + return instance.long_name + + class TermItemCountSerializer(serializers.ModelSerializer): class Meta: model = Term @@ -88,3 +99,18 @@ class ItemSerializer(serializers.ModelSerializer): item.save() return item + + +class LocationCoverageSerializer(ItemSerializer): + terms = LocationCoverageTermSerializer(many=True) + + # https://github.com/wq/django-rest-pandas#date-formatting + timestamp = serializers.DateField(format=None) + + class Meta: + model = Item + fields = ( + 'location', + 'terms', + 'timestamp', + ) diff --git a/internewshid/rest_api/tests/location_coverage_tests.py b/internewshid/rest_api/tests/location_coverage_tests.py new file mode 100644 index 00000000..ecc29bc4 --- /dev/null +++ b/internewshid/rest_api/tests/location_coverage_tests.py @@ -0,0 +1,46 @@ +import csv +from StringIO import StringIO + +from django.urls import reverse +from django.utils import timezone + +import pytest +from rest_framework.test import APIClient + +from data_layer.models import Item +from taxonomies.models import Taxonomy, Term + + +@pytest.fixture +def client(django_user_model): + client = APIClient() + admin = django_user_model.objects.create(is_staff=True) + client.force_login(user=admin) + return client + + +def test_location_coverage(client): + NOW = timezone.now().replace(microsecond=0) + + taxonomy = Taxonomy.objects.create(name='topicbaz') + item1 = Item.objects.create(location='locationfoo', timestamp=NOW) + + term = Term.objects.create(taxonomy=taxonomy, long_name='termbar') + item1.terms.add(term) + item1.save() + + item2 = Item.objects.create() + + response = client.get(reverse('location-coverage')) + assert response.accepted_media_type == 'text/csv' + reader = csv.DictReader(StringIO(response.content)) + + assert reader.fieldnames == ['row', 'location', 'terms', 'timestamp'] + + rendered = [item for item in reader] + + assert rendered[0]['location'] == 'locationfoo' + assert rendered[0]['terms'] == '[termbar]' + + assert rendered[1]['location'] == '' + assert rendered[1]['terms'] == '[]' diff --git a/internewshid/rest_api/urls.py b/internewshid/rest_api/urls.py index 1f25ac02..af86d100 100644 --- a/internewshid/rest_api/urls.py +++ b/internewshid/rest_api/urls.py @@ -1,8 +1,10 @@ -from django.conf import settings +from django.conf.urls import url from rest_framework import routers -from .views import ItemViewSet, TaxonomyViewSet, TermViewSet +from .views import ( + ItemViewSet, LocationCoverageView, TaxonomyViewSet, TermViewSet +) router = routers.SimpleRouter() router.register( @@ -19,5 +21,10 @@ router.register( TermViewSet, ) - -urlpatterns = router.urls if settings.DEBUG else [] +urlpatterns = [ + url( + r'location-coverage/$', + LocationCoverageView.as_view(), + name='location-coverage' + ), +] + router.urls diff --git a/internewshid/rest_api/views.py b/internewshid/rest_api/views.py index 21b7b10a..556e586c 100644 --- a/internewshid/rest_api/views.py +++ b/internewshid/rest_api/views.py @@ -5,13 +5,14 @@ from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from rest_framework_bulk.mixins import BulkDestroyModelMixin +from rest_pandas import PandasView from data_layer.models import Item from taxonomies.models import Taxonomy, Term from .serializers import ( - ItemSerializer, TaxonomySerializer, TermItemCountSerializer, - TermSerializer + ItemSerializer, LocationCoverageSerializer, TaxonomySerializer, + TermItemCountSerializer, TermSerializer ) @@ -194,3 +195,8 @@ class TermViewSet(viewsets.ModelViewSet): items = items.filter(taxonomy__slug=taxonomy_slug) return items + + +class LocationCoverageView(PandasView): + queryset = Item.objects.all() + serializer_class = LocationCoverageSerializer -- GitLab