diff --git a/akvo/api/fields.py b/akvo/api/fields.py index 963b49d951..221bab5716 100644 --- a/akvo/api/fields.py +++ b/akvo/api/fields.py @@ -43,7 +43,7 @@ def bundle_related_data_info_factory(request=None, parent_bundle=None): parent_info = parent_bundle.related_info depth = parent_info.depth - 1 ancestors = parent_info.ancestors[:] # Create a copy of the list - if not parent_bundle.obj.__class__ in parent_info.ancestors: + if parent_bundle.obj.__class__ not in parent_info.ancestors: ancestors.append(parent_bundle.obj.__class__) full = parent_info.full return BundleRelatedDataInfo(depth, ancestors, full) @@ -83,7 +83,7 @@ def dehydrate_related(self, bundle, related_resource): return related_resource.full_dehydrate(new_bundle) parent_info = getattr(bundle, 'related_info', False) - if parent_info and parent_info.depth > 0 and (not related_resource.instance.__class__ in parent_info.ancestors or parent_info.full): + if parent_info and parent_info.depth > 0 and (related_resource.instance.__class__ not in parent_info.ancestors or parent_info.full): new_bundle = related_resource.build_bundle(obj=related_resource.instance, request=bundle.request) new_bundle.related_info = bundle_related_data_info_factory(parent_bundle=bundle) return related_resource.full_dehydrate(new_bundle) @@ -126,7 +126,7 @@ def dehydrate(self, bundle, for_list=False): the_m2ms = None if isinstance(self.attribute, basestring): - the_m2ms = getattr(bundle.obj, self.attribute) + the_m2ms = getattr(bundle.obj, self.attribute) elif callable(self.attribute): the_m2ms = self.attribute(bundle) @@ -203,8 +203,7 @@ def dehydrate(self, bundle, for_list=False, return_url=True, url=""): pass return url else: - if (not self.instance_name in bundle.data - and hasattr(bundle.obj, self.instance_name)): + if self.instance_name not in bundle.data and hasattr(bundle.obj, self.instance_name): file_field = getattr(bundle.obj, self.instance_name) if file_field: try: diff --git a/akvo/api/resources/partnership.py b/akvo/api/resources/partnership.py index 34cacc9f79..0983d6daa5 100644 --- a/akvo/api/resources/partnership.py +++ b/akvo/api/resources/partnership.py @@ -98,14 +98,14 @@ def create_organisation(bundle, bundle_field_to_use): if bundle_field_to_use == FIELD_IATI_ORG_ID: kwargs[FIELD_IATI_ORG_ID] = bundle.data[FIELD_IATI_ORG_ID] try: - logger.debug("Trying to create an org with kwargs: {kwargs}".format(kwargs=kwargs)) + logger.debug("Trying to create an org") organisation = Organisation.objects.create(**kwargs) except Exception, e: - logger.exception('{message} Locals:\n {locals}\n\n'.format(message=e.message, locals=locals(), )) + logger.exception('%s' % e.message) # otherwise fall back to using the reporting_org's internal ID elif bundle_field_to_use == FIELD_INTERNAL_ORG_ID: try: - logger.debug("Trying to create an org with kwargs: {kwargs}".format(kwargs=kwargs)) + logger.debug("Trying to create an org") organisation = Organisation.objects.create(**kwargs) our_organisation = Organisation.objects.get(iati_org_id=bundle.data[FIELD_REPORTING_ORG]) InternalOrganisationID.objects.create( @@ -114,7 +114,7 @@ def create_organisation(bundle, bundle_field_to_use): identifier=bundle.data[FIELD_INTERNAL_ORG_ID], ) except Exception, e: - logger.exception('{message} Locals:\n {locals}\n\n'.format(message=e.message, locals=locals(), )) + logger.exception('%s' % e.message) return organisation diff --git a/akvo/api/resources/project_update.py b/akvo/api/resources/project_update.py index bb1902c6c5..8a30715d6e 100644 --- a/akvo/api/resources/project_update.py +++ b/akvo/api/resources/project_update.py @@ -54,18 +54,18 @@ class Meta: ) def dehydrate(self, bundle): - """ Revert the field names "created_at" and "last_modified_at" to the old "time" and "time_last_updated" - respectively to keep the API signature stable - """ - # TODO: remove this for v2 of API - bundle = super(ProjectUpdateResource, self).dehydrate(bundle) - bundle.data['time'] = bundle.data['created_at'] - bundle.data['time_last_updated'] = bundle.data['last_modified_at'] - del bundle.data['created_at'] - del bundle.data['last_modified_at'] - if isinstance(bundle.data['time'], bool): bundle.data['time'] = None - if isinstance(bundle.data['time_last_updated'], bool): bundle.data['time_last_updated'] = None - return bundle + """ Revert the field names "created_at" and "last_modified_at" to the old "time" and "time_last_updated" + respectively to keep the API signature stable + """ + # TODO: remove this for v2 of API + bundle = super(ProjectUpdateResource, self).dehydrate(bundle) + bundle.data['time'] = bundle.data['created_at'] + bundle.data['time_last_updated'] = bundle.data['last_modified_at'] + del bundle.data['created_at'] + del bundle.data['last_modified_at'] + if isinstance(bundle.data['time'], bool): bundle.data['time'] = None + if isinstance(bundle.data['time_last_updated'], bool): bundle.data['time_last_updated'] = None + return bundle class ProjectUpdateResourceExtra(ProjectUpdateResource): @@ -145,9 +145,10 @@ def build_schema(self): 'nullable': False, 'blank': False, 'readonly': True, - 'help_text': "A custom related resource with parts of data from user and the organisation the user belongs to. " - "Includes the fields full_name, organisation and resource_uri of user and absolute_url, long_name, name and resource_uri " - "of organisation.", + 'help_text': "A custom related resource with parts of data from user and the " + "organisation the user belongs to. Includes the fields full_name, " + "organisation and resource_uri of user and absolute_url, long_name, name " + "and resource_uri of organisation.", 'unique': False, } return data diff --git a/akvo/codelists/scripts/iati_codelist_generator.py b/akvo/codelists/scripts/iati_codelist_generator.py index 5a89bd9704..7c58ae9c24 100644 --- a/akvo/codelists/scripts/iati_codelist_generator.py +++ b/akvo/codelists/scripts/iati_codelist_generator.py @@ -73,7 +73,7 @@ def codelist_to_tuples(xml_string, codelist, version): for codelist_field in list(codelist_tree): for codelist_field_item in codelist_field.findall('*'): unicode_field_tag = "u'" + codelist_field_item.tag + "'" - if not unicode_field_tag in fields: + if unicode_field_tag not in fields: fields.append(unicode_field_tag) fields_string = ', '.join(fields) @@ -82,7 +82,7 @@ def codelist_to_tuples(xml_string, codelist, version): codelist_field_content = ["" for _field in fields] codelist_tags = [] for codelist_field_item in codelist_field.findall('*'): - if not codelist_field_item.tag in codelist_tags: + if codelist_field_item.tag not in codelist_tags: codelist_tags.append(codelist_field_item.tag) list_index = fields.index("u'" + codelist_field_item.tag + "'") if codelist_field_item.text: @@ -141,7 +141,7 @@ def generate_code_lists(version): result = requests.get(codelist_url % codelist) if result.status_code == 200 and len(result.text) > 0: print "Generating python for %s..." % codelist - if not codelist in ["IATIOrganisationIdentifier", ]: + if codelist not in ["IATIOrganisationIdentifier", ]: # IATIOrganisationIdentifier is not a codelist python_code.append('# From %s' % codelist_url % codelist) python_code.append('\n') @@ -159,7 +159,7 @@ def generate_code_lists(version): args = parser.parse_args() # Version has to be one of the allowed versions - if not args.version in VERSIONS.keys(): + if args.version not in VERSIONS.keys(): print "Error; Version should be one of the following:" for version in VERSIONS.keys(): print "- %s" % version diff --git a/akvo/cordaid_org_importer.py b/akvo/cordaid_org_importer.py index 046a545799..4a273cf87c 100644 --- a/akvo/cordaid_org_importer.py +++ b/akvo/cordaid_org_importer.py @@ -101,8 +101,7 @@ def import_orgs(xml_file): action = "failed" internal_org_id.delete() referenced_org.delete() - print(u"*** UNABLE TO CREATE NEW ORGANISATION! " - "Reason: {message}.".format(e.message)) + print(u"*** UNABLE TO CREATE NEW ORGANISATION! ") name = element.findtext("name") referenced_org.name, referenced_org.long_name = name[:25], name referenced_org.description = element.findtext("description") or "N/A" diff --git a/akvo/iati/elements/transaction.py b/akvo/iati/elements/transaction.py index 073b5c3fce..b935d07587 100644 --- a/akvo/iati/elements/transaction.py +++ b/akvo/iati/elements/transaction.py @@ -7,6 +7,76 @@ from lxml import etree +def _provider_organisation(element, trans): + """ + Helper function for transaction() + """ + org = trans.provider_organisation + provider_org_element = etree.SubElement(element, "provider-org") + + if trans.provider_organisation_activity: + provider_org_element.attrib['provider-activity-id'] = trans.provider_organisation_activity + + if org.iati_org_id: + provider_org_element.attrib['ref'] = org.iati_org_id + + if org.long_name: + narrative_element = etree.SubElement(provider_org_element, "narrative") + narrative_element.text = org.long_name + elif org.name: + narrative_element = etree.SubElement(provider_org_element, "narrative") + narrative_element.text = org.name + + return element + + +def _receiver_organisation(element, trans): + """ + Helper function for transaction() + """ + org = trans.receiver_organisation + receiver_org_element = etree.SubElement(element, "receiver-org") + + if trans.receiver_organisation_activity: + receiver_org_element.attrib['receiver-activity-id'] = trans.receiver_organisation_activity + + if org.iati_org_id: + receiver_org_element.attrib['ref'] = org.iati_org_id + + if org.long_name: + narrative_element = etree.SubElement(receiver_org_element, "narrative") + narrative_element.text = org.long_name + elif org.name: + narrative_element = etree.SubElement(receiver_org_element, "narrative") + narrative_element.text = org.name + + return element + + +def _sector(element, sector): + """ + Helper function for transaction() + """ + if sector.code: + sector_element = etree.SubElement(element, "sector") + sector_element.attrib['code'] = sector.code + + if not sector.vocabulary or sector.vocabulary == 'DAC': + sector_element.attrib['vocabulary'] = '1' + elif sector.vocabulary == 'DAC-3': + sector_element.attrib['vocabulary'] = '2' + else: + sector_element.attrib['vocabulary'] = sector.vocabulary + + if sector.percentage: + sector_element.attrib['percentage'] = str(sector.percentage) + + if sector.text: + sector_element.text = sector.text + + return element + + def transaction(project): """ Generate the transaction elements. @@ -47,60 +117,17 @@ def transaction(project): narrative_element.text = trans.description if trans.provider_organisation: - org = trans.provider_organisation - provider_org_element = etree.SubElement(element, "provider-org") - - if trans.provider_organisation_activity: - provider_org_element.attrib['provider-activity-id'] = trans.provider_organisation_activity - - if org.iati_org_id: - provider_org_element.attrib['ref'] = org.iati_org_id - - if org.long_name: - narrative_element = etree.SubElement(provider_org_element, "narrative") - narrative_element.text = org.long_name - elif org.name: - narrative_element = etree.SubElement(provider_org_element, "narrative") - narrative_element.text = org.name + element = _provider_organisation(element, trans) if trans.receiver_organisation: - org = trans.receiver_organisation - receiver_org_element = etree.SubElement(element, "receiver-org") - - if trans.receiver_organisation_activity: - receiver_org_element.attrib['receiver-activity-id'] = trans.receiver_organisation_activity - - if org.iati_org_id: - receiver_org_element.attrib['ref'] = org.iati_org_id - - if org.long_name: - narrative_element = etree.SubElement(receiver_org_element, "narrative") - narrative_element.text = org.long_name - elif org.name: - narrative_element = etree.SubElement(receiver_org_element, "narrative") - narrative_element.text = org.name + element = _receiver_organisation(element, trans) if trans.disbursement_channel: disbursement_channel_element = etree.SubElement(element, "disbursement-channel") disbursement_channel_element.attrib['code'] = trans.disbursement_channel for sector in trans.sectors.all(): - if sector.code: - sector_element = etree.SubElement(element, "sector") - sector_element.attrib['code'] = sector.code - - if not sector.vocabulary or sector.vocabulary == 'DAC': - sector_element.attrib['vocabulary'] = '1' - elif sector.vocabulary == 'DAC-3': - sector_element.attrib['vocabulary'] = '2' - else: - sector_element.attrib['vocabulary'] = sector.vocabulary - - if sector.percentage: - sector_element.attrib['percentage'] = str(sector.percentage) - - if sector.text: - sector_element.text = sector.text + element = _sector(element, sector) if trans.recipient_country: recipient_country_element = etree.SubElement(element, "recipient-country") diff --git a/akvo/rest/rsr_api_upload/create_rsr_data.py b/akvo/rest/rsr_api_upload/create_rsr_data.py index 4f6945410f..930361db05 100644 --- a/akvo/rest/rsr_api_upload/create_rsr_data.py +++ b/akvo/rest/rsr_api_upload/create_rsr_data.py @@ -131,59 +131,59 @@ def project_update__photo(self): self.data['photo'] = photo.to_base64() - def partnership__organisation(self): - """ The Partnership object has two FKs, one to the Project and one to the Organisation. Since the Organisation - may not exist when the JSON is created we need a way to lookup the Organisation from the API. This is done - using the IATI organisation ID if it exists, and if not lookup of the organisation name is used. - Note that currently Organisation.name isn't unique. This should be fixed. - """ - def lookup_organisation(instance): - request = Requester( - url_template='http://{host}/rest/v1/{model}/?iati_org_id={iati_org_id}', - url_args=dict(host=instance.host, model='organisation', iati_org_id=instance.data['organisation']), - headers={ - 'Content-type': 'application/json', - 'Accept': 'application/json', - 'Encoding': 'utf-8', - 'Authorization': 'Token {}'.format(instance.api_token), - }, - accept_codes=[codes.ok], - ) - if request.response.status_code == codes.ok: - data = data_from_json_reponse(request) - if data['count'] == 1: - return data['results'][0]['id'] - - request = Requester( - url_template='http://{host}/rest/v1/{model}/?name={name}', - url_args=dict(host=instance.host, model='organisation', name=instance.data['organisation']), - headers={ - 'Content-type': 'application/json', - 'Accept': 'application/json', - 'Encoding': 'utf-8', - 'Authorization': 'Token {}'.format(instance.api_token), - }, - accept_codes=[codes.ok], - ) - if request.response.status_code == codes.ok: - data = data_from_json_reponse(request) - if data['count'] == 1: - return data['results'][0]['id'] - - raise NotFoundError, 'Organisation not found, using filters "iati_org_id={}" and "name={}"'.format( - instance.data['organisation'], instance.data['organisation'] - ) - - organisation = self.data.get('organisation', False) - if organisation: - # if we have an integer ID we're happy - if isinstance(organisation, int): - return - # otherwise try to get the ID from the API - elif isinstance(organisation, (str, unicode)): - id = lookup_organisation(self) - if id: - self.data['organisation'] = id + # def partnership__organisation(self): + # """ The Partnership object has two FKs, one to the Project and one to the Organisation. Since the Organisation + # may not exist when the JSON is created we need a way to lookup the Organisation from the API. This is done + # using the IATI organisation ID if it exists, and if not lookup of the organisation name is used. + # Note that currently Organisation.name isn't unique. This should be fixed. + # """ + # def lookup_organisation(instance): + # request = Requester( + # url_template='http://{host}/rest/v1/{model}/?iati_org_id={iati_org_id}', + # url_args=dict(host=instance.host, model='organisation', iati_org_id=instance.data['organisation']), + # headers={ + # 'Content-type': 'application/json', + # 'Accept': 'application/json', + # 'Encoding': 'utf-8', + # 'Authorization': 'Token {}'.format(instance.api_token), + # }, + # accept_codes=[codes.ok], + # ) + # if request.response.status_code == codes.ok: + # data = data_from_json_reponse(request) + # if data['count'] == 1: + # return data['results'][0]['id'] + # + # request = Requester( + # url_template='http://{host}/rest/v1/{model}/?name={name}', + # url_args=dict(host=instance.host, model='organisation', name=instance.data['organisation']), + # headers={ + # 'Content-type': 'application/json', + # 'Accept': 'application/json', + # 'Encoding': 'utf-8', + # 'Authorization': 'Token {}'.format(instance.api_token), + # }, + # accept_codes=[codes.ok], + # ) + # if request.response.status_code == codes.ok: + # data = data_from_json_reponse(request) + # if data['count'] == 1: + # return data['results'][0]['id'] + # + # raise NotFoundError, 'Organisation not found, using filters "iati_org_id={}" and "name={}"'.format( + # instance.data['organisation'], instance.data['organisation'] + # ) + # + # organisation = self.data.get('organisation', False) + # if organisation: + # # if we have an integer ID we're happy + # if isinstance(organisation, int): + # return + # # otherwise try to get the ID from the API + # elif isinstance(organisation, (str, unicode)): + # id = lookup_organisation(self) + # if id: + # self.data['organisation'] = id def lookup_organisation(self): diff --git a/akvo/rest/views/__init__.py b/akvo/rest/views/__init__.py index fe3bf31529..b9caab0aec 100644 --- a/akvo/rest/views/__init__.py +++ b/akvo/rest/views/__init__.py @@ -100,7 +100,7 @@ 'SectorViewSet', 'set_group', 'TransactionViewSet', - 'typeahead_country,', + 'typeahead_country', 'typeahead_organisation', 'typeahead_project', 'typeahead_projectupdate', diff --git a/akvo/rsr/admin.py b/akvo/rsr/admin.py index 27a3ea2be7..e7cb4a5db7 100644 --- a/akvo/rsr/admin.py +++ b/akvo/rsr/admin.py @@ -14,7 +14,7 @@ from django.contrib.admin.options import IS_POPUP_VAR from django.contrib.admin.util import flatten_fieldsets from django.contrib.auth import get_permission_codename, get_user_model -from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin from django.contrib.auth.models import Group from django.db import models, transaction from django.db.models import get_model @@ -780,7 +780,7 @@ def add_view(self, request, form_url='', extra_context=None): prefix = FormSet.get_default_prefix() # check if we're trying to create a new project by copying an existing one. If so # we ignore location and benchmark inlines - if not "_saveasnew" in request.POST or not prefix in ['benchmarks', 'rsr-location-content_type-object_id']: + if "_saveasnew" not in request.POST or not prefix in ['benchmarks', 'rsr-location-content_type-object_id']: # end of add although the following block is indented as a result prefixes[prefix] = prefixes.get(prefix, 0) + 1 if prefixes[prefix] != 1 or not prefix: @@ -869,7 +869,7 @@ def has_delete_permission(self, request, obj=None, **kwargs): return False -class UserAdmin(UserAdmin): +class UserAdmin(DjangoUserAdmin): model = get_model('rsr', 'user') fieldsets = ( (None, {'fields': ('username', 'email', 'password')}), diff --git a/akvo/rsr/fields.py b/akvo/rsr/fields.py index 244efe6b24..5d4c5d9a1d 100644 --- a/akvo/rsr/fields.py +++ b/akvo/rsr/fields.py @@ -89,7 +89,7 @@ def clean(self, value, model_instance): """ if model_instance.id and model_instance.id <= getattr(settings, 'OLD_PROJECT_MAX_ID', 0): self.validators = [v for v in self.validators if type(v) != MaxLengthValidator] - return super(LimitedTextField, self).clean(value, model_instance) + return super(ProjectLimitedTextField, self).clean(value, model_instance) # needed to get custom fields work with South. diff --git a/akvo/rsr/mixins.py b/akvo/rsr/mixins.py index 1683852738..89db4336d0 100644 --- a/akvo/rsr/mixins.py +++ b/akvo/rsr/mixins.py @@ -30,7 +30,7 @@ def get_fieldsets(self, request, obj=None): def add_or_remove(obj, fields, name): # if the field has a value, add it to the fields tuple, unless it's already there if getattr(obj, name, None): - if not name in fields: + if name not in fields: fields += (name,) # otherwise make sure it's removed else: diff --git a/akvo/rsr/models/employment.py b/akvo/rsr/models/employment.py index 023b3783d8..6d7d038fd2 100644 --- a/akvo/rsr/models/employment.py +++ b/akvo/rsr/models/employment.py @@ -9,7 +9,7 @@ from django.contrib.auth.models import Group from django.contrib.contenttypes.models import ContentType from django.db import models -from django.db.models.query import QuerySet +from django.db.models.query import QuerySet as DjangoQuerySet from django.forms.models import model_to_dict from django.utils.encoding import force_unicode from django.utils.translation import ugettext_lazy as _ @@ -34,7 +34,7 @@ class Employment(models.Model): objects = QuerySetManager() - class QuerySet(QuerySet): + class QuerySet(DjangoQuerySet): def organisations(self): """ Return an Organisation QuerySet containing the organisations of the Employment QuerySet diff --git a/akvo/rsr/models/iati_export.py b/akvo/rsr/models/iati_export.py index 6bf84fac38..4d95bc2469 100644 --- a/akvo/rsr/models/iati_export.py +++ b/akvo/rsr/models/iati_export.py @@ -44,7 +44,7 @@ class Meta: verbose_name_plural = _(u'IATI exports') def show_status(self): - if not self.status in STATUS_CODE.keys(): + if self.status not in STATUS_CODE.keys(): return _(u'unknown status') else: return STATUS_CODE[int(self.status)] diff --git a/akvo/rsr/models/organisation.py b/akvo/rsr/models/organisation.py index 3a71c41784..a42e7a2ba3 100644 --- a/akvo/rsr/models/organisation.py +++ b/akvo/rsr/models/organisation.py @@ -8,7 +8,7 @@ from django.conf import settings from django.db import models from django.db.models import Sum -from django.db.models.query import QuerySet +from django.db.models.query import QuerySet as DjangoQuerySet from django.utils.translation import ugettext_lazy as _ from sorl.thumbnail.fields import ImageField @@ -171,7 +171,7 @@ def org_type_from_iati_type(cls, iati_type): def get_absolute_url(self): return ('organisation-main', (), {'organisation_id': self.pk}) - class QuerySet(QuerySet): + class QuerySet(DjangoQuerySet): def has_location(self): return self.filter(primary_location__isnull=False) diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py index 5d2c85fcc8..fe76e6beac 100644 --- a/akvo/rsr/models/project.py +++ b/akvo/rsr/models/project.py @@ -12,7 +12,7 @@ from django.db import models from django.db.models import Max, Sum from django.db.models.signals import post_save -from django.db.models.query import QuerySet +from django.db.models.query import QuerySet as DjangoQuerySet from django.dispatch import receiver from django.utils.safestring import mark_safe from django.utils.translation import ugettext, ugettext_lazy as _ @@ -411,7 +411,7 @@ def view_count(self): return counter.count or 0 - class QuerySet(QuerySet): + class QuerySet(DjangoQuerySet): def of_partner(self, organisation): "return projects that have organisation as partner" @@ -773,7 +773,7 @@ def partners_info(self): partners_info = {} for partnership in Partnership.objects.filter(project=self): funding_amount = partnership.funding_amount if partnership.funding_amount else None - if not partnership.organisation in partners_info.keys(): + if partnership.organisation not in partners_info.keys(): partners_info[partnership.organisation] = [[partnership], funding_amount] else: partners_info[partnership.organisation][0].append(partnership) diff --git a/akvo/rsr/models/user.py b/akvo/rsr/models/user.py index 945ad9ed46..73d0b13631 100644 --- a/akvo/rsr/models/user.py +++ b/akvo/rsr/models/user.py @@ -308,7 +308,7 @@ def allow_edit(self, project): return True # Only Partner admins on the list of "limiters" list may edit else: - if self.get_is_org_admin() and self.organisation in partner_admins_allowed: + if self.organisation in partner_admins_allowed: return True return False diff --git a/akvo/rsr/signals.py b/akvo/rsr/signals.py index 9136f7c218..3c433115c6 100644 --- a/akvo/rsr/signals.py +++ b/akvo/rsr/signals.py @@ -17,7 +17,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.core.files.uploadedfile import InMemoryUploadedFile -from django.db.models import get_model, ImageField +from django.db.models import get_model from django.db.models.signals import post_save from sorl.thumbnail import ImageField diff --git a/akvo/rsr/templatetags/maps.py b/akvo/rsr/templatetags/maps.py index b85d95eb27..0cb419b3c1 100644 --- a/akvo/rsr/templatetags/maps.py +++ b/akvo/rsr/templatetags/maps.py @@ -304,6 +304,7 @@ def global_project_map(width, height, dynamic='dynamic'): map_id = 'akvo_map_%s' % os.urandom(8).encode('hex') locations = [] + update_locations = [] for project in Project.objects.published(): try: diff --git a/akvo/rsr/views/project.py b/akvo/rsr/views/project.py index 989db59b26..24ec14f23c 100644 --- a/akvo/rsr/views/project.py +++ b/akvo/rsr/views/project.py @@ -163,10 +163,12 @@ def _get_timeline_data(project): for date in (date_start, date_end): if date: + headline = date[1] + " date of project" + if not date[2] == 'actual': + headline += " (planned)" timeline_dates.append({ 'startDate': ','.join((str(date[0].year), str(date[0].month), str(date[0].day))), - 'headline': date[1] + " date of project" if date[2] == 'actual' else date[1] + - " date of project (planned)", + 'headline': headline, }) # Project updates diff --git a/akvo/scripts/cordaid/cordaid_project_upload.py b/akvo/scripts/cordaid/cordaid_project_upload.py index 20792a9289..5252941494 100644 --- a/akvo/scripts/cordaid/cordaid_project_upload.py +++ b/akvo/scripts/cordaid/cordaid_project_upload.py @@ -43,9 +43,9 @@ def compare_dicts(dict1, dict2): dict2_extra = 0 # Check if xml:lang attribute is present in one dict and missing in the other - if XML_LANG in dict1 and (not XML_LANG in dict2): + if XML_LANG in dict1 and XML_LANG not in dict2: dict1_extra += 1 - elif XML_LANG in dict2 and (not XML_LANG in dict1): + elif XML_LANG in dict2 and XML_LANG not in dict1: dict2_extra += 1 # Return False if the number of shared attributes is different @@ -66,7 +66,7 @@ def check_lang(element, lang): """Check if the element has the xml:lang corresponding to the activity language or no xml:lang attribute. Return True if so, False otherwise.""" - if not XML_LANG in element.attrib: + if XML_LANG not in element.attrib: return True elif element.attrib[XML_LANG].lower() == lang: @@ -113,7 +113,7 @@ def post_an_activity(activity_element, user): project = Requester( method='post', url_template="http://{domain}/api/{api_version}/iati_activity/" - "?format=xml&api_key={api_key}&username={username}", + "?format=xml&api_key={api_key}&username={username}", url_args=user, headers={'content-type': 'application/xml', 'encoding': 'utf-8'}, data=etree.tostring(activity_element), @@ -158,7 +158,7 @@ def put_an_activity(activity_element, pk, url_args): project = Requester( method='put', url_template="http://{domain}/api/{api_version}/iati_activity/{pk}/?" - "format=xml&api_key={api_key}&username={username}", + "format=xml&api_key={api_key}&username={username}", url_args=url_args, headers={'content-type': 'application/xml', 'encoding': 'utf-8'}, data=etree.tostring(activity_element), @@ -268,7 +268,7 @@ def get_project_count(user, **q_args): try: project = Requester( url_template="http://{domain}/api/{api_version}/project/?" - "format=json&api_key={api_key}&username={username}&{extra_args}", + "format=json&api_key={api_key}&username={username}&{extra_args}", url_args=url_args ) except Exception, e: @@ -311,7 +311,7 @@ def upload_activities(argv): log(None, data) print( "**** Error updating iati-activity: {iati_id}. " - "More than one project with internal ID {extra} exists.".format(**data) + "More than one project with internal ID {extra} exists.".format(**data) ) else: message = "Iati-activity {iati_id} has no participating-orgs, aborting" diff --git a/akvo/scripts/cordaid/organisation_upload.py b/akvo/scripts/cordaid/organisation_upload.py index dd64ba8230..d7f07a0b24 100644 --- a/akvo/scripts/cordaid/organisation_upload.py +++ b/akvo/scripts/cordaid/organisation_upload.py @@ -51,7 +51,8 @@ def user_org(user_cred): try: profile = Requester( url_template="http://{domain}/api/{api_version}/user/?" - "format=json&api_key={api_key}&username={username}&user__username={username}", + "format=json&api_key={api_key}&username={username}&" + "user__username={username}", url_args=user_cred ) # find the organisation ID in the path string, e.g. "/api/v1/organisation/42/" @@ -72,7 +73,7 @@ def find_org(user_cred, reporting_org_id, internal_org_id): try: ioi = Requester( url_template="http://{domain}/rest/v1/internal_organisation_id/?" - "recording_org={recording_org}&identifier={identifier}&format=json", + "recording_org={recording_org}&identifier={identifier}&format=json", url_args=url_args, headers={ 'content-type': 'application/xml', 'encoding': 'utf-8', diff --git a/akvo/scripts/cordaid/pre_import.py b/akvo/scripts/cordaid/pre_import.py index 8392164d4c..688240cb31 100644 --- a/akvo/scripts/cordaid/pre_import.py +++ b/akvo/scripts/cordaid/pre_import.py @@ -11,6 +11,7 @@ import os from os.path import splitext, basename import sys +import django from django.core.files import File from django.core.files.temp import NamedTemporaryFile @@ -179,269 +180,270 @@ def normalize_url(url): url = u"" return url -def import_orgs(xml_file): - outsys("\nRunning {}() ".format(who_am_i())) - - def text_from_xpath(tree, xpath): - """ utility to get the text of an element using xpath, stripped - returns '' unless the xpath returns exactly one element - """ - element = tree.xpath(xpath) - if len(element) != 1: - return '' - return element[0].text.strip() if element[0].text else '' - - def data_from_xpaths(xpaths, etree): - """ use the xpaths dict to replace the values with the actual data in the etree - that is retrieved when using text_from_xpath() with the xpath - """ - return {key: text_from_xpath(etree, xpath) for key, xpath in xpaths.items()} - - def org_data_from_xml(org_etree): - # keys are Organisation field names, values are xpath expressions for getting those values from the org_etree - xpaths = dict( - name='name', - description='description', - url='url', - new_organisation_type='iati_organisation_type', - ) - # get the raw data from the org_etree - org_data = data_from_xpaths(xpaths, org_etree) - # transform data - org_data['long_name'] = org_data['name'] - org_data['name'] = org_data['name'][:25] - org_data['organisation_type'] = Organisation.org_type_from_iati_type(int(org_data['new_organisation_type'])) - return org_data - - def create_new_organisation(org_etree, internal_id): - try: - org_dict = org_data_from_xml(org_etree) - referenced_org = Organisation.objects.create(**org_dict) - log( - u"Created new organisation: {label}, Akvo ID: {pk}", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - label=referenced_org.name, - pk=referenced_org.pk, - event=ACTION_CREATE_ORG - ) - ) - return referenced_org - except Exception, e: - log( - u"Error trying to create organisation with Cordaid ID {internal_id} ", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - event=ERROR_EXCEPTION, - extra=e.message - ) - ) - - def update_organisation(org_etree, internal_org_id, cordaid): - try: - org_dict = org_data_from_xml(org_etree) - referenced_org = internal_org_id.referenced_org - update_org = Organisation.objects.filter(pk=referenced_org.pk) - content_owner = update_org[0].content_owner - if content_owner and content_owner != cordaid: - log( - u"Organisation content owned by different organisation: {label}, Akvo ID: {pk}, owned by: {extra}", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_org_id.identifier, - label=referenced_org.name, - pk=referenced_org.pk, - event=OWNER_CONTENT, - extra=content_owner.name - ) - ) - # return None so that organisation does not get updated afterwards - return None - else: - update_org.update(**org_dict) - log( - u"Updated organisation: {label}, Akvo ID: {pk}", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_org_id.identifier, - label=referenced_org.name, - pk=referenced_org.pk, - event=ACTION_UPDATE_ORG - ) - ) - # return the updated organisation record to be used in the following steps - return update_org[0] - except Exception, e: - log( - u"Error trying to update organisation with Cordaid ID {internal_id} ", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_org_id.identifier, - event=ERROR_EXCEPTION, - extra=e.message - ) - ) - - def set_location_for_org(org_etree, internal_id, org): - if not org.primary_location: - iso_code = text_from_xpath(org_etree, 'location/object/iso_code').lower() - if not iso_code == "ww!": - country = custom_get_or_create_country(iso_code) - location = OrganisationLocation.objects.create( - country=country, - location_target=org - ) - org.locations.add(location) - org.primary_location = location - org.save() - log( - u" Added location to org {pk}", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - pk=org.pk, - label=org.name, - event=ACTION_LOCATION_SET, - ) - ) - else: - log( - u"Couldn't create location for org {pk}, no proper country code", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - pk=org.pk, - label=org.name, - event=ERROR_COUNTRY_CODE, - ) - ) - else: - log( - u" Org {pk} already has a location.", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - pk=org.pk, - label=org.name, - event=ACTION_LOCATION_FOUND, - ) - ) - - def organisation_logo(org_etree, internal_id, org): - logo_file = glob.glob( - os.path.join( - CORDAID_LOGOS_DIR, - u"{logo_id}.*".format(logo_id=text_from_xpath(org_etree, 'logo_id')) - ) - ) - if len(logo_file) == 1: - logo_filename = basename(logo_file[0]) - _, extension = splitext(logo_filename) - if extension.lower() in (".png", ".jpg", ".jpeg", ".gif"): - filename = model_and_instance_based_filename( - "Organisation", - org.pk, - "logo", - logo_filename - ) - with open(os.path.join(CORDAID_LOGOS_DIR, logo_filename), "rb") as f: - logo_data = f.read() - logo_tmp = NamedTemporaryFile(delete=True) - logo_tmp.write(logo_data) - logo_tmp.flush() - org.logo.save( - filename, File(logo_tmp), save=True - ) - log( - u" Added logo {extra} to org {pk}, ", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - pk=org.pk, - label=org.name, - event=ACTION_SET_IMAGE, - extra= filename, - ) - ) - - with open(xml_file, "rb") as f: - root = etree.fromstring(f.read()) - cordaid = Organisation.objects.get(id=CORDAID_ORG_ID) - for org_etree in root: - outsys('.') - internal_id = text_from_xpath(org_etree, 'org_id') - try: - internal_org_id = InternalOrganisationID.objects.get( - recording_org=cordaid, - identifier=internal_id - ) - log( - u"Found existing org {label} (Akvo PK {pk}) with Cordaid internal ID '{internal_id}'", - dict( - log_type=LOG_ORGANISATIONS, - label=internal_org_id.referenced_org.name, - pk=internal_org_id.referenced_org.pk, - internal_id=internal_id, - event=ACTION_FOUND - ) - ) - referenced_org = update_organisation(org_etree, internal_org_id, cordaid) - if referenced_org: - set_location_for_org(org_etree, internal_id, referenced_org) - except InternalOrganisationID.MultipleObjectsReturned: - log( - u"Error from lookup of internal ID {internal_id}. Multiple objects found.", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - event=ERROR_MULTIPLE_OBJECTS - ), - ) - continue - except InternalOrganisationID.DoesNotExist: - referenced_org = create_new_organisation(org_etree, internal_id) - if referenced_org: - try: - set_location_for_org(org_etree, internal_id, referenced_org) - internal_org_id=InternalOrganisationID.objects.create( - recording_org=cordaid, - referenced_org=referenced_org, - identifier=internal_id - ) - log( - u"Created InternalOrganisationID for org: {label} (Akvo PK {pk}) with Cordaid internal ID '{internal_id}'", - dict( - log_type=LOG_ORGANISATIONS, - label=internal_org_id.referenced_org.name, - pk=internal_org_id.referenced_org.pk, - internal_id=internal_id, - event=ACTION_CREATE_IOI - ) - ) - except Exception, e: - log( - u"Error trying to organisation location for org with Cordaid ID {internal_id} ", - dict( - log_type=LOG_ORGANISATIONS, - internal_id=internal_id, - event=ERROR_EXCEPTION, - extra=e.message - ) - ) - else: - continue - if referenced_org: - organisation_logo(org_etree, internal_id, referenced_org) - outsys('\n') +# def import_orgs(xml_file): +# outsys("\nRunning {}() ".format(who_am_i())) +# +# def text_from_xpath(tree, xpath): +# """ utility to get the text of an element using xpath, stripped +# returns '' unless the xpath returns exactly one element +# """ +# element = tree.xpath(xpath) +# if len(element) != 1: +# return '' +# return element[0].text.strip() if element[0].text else '' +# +# def data_from_xpaths(xpaths, etree): +# """ use the xpaths dict to replace the values with the actual data in the etree +# that is retrieved when using text_from_xpath() with the xpath +# """ +# return {key: text_from_xpath(etree, xpath) for key, xpath in xpaths.items()} +# +# def org_data_from_xml(org_etree): +# # keys are Organisation field names, values are xpath expressions for getting those values from the org_etree +# xpaths = dict( +# name='name', +# description='description', +# url='url', +# new_organisation_type='iati_organisation_type', +# ) +# # get the raw data from the org_etree +# org_data = data_from_xpaths(xpaths, org_etree) +# # transform data +# org_data['long_name'] = org_data['name'] +# org_data['name'] = org_data['name'][:25] +# org_data['organisation_type'] = Organisation.org_type_from_iati_type(int(org_data['new_organisation_type'])) +# return org_data +# +# def create_new_organisation(org_etree, internal_id): +# try: +# org_dict = org_data_from_xml(org_etree) +# referenced_org = Organisation.objects.create(**org_dict) +# log( +# u"Created new organisation: {label}, Akvo ID: {pk}", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# label=referenced_org.name, +# pk=referenced_org.pk, +# event=ACTION_CREATE_ORG +# ) +# ) +# return referenced_org +# except Exception, e: +# log( +# u"Error trying to create organisation with Cordaid ID {internal_id} ", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# event=ERROR_EXCEPTION, +# extra=e.message +# ) +# ) +# +# def update_organisation(org_etree, internal_org_id, cordaid): +# try: +# org_dict = org_data_from_xml(org_etree) +# referenced_org = internal_org_id.referenced_org +# update_org = Organisation.objects.filter(pk=referenced_org.pk) +# content_owner = update_org[0].content_owner +# if content_owner and content_owner != cordaid: +# log( +# u"Organisation content owned by different organisation: {label}, Akvo ID: {pk}, owned by: {extra}", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_org_id.identifier, +# label=referenced_org.name, +# pk=referenced_org.pk, +# event=OWNER_CONTENT, +# extra=content_owner.name +# ) +# ) +# # return None so that organisation does not get updated afterwards +# return None +# else: +# update_org.update(**org_dict) +# log( +# u"Updated organisation: {label}, Akvo ID: {pk}", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_org_id.identifier, +# label=referenced_org.name, +# pk=referenced_org.pk, +# event=ACTION_UPDATE_ORG +# ) +# ) +# # return the updated organisation record to be used in the following steps +# return update_org[0] +# except Exception, e: +# log( +# u"Error trying to update organisation with Cordaid ID {internal_id} ", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_org_id.identifier, +# event=ERROR_EXCEPTION, +# extra=e.message +# ) +# ) +# +# def set_location_for_org(org_etree, internal_id, org): +# if not org.primary_location: +# iso_code = text_from_xpath(org_etree, 'location/object/iso_code').lower() +# if not iso_code == "ww!": +# country = custom_get_or_create_country(iso_code) +# location = OrganisationLocation.objects.create( +# country=country, +# location_target=org +# ) +# org.locations.add(location) +# org.primary_location = location +# org.save() +# log( +# u" Added location to org {pk}", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# pk=org.pk, +# label=org.name, +# event=ACTION_LOCATION_SET, +# ) +# ) +# else: +# log( +# u"Couldn't create location for org {pk}, no proper country code", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# pk=org.pk, +# label=org.name, +# event=ERROR_COUNTRY_CODE, +# ) +# ) +# else: +# log( +# u" Org {pk} already has a location.", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# pk=org.pk, +# label=org.name, +# event=ACTION_LOCATION_FOUND, +# ) +# ) +# +# def organisation_logo(org_etree, internal_id, org): +# logo_file = glob.glob( +# os.path.join( +# CORDAID_LOGOS_DIR, +# u"{logo_id}.*".format(logo_id=text_from_xpath(org_etree, 'logo_id')) +# ) +# ) +# if len(logo_file) == 1: +# logo_filename = basename(logo_file[0]) +# _, extension = splitext(logo_filename) +# if extension.lower() in (".png", ".jpg", ".jpeg", ".gif"): +# filename = model_and_instance_based_filename( +# "Organisation", +# org.pk, +# "logo", +# logo_filename +# ) +# with open(os.path.join(CORDAID_LOGOS_DIR, logo_filename), "rb") as f: +# logo_data = f.read() +# logo_tmp = NamedTemporaryFile(delete=True) +# logo_tmp.write(logo_data) +# logo_tmp.flush() +# org.logo.save( +# filename, File(logo_tmp), save=True +# ) +# log( +# u" Added logo {extra} to org {pk}, ", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# pk=org.pk, +# label=org.name, +# event=ACTION_SET_IMAGE, +# extra= filename, +# ) +# ) +# +# with open(xml_file, "rb") as f: +# root = etree.fromstring(f.read()) +# cordaid = Organisation.objects.get(id=CORDAID_ORG_ID) +# for org_etree in root: +# outsys('.') +# internal_id = text_from_xpath(org_etree, 'org_id') +# try: +# internal_org_id = InternalOrganisationID.objects.get( +# recording_org=cordaid, +# identifier=internal_id +# ) +# log( +# u"Found existing org {label} (Akvo PK {pk}) with Cordaid internal ID '{internal_id}'", +# dict( +# log_type=LOG_ORGANISATIONS, +# label=internal_org_id.referenced_org.name, +# pk=internal_org_id.referenced_org.pk, +# internal_id=internal_id, +# event=ACTION_FOUND +# ) +# ) +# referenced_org = update_organisation(org_etree, internal_org_id, cordaid) +# if referenced_org: +# set_location_for_org(org_etree, internal_id, referenced_org) +# except InternalOrganisationID.MultipleObjectsReturned: +# log( +# u"Error from lookup of internal ID {internal_id}. Multiple objects found.", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# event=ERROR_MULTIPLE_OBJECTS +# ), +# ) +# continue +# except InternalOrganisationID.DoesNotExist: +# referenced_org = create_new_organisation(org_etree, internal_id) +# if referenced_org: +# try: +# set_location_for_org(org_etree, internal_id, referenced_org) +# internal_org_id=InternalOrganisationID.objects.create( +# recording_org=cordaid, +# referenced_org=referenced_org, +# identifier=internal_id +# ) +# log( +# u"Created InternalOrganisationID for org: {label} (Akvo PK {pk}) with Cordaid internal ID '{internal_id}'", +# dict( +# log_type=LOG_ORGANISATIONS, +# label=internal_org_id.referenced_org.name, +# pk=internal_org_id.referenced_org.pk, +# internal_id=internal_id, +# event=ACTION_CREATE_IOI +# ) +# ) +# except Exception, e: +# log( +# u"Error trying to organisation location for org with Cordaid ID {internal_id} ", +# dict( +# log_type=LOG_ORGANISATIONS, +# internal_id=internal_id, +# event=ERROR_EXCEPTION, +# extra=e.message +# ) +# ) +# else: +# continue +# if referenced_org: +# organisation_logo(org_etree, internal_id, referenced_org) +# outsys('\n') if __name__ == '__main__': + django.setup() #business_units = import_cordaid_benchmarks(CORDAID_INDICATORS_CSV) business_units = find_benchmarknames_and_BUs() business_units = find_cordaid_business_units(business_units) create_cats_and_benches(business_units) - import_orgs(CORDAID_ORGANISATIONS_XML) + # import_orgs(CORDAID_ORGANISATIONS_XML) log_file = init_log(CORDAID_ORG_CSV_FILE) names = (u'internal_id', u'pk', u'label', u'event', u'extra') print_log(log_file, names, True) diff --git a/akvo/scripts/rain/organisation_upload.py b/akvo/scripts/rain/organisation_upload.py index 88caba213f..0a93b9dbe9 100644 --- a/akvo/scripts/rain/organisation_upload.py +++ b/akvo/scripts/rain/organisation_upload.py @@ -35,7 +35,8 @@ def user_org(user_cred): try: profile = Requester( url_template="http://{domain}/api/{api_version}/user_profile/?" - "format=json&api_key={api_key}&username={username}&user__username={username}", + "format=json&api_key={api_key}&username={username}&" + "user__username={username}", url_args=user_cred, ) # find the organisation ID in the path string, e.g. "/api/v1/organisation/42/" @@ -99,7 +100,7 @@ def find_by_internal_org_id(user_cred, internal_org_id): user_cred.update({'recording_org': reporting_org_id, 'identifier': internal_org_id}) return query_rsr_for_org( "http://{domain}/rest/v1/internal_organisation_id/?" - "recording_org={recording_org}&identifier={identifier}&format=json", + "recording_org={recording_org}&identifier={identifier}&format=json", user_cred, user_cred['api_key'], 'referenced_org' @@ -121,7 +122,7 @@ def find_by_name(user_cred, name): if iati_org_id: org_id_from_iati_org_id, content_owner_id = find_by_iati_org_id(user_cred, iati_org_id) if org_id_from_iati_org_id: - org_ids.append(org_id_from_iati_org_id) + org_ids.append(org_id_from_iati_org_id) if internal_org_id: org_id_from_internal_org_id, content_owner_id = find_by_internal_org_id(user_cred, internal_org_id) if org_id_from_internal_org_id: diff --git a/akvo/scripts/rain/rain_project_upload.py b/akvo/scripts/rain/rain_project_upload.py index 868c4f6064..2efe025b08 100644 --- a/akvo/scripts/rain/rain_project_upload.py +++ b/akvo/scripts/rain/rain_project_upload.py @@ -176,7 +176,7 @@ def get_project_count(user, **q_args): try: project = Requester( url_template="http://{domain}/api/{api_version}/project/" - "?format=json&api_key={api_key}&username={username}&{extra_args}", + "?format=json&api_key={api_key}&username={username}&{extra_args}", url_args=url_args, ) except Exception, e: diff --git a/akvo/scripts/rvo/rvo_project_upload.py b/akvo/scripts/rvo/rvo_project_upload.py index c7139b0d2c..194b07082f 100644 --- a/akvo/scripts/rvo/rvo_project_upload.py +++ b/akvo/scripts/rvo/rvo_project_upload.py @@ -176,7 +176,7 @@ def get_project_count(user, **q_args): try: project = Requester( url_template="http://{domain}/api/{api_version}/project/" - "?format=json&api_key={api_key}&username={username}&{extra_args}", + "?format=json&api_key={api_key}&username={username}&{extra_args}", url_args=url_args, ) except Exception, e: diff --git a/akvo/update_rsr.py b/akvo/update_rsr.py index a4057a7f46..0945e61c28 100755 --- a/akvo/update_rsr.py +++ b/akvo/update_rsr.py @@ -7,6 +7,8 @@ import os os.environ['DJANGO_SETTINGS_MODULE'] = 'akvo.settings' +import datetime + from os.path import basename, splitext from rsr.models import * diff --git a/akvo/utils.py b/akvo/utils.py index 1446878188..c75414d01a 100644 --- a/akvo/utils.py +++ b/akvo/utils.py @@ -64,7 +64,9 @@ def rsr_image_path(instance, file_name, path_template='db/project/%s/%s'): def rsr_send_mail(to_list, subject='templates/email/test_subject.txt', - message='templates/email/test_message.txt', subject_context={}, msg_context={}): + message='templates/email/test_message.txt', + subject_context=None, + msg_context=None): """ Send template driven email. to_list is a list of email addresses @@ -72,6 +74,10 @@ def rsr_send_mail(to_list, subject='templates/email/test_subject.txt', subject_context and msg_context are dicts used when renedering the respective templates settings.RSR_DOMAIN is added to both contexts as current_site, defaulting to 'akvo.org' if undefined """ + if not subject_context: + subject_context = {} + if not msg_context: + msg_context = {} current_site = getattr(settings, 'RSR_DOMAIN', 'rsr.akvo.org') subject_context.update({'site': current_site}) subject = loader.render_to_string(subject, subject_context) @@ -84,11 +90,18 @@ def rsr_send_mail(to_list, subject='templates/email/test_subject.txt', ) -def rsr_send_mail_to_users(users, subject='templates/email/test_subject.txt', - message='templates/email/test_message.txt', subject_context={}, msg_context={}): +def rsr_send_mail_to_users(users, + subject='templates/email/test_subject.txt', + message='templates/email/test_message.txt', + subject_context=None, + msg_context=None): """ Send mail to many users supplied through a queryset """ + if not subject_context: + subject_context = {} + if not msg_context: + msg_context = {} to_list = [user.email for user in users if user.email] rsr_send_mail(to_list, subject, message, subject_context, msg_context)