Skip to content

Commit

Permalink
Merge pull request #237 from unicef/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
robertavram authored Nov 10, 2016
2 parents 7d22ecd + 1b059bb commit 44e3879
Show file tree
Hide file tree
Showing 39 changed files with 863 additions and 272 deletions.
7 changes: 4 additions & 3 deletions EquiTrack/EquiTrack/settings/test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from local_base import *

from base import *

ALLOWED_HOSTS = ['127.0.0.1']
# This has to be set to this particular backend in order for django to grab email and expose in tests
POST_OFFICE['BACKENDS']['default'] = 'django.core.mail.backends.locmem.EmailBackend'

Expand All @@ -17,9 +17,10 @@ def __contains__(self, item):
def __getitem__(self, item):
return "notmigrations"

MIGRATION_MODULES = DisableMigrations()
# MIGRATION_MODULES = DisableMigrations()

# MIGRATION_MODULES = dict((app, '%s.fake_migrations' % app) for app in INSTALLED_APPS)
# MIGRATION_MODULES = dict((app, None) for app in INSTALLED_APPS)


TEST_RUNNER = 'EquiTrack.tests.runners.TestRunner'
48 changes: 44 additions & 4 deletions EquiTrack/EquiTrack/tests/mixins.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,64 @@

from django.core.urlresolvers import resolve

from rest_framework.test import APIClient, force_authenticate, APIRequestFactory

from tenant_schemas.test.cases import TenantTestCase
from tenant_schemas.test.client import TenantClient

from django.db import connection
from django.core.management import call_command
from tenant_schemas.utils import get_tenant_model


class FastTenantTestCase(TenantTestCase):

@classmethod
def setUpClass(cls):
cls.sync_shared()
tenant_domain = 'tenant.test.com'

TenantModel = get_tenant_model()
try:
cls.tenant = TenantModel.objects.get(domain_url=tenant_domain, schema_name='test')
except:
cls.tenant = TenantModel(domain_url=tenant_domain, schema_name='test')
cls.tenant.save(verbosity=0)

connection.set_tenant(cls.tenant)

if cls.fixtures:
for db_name in cls._databases_names(include_mirrors=False):
try:
call_command('loaddata', *cls.fixtures, **{
'verbosity': 0,
'commit': False,
'database': db_name,
})
except Exception:
cls._rollback_atomics(cls.cls_atomics)
raise
try:
cls.setUpTestData()
except Exception:
cls._rollback_atomics(cls.cls_atomics)
raise

@classmethod
def tearDownClass(cls):
connection.set_schema_to_public()

class APITenantClient(TenantClient, APIClient):
def __init__(self, tenant, **defaults):
super(APITenantClient, self).__init__(tenant=tenant, defaults=defaults)


class APITenantTestCase(TenantTestCase):
class APITenantTestCase(FastTenantTestCase):
"""
Base test case for testing APIs
"""
client_class = APIClient

def forced_auth_req(self, method, url, user=None, data=None):
def forced_auth_req(self, method, url, user=None, data=None, **kwargs):
"""
Function that allows api methods to be called with forced authentication
Expand All @@ -37,7 +77,7 @@ def forced_auth_req(self, method, url, user=None, data=None):
data = data or {}
view = view_info.func
req_to_call = getattr(factory, method)
request = req_to_call(url, data, format='json')
request = req_to_call(url, data, format='json', **kwargs)

user = user or self.user
force_authenticate(request, user=user)
Expand Down
3 changes: 1 addition & 2 deletions EquiTrack/EquiTrack/tests/runners.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ def setup_databases(self, **kwargs):
:param kwargs:
:return:
"""
old_names, mirrors = super(TestRunner, self).setup_databases(**kwargs)
cursor = connection.cursor()
cursor.execute("CREATE EXTENSION IF NOT EXISTS hstore;")

return old_names, mirrors
return super(TestRunner, self).setup_databases(**kwargs)

2 changes: 2 additions & 0 deletions EquiTrack/EquiTrack/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
partners_api,
staffm_api,
agreement_api,
simple_agreements_api,
)

from workplan.views import (
Expand Down Expand Up @@ -130,6 +131,7 @@
url(r'^api/', include(partners_api.urls)),
url(r'^api/', include(staffm_api.urls)),
url(r'^api/', include(agreement_api.urls)),
url(r'^api/', include(simple_agreements_api.urls)),
url(r'^api/', include(interventions_api.urls)),
url(r'^api/', include(simple_interventions_api.urls)),
url(r'^api/', include(simple_results_api.urls)),
Expand Down
Binary file added EquiTrack/assets/admin/img/icon-unknown.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added EquiTrack/assets/admin/img/icon_addlink.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added EquiTrack/assets/admin/img/icon_deletelink.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions EquiTrack/locations/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class LocationAdmin(LeafletGeoAdmin, MPTTModelAdmin):
)
search_fields = ('name', 'p_code',)

