diff --git a/akvo/api/resources/partnership.py b/akvo/api/resources/partnership.py
index 0983d6daa5..40915b7e02 100644
--- a/akvo/api/resources/partnership.py
+++ b/akvo/api/resources/partnership.py
@@ -31,7 +31,7 @@
FIELD_NAME = 'name'
FIELD_LONG_NAME = 'long_name'
FIELD_NEW_ORGANISATION_TYPE = 'new_organisation_type'
-FIELD_PARTNER_TYPE = 'partner_type'
+FIELD_PARTNER_TYPE = 'iati_organisation_role'
ORG_FIELDS = [FIELD_IATI_ORG_ID, FIELD_NAME, FIELD_LONG_NAME, FIELD_NEW_ORGANISATION_TYPE]
# InternalOrganisationID
FIELD_INTERNAL_ORG_ID = 'internal_org_id'
@@ -173,7 +173,7 @@ def hydrate(self, bundle):
bundle.data[FIELD_ORGANISATION] = organisation
if (
organisation.iati_org_id != bundle.data[FIELD_REPORTING_ORG] or
- bundle.data[FIELD_PARTNER_TYPE] != Partnership.SUPPORT_PARTNER
+ bundle.data[FIELD_PARTNER_TYPE] != Partnership.IATI_ACCOUNTABLE_PARTNER
):
bundle.data[FIELD_IATI_ACTIVITY_ID] = None
bundle.data[FIELD_INTERNAL_ID] = None
@@ -197,15 +197,48 @@ def hydrate_organisation(self, bundle):
class PartnershipResource(ConditionalFullResource):
organisation = ConditionalFullToOneField('akvo.api.resources.OrganisationResource', 'organisation')
project = ConditionalFullToOneField('akvo.api.resources.ProjectResource', 'project')
+ partner_type = fields.CharField(attribute='iati_role_to_partner_type')
def __init__(self, api_name=None):
""" override to be able to create custom help_text on the partner_type field
"""
super(PartnershipResource, self).__init__(api_name=None)
- self.fields['partner_type'].help_text = "Uses the following key-value pair list: {%s}" % ', '.join(
- ['"%s": "%s"' % (k, v) for k, v in Partnership.PARTNER_TYPES]
+ self.fields[
+ 'iati_organisation_role'].help_text = "Uses the following key-value pair list: {%s}" % ', '.join(
+ ['"%s": "%s"' % (k, v) for k, v in Partnership.IATI_ROLES]
)
+ def apply_filters(self, request, applicable_filters):
+ """ Custom filtering for the "fake" field partner_type allowing the filtering of the
+ resource using the deprecated Akvo partner type values
+
+ Uses Partnership.PARTNER_TYPES_TO_ROLES_MAP map the filter to the
+ Partnership.iati_organisation_role field
+ """
+
+ partner_type_filter = {}
+ if 'iati_role_to_partner_type' in [k.split('__')[0] for k in applicable_filters.keys()]:
+ for k in applicable_filters.keys():
+ if k.startswith('iati_role_to_partner_type'):
+ operator = k.split('__')[1]
+ if operator == 'in':
+ values = [
+ Partnership.PARTNER_TYPES_TO_ROLES_MAP[v] for v in applicable_filters[k]
+ ]
+ partner_type_filter['iati_organisation_role__in'] = values
+ else:
+ partner_type_filter[
+ "iati_organisation_role__{}".format(operator)
+ ] = Partnership.PARTNER_TYPES_TO_ROLES_MAP[applicable_filters[k]]
+ applicable_filters.pop(k)
+
+ default_filtering = super(PartnershipResource, self).apply_filters(request, applicable_filters)
+
+ if partner_type_filter:
+ return default_filtering.filter(**partner_type_filter)
+ else:
+ return default_filtering
+
class Meta:
max_limit = 10
allowed_methods = ['get']
@@ -214,10 +247,11 @@ class Meta:
filtering = dict(organisation=ALL_WITH_RELATIONS)
filtering = dict(
# other fields
- iati_activity_id = ALL,
- internal_id = ALL,
- partner_type = ALL,
+ iati_activity_id = ALL,
+ internal_id = ALL,
+ partner_type = ALL,
+ iati_organisation_role = ALL,
# foreign keys
- organisation = ALL_WITH_RELATIONS,
- project = ALL_WITH_RELATIONS,
+ organisation = ALL_WITH_RELATIONS,
+ project = ALL_WITH_RELATIONS,
)
diff --git a/akvo/api/resources/project.py b/akvo/api/resources/project.py
index a84ca95b76..85f3a72769 100644
--- a/akvo/api/resources/project.py
+++ b/akvo/api/resources/project.py
@@ -216,7 +216,7 @@ def alter_deserialized_detail_data(self, request, data):
reporting_org=temp_org['reporting_org'],
name='Incorrect business unit', #this should never be used, if it is the lookup of existing BUs is borked
long_name='Incorrect business unit',
- partner_type='sponsor',
+ iati_organisation_role=100, # old partner type sponsor partner
new_organisation_type='21',
organisation=None,
)]
diff --git a/akvo/api/xml/iati-xslt.xsl b/akvo/api/xml/iati-xslt.xsl
index 32d20a2a0e..5eb016e6d2 100644
--- a/akvo/api/xml/iati-xslt.xsl
+++ b/akvo/api/xml/iati-xslt.xsl
@@ -524,16 +524,16 @@
- support
+ 2
- support
+ 3
- funding
+ 1
- field
+ 4
diff --git a/akvo/cordaid_org_importer.py b/akvo/cordaid_org_importer.py
index 4a273cf87c..901004ffa6 100644
--- a/akvo/cordaid_org_importer.py
+++ b/akvo/cordaid_org_importer.py
@@ -16,7 +16,7 @@
from django.core.files.temp import NamedTemporaryFile
from akvo.codelists.store.codelists_v201 import ORGANISATION_TYPE as IATI_LIST_ORGANISATION_TYPE
-from akvo.rsr.models import InternalOrganisationID, Organisation, PartnerType
+from akvo.rsr.models import InternalOrganisationID, Organisation
from akvo.utils import model_and_instance_based_filename
@@ -95,8 +95,6 @@ def import_orgs(xml_file):
identifier=identifier
)
internal_org_id.save()
- for partner_type in PartnerType.objects.all():
- referenced_org.partner_types.add(partner_type)
except Exception, e:
action = "failed"
internal_org_id.delete()
diff --git a/akvo/iati/checks/v201.py b/akvo/iati/checks/v201.py
index 45403c761f..07abcb6262 100644
--- a/akvo/iati/checks/v201.py
+++ b/akvo/iati/checks/v201.py
@@ -109,15 +109,16 @@ def partners(self):
if partnership.organisation:
org = partnership.organisation
org_name = org.long_name or org.name
- if partnership.partner_type and (org.iati_org_id or org_name):
+ if (partnership.iati_organisation_role and
+ partnership.iati_organisation_role < 100 and (org.iati_org_id or org_name)):
valid_partner = True
- if not partnership.partner_type:
+ if not partnership.iati_organisation_role:
checks.append((u'error', u'missing role for partner %s' % org_name))
if not org.iati_org_id:
checks.append((u'warning', u'partner %s has no IATI identifier' % org_name))
if not org_name:
- checks.append((u'warning', u'%s partner has no organisation '
- u'name' % partnership.partner_type))
+ checks.append((u'warning', u'%s has no organisation '
+ u'name' % partnership.iati_organisation_role))
else:
checks.append((u'error', u'partnership has no organisation'))
diff --git a/akvo/iati/elements/participating_org.py b/akvo/iati/elements/participating_org.py
index 925320726f..a8346acdb7 100644
--- a/akvo/iati/elements/participating_org.py
+++ b/akvo/iati/elements/participating_org.py
@@ -23,6 +23,8 @@ def participating_org(project):
"""
partnership_elements = []
+ from akvo.rsr.models import Partnership
+
for partnership in project.partnerships.all():
org = partnership.organisation
element = etree.Element("participating-org")
@@ -32,9 +34,9 @@ def participating_org(project):
if org.new_organisation_type:
element.attrib['type'] = str(org.new_organisation_type)
-
- if partnership.partner_type in TYPE_TO_CODE.keys():
- element.attrib['role'] = TYPE_TO_CODE[partnership.partner_type]
+ # don't include old akvo sponsor partner value when checking
+ if partnership.iati_organisation_role in Partnership.IATI_ROLE_LIST[:-1]:
+ element.attrib['role'] = str(partnership.iati_organisation_role)
narrative_element = etree.SubElement(element, "narrative")
diff --git a/akvo/rest/rsr_api_upload/json/organisation_1.json b/akvo/rest/rsr_api_upload/json/organisation_1.json
index 700f29c6fc..b2010ddd18 100644
--- a/akvo/rest/rsr_api_upload/json/organisation_1.json
+++ b/akvo/rest/rsr_api_upload/json/organisation_1.json
@@ -18,11 +18,6 @@
"content_owner": null,
"allow_edit": true,
"primary_location": null,
- "partner_types": [
- "field",
- "funding",
- "sponsor"
- ],
"internal_org_ids": []
},
"organisation_location": {
diff --git a/akvo/rest/rsr_api_upload/json/project_1.json b/akvo/rest/rsr_api_upload/json/project_1.json
index 511d59dd7c..358f7c1ccf 100644
--- a/akvo/rest/rsr_api_upload/json/project_1.json
+++ b/akvo/rest/rsr_api_upload/json/project_1.json
@@ -98,7 +98,7 @@
},
"partnership": {
"organisation": "Akvo",
- "partner_type": "field",
+ "iati_organisation_role": "4",
"funding_amount": null,
"partner_type_extra": null,
"iati_activity_id": null,
@@ -107,7 +107,7 @@
},
"partnership": {
"organisation": "Akvo",
- "partner_type": "funding",
+ "iati_organisation_role": "1",
"funding_amount": 4711,
"partner_type_extra": null,
"iati_activity_id": null,
diff --git a/akvo/rest/serializers/__init__.py b/akvo/rest/serializers/__init__.py
index 1215db53e4..c4cb7871a2 100644
--- a/akvo/rest/serializers/__init__.py
+++ b/akvo/rest/serializers/__init__.py
@@ -27,7 +27,6 @@
from .organisation_location import (OrganisationLocationSerializer,
MapOrganisationLocationSerializer)
from .partner_site import PartnerSiteSerializer
-from .partner_type import PartnerTypeSerializer
from .partnership import PartnershipSerializer
from .planned_disbursement import PlannedDisbursementSerializer
from .policy_marker import PolicyMarkerSerializer
@@ -82,7 +81,6 @@
'OrganisationLocationSerializer',
'PartnershipSerializer',
'PartnerSiteSerializer',
- 'PartnerTypeSerializer',
'PlannedDisbursementSerializer',
'PolicyMarkerSerializer',
'ProjectCommentSerializer',
diff --git a/akvo/rest/serializers/partner_type.py b/akvo/rest/serializers/partner_type.py
deleted file mode 100644
index a3d123fc05..0000000000
--- a/akvo/rest/serializers/partner_type.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Akvo RSR is covered by the GNU Affero General Public License.
-# See more details in the license.txt file located at the root folder of the Akvo RSR module.
-# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
-
-
-from akvo.rsr.models import PartnerType
-
-from .rsr_serializer import BaseRSRSerializer
-
-
-class PartnerTypeSerializer(BaseRSRSerializer):
-
- class Meta:
- model = PartnerType
diff --git a/akvo/rest/serializers/partnership.py b/akvo/rest/serializers/partnership.py
index 2ba83769dd..90f38d0c5f 100644
--- a/akvo/rest/serializers/partnership.py
+++ b/akvo/rest/serializers/partnership.py
@@ -4,6 +4,7 @@
# See more details in the license.txt file located at the root folder of the Akvo RSR module.
# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
+from rest_framework import serializers
from akvo.rsr.models import Partnership
@@ -12,5 +13,7 @@
class PartnershipSerializer(BaseRSRSerializer):
+ partner_type = serializers.Field(source='iati_role_to_partner_type')
+
class Meta:
model = Partnership
diff --git a/akvo/rest/serializers/project_update_location.py b/akvo/rest/serializers/project_update_location.py
index 0bd1f624f8..dc1766e81a 100644
--- a/akvo/rest/serializers/project_update_location.py
+++ b/akvo/rest/serializers/project_update_location.py
@@ -18,6 +18,9 @@ class Meta:
class ProjectUpdateLocationExtraSerializer(ProjectUpdateLocationSerializer):
+ # Limit update data to its PK, this is needed because of Meta.depth = 2
+ location_target = serializers.Field(source='location_target.pk')
+
class Meta(ProjectUpdateLocationSerializer.Meta):
depth = 2
diff --git a/akvo/rest/urls.py b/akvo/rest/urls.py
index 23f7bf10fb..425277c909 100644
--- a/akvo/rest/urls.py
+++ b/akvo/rest/urls.py
@@ -36,7 +36,6 @@
router.register(r'organisation_location', views.OrganisationLocationViewSet)
router.register(r'organisation_map_location', views.MapOrganisationLocationViewSet)
router.register(r'partner_site', views.PartnerSiteViewSet)
-router.register(r'partner_type', views.PartnerTypeViewSet)
router.register(r'partnership', views.PartnershipViewSet)
router.register(r'planned_disbursement', views.PlannedDisbursementViewSet)
router.register(r'policy_marker', views.PolicyMarkerViewSet)
diff --git a/akvo/rest/views/__init__.py b/akvo/rest/views/__init__.py
index 38977b6865..e462ea814c 100644
--- a/akvo/rest/views/__init__.py
+++ b/akvo/rest/views/__init__.py
@@ -26,7 +26,6 @@
from .organisation import OrganisationViewSet
from .organisation_location import OrganisationLocationViewSet, MapOrganisationLocationViewSet
from .partner_site import PartnerSiteViewSet
-from .partner_type import PartnerTypeViewSet
from .partnership import PartnershipViewSet
from .planned_disbursement import PlannedDisbursementViewSet
from .policy_marker import PolicyMarkerViewSet
@@ -98,7 +97,6 @@
'OrganisationCustomFieldViewSet',
'PartnershipViewSet',
'PartnerSiteViewSet',
- 'PartnerTypeViewSet',
'PlannedDisbursementViewSet',
'PolicyMarkerViewSet',
'ProjectCommentViewSet',
diff --git a/akvo/rest/views/internal_organisation_id.py b/akvo/rest/views/internal_organisation_id.py
index 40a9603720..4adf3f5be9 100644
--- a/akvo/rest/views/internal_organisation_id.py
+++ b/akvo/rest/views/internal_organisation_id.py
@@ -18,19 +18,3 @@ class InternalOrganisationIDViewSet(BaseRSRViewSet):
serializer_class = InternalOrganisationIDSerializer
queryset = InternalOrganisationID.objects.all()
filter_fields = ('recording_org', 'referenced_org', 'identifier', )
-
- def get_queryset(self):
- """
- filter on recording org and/or identifier
- """
- queryset = self.queryset
- recording_org = self.request.QUERY_PARAMS.get('recording_org', None)
- identifier = self.request.QUERY_PARAMS.get('identifier', None)
- filter_params = {}
- if recording_org is not None:
- filter_params.update(recording_org__id=recording_org)
- if identifier is not None:
- filter_params.update(identifier=identifier)
- if filter:
- queryset = queryset.filter(**filter_params)
- return queryset
\ No newline at end of file
diff --git a/akvo/rest/views/organisation.py b/akvo/rest/views/organisation.py
index e78b6e835c..f4fc116bcd 100644
--- a/akvo/rest/views/organisation.py
+++ b/akvo/rest/views/organisation.py
@@ -71,24 +71,3 @@ class OrganisationViewSet(BaseRSRViewSet):
serializer_class = OrganisationSerializer
parser_classes = (AkvoOrganisationParser, JSONParser,)
filter_fields = ('name', 'long_name', 'iati_org_id', 'content_owner')
-
- def get_queryset(self):
- """ Enable filtering of Organisations on iati_org_id or name
- """
- queryset = super(OrganisationViewSet, self).get_queryset()
- pk = self.request.QUERY_PARAMS.get('id', None)
- if pk is not None:
- try:
- queryset = queryset.filter(pk=pk)
- except ValueError:
- pass
- iati_org_id = self.request.QUERY_PARAMS.get('iati_org_id', None)
- if iati_org_id is not None:
- queryset = queryset.filter(iati_org_id=iati_org_id)
- name = self.request.QUERY_PARAMS.get('name', None)
- if name is not None:
- queryset = queryset.filter(name=name)
- long_name = self.request.QUERY_PARAMS.get('long_name', None)
- if long_name is not None:
- queryset = queryset.filter(long_name=long_name)
- return queryset
diff --git a/akvo/rest/views/partner_type.py b/akvo/rest/views/partner_type.py
deleted file mode 100644
index 3bcd022fd2..0000000000
--- a/akvo/rest/views/partner_type.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Akvo RSR is covered by the GNU Affero General Public License.
-# See more details in the license.txt file located at the root folder of the Akvo RSR module.
-# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
-
-
-from akvo.rsr.models import PartnerType
-
-from ..serializers import PartnerTypeSerializer
-from ..viewsets import BaseRSRViewSet
-
-
-class PartnerTypeViewSet(BaseRSRViewSet):
- """
- """
- queryset = PartnerType.objects.all()
- serializer_class = PartnerTypeSerializer
diff --git a/akvo/rest/views/partnership.py b/akvo/rest/views/partnership.py
index 8fdc4c9405..9e151c4828 100644
--- a/akvo/rest/views/partnership.py
+++ b/akvo/rest/views/partnership.py
@@ -16,4 +16,14 @@ class PartnershipViewSet(BaseRSRViewSet):
"""
queryset = Partnership.objects.all()
serializer_class = PartnershipSerializer
- filter_fields = ('project', 'organisation', 'partner_type', )
+ filter_fields = ('project', 'organisation', 'iati_organisation_role', )
+
+ def get_queryset(self):
+ """Allow filtering on partner_type."""
+ queryset = self.queryset
+ partner_type = self.request.QUERY_PARAMS.get('partner_type', None)
+ if partner_type and partner_type in Partnership.PARTNER_TYPES_TO_ROLES_MAP.keys():
+ queryset = queryset.filter(
+ iati_organisation_role=Partnership.PARTNER_TYPES_TO_ROLES_MAP[partner_type]
+ )
+ return queryset
diff --git a/akvo/rest/views/project_editor.py b/akvo/rest/views/project_editor.py
index 634c47c0fe..4ba25e1bd0 100644
--- a/akvo/rest/views/project_editor.py
+++ b/akvo/rest/views/project_editor.py
@@ -80,7 +80,7 @@
PARTNER_FIELDS = (
('organisation', 'value-partner-', 'related-object'),
- ('partner_type', 'partner-type-', 'text'),
+ ('iati_organisation_role', 'partner-role-', 'integer'),
('funding_amount', 'funding-amount-', 'decimal'),
)
@@ -487,6 +487,7 @@ def log_addition(obj, user):
change_message=change_message
)
+
@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def project_editor_delete_document(request, project_pk=None, document_pk=None):
diff --git a/akvo/rest/views/project_update.py b/akvo/rest/views/project_update.py
index 38019fe90e..94316ee75f 100644
--- a/akvo/rest/views/project_update.py
+++ b/akvo/rest/views/project_update.py
@@ -19,25 +19,45 @@ class ProjectUpdateViewSet(BaseRSRViewSet):
queryset = ProjectUpdate.objects.select_related('project',
'user').prefetch_related('locations')
serializer_class = ProjectUpdateSerializer
- filter_fields = ('project', 'user', )
+ filter_fields = {
+ 'project': ['exact', ],
+ 'user': ['exact', ],
+ 'uuid': ['exact', 'icontains', ],
+ # These filters only accept a date, not a datetime
+ # 'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
+ # 'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
+ }
+
+ # filter_fields = ('project', 'user', )
paginate_by_param = 'limit'
max_paginate_by = 1000
def get_queryset(self):
- """Allow simple filtering on selected fields."""
+ """
+ Allow simple filtering on selected fields.
+ We don't use the default filter_fields, because Up filters on
+ datetime for last_modified_at, and they only support a date, not datetime.
+ """
queryset = self.queryset
- project = self.request.QUERY_PARAMS.get('project', None)
- if project is not None:
- queryset = self.queryset.filter(project=project)
- uuid = self.request.QUERY_PARAMS.get('uuid', None)
- if uuid is not None:
- queryset = self.queryset.filter(uuid=uuid)
- created_at = self.request.QUERY_PARAMS.get('created_at__gt', None)
- if created_at is not None:
- queryset = self.queryset.filter(created_at__gt=created_at)
- last_modified_at = self.request.QUERY_PARAMS.get('last_modified_at__gt', None)
- if last_modified_at is not None:
- queryset = self.queryset.filter(last_modified_at__gt=last_modified_at)
+ created_at__gt = self.request.QUERY_PARAMS.get('created_at__gt', None)
+ if created_at__gt is not None:
+ queryset = queryset.filter(created_at__gt=created_at__gt)
+ created_at__lt = self.request.QUERY_PARAMS.get('created_at__lt', None)
+ if created_at__lt is not None:
+ queryset = queryset.filter(created_at__lt=created_at__lt)
+ last_modified_at__gt = self.request.QUERY_PARAMS.get('last_modified_at__gt', None)
+ if last_modified_at__gt is not None:
+ queryset = queryset.filter(last_modified_at__gt=last_modified_at__gt)
+ last_modified_at__lt = self.request.QUERY_PARAMS.get('last_modified_at__lt', None)
+ if last_modified_at__lt is not None:
+ queryset = queryset.filter(last_modified_at__lt=last_modified_at__lt)
+ # Get updates per organisation
+ project__partners = self.request.QUERY_PARAMS.get('project__partners', None)
+ if project__partners:
+ queryset = queryset.filter(project__partners=project__partners)
+ user__organisations = self.request.QUERY_PARAMS.get('user__organisations', None)
+ if user__organisations:
+ queryset = queryset.filter(user__organisations=user__organisations)
return queryset
@@ -61,31 +81,47 @@ class ProjectUpdateExtraViewSet(BaseRSRViewSet):
'user__organisation__primary_location',
'user__organisation__primary_location__country',
'user__organisation__primary_location__location_target',
- 'user__organisation__primary_location__location_target__partner_types',
'user__organisation__primary_location__location_target__internal_org_ids',
).prefetch_related(
'user__organisations',
'user__organisations__primary_location',
'user__organisations__primary_location__country',
- 'user__organisations__primary_location__location_target',
- 'user__organisations__primary_location__location_target__partner_types')
+ 'user__organisations__primary_location__location_target')
serializer_class = ProjectUpdateExtraSerializer
- filter_fields = ('project', 'user', )
+ filter_fields = {
+ 'project': ['exact', ],
+ 'user': ['exact', ],
+ 'uuid': ['exact', 'icontains', ],
+ # These filters only accept a date, not a datetime
+ # 'created_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
+ # 'last_modified_at': ['exact', 'gt', 'gte', 'lt', 'lte', ],
+ }
def get_queryset(self):
- """Allow simple filtering on selected fields."""
+ """
+ Allow simple filtering on selected fields.
+ We don't use the default filter_fields, because Up filters on
+ datetime for last_modified_at, and they only support a date, not datetime.
+ """
queryset = self.queryset
- project = self.request.QUERY_PARAMS.get('project', None)
- if project is not None:
- queryset = self.queryset.filter(project=project)
- uuid = self.request.QUERY_PARAMS.get('uuid', None)
- if uuid is not None:
- queryset = self.queryset.filter(uuid=uuid)
- created_at = self.request.QUERY_PARAMS.get('created_at__gt', None)
- if created_at is not None:
- queryset = self.queryset.filter(created_at__gt=created_at)
- last_modified_at = self.request.QUERY_PARAMS.get('last_modified_at__gt', None)
- if last_modified_at is not None:
- queryset = self.queryset.filter(last_modified_at__gt=last_modified_at)
+ created_at__gt = self.request.QUERY_PARAMS.get('created_at__gt', None)
+ if created_at__gt is not None:
+ queryset = queryset.filter(created_at__gt=created_at__gt)
+ created_at__lt = self.request.QUERY_PARAMS.get('created_at__lt', None)
+ if created_at__lt is not None:
+ queryset = queryset.filter(created_at__lt=created_at__lt)
+ last_modified_at__gt = self.request.QUERY_PARAMS.get('last_modified_at__gt', None)
+ if last_modified_at__gt is not None:
+ queryset = queryset.filter(last_modified_at__gt=last_modified_at__gt)
+ last_modified_at__lt = self.request.QUERY_PARAMS.get('last_modified_at__lt', None)
+ if last_modified_at__lt is not None:
+ queryset = queryset.filter(last_modified_at__lt=last_modified_at__lt)
+ # Get updates per organisation
+ project__partners = self.request.QUERY_PARAMS.get('project__partners', None)
+ if project__partners:
+ queryset = queryset.filter(project__partners=project__partners)
+ user__organisations = self.request.QUERY_PARAMS.get('user__organisations', None)
+ if user__organisations:
+ queryset = queryset.filter(user__organisations=user__organisations)
return queryset
diff --git a/akvo/rest/views/user.py b/akvo/rest/views/user.py
index 8332bba66a..c8173fafae 100644
--- a/akvo/rest/views/user.py
+++ b/akvo/rest/views/user.py
@@ -30,14 +30,12 @@ class UserViewSet(BaseRSRViewSet):
'organisation__primary_location',
'organisation__primary_location__country',
'organisation__primary_location__location_target',
- 'organisation__primary_location__location_target__partner_types',
'organisation__primary_location__location_target__internal_org_ids',
).prefetch_related(
'organisations',
'organisations__primary_location',
'organisations__primary_location__country',
- 'organisations__primary_location__location_target',
- 'organisations__primary_location__location_target__partner_types')
+ 'organisations__primary_location__location_target',)
serializer_class = UserSerializer
filter_fields = ('username', 'email', 'first_name', 'last_name', 'is_active', 'is_staff',
'is_admin')
diff --git a/akvo/rsr/admin.py b/akvo/rsr/admin.py
index 257881d901..56054e603a 100644
--- a/akvo/rsr/admin.py
+++ b/akvo/rsr/admin.py
@@ -99,10 +99,9 @@ class OrganisationAdmin(TimestampsAdminDisplayMixin, ObjectPermissionsModelAdmin
fieldsets = (
(_(u'General information'), {'fields': (
- 'name', 'long_name', 'partner_types', 'organisation_type',
- 'new_organisation_type', 'can_become_reporting', 'logo', 'url', 'facebook',
- 'twitter', 'linkedin', 'iati_org_id', 'public_iati_file', 'language', 'content_owner',
- 'allow_edit',)}),
+ 'name', 'long_name', 'organisation_type', 'new_organisation_type',
+ 'can_become_reporting', 'logo', 'url', 'facebook', 'twitter', 'linkedin', 'iati_org_id',
+ 'public_iati_file', 'language', 'content_owner', 'allow_edit',)}),
(_(u'Contact information'),
{'fields': ('phone', 'mobile', 'fax', 'contact_person', 'contact_email', ), }),
(_(u'About the organisation'), {'fields': ('description', 'notes',)}),
@@ -121,16 +120,6 @@ def __init__(self, model, admin_site):
self.formfield_overrides = {ImageField: {'widget': widgets.AdminFileWidget}, }
super(OrganisationAdmin, self).__init__(model, admin_site)
- def allowed_partner_types(self, obj):
- return ', '.join([pt.label for pt in obj.partner_types.all()])
-
- def get_list_display(self, request):
- # see the notes fields in the change list if you have the right permissions
- if request.user.has_perm(self.opts.app_label + '.' + get_permission_codename('change',
- self.opts)):
- return list(self.list_display) + ['allowed_partner_types']
- return super(OrganisationAdmin, self).get_list_display(request)
-
def get_readonly_fields(self, request, obj=None):
"""Make sure only super users can set the ability to become a reporting org"""
if request.user.is_superuser:
@@ -296,21 +285,21 @@ def duplicates_in_list(seq):
if not my_org_found:
errors += [_(u'Your organisation should be somewhere here.')]
- # now check that the same org isn't assigned the same partner_type more than once
- partner_types = {}
+ # now check that the same org isn't assigned the same iati_organisation_role more than once
+ iati_organisation_roles = {}
for form in self.forms:
- # populate a dict with org names as keys and a list of partner_types as values
+ # populate a dict with org names as keys and a list of iati_organisation_roles as values
try:
if not form.cleaned_data.get('DELETE', False):
- partner_types.setdefault(
+ iati_organisation_roles.setdefault(
form.cleaned_data['organisation'], []
- ).append(form.cleaned_data['partner_type'])
+ ).append(form.cleaned_data['iati_organisation_role'])
except:
pass
- for org, types in partner_types.items():
- # are there duplicates in the list of partner_types?
- if duplicates_in_list(types):
- errors += [_(u'{} has duplicate partner types of the same kind.'.format(org))]
+ for org, roles in iati_organisation_roles.items():
+ # are there duplicates in the list of organisation roles?
+ if duplicates_in_list(roles):
+ errors += [_(u'{} has duplicate organisation roles of the same kind.'.format(org))]
self._non_form_errors = ErrorList(errors)
@@ -318,7 +307,7 @@ def duplicates_in_list(seq):
class PartnershipInline(NestedTabularInline):
model = get_model('rsr', 'Partnership')
- fields = ('organisation', 'partner_type', 'funding_amount', 'internal_id')
+ fields = ('organisation', 'iati_organisation_role', 'funding_amount', 'internal_id')
extra = 0
formset = RSR_PartnershipInlineFormFormSet
formfield_overrides = {
diff --git a/akvo/rsr/migrations/0024_auto_20150817_1120.py b/akvo/rsr/migrations/0024_auto_20150817_1120.py
new file mode 100644
index 0000000000..2ee162c3c1
--- /dev/null
+++ b/akvo/rsr/migrations/0024_auto_20150817_1120.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0023_auto_20150821_1145'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='organisation',
+ name='partner_types',
+ ),
+ migrations.DeleteModel(
+ name='PartnerType',
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0025_auto_20150819_1029.py b/akvo/rsr/migrations/0025_auto_20150819_1029.py
new file mode 100644
index 0000000000..ec5a218a57
--- /dev/null
+++ b/akvo/rsr/migrations/0025_auto_20150819_1029.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0024_auto_20150817_1120'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='partnership',
+ options={'ordering': ['iati_organisation_role'], 'verbose_name': 'project partner', 'verbose_name_plural': 'project partners'},
+ ),
+ migrations.AddField(
+ model_name='partnership',
+ name='iati_organisation_role',
+ field=models.PositiveSmallIntegerField(db_index=True, null=True, verbose_name='Organisation role', choices=[(1, 'Funding partner'), (2, 'Accountable partner'), (3, 'Extending partner'), (4, 'Implementing partner'), (100, 'Sponsor partner')]),
+ preserve_default=True,
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0026_auto_20150819_1149.py b/akvo/rsr/migrations/0026_auto_20150819_1149.py
new file mode 100644
index 0000000000..b5dc24a4b4
--- /dev/null
+++ b/akvo/rsr/migrations/0026_auto_20150819_1149.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+def merge_keys(apps, schema_editor):
+ """ This operation changes a number of keyword labels, listed in the keyword_mergers_labels dict
+ from the key (old label) of keyword_mergers_labels to the value (new label).
+ """
+ # dict with old label: new label
+ keyword_mergers_labels = {
+ u'Dutch WASH Alliance': u'WASH Alliance',
+ u'C4C': u'Connect4Change',
+ u'SRHR': u'SRHR Alliance',
+ u'f4winternational': u'Football for Water',
+ u'IGG-Water': u'IGG-water program',
+ }
+ Keyword = apps.get_model('rsr', 'Keyword')
+ Project = apps.get_model('rsr', 'Project')
+ print
+ print "Keyword distribution before migration:"
+ for keyword in Keyword.objects.all():
+ projects = Project.objects.filter(keywords__exact=keyword)
+ print keyword.label, projects.count()
+
+ # dict holding old labels as keys and the keword objects that replace the old labelled keywords
+ keyword_merger_objects = {}
+ for old_label in keyword_mergers_labels.keys():
+ try:
+ new_key = Keyword.objects.get(label=keyword_mergers_labels[old_label])
+ keyword_merger_objects[old_label] = new_key
+ except:
+ print "Error: %s keyword does not exist" % str(keyword_mergers_labels[old_label])
+
+ for project in Project.objects.all():
+ # create a list of keyword objects for the project
+ keywords = [keyword for keyword in project.keywords.all()]
+ #loop over project's keywords
+ for keyword in keywords:
+ # when we find an old labelled key
+ if keyword.label in keyword_merger_objects.keys():
+ # is the new label not there?
+ if not keyword_merger_objects[keyword.label] in keywords:
+ # then add it
+ project.keywords.add(keyword_merger_objects[keyword.label])
+ # remove old keyword
+ project.keywords.remove(keyword)
+ print "Project {}: keyword {} changed to {}".format(
+ project.id, keyword.label, keyword_merger_objects[keyword.label].label
+ )
+ project.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0025_auto_20150819_1029'),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ merge_keys
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0027_auto_20150820_0144.py b/akvo/rsr/migrations/0027_auto_20150820_0144.py
new file mode 100644
index 0000000000..33be684e11
--- /dev/null
+++ b/akvo/rsr/migrations/0027_auto_20150820_0144.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+def remove_unused_keys(apps, schema_editor):
+ Keyword = apps.get_model('rsr', 'Keyword')
+ Project = apps.get_model('rsr', 'Project')
+ print
+
+ for keyword in Keyword.objects.all():
+ projects = Project.objects.filter(keywords__exact=keyword)
+ if not projects:
+ print "Deleting keyword {}".format(keyword.label)
+ keyword.delete()
+
+ print "Keyword distribution after migration:"
+ for keyword in Keyword.objects.all():
+ projects = Project.objects.filter(keywords__exact=keyword)
+ print keyword.label, projects.count()
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0026_auto_20150819_1149'),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ remove_unused_keys
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0028_auto_20150820_0251.py b/akvo/rsr/migrations/0028_auto_20150820_0251.py
new file mode 100644
index 0000000000..9fd45bba33
--- /dev/null
+++ b/akvo/rsr/migrations/0028_auto_20150820_0251.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+def keywords_from_sponsor_partners(apps, schema_editor):
+ Keyword = apps.get_model('rsr', 'Keyword')
+ Project = apps.get_model('rsr', 'Project')
+ print
+
+ projects = Project.objects.filter(partnerships__partner_type=u'sponsor')
+ for project in projects:
+ for sponsor in project.partnerships.filter(partner_type='sponsor'):
+ keyword, created = Keyword.objects.get_or_create(
+ label="{}:{}".format(
+ sponsor.organisation.id,
+ sponsor.organisation.name,
+ )
+ )
+ project.keywords.add(keyword)
+
+ print "Keyword distribution after migration:"
+ for keyword in Keyword.objects.all():
+ projects = Project.objects.filter(keywords__exact=keyword)
+ print keyword.label, projects.count()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0027_auto_20150820_0144'),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ keywords_from_sponsor_partners
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0029_auto_20150824_1452.py b/akvo/rsr/migrations/0029_auto_20150824_1452.py
new file mode 100644
index 0000000000..9898318ba5
--- /dev/null
+++ b/akvo/rsr/migrations/0029_auto_20150824_1452.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+def types_to_roles(apps, schema_editor):
+ from akvo.rsr.models import Partnership as ps
+ Partnership = apps.get_model("rsr", "Partnership")
+ partnerships = Partnership.objects.all()
+ for partnership in partnerships:
+ if partnership.partner_type:
+ partnership.iati_organisation_role = ps.PARTNER_TYPES_TO_ROLES_MAP[
+ partnership.partner_type
+ ]
+ partnership.save()
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0028_auto_20150820_0251'),
+ ]
+
+ operations = [
+ migrations.RunPython(
+ types_to_roles,
+ ),
+ ]
diff --git a/akvo/rsr/migrations/0030_remove_partnership_partner_type.py b/akvo/rsr/migrations/0030_remove_partnership_partner_type.py
new file mode 100644
index 0000000000..250124037d
--- /dev/null
+++ b/akvo/rsr/migrations/0030_remove_partnership_partner_type.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rsr', '0029_auto_20150824_1452'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='partnership',
+ name='partner_type',
+ ),
+ ]
diff --git a/akvo/rsr/models/__init__.py b/akvo/rsr/models/__init__.py
index bd1de081e9..f1a907e447 100644
--- a/akvo/rsr/models/__init__.py
+++ b/akvo/rsr/models/__init__.py
@@ -43,7 +43,6 @@
from .organisation import Organisation
from .organisation_account import OrganisationAccount
from .partner_site import PartnerSite
-from .partner_type import PartnerType
from .partnership import Partnership
from .payment_gateway import PayPalGateway, MollieGateway, PaymentGatewaySelector
from .planned_disbursement import PlannedDisbursement
@@ -94,7 +93,6 @@
'OrganisationAccount',
'OrganisationCustomField',
'PartnerSite',
- 'PartnerType',
'Partnership',
'PayPalGateway',
'MollieGateway',
@@ -154,9 +152,6 @@
rules.add_perm('rsr.add_partnersite', is_rsr_admin)
rules.add_perm('rsr.change_partnersite', is_rsr_admin | is_org_admin)
-rules.add_perm('rsr.add_partnertype', is_rsr_admin)
-rules.add_perm('rsr.change_partnertype', is_rsr_admin)
-
rules.add_perm('rsr.change_organisationaccount', is_rsr_admin)
rules.add_perm('rsr.add_projectupdate', is_rsr_admin | is_org_admin | is_org_user_manager |
diff --git a/akvo/rsr/models/organisation.py b/akvo/rsr/models/organisation.py
index e54c3668fd..4c0d003f78 100644
--- a/akvo/rsr/models/organisation.py
+++ b/akvo/rsr/models/organisation.py
@@ -20,7 +20,6 @@
from akvo.codelists.store.codelists_v201 import ORGANISATION_TYPE as IATI_LIST_ORGANISATION_TYPE
from .country import Country
-from .partner_type import PartnerType
from .partner_site import PartnerSite
from .partnership import Partnership
from .publishing_status import PublishingStatus
@@ -88,7 +87,6 @@ def org_type_from_iati_type(cls, iati_type):
_(u'language'), max_length=2, choices=settings.LANGUAGES, default='en',
help_text=_(u'The main language of the organisation'),
)
- partner_types = models.ManyToManyField(PartnerType)
organisation_type = ValidXMLCharField(
_(u'organisation type'), max_length=1, db_index=True, choices=ORG_TYPES
)
@@ -179,31 +177,34 @@ class QuerySet(DjangoQuerySet):
def has_location(self):
return self.filter(primary_location__isnull=False)
- def partners(self, partner_type):
- "return the organisations in the queryset that are partners of type partner_type"
- return self.filter(partnerships__partner_type__exact=partner_type).distinct()
+ def partners(self, role):
+ "return the organisations in the queryset that are partners of type role"
+ return self.filter(partnerships__iati_organisation_role__exact=role).distinct()
def allpartners(self):
return self.distinct()
def fieldpartners(self):
- return self.partners(Partnership.FIELD_PARTNER)
+ return self.partners(Partnership.IATI_IMPLEMENTING_PARTNER)
def fundingpartners(self):
- return self.partners(Partnership.FUNDING_PARTNER)
+ return self.partners(Partnership.IATI_FUNDING_PARTNER)
def sponsorpartners(self):
- return self.partners(Partnership.SPONSOR_PARTNER)
+ return self.partners(Partnership.AKVO_SPONSOR_PARTNER)
def supportpartners(self):
- return self.partners(Partnership.SUPPORT_PARTNER)
+ return self.partners(Partnership.IATI_ACCOUNTABLE_PARTNER)
+
+ def extendingpartners(self):
+ return self.partners(Partnership.IATI_EXTENDING_PARTNER)
def supportpartners_with_projects(self):
"""return the organisations in the queryset that are support partners with published
projects, not counting archived projects"""
from .project import Project
return self.filter(
- partnerships__partner_type=Partnership.SUPPORT_PARTNER,
+ partnerships__iati_organisation_role=Partnership.IATI_ACCOUNTABLE_PARTNER,
partnerships__project__publishingstatus__status=PublishingStatus.STATUS_PUBLISHED,
partnerships__project__status__in=[
Project.STATUS_ACTIVE,
@@ -248,27 +249,6 @@ def iati_org_type(self):
return dict(IATI_LIST_ORGANISATION_TYPE)[str(self.new_organisation_type)] if \
self.new_organisation_type else ""
- def is_partner_type(self, partner_type):
- """returns True if the organisation is a partner of type partner_type to
- at least one project"""
- return self.partnerships.filter(partner_type__exact=partner_type).count() > 0
-
- def is_field_partner(self):
- "returns True if the organisation is a field partner to at least one project"
- return self.is_partner_type(Partnership.FIELD_PARTNER)
-
- def is_funding_partner(self):
- "returns True if the organisation is a funding partner to at least one project"
- return self.is_partner_type(Partnership.FUNDING_PARTNER)
-
- def is_sponsor_partner(self):
- "returns True if the organisation is a sponsor partner to at least one project"
- return self.is_partner_type(Partnership.SPONSOR_PARTNER)
-
- def is_support_partner(self):
- "returns True if the organisation is a support partner to at least one project"
- return self.is_partner_type(Partnership.SUPPORT_PARTNER)
-
def partnersites(self):
"returns the partnersites belonging to the organisation in a PartnerSite queryset"
return PartnerSite.objects.filter(organisation=self)
@@ -306,11 +286,10 @@ def support_partners(self):
def has_partner_types(self, project):
"""Return a list of partner types of this organisation to the project"""
- from .partnership import Partnership
partner_types = []
for ps in Partnership.objects.filter(project=project, organisation=self):
- if ps.partner_type:
- partner_types.append(ps.partner_type)
+ if ps.iati_organisation_role:
+ partner_types.append(ps.iati_organisation_role_label())
return partner_types
def countries_where_active(self):
@@ -338,7 +317,7 @@ def euros_pledged(self):
"How much € the organisation has pledged to projects it is a partner to"
return self.active_projects().euros().filter(
partnerships__organisation__exact=self,
- partnerships__partner_type__exact=Partnership.FUNDING_PARTNER
+ partnerships__iati_organisation_role__exact=Partnership.IATI_FUNDING_PARTNER
).aggregate(
euros_pledged=Sum('partnerships__funding_amount')
)['euros_pledged'] or 0
@@ -347,7 +326,7 @@ def dollars_pledged(self):
"How much $ the organisation has pledged to projects"
return self.active_projects().dollars().filter(
partnerships__organisation__exact=self,
- partnerships__partner_type__exact=Partnership.FUNDING_PARTNER
+ partnerships__iati_organisation_role__exact=Partnership.IATI_FUNDING_PARTNER
).aggregate(
dollars_pledged=Sum('partnerships__funding_amount')
)['dollars_pledged'] or 0
diff --git a/akvo/rsr/models/partner_type.py b/akvo/rsr/models/partner_type.py
deleted file mode 100644
index 4f5698c656..0000000000
--- a/akvo/rsr/models/partner_type.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Akvo RSR is covered by the GNU Affero General Public License.
-# See more details in the license.txt file located at the root folder of the Akvo RSR module.
-# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
-
-
-from django.db import models
-
-from ..fields import ValidXMLCharField
-
-
-class PartnerType(models.Model):
- id = ValidXMLCharField(max_length=8, primary_key=True, unique=True)
- label = ValidXMLCharField(max_length=30, unique=True)
-
- def __unicode__(self):
- return self.label
-
- class Meta:
- app_label = 'rsr'
- ordering = ('label',)
diff --git a/akvo/rsr/models/partnership.py b/akvo/rsr/models/partnership.py
index 5a493d752f..606f1d6da3 100644
--- a/akvo/rsr/models/partnership.py
+++ b/akvo/rsr/models/partnership.py
@@ -12,6 +12,7 @@
class Partnership(models.Model):
+ # the old way
FIELD_PARTNER = u'field'
FUNDING_PARTNER = u'funding'
SPONSOR_PARTNER = u'sponsor'
@@ -28,9 +29,46 @@ class Partnership(models.Model):
_(u'Accountable partner'),
_(u'Extending partner'),
]
-
PARTNER_TYPES = zip(PARTNER_TYPE_LIST, PARTNER_LABELS)
+ # the new way
+ IATI_FUNDING_PARTNER = 1
+ IATI_ACCOUNTABLE_PARTNER = 2
+ IATI_EXTENDING_PARTNER = 3
+ IATI_IMPLEMENTING_PARTNER = 4
+ AKVO_SPONSOR_PARTNER = 100 # not part of the IATI OrganisationRole codelist!
+
+ # make sure the AKVO_SPONSOR_PARTNER is last in the list
+ IATI_ROLE_LIST = [
+ IATI_FUNDING_PARTNER, IATI_ACCOUNTABLE_PARTNER, IATI_EXTENDING_PARTNER,
+ IATI_IMPLEMENTING_PARTNER, AKVO_SPONSOR_PARTNER,
+ ]
+ IATI_ROLE_LABELS = [
+ _(u'Funding partner'),
+ _(u'Accountable partner'),
+ _(u'Extending partner'),
+ _(u'Implementing partner'),
+ _(u'Sponsor partner'),
+ ]
+ IATI_ROLES = zip(IATI_ROLE_LIST, IATI_ROLE_LABELS)
+
+ # used when migrating
+ PARTNER_TYPES_TO_ROLES_MAP = {
+ FUNDING_PARTNER: IATI_FUNDING_PARTNER,
+ SUPPORT_PARTNER: IATI_ACCOUNTABLE_PARTNER,
+ FIELD_PARTNER: IATI_IMPLEMENTING_PARTNER,
+ SPONSOR_PARTNER: AKVO_SPONSOR_PARTNER,
+ }
+
+ # backwards compatibility
+ ROLES_TO_PARTNER_TYPES_MAP = {
+ IATI_FUNDING_PARTNER: FUNDING_PARTNER,
+ IATI_ACCOUNTABLE_PARTNER: SUPPORT_PARTNER,
+ IATI_EXTENDING_PARTNER: EXTENDING_PARTNER,
+ IATI_IMPLEMENTING_PARTNER: FIELD_PARTNER,
+ AKVO_SPONSOR_PARTNER: SPONSOR_PARTNER,
+ }
+
ALLIANCE_PARTNER = u'alliance'
KNOWLEDGE_PARTNER = u'knowledge'
NETWORK_PARTNER = u'network'
@@ -48,9 +86,8 @@ class Partnership(models.Model):
'Organisation', verbose_name=_(u'organisation'), related_name='partnerships', null=True,
help_text=_(u'Select an organisation that is taking an active role in the project.'))
project = models.ForeignKey('Project', verbose_name=_(u'project'), related_name='partnerships')
- partner_type = ValidXMLCharField(
- _(u'partner type'), max_length=9, db_index=True, choices=PARTNER_TYPES, blank=True,
- help_text=_(u'Select the role that the organisation is taking within the project.'))
+ iati_organisation_role = models.PositiveSmallIntegerField(
+ u'Organisation role', choices=IATI_ROLES, db_index=True, null=True)
funding_amount = models.DecimalField(
_(u'funding amount'), max_digits=10, decimal_places=2, blank=True, null=True, db_index=True,
help_text=_(u'The funding amount of the partner.
'
@@ -81,11 +118,20 @@ class Partnership(models.Model):
_(u'related IATI activity ID'), max_length=50, blank=True
)
+ def iati_organisation_role_label(self):
+ return dict(self.IATI_ROLES)[self.iati_organisation_role]
+
+ def iati_role_to_partner_type(self):
+ if self.iati_organisation_role:
+ return self.ROLES_TO_PARTNER_TYPES_MAP[int(self.iati_organisation_role)]
+ else:
+ return None
+
class Meta:
app_label = 'rsr'
verbose_name = _(u'project partner')
verbose_name_plural = _(u'project partners')
- ordering = ['partner_type']
+ ordering = ['iati_organisation_role']
def __unicode__(self):
if self.organisation:
@@ -98,8 +144,8 @@ def __unicode__(self):
else:
organisation_unicode = u'%s' % _(u'Organisation not specified')
- if self.partner_type:
- organisation_unicode += u' (' + unicode(dict(self.PARTNER_TYPES)[self.partner_type]) \
- + u')'
-
+ if self.iati_organisation_role:
+ organisation_unicode += u' ({})'.format(
+ unicode(dict(self.IATI_ROLES)[self.iati_organisation_role])
+ )
return organisation_unicode
diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py
index c89d3b2382..70a7a9aaa8 100644
--- a/akvo/rsr/models/project.py
+++ b/akvo/rsr/models/project.py
@@ -426,7 +426,7 @@ def get_pending_donations(self):
def get_pledged(self):
""" How much is pledges by funding organisations"""
return Partnership.objects.filter(project__exact=self).filter(
- partner_type__exact=Partnership.FUNDING_PARTNER
+ iati_organisation_role__exact=Partnership.IATI_FUNDING_PARTNER
).aggregate(Sum('funding_amount'))['funding_amount__sum'] or 0
def get_funds(self):
@@ -607,23 +607,26 @@ def all_updates(self):
return qs
#the following 6 methods return organisation querysets!
- def _partners(self, partner_type=None):
+ def _partners(self, role=None):
orgs = Organisation.objects.filter(partnerships__project__in=self)
- if partner_type:
- orgs = orgs.filter(partnerships__partner_type=partner_type)
+ if role:
+ orgs = orgs.filter(partnerships__iati_organisation_role=role)
return orgs.distinct()
def field_partners(self):
- return self._partners(Partnership.FIELD_PARTNER)
+ return self._partners(Partnership.IATI_IMPLEMENTING_PARTNER)
def funding_partners(self):
- return self._partners(Partnership.FUNDING_PARTNER)
+ return self._partners(Partnership.IATI_FUNDING_PARTNER)
def sponsor_partners(self):
- return self._partners(Partnership.SPONSOR_PARTNER)
+ return self._partners(Partnership.AKVO_SPONSOR_PARTNER)
def support_partners(self):
- return self._partners(Partnership.SUPPORT_PARTNER)
+ return self._partners(Partnership.IATI_ACCOUNTABLE_PARTNER)
+
+ def extending_partners(self):
+ return self._partners(Partnership.IATI_EXTENDING_PARTNER)
def all_partners(self):
return self._partners()
@@ -773,14 +776,14 @@ def areas_and_categories(self):
return areas
#shortcuts to linked orgs for a single project
- def _partners(self, partner_type=None):
+ def _partners(self, role=None):
"""
Return the partner organisations to the project.
- If partner_type is specified only organisations having that role are returned
+ If role is specified only organisations having that role are returned
"""
orgs = self.partners.all()
- if partner_type:
- return orgs.filter(partnerships__partner_type=partner_type).distinct()
+ if role:
+ return orgs.filter(partnerships__iati_organisation_role=role).distinct()
else:
return orgs.distinct()
@@ -795,16 +798,19 @@ def reporting_org(self):
return None
def field_partners(self):
- return self._partners(Partnership.FIELD_PARTNER)
+ return self._partners(Partnership.IATI_IMPLEMENTING_PARTNER)
def funding_partners(self):
- return self._partners(Partnership.FUNDING_PARTNER)
+ return self._partners(Partnership.IATI_FUNDING_PARTNER)
def sponsor_partners(self):
- return self._partners(Partnership.SPONSOR_PARTNER)
+ return self._partners(Partnership.AKVO_SPONSOR_PARTNER)
def support_partners(self):
- return self._partners(Partnership.SUPPORT_PARTNER)
+ return self._partners(Partnership.IATI_ACCOUNTABLE_PARTNER)
+
+ def extending_partners(self):
+ return self._partners(Partnership.IATI_EXTENDING_PARTNER)
def all_partners(self):
return self._partners()
@@ -832,7 +838,7 @@ def partners_info(self):
def funding_partnerships(self):
"Return the Partnership objects associated with the project that have funding information"
- return self.partnerships.filter(partner_type=Partnership.FUNDING_PARTNER)
+ return self.partnerships.filter(iati_organisation_role=Partnership.IATI_FUNDING_PARTNER)
def show_status_large(self):
"Show the current project status with background"
diff --git a/akvo/rsr/models/publishing_status.py b/akvo/rsr/models/publishing_status.py
index 6b31f615e9..af62654aec 100644
--- a/akvo/rsr/models/publishing_status.py
+++ b/akvo/rsr/models/publishing_status.py
@@ -11,6 +11,7 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _
+from .partnership import Partnership
from ..fields import ValidXMLCharField
@@ -72,16 +73,20 @@ def clean(self):
)
if not self.project.partnerships.filter(
- partner_type__in=['field', 'funding', 'support']
+ iati_organisation_role__in=[Partnership.IATI_FUNDING_PARTNER,
+ Partnership.IATI_IMPLEMENTING_PARTNER,
+ Partnership.IATI_ACCOUNTABLE_PARTNER]
).exists():
validation_errors.append(
ValidationError(
- _('Project needs to have at least one field, funding or support partner.'),
+ _('Project needs to have at least one funding, implementing or accountable '
+ 'partner.'),
code='partners'
)
)
else:
- for funding_partner in self.project.partnerships.filter(partner_type='funding'):
+ for funding_partner in self.project.partnerships.filter(
+ iati_organisation_role=Partnership.IATI_FUNDING_PARTNER):
if not funding_partner.funding_amount:
validation_errors.append(
ValidationError(_('All funding partners should have a funding amount.'),
diff --git a/akvo/rsr/static/scripts-src/admin/budget_item.js b/akvo/rsr/static/scripts-src/admin/budget_item.js
index 1102dd57d8..fa6e1d9dc0 100755
--- a/akvo/rsr/static/scripts-src/admin/budget_item.js
+++ b/akvo/rsr/static/scripts-src/admin/budget_item.js
@@ -23,7 +23,7 @@ $(function() {
// show or hide the associated partnerships-*-funding_amount field depending
// on the selected partner type
var selected = select.options[select.selectedIndex].value;
- if ( selected == 'funding' )
+ if ( selected == '1' )
$("input[name='partnerships-"+ select.name.split('-')[1] +"-funding_amount']").show();
else
$("input[name='partnerships-"+ select.name.split('-')[1] +"-funding_amount']").hide();
@@ -43,11 +43,11 @@ $(function() {
// find all inputs named partnerships-*-funding_amount except
// partnerships-__prefix__-funding_amount
$("input[name^='partnerships-'][name$='-funding_amount'][name!='partnerships-__prefix__-funding_amount']").each(function(i) {
- var select = $("select[name='partnerships-" + i + "-partner_type']");
+ var select = $("select[name='partnerships-" + i + "-iati_organisation_role']");
checkPartnerType(select[0]);
});
- $("select[name^='partnerships-'][name$='-partner_type']").change(function() {
+ $("select[name^='partnerships-'][name$='-iati_organisation_role']").change(function() {
checkPartnerType(this);
});
diff --git a/akvo/rsr/views/project.py b/akvo/rsr/views/project.py
index 1932177463..d70b95a434 100644
--- a/akvo/rsr/views/project.py
+++ b/akvo/rsr/views/project.py
@@ -461,17 +461,15 @@ def search(request):
def partners(request, project_id):
"""."""
project = get_object_or_404(Project, pk=project_id)
- partner_vals = project.all_partners().values()
- for partner in partner_vals:
+ partners = project.all_partners().values()
+ for partner in partners:
id_key = "id".decode('unicode-escape')
p = Organisation.objects.get(pk=partner[id_key])
partner['partner_types'] = p.has_partner_types(project)
partner['organisation_obj'] = p
- partner_types = _get_project_partners(project)
context = {
'project': project,
- 'partner_vals': partner_vals,
- 'partner_types': partner_types
+ 'partners': partners,
}
return render(request, 'project_partners.html', context)
diff --git a/akvo/scripts/cordaid/post_import.py b/akvo/scripts/cordaid/post_import.py
index 1edb73192f..cb9081250c 100644
--- a/akvo/scripts/cordaid/post_import.py
+++ b/akvo/scripts/cordaid/post_import.py
@@ -78,7 +78,7 @@ def assign_funding_partner(project, organisation, amount):
funding_partnership, created = Partnership.objects.get_or_create(
organisation=organisation,
project=project,
- partner_type=Partnership.FUNDING_PARTNER,
+ iati_organisation_role=Partnership.IATI_FUNDING_PARTNER,
defaults={'funding_amount': amount}
)
if created:
diff --git a/akvo/scripts/set_partner_types.py b/akvo/scripts/set_partner_types.py
deleted file mode 100644
index 5435be0af7..0000000000
--- a/akvo/scripts/set_partner_types.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-#!/usr/bin/env python
-
-"""
-Script for setting all organisations to at least 'field' and 'funding' partner type.
-"""
-
-import os
-os.environ['DJANGO_SETTINGS_MODULE'] = 'akvo.settings'
-
-from akvo.rsr.models import Organisation, PartnerType
-
-
-def set_partner_types():
- # Collect all organisations
- organisations = Organisation.objects.all()
-
- # Set counters
- total_count_org = len(organisations)
- field_count = 0
- funding_count = 0
-
- # Get field and funding partner type
- field_partnertype, created = PartnerType.objects.get_or_create(id="field", label="Field partner")
- funding_partnertype, created = PartnerType.objects.get_or_create(id="funding", label="Funding partner")
-
- # For each organisation, check whether they are field and/or funding partners
- for count, organisation in enumerate(organisations):
- print "Checking organisation " + str(count + 1) + " of " + str(total_count_org) + ": " + organisation.long_name
-
- if field_partnertype not in organisation.partner_types.all():
- organisation.partner_types.add(field_partnertype)
- print organisation.long_name + " added as field partner"
- field_count += 1
-
- if funding_partnertype not in organisation.partner_types.all():
- organisation.partner_types.add(funding_partnertype)
- print organisation.long_name + " added as funding partner"
- funding_count += 1
-
- print "\nDone:\n"
- print "- " + str(field_count) + " organisations added as field partner."
- print "- " + str(funding_count) + " organisations added as funding partner.\n"
-
-
-if __name__ == '__main__':
- set_partner_types()
\ No newline at end of file
diff --git a/akvo/templates/myrsr/project_editor.html b/akvo/templates/myrsr/project_editor.html
index 899eaf3178..00059c36fe 100644
--- a/akvo/templates/myrsr/project_editor.html
+++ b/akvo/templates/myrsr/project_editor.html
@@ -1248,7 +1248,7 @@
{% trans 'Keywords' %}
fundingInputNode = parentNode.getElementsByTagName("input")[1];
fundingNode = fundingInputNode.parentNode.parentNode;
- if (node.options[node.selectedIndex].value === 'funding') {
+ if (node.options[node.selectedIndex].value === '1') {
fundingInputNode.removeAttribute('disabled');
fundingInputNode.className += ' priority1';
diff --git a/akvo/templates/myrsr/project_editor_partials/partner_input.html b/akvo/templates/myrsr/project_editor_partials/partner_input.html
index 7fe670932b..a59c0fbfc9 100644
--- a/akvo/templates/myrsr/project_editor_partials/partner_input.html
+++ b/akvo/templates/myrsr/project_editor_partials/partner_input.html
@@ -21,16 +21,16 @@
-