From d8a2a5a33514a4473b5f0443b730dafdfc4764e8 Mon Sep 17 00:00:00 2001 From: Eduardo Cuducos Date: Thu, 22 Dec 2016 16:23:21 -0200 Subject: [PATCH 1/3] Minor edits --- .../0021_make_reciept_fetched_a_db_index.py | 20 +++++++++++++++++++ jarbas/core/models.py | 4 +--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 jarbas/core/migrations/0021_make_reciept_fetched_a_db_index.py diff --git a/jarbas/core/migrations/0021_make_reciept_fetched_a_db_index.py b/jarbas/core/migrations/0021_make_reciept_fetched_a_db_index.py new file mode 100644 index 000000000..4eae0fb93 --- /dev/null +++ b/jarbas/core/migrations/0021_make_reciept_fetched_a_db_index.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2016-12-22 18:22 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0020_rename_supplier_to_company'), + ] + + operations = [ + migrations.AlterField( + model_name='reimbursement', + name='receipt_fetched', + field=models.BooleanField(db_index=True, default=False, verbose_name='Was the receipt URL fetched?'), + ), + ] diff --git a/jarbas/core/models.py b/jarbas/core/models.py index e75cd983d..b7d554f08 100644 --- a/jarbas/core/models.py +++ b/jarbas/core/models.py @@ -21,9 +21,7 @@ def get_url(self): @property def exists(self): status = head(self.get_url()).status_code - if 200 <= status < 400: - return True - return False + return 200 <= status < 400 class Reimbursement(models.Model): From 1611ccaad43d94d817fd19ba3e0d844f35121c21 Mon Sep 17 00:00:00 2001 From: Eduardo Cuducos Date: Thu, 22 Dec 2016 16:23:46 -0200 Subject: [PATCH 2/3] Add queryset for same day search --- jarbas/core/models.py | 4 +++ jarbas/core/querysets.py | 17 ++++++++++ jarbas/core/tests/test_reimbursement_model.py | 31 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 jarbas/core/querysets.py diff --git a/jarbas/core/models.py b/jarbas/core/models.py index b7d554f08..6c6e03446 100644 --- a/jarbas/core/models.py +++ b/jarbas/core/models.py @@ -2,6 +2,8 @@ from django.db import models from requests import head +from jarbas.core.querysets import SameDayQuerySet + class Receipt: @@ -72,6 +74,8 @@ class Reimbursement(models.Model): receipt_fetched = models.BooleanField('Was the receipt URL fetched?', default=False, db_index=True) receipt_url = models.CharField('Receipt URL', max_length=140, blank=True, null=True) + objects = models.Manager.from_queryset(SameDayQuerySet)() + class Meta: ordering = ['-issue_date'] unique_together = ('year', 'applicant_id', 'document_id') diff --git a/jarbas/core/querysets.py b/jarbas/core/querysets.py new file mode 100644 index 000000000..e43fc2e68 --- /dev/null +++ b/jarbas/core/querysets.py @@ -0,0 +1,17 @@ +from django.db import models + + +class SameDayQuerySet(models.QuerySet): + + def same_day(self, **kwargs): + keys = ('year', 'applicant_id', 'document_id') + unique_id = {k: v for k, v in kwargs.items()} + if not all(map(unique_id.get, keys)): + msg = 'A same_day queryset requires the kwargs: ' + ', '.join(keys) + raise TypeError(msg) + + reference = self.get(**unique_id) + return self.exclude(**unique_id).filter( + issue_date=reference.issue_date, + applicant_id=reference.applicant_id + ) diff --git a/jarbas/core/tests/test_reimbursement_model.py b/jarbas/core/tests/test_reimbursement_model.py index ad4660db8..a61d1dbdc 100644 --- a/jarbas/core/tests/test_reimbursement_model.py +++ b/jarbas/core/tests/test_reimbursement_model.py @@ -46,6 +46,37 @@ def test_optional_fields(self): self.assertEqual(1, Reimbursement.objects.count()) +class TestManager(TestReimbursement): + + def test_same_day(self): + data1 = sample_reimbursement_data.copy() + data1['document_id'] = 42 * 2 + data1['issue_date'] = '1970-12-31' + Reimbursement.objects.create(**data1) + + data2 = sample_reimbursement_data.copy() + data2['document_id'] = 42 * 3 + data2['issue_date'] = '1970-12-31' + Reimbursement.objects.create(**data2) + + data3 = sample_reimbursement_data.copy() + data3['document_id'] = 42 * 4 + data3['issue_date'] = '1970-12-31' + Reimbursement.objects.create(**data3) + + unique_id = dict( + year=1970, + applicant_id=13, + document_id=84 + ) + qs = Reimbursement.objects.same_day(**unique_id) + self.assertEqual(2, qs.count()) + + def test_same_day_error(self): + with self.assertRaises(TypeError): + Reimbursement.objects.same_day(year=2016, document_id=42) + + class TestCustomMethods(TestReimbursement): def test_as_list(self): From 4551050f41843109ad36173905d6b215c95d3426 Mon Sep 17 00:00:00 2001 From: Eduardo Cuducos Date: Thu, 22 Dec 2016 17:46:10 -0200 Subject: [PATCH 3/3] Add same day endpoit to the API --- jarbas/api/serializers.py | 39 +++++++++++ jarbas/api/tests/test_same_day_view.py | 92 ++++++++++++++++++++++++++ jarbas/api/urls.py | 6 ++ jarbas/api/views.py | 21 +++--- 4 files changed, 149 insertions(+), 9 deletions(-) create mode 100644 jarbas/api/tests/test_same_day_view.py diff --git a/jarbas/api/serializers.py b/jarbas/api/serializers.py index 8dd90b2bb..272657f9d 100644 --- a/jarbas/api/serializers.py +++ b/jarbas/api/serializers.py @@ -61,6 +61,35 @@ class Meta: ) +class SameDayReimbursementSerializer(serializers.ModelSerializer): + + city = serializers.SerializerMethodField() + + def get_city(self, obj): + try: + company = Company.objects.get(cnpj=format_cnpj(obj.cnpj_cpf)) + except Company.DoesNotExist: + return None + + location = company.city, company.state + if not any(location): + return None + + return ' - '.join(v for v in location if v) + + class Meta: + model = Reimbursement + fields = ( + 'applicant_id', + 'city', + 'document_id', + 'subquota_description', + 'supplier', + 'total_net_value', + 'year' + ) + + class ReceiptSerializer(serializers.ModelSerializer): reimbursement = serializers.SerializerMethodField() @@ -114,3 +143,13 @@ class Meta: model = Company exclude = ('id',) depth = 1 + + +def format_cnpj(cnpj): + return '{}.{}.{}/{}-{}'.format( + cnpj[0:2], + cnpj[2:5], + cnpj[5:8], + cnpj[8:12], + cnpj[12:14] + ) diff --git a/jarbas/api/tests/test_same_day_view.py b/jarbas/api/tests/test_same_day_view.py new file mode 100644 index 000000000..30a1f6ac1 --- /dev/null +++ b/jarbas/api/tests/test_same_day_view.py @@ -0,0 +1,92 @@ +from json import loads + +from django.shortcuts import resolve_url +from django.test import TestCase + +from jarbas.core.models import Company, Reimbursement +from jarbas.core.tests import sample_company_data, sample_reimbursement_data +from jarbas.api.serializers import format_cnpj + + +class TestSameDay(TestCase): + + def setUp(self): + self.maxDiff = 2 ** 10 + cnpj = '12345678901234' + + company1 = sample_company_data.copy() + company1['cnpj'] = format_cnpj(cnpj) + company1['city'] = 'Atlantis' + company1['state'] = 'WE' + Company.objects.create(**company1) + + company2 = sample_company_data.copy() + company2['cnpj'] = format_cnpj(cnpj[::-1]) + Company.objects.create(**company2) + + data1 = sample_reimbursement_data.copy() + data1['document_id'] = 42 * 2 + data1['cnpj_cpf'] = cnpj + Reimbursement.objects.create(**data1) + + data2 = sample_reimbursement_data.copy() + data2['document_id'] = 42 * 3 + data2['cnpj_cpf'] = cnpj + Reimbursement.objects.create(**data2) + + data3 = sample_reimbursement_data.copy() + data3['document_id'] = 42 * 4 + data3['cnpj_cpf'] = cnpj[::-1] + Reimbursement.objects.create(**data3) + + data4 = sample_reimbursement_data.copy() + data4['document_id'] = 42 * 5 + data4['cnpj_cpf'] = '11111111111111' + Reimbursement.objects.create(**data4) + + unique_id = dict( + year=1970, + applicant_id=13, + document_id=84 + ) + url = resolve_url('api:reimbursement-same-day', **unique_id) + self.resp = self.client.get(url) + + def test_status_code(self): + self.assertEqual(200, self.resp.status_code) + + def test_contents(self): + expected = ( + { + 'applicant_id': 13, + 'city': 'Atlantis - WE', + 'document_id': 126, + 'subquota_description': 'Subquota description', + 'supplier': 'Acme', + 'total_net_value': '4.560', + 'year': 1970 + }, + { + 'applicant_id': 13, + 'city': None, + 'document_id': 168, + 'subquota_description': 'Subquota description', + 'supplier': 'Acme', + 'total_net_value': '4.560', + 'year': 1970 + }, + { + 'applicant_id': 13, + 'city': None, + 'document_id': 210, + 'subquota_description': 'Subquota description', + 'supplier': 'Acme', + 'total_net_value': '4.560', + 'year': 1970 + } + ) + content = loads(self.resp.content.decode('utf-8')) + self.assertEqual(3, int(content['count'])) + for reimbursement in expected: + with self.subTest(): + self.assertIn(reimbursement, content['results']) diff --git a/jarbas/api/urls.py b/jarbas/api/urls.py index 3feb726ee..c21f1ebf4 100644 --- a/jarbas/api/urls.py +++ b/jarbas/api/urls.py @@ -6,6 +6,7 @@ ReceiptDetailView, ReimbursementDetailView, ReimbursementListView, + SameDayReimbursementListView, SubquotaListView, ) @@ -26,6 +27,11 @@ ReceiptDetailView.as_view(), name='reimbursement-receipt' ), + url( + r'^reimbursement/(?P\d{4})/(?P\d+)/(?P\d+)/same_day/$', + SameDayReimbursementListView.as_view(), + name='reimbursement-same-day' + ), url(r'^applicant/$', ApplicantListView.as_view(), name='applicant-list'), url(r'^company/(?P\d{14})/$', CompanyDetailView.as_view(), name='company-detail'), url(r'^subquota/$', SubquotaListView.as_view(), name='subquota-list'), diff --git a/jarbas/api/views.py b/jarbas/api/views.py index 700750280..d90aeee32 100644 --- a/jarbas/api/views.py +++ b/jarbas/api/views.py @@ -5,8 +5,10 @@ ApplicantSerializer, ReceiptSerializer, ReimbursementSerializer, + SameDayReimbursementSerializer, SubquotaSerializer, - CompanySerializer + CompanySerializer, + format_cnpj ) from jarbas.core.models import Reimbursement, Company @@ -89,6 +91,14 @@ def get_object(self): return obj +class SameDayReimbursementListView(ListAPIView): + + serializer_class = SameDayReimbursementSerializer + + def get_queryset(self): + return Reimbursement.objects.same_day(**self.kwargs) + + class ApplicantListView(ListAPIView): serializer_class = ApplicantSerializer @@ -115,11 +125,4 @@ class CompanyDetailView(RetrieveAPIView): def get_object(self): cnpj = self.kwargs.get(self.lookup_field, '00000000000000') - formatted = '{}.{}.{}/{}-{}'.format( - cnpj[0:2], - cnpj[2:5], - cnpj[5:8], - cnpj[8:12], - cnpj[12:14] - ) - return get_object_or_404(Company, cnpj=formatted) + return get_object_or_404(Company, cnpj=format_cnpj(cnpj))