def get_form(self, request, obj=None, **kwargs):
self.readonly_fields = [] if request.user.is_superuser else ['p_code', 'geom', 'point', 'gateway']

return super(LocationAdmin, self).get_form(request, obj, **kwargs)

# def get_fields(self, request, obj=None):
#
# fields = super(LocationAdmin, self).get_fields(request, obj)
Expand All @@ -44,6 +49,7 @@ class LocationAdmin(LeafletGeoAdmin, MPTTModelAdmin):
# return fields



class GovernorateAdmin(LeafletGeoAdmin):
list_display = (
'name',
Expand Down
22 changes: 22 additions & 0 deletions EquiTrack/locations/migrations/0007_auto_20161027_2022.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.10 on 2016-10-27 17:22
from __future__ import unicode_literals

from django.db import migrations
import django.db.models.manager


class Migration(migrations.Migration):

dependencies = [
('locations', '0006_auto_20160229_1545'),
]

operations = [
migrations.AlterModelManagers(
name='cartodbtable',
managers=[
('_default_manager', django.db.models.manager.Manager()),
],
),
]
16 changes: 14 additions & 2 deletions EquiTrack/locations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@

import logging

from django.db import IntegrityError
from django.db import IntegrityError, connection
from django.db.models.signals import post_delete, post_save
from django.dispatch.dispatcher import receiver
from django.core.cache import cache
from django.contrib.gis.db import models
from django.contrib.contenttypes.models import ContentType

Expand Down Expand Up @@ -151,6 +154,16 @@ class Meta:
ordering = ['name']


@receiver(post_delete, sender=Location)
@receiver(post_save, sender=Location)
def invalidate_locations_etag(sender, instance, **kwargs):
"""
Invalidate the locations etag in the cache on every change.
"""
schema_name = connection.schema_name
cache.delete("{}-locations-etag".format(schema_name))


class LinkedLocation(models.Model):
"""
Generic model for linking locations to anything
Expand Down Expand Up @@ -218,4 +231,3 @@ class CartoDBTable(MPTTModel):

def __unicode__(self):
return self.table_name

40 changes: 34 additions & 6 deletions EquiTrack/locations/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
__author__ = 'achamseddine'

from django.db import connection
from django.core.cache import cache
from rest_framework import status

from EquiTrack.factories import UserFactory
from EquiTrack.factories import UserFactory, LocationFactory
from EquiTrack.tests.mixins import APITenantTestCase
from locations.models import Location


class TestLocationViews(APITenantTestCase):

def setUp(self):
self.unicef_staff = UserFactory(is_staff=True)
self.locations = [LocationFactory() for x in xrange(5)]

def test_api_locationtypes_list(self):
response = self.forced_auth_req('get', '/api/locations-types/', user=self.unicef_staff)

self.assertEquals(response.status_code, status.HTTP_200_OK)

# def test_api_location_detail(self):
# response = self.forced_auth_req('get', '/api/locations/454545/', user=self.unicef_staff)
#
# self.assertEquals(response.status_code, status.HTTP_200_OK)
def test_api_location_list_cached(self):
response = self.forced_auth_req('get', '/api/locations/', user=self.unicef_staff)
self.assertEquals(response.status_code, status.HTTP_200_OK)
self.assertEquals(len(response.data), 5)
etag = response["ETag"]

response = self.forced_auth_req('get', '/api/locations/', user=self.unicef_staff, HTTP_IF_NONE_MATCH=etag)
self.assertEquals(response.status_code, status.HTTP_304_NOT_MODIFIED)

def test_api_location_list_modified(self):
response = self.forced_auth_req('get', '/api/locations/', user=self.unicef_staff)
self.assertEquals(response.status_code, status.HTTP_200_OK)
self.assertEquals(len(response.data), 5)
etag = response["ETag"]

location = LocationFactory()

response = self.forced_auth_req('get', '/api/locations/', user=self.unicef_staff, HTTP_IF_NONE_MATCH=etag)
self.assertEquals(response.status_code, status.HTTP_200_OK)
self.assertEquals(len(response.data), 6)

def test_location_delete_etag(self):
# Activate cache-aside with a request.
response = self.forced_auth_req('get', '/api/locations/', user=self.unicef_staff)
schema_name = connection.schema_name
etag_before = cache.get("{}-locations-etag".format(schema_name))
Location.objects.all().delete()
etag_after = cache.get("{}-locations-etag".format(schema_name))
assert etag_before != etag_after
31 changes: 29 additions & 2 deletions EquiTrack/locations/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
__author__ = 'unicef-leb-inn'
import uuid

from rest_framework import viewsets, mixins, permissions
from django.db import connection
from django.core.cache import cache
from rest_framework import viewsets, mixins, permissions, status
from rest_framework.response import Response
from rest_framework.generics import ListAPIView

from .models import CartoDBTable, GatewayType, Location
Expand Down Expand Up @@ -44,6 +48,29 @@ class LocationsViewSet(mixins.RetrieveModelMixin,
queryset = Location.objects.all()
serializer_class = LocationSerializer

def list(self, *args, **kwargs):
"""
Returns list of instances only if there's a new ETag, and it does not
match the one sent along with the request.
Otherwise it returns 304 NOT MODIFIED.
"""
schema_name = connection.schema_name
cache_etag = cache.get("{}-locations-etag".format(schema_name))
if not cache_etag:
new_etag = uuid.uuid4().hex
response = super(LocationsViewSet, self).list(*args, **kwargs)
response["ETag"] = new_etag
cache.set("{}-locations-etag".format(schema_name), new_etag)
return response

request_etag = self.request.META.get("HTTP_IF_NONE_MATCH", None)
if cache_etag == request_etag:
return Response(status=status.HTTP_304_NOT_MODIFIED)
else:
response = super(LocationsViewSet, self).list(*args, **kwargs)
response["ETag"] = cache_etag
return response

def get_queryset(self):
queryset = super(LocationsViewSet, self).get_queryset()
p_code = self.kwargs.get('p_code')
Expand All @@ -64,4 +91,4 @@ def get_queryset(self):
qs = qs.filter(name__icontains=q)

# return maximum 7 records
return qs.all()[:7]
return qs.all()[:7]
9 changes: 9 additions & 0 deletions EquiTrack/partners/filters.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from rest_framework.filters import BaseFilterBackend

__author__ = 'jcranwellward'

from django.contrib import admin
Expand Down Expand Up @@ -187,4 +189,11 @@ def queryset(self, request, queryset):
for ip in PCASectorOutput.objects.filter(output=self.value()):
pca_ids.append(ip.pca.id)
return queryset.filter(id__in=pca_ids)
return queryset


class PartnerScopeFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if request.parser_context['kwargs']:
return queryset.filter(partner__pk=request.parser_context['kwargs']['partner_pk'])
return queryset
2 changes: 1 addition & 1 deletion EquiTrack/partners/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ def clean(self):
u'Please select the date {} signed the partnership'.format(partner_manager)
)

if signed_by_partner_date and signed_by_partner_date < initiation_date:
if signed_by_partner_date and initiation_date and signed_by_partner_date < initiation_date:
raise ValidationError({'signed_by_partner_date': self.ERROR_MESSAGES['signed_by_partner']})

if signed_by_partner_date and not partner_manager:
Expand Down
36 changes: 36 additions & 0 deletions EquiTrack/partners/migrations/0074_auto_20161027_2022.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.10 on 2016-10-27 17:22
from __future__ import unicode_literals

import django.contrib.postgres.fields.jsonb
from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('partners', '0073_governmentinterventionresult_planned_visits'),
]

operations = [
migrations.AlterField(
model_name='distributionplan',
name='document',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True),
),
migrations.AlterField(
model_name='indicatorreport',
name='disaggregation',
field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
),
migrations.AlterField(
model_name='partnerorganization',
name='hact_values',
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default={}, null=True),
),
migrations.AlterField(
model_name='resultchain',
name='disaggregation',
field=django.contrib.postgres.fields.jsonb.JSONField(null=True),
),
]
6 changes: 2 additions & 4 deletions EquiTrack/partners/models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from __future__ import absolute_import

__author__ = 'jcranwellward'

import datetime
from dateutil.relativedelta import relativedelta

Expand All @@ -14,7 +12,7 @@
from django.utils.translation import ugettext as _
from django.utils.functional import cached_property

from jsonfield import JSONField
from django.contrib.postgres.fields import JSONField
from django_hstore import hstore
from smart_selects.db_fields import ChainedForeignKey
from model_utils.models import (
Expand Down Expand Up @@ -336,7 +334,7 @@ def planned_cash_transfers(cls, partner, budget_record=None):
@cached_property
def cp_cycle_trip_links(self):
from trips.models import Trip
crs = ResultStructure.current()
crs = CountryProgramme.current()
if self.partner_type == u'Government':
return self.linkedgovernmentpartner_set.filter(
trip__from_date__lt=crs.to_date,
Expand Down
Loading

0 comments on commit 44e3879

Please sign in to comment.