From 7a79901dd7019cdb84d6682b845c0d5f14a994ac Mon Sep 17 00:00:00 2001 From: Daniel Karlsson Date: Mon, 9 Mar 2015 09:36:05 +0100 Subject: [PATCH 1/9] [#1258] Enabled debug toolbar - Added provisioning step for Vagrantbox - Removed gzip middleware - Updated local template to use the debug toolbar --- akvo/settings/10-base.conf | 2 +- akvo/settings/66_local.template | 20 +++++++++++++++++++ scripts/deployment/pip/requirements/5_dev.txt | 1 + vagrant/Vagrantfile | 1 + .../provisioning/install_dev_dependencies.sh | 11 ++++++++++ 5 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 scripts/deployment/pip/requirements/5_dev.txt create mode 100644 vagrant/provisioning/install_dev_dependencies.sh diff --git a/akvo/settings/10-base.conf b/akvo/settings/10-base.conf index 9437c3f2a3..e97c9ab130 100644 --- a/akvo/settings/10-base.conf +++ b/akvo/settings/10-base.conf @@ -66,7 +66,7 @@ AUTH_USER_MODEL = 'rsr.User' MIDDLEWARE_CLASSES = ( 'akvo.rsr.middleware.PagesRouterMiddleware', 'akvo.rsr.middleware.PagesLocaleMiddleware', - 'django.middleware.gzip.GZipMiddleware', + # 'django.middleware.gzip.GZipMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.http.ConditionalGetMiddleware', diff --git a/akvo/settings/66_local.template b/akvo/settings/66_local.template index dad6e50fff..73eddd3f50 100644 --- a/akvo/settings/66_local.template +++ b/akvo/settings/66_local.template @@ -13,3 +13,23 @@ PIPELINE_ENABLED = False # The console email backend will print emails to console, useful for dev server EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' + +# Django Debug Toolbar +INSTALLED_APPS += ('debug_toolbar',) +MIDDLEWARE_CLASSES = ( + ('debug_toolbar.middleware.DebugToolbarMiddleware',) + + MIDDLEWARE_CLASSES) +DEBUG_TOOLBAR_PANELS = [ + # 'debug_toolbar.panels.versions.VersionsPanel', + 'debug_toolbar.panels.timer.TimerPanel', + 'debug_toolbar.panels.settings.SettingsPanel', + 'debug_toolbar.panels.headers.HeadersPanel', + 'debug_toolbar.panels.request.RequestPanel', + 'debug_toolbar.panels.sql.SQLPanel', + 'debug_toolbar.panels.staticfiles.StaticFilesPanel', + 'debug_toolbar.panels.templates.TemplatesPanel', + 'debug_toolbar.panels.cache.CachePanel', + 'debug_toolbar.panels.signals.SignalsPanel', + 'debug_toolbar.panels.logging.LoggingPanel', + 'debug_toolbar.panels.redirects.RedirectsPanel', +] diff --git a/scripts/deployment/pip/requirements/5_dev.txt b/scripts/deployment/pip/requirements/5_dev.txt new file mode 100644 index 0000000000..b3fc1f0936 --- /dev/null +++ b/scripts/deployment/pip/requirements/5_dev.txt @@ -0,0 +1 @@ +django-debug-toolbar==1.1.0 diff --git a/vagrant/Vagrantfile b/vagrant/Vagrantfile index f00d98e287..11a7fde78e 100644 --- a/vagrant/Vagrantfile +++ b/vagrant/Vagrantfile @@ -20,6 +20,7 @@ Vagrant.configure("2") do |config| config.vm.provision "shell", run: "always", path: "./provisioning/start_postgres.sh" config.vm.provision "shell", path: "./provisioning/link_code.sh" config.vm.provision "shell", path: "./provisioning/install_dependencies.sh" + config.vm.provision "shell", path: "./provisioning/install_dev_dependencies.sh" config.vm.provision "shell", run: "always", path: "./provisioning/fix_dns.sh" config.vm.provision "shell", path: "./provisioning/load_test_db.sh" config.vm.provision "shell", run: "always", path: "./provisioning/prepare_rsr.sh" diff --git a/vagrant/provisioning/install_dev_dependencies.sh b/vagrant/provisioning/install_dev_dependencies.sh new file mode 100644 index 0000000000..d99a7f8533 --- /dev/null +++ b/vagrant/provisioning/install_dev_dependencies.sh @@ -0,0 +1,11 @@ +# 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 >. + +set -e + +sudo -H -u rsr < Date: Tue, 10 Mar 2015 11:29:07 +0100 Subject: [PATCH 2/9] [#1258] Project list optimizations - Moved code for getting the project list to a decorator that is reusable but not affects all requests. - Disabled the donation button in the project list. - Added a db_index on the Publishingstatus model. - Cleaned up a lot of markup with help of linter. --- akvo/rsr/context_processors.py | 46 +- akvo/rsr/middleware.py | 93 +-- .../migrations/0088_added_pub_index_1258.py | 600 ++++++++++++++++++ akvo/rsr/models/project.py | 1 + akvo/rsr/models/publishing_status.py | 4 +- akvo/rsr/views/project.py | 189 ++++-- akvo/settings/10-base.conf | 6 +- akvo/templates/partials/project_budget.html | 2 + 8 files changed, 826 insertions(+), 115 deletions(-) create mode 100644 akvo/rsr/migrations/0088_added_pub_index_1258.py diff --git a/akvo/rsr/context_processors.py b/akvo/rsr/context_processors.py index 5a4a8e23d1..1758ef99ec 100644 --- a/akvo/rsr/context_processors.py +++ b/akvo/rsr/context_processors.py @@ -1,11 +1,10 @@ # -*- 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 >. +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 >. """ import django @@ -13,18 +12,18 @@ from django.conf import settings from django.contrib.sites.models import get_current_site -from akvo.rsr.models import Project - def extra_context(request, protocol="http"): + """Add information to the request context.""" current_site = get_current_site(request) django_version = django.get_version() deploy_tag = getattr(settings, 'DEPLOY_TAG', 'Unknown') deploy_branch = getattr(settings, 'DEPLOY_BRANCH', 'Unknown') deploy_commit_id = getattr(settings, 'DEPLOY_COMMIT_ID', 'Unknown') - deploy_commit_full_id = getattr(settings, 'DEPLOY_COMMIT_FULL_ID', 'Unknown') + deploy_commit_full_id = getattr( + settings, 'DEPLOY_COMMIT_FULL_ID', 'Unknown') - template_context = dict( + return dict( current_site=current_site, django_version=django_version, deploy_tag=deploy_tag, @@ -33,10 +32,9 @@ def extra_context(request, protocol="http"): deploy_commit_full_id=deploy_commit_full_id ) - return template_context - def get_current_path_without_lang(request): + """Return current path without lang.""" path = request.get_full_path() path_bits = path.split('/') path = '/'.join(path_bits[2:]) @@ -44,22 +42,22 @@ def get_current_path_without_lang(request): def extra_pages_context(request): - """Add context information of an RSR Page""" + """Add context information of an RSR Page.""" if request.rsr_page: page = request.rsr_page # Check if only projects of the partner should be shown or all projects - if page.partner_projects: - projects = page.organisation.published_projects().prefetch_related('locations') - else: - projects = Project.objects.all().published().prefetch_related('locations') - - # Check if keywords have been specified for the partner site and filter projects based on keywords if so - if page.keywords.all(): - if page.exclude_keywords: - projects = projects.exclude(keywords__in=page.keywords.all()) - else: - projects = projects.filter(keywords__in=page.keywords.all()) + # if page.partner_projects: + # projects = page.organisation.published_projects().prefetch_related('locations') + # else: + # projects = Project.objects.all().published().prefetch_related('locations') + # + # # Check if keywords have been specified for the partner site and filter projects based on keywords if so + # if page.keywords.all(): + # if page.exclude_keywords: + # projects = projects.exclude(keywords__in=page.keywords.all()) + # else: + # projects = projects.filter(keywords__in=page.keywords.all()) return { 'rsr_page': page, @@ -71,7 +69,7 @@ def extra_pages_context(request): 'stylesheet': page.stylesheet, 'akvoapp_root_url': request.akvoapp_root_url, 'domain_url': request.domain_url, - 'projects_qs': projects.latest_update_fields().order_by('-id'), + # 'projects_qs': projects.latest_update_fields().order_by('-id'), 'no_facebook': not page.facebook_button, 'facebook_app_id': page.facebook_app_id, 'no_twitter': not page.twitter_button, diff --git a/akvo/rsr/middleware.py b/akvo/rsr/middleware.py index 55c7fe1f1a..8440d5cc52 100644 --- a/akvo/rsr/middleware.py +++ b/akvo/rsr/middleware.py @@ -1,17 +1,16 @@ # -*- 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 >. +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.conf import settings from django.contrib.sites.models import Site from django.core.urlresolvers import (LocaleRegexURLResolver, is_valid_path, - get_resolver, set_urlconf) + get_resolver) from django.http import HttpResponseRedirect from django.middleware.locale import LocaleMiddleware from django.shortcuts import redirect @@ -29,13 +28,12 @@ "PagesRouterMiddleware", "get_domain", "get_or_create_site", - "is_partner_site_instance", "is_rsr_instance", "make_tls_property"] def make_tls_property(default=None): - "Creates a class-wide instance property with a thread-specific value." + """Create a class-wide instance property with a thread-specific value.""" class TLSProperty(object): def __init__(self): from threading import local @@ -60,17 +58,16 @@ def _set_value(self, value): def _patch_setattr(obj): - """Purpose of this is to allow changes to settings object again after it is - changed to tls property. + """Allow changes to tls property objects. Without this patch the following is not possible:: - settings.SITE_ID = 1 - settings.SITE_ID = 42 - assert settings.SITE_ID == 42 # this fails without this patch - + settings.SITE_ID = 1 + settings.SITE_ID = 42 + assert settings.SITE_ID == 42 # this fails without this patch """ old_setattr = obj.__setattr__ + def wrap_setattr(self, name, value): try: getattr(self.__class__, name).value = value @@ -92,6 +89,7 @@ def wrap_setattr(self, name, value): def get_domain(request): + """Get domain from request.""" original_domain = request.get_host().split(":")[0] if original_domain == "rsr.akvo.org": domain = original_domain @@ -102,24 +100,31 @@ def get_domain(request): def is_rsr_instance(hostname): + """.""" return any([site.search(hostname) for site in RSR_SITE_REGEXPS]) def is_rsr_page_instance(hostname): + """.""" return any([site.search(hostname) for site in PARTNER_SITE_REGEXPS]) def get_or_create_site(domain): + """Make sure we have a matching SITE object. + + As a result of an issue(1) we need to ensure that we don't + delete the fixture should we find duplicates + There is no guaranteed ordering(2) we should explicitly order them in such + a way that the fixture would appear first, i.e. by ensuring 'ORDER BY id + ASC'. + + (1) https://github.com/akvo/akvo-provisioning/issues/29 + (2) http://stackoverflow.com/questions/7163640/ + what-is-the-default-order-of-a-list-returned-from-a-django-filter-call + """ if domain.startswith('www.'): domain = domain[4:] - # As a result of an issue(1) we need to ensure that we don't - # delete the fixture should we find duplicates - # There is no guaranteed ordering(2) we should explicitly order them in such - # a way that the fixture would appear first, i.e. by ensuring 'ORDER BY id ASC' - # - # (1) https://github.com/akvo/akvo-provisioning/issues/29 - # (2) http://stackoverflow.com/questions/7163640/what-is-the-default-order-of-a-list-returned-from-a-django-filter-call sites = Site.objects.filter(domain=domain).order_by('id') if sites.count() >= 1: site, duplicates = sites[0], sites[1:] @@ -134,8 +139,10 @@ def get_or_create_site(domain): class PagesRouterMiddleware(object): - def process_request(self, request, cname_domain=False, rsr_page=None): + """Dispatch to normal or RSR page based on request domain.""" + def process_request(self, request, cname_domain=False, rsr_page=None): + """.""" domain = get_domain(request) if is_rsr_instance(domain): @@ -163,7 +170,8 @@ def process_request(self, request, cname_domain=False, rsr_page=None): if rsr_page is not None and rsr_page.enabled: if cname_domain: - rsr_page_domain = getattr(settings, 'AKVOAPP_DOMAIN', 'akvoapp.org') + rsr_page_domain = getattr( + settings, 'AKVOAPP_DOMAIN', 'akvoapp.org') else: rsr_page_domain = ".".join(domain.split(".")[1:]) request.rsr_page = settings.RSR_PAGE = rsr_page @@ -178,18 +186,20 @@ def process_request(self, request, cname_domain=False, rsr_page=None): request.domain_url = "http://%s" % settings.RSR_DOMAIN site = get_or_create_site(domain) settings.SITE_ID = site.id - return + return None class PagesLocaleMiddleware(LocaleMiddleware): + """ - Partner sites aware version of Django's LocaleMiddleware. Since we - swap out the root urlconf for a partner sites specific one, and the - original Django LocaleMiddleware didn't like that. + Partner sites aware version of Django's LocaleMiddleware. + Since we swap out the root urlconf for a partner sites specific one, and + the original Django LocaleMiddleware didn't like that. """ def process_request(self, request): + """.""" check_path = self.is_language_prefix_patterns_used(request) language = translation.get_language_from_request( request, check_path=check_path) @@ -197,6 +207,7 @@ def process_request(self, request): request.LANGUAGE_CODE = translation.get_language() def process_response(self, request, response): + """.""" # First set the default language, this will be used if there is none # in the path default_language = getattr(request, 'default_language', '') @@ -224,8 +235,11 @@ def process_response(self, request, response): return response def is_language_prefix_patterns_used(self, request): - """Returns `True` if the `LocaleRegexURLResolver` is used - at root level of the urlpatterns, else it returns `False`.""" + """Return True if languge prefix is used. + + Return `True` if the `LocaleRegexURLResolver` is used + at root level of the urlpatterns, else it returns `False`. + """ urlconf = getattr(request, 'urlconf', None) for url_pattern in get_resolver(urlconf).url_patterns: if isinstance(url_pattern, LocaleRegexURLResolver): @@ -234,21 +248,22 @@ def is_language_prefix_patterns_used(self, request): class ExceptionLoggingMiddleware(object): - """ Used to log exceptions on production systems - """ - def process_exception(self, request, exception): + """Used to log exceptions on production systems.""" + + def process_exception(self, request, exception): logging.exception('Exception handling request for ' + request.path) class RSRVersionHeaderMiddleware(object): - """ Add a response header with RSR version info - """ + + """Add a response header with RSR version info.""" + def process_response(self, request, response): context = extra_context(request) - response['X-RSR-Version'] = "Tag:{deploy_tag} Commit:{deploy_commit_id} Branch:{deploy_branch}".format( - deploy_tag=context['deploy_tag'], - deploy_commit_id=context['deploy_commit_id'], - deploy_branch=context['deploy_branch'], - ) + + response['X-RSR-Version'] = "tag={}, commit={}, branch={}".format( + context['deploy_tag'], + context['deploy_commit_id'], + context['deploy_branch']) return response diff --git a/akvo/rsr/migrations/0088_added_pub_index_1258.py b/akvo/rsr/migrations/0088_added_pub_index_1258.py new file mode 100644 index 0000000000..3800d90b49 --- /dev/null +++ b/akvo/rsr/migrations/0088_added_pub_index_1258.py @@ -0,0 +1,600 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding index on 'PublishingStatus', fields ['status'] + db.create_index(u'rsr_publishingstatus', ['status']) + + + def backwards(self, orm): + # Removing index on 'PublishingStatus', fields ['status'] + db.delete_index(u'rsr_publishingstatus', ['status']) + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'rsr.benchmark': { + 'Meta': {'ordering': "('category__name', 'name__order')", 'object_name': 'Benchmark'}, + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Category']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Benchmarkname']"}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'benchmarks'", 'to': "orm['rsr.Project']"}), + 'value': ('django.db.models.fields.IntegerField', [], {}) + }, + 'rsr.benchmarkname': { + 'Meta': {'ordering': "['order', 'name']", 'object_name': 'Benchmarkname'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '80'}), + 'order': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'rsr.budgetitem': { + 'Meta': {'ordering': "('label',)", 'unique_together': "(('project', 'label'),)", 'object_name': 'BudgetItem'}, + 'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2'}), + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.BudgetItemLabel']"}), + 'other_extra': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}), + 'period_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'period_end_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'period_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'period_start_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'budget_items'", 'to': "orm['rsr.Project']"}), + 'type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) + }, + 'rsr.budgetitemlabel': { + 'Meta': {'ordering': "('label',)", 'object_name': 'BudgetItemLabel'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '20', 'db_index': 'True'}) + }, + 'rsr.category': { + 'Meta': {'ordering': "['name']", 'object_name': 'Category'}, + 'benchmarknames': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['rsr.Benchmarkname']", 'symmetrical': 'False', 'blank': 'True'}), + 'focus_area': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'categories'", 'symmetrical': 'False', 'to': "orm['rsr.FocusArea']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'db_index': 'True'}) + }, + 'rsr.country': { + 'Meta': {'ordering': "['name']", 'object_name': 'Country'}, + 'continent': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '20', 'db_index': 'True'}), + 'continent_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'iso_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}) + }, + 'rsr.countrybudgetitem': { + 'Meta': {'object_name': 'CountryBudgetItem'}, + 'code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '6', 'blank': 'True'}), + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percentage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '4', 'decimal_places': '1', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'country_budget_items'", 'to': "orm['rsr.Project']"}), + 'vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}) + }, + 'rsr.employment': { + 'Meta': {'object_name': 'Employment'}, + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Country']", 'null': 'True', 'blank': 'True'}), + 'group': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employments'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'job_title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'organisation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employees'", 'to': "orm['rsr.Organisation']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'employers'", 'to': "orm['rsr.User']"}) + }, + 'rsr.focusarea': { + 'Meta': {'ordering': "['name']", 'object_name': 'FocusArea'}, + 'description': ('akvo.rsr.fields.ValidXMLTextField', [], {'max_length': '500'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': (u'sorl.thumbnail.fields.ImageField', [], {'max_length': '100'}), + 'link_to': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}) + }, + 'rsr.goal': { + 'Meta': {'object_name': 'Goal'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'goals'", 'to': "orm['rsr.Project']"}), + 'text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'rsr.indicator': { + 'Meta': {'object_name': 'Indicator'}, + 'ascending': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'baseline_comment': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'baseline_value': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'baseline_year': ('django.db.models.fields.PositiveIntegerField', [], {'max_length': '4', 'null': 'True', 'blank': 'True'}), + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'description_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'measure': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'result': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'indicators'", 'to': "orm['rsr.Result']"}), + 'title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'rsr.indicatorperiod': { + 'Meta': {'object_name': 'IndicatorPeriod'}, + 'actual_comment': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'actual_value': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'indicator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'periods'", 'to': "orm['rsr.Indicator']"}), + 'period_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'period_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'target_comment': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'target_value': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'rsr.internalorganisationid': { + 'Meta': {'unique_together': "(('recording_org', 'referenced_org'),)", 'object_name': 'InternalOrganisationID'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'identifier': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '200'}), + 'recording_org': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'internal_ids'", 'to': "orm['rsr.Organisation']"}), + 'referenced_org': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reference_ids'", 'to': "orm['rsr.Organisation']"}) + }, + 'rsr.invoice': { + 'Meta': {'ordering': "['-id']", 'object_name': 'Invoice'}, + 'amount': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'amount_received': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '2', 'blank': 'True'}), + 'bank': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '4', 'blank': 'True'}), + 'campaign_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '15', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'engine': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'paypal'", 'max_length': '10'}), + 'http_referer': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ipn': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'is_anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'invoices'", 'to': "orm['rsr.Project']"}), + 'status': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}), + 'test': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'transaction_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.User']", 'null': 'True', 'blank': 'True'}) + }, + 'rsr.keyword': { + 'Meta': {'ordering': "('label',)", 'object_name': 'Keyword'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '30', 'db_index': 'True'}) + }, + 'rsr.legacydata': { + 'Meta': {'object_name': 'LegacyData'}, + 'iati_equivalent': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'legacy_data'", 'to': "orm['rsr.Project']"}), + 'value': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}) + }, + 'rsr.link': { + 'Meta': {'object_name': 'Link'}, + 'caption': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'kind': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'links'", 'to': "orm['rsr.Project']"}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200'}) + }, + 'rsr.minicms': { + 'Meta': {'ordering': "['-active', '-id']", 'object_name': 'MiniCMS'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'feature_box': ('akvo.rsr.fields.ValidXMLTextField', [], {'max_length': '350'}), + 'feature_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50'}), + 'lower_height': ('django.db.models.fields.IntegerField', [], {'default': '500'}), + 'top_right_box': ('akvo.rsr.fields.ValidXMLTextField', [], {'max_length': '350'}) + }, + 'rsr.molliegateway': { + 'Meta': {'object_name': 'MollieGateway'}, + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'EUR'", 'max_length': '3'}), + 'description': ('akvo.rsr.fields.ValidXMLTextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255'}), + 'notification_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'partner_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '10'}) + }, + 'rsr.organisation': { + 'Meta': {'ordering': "['name']", 'object_name': 'Organisation'}, + 'allow_edit': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'contact_email': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'contact_person': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '30', 'blank': 'True'}), + 'content_owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Organisation']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'description': ('akvo.rsr.fields.ValidXMLTextField', [], {'blank': 'True'}), + 'facebook': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'fax': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '20', 'blank': 'True'}), + 'iati_org_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'db_index': 'True', 'max_length': '75', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_org_ids': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'recording_organisation'", 'symmetrical': 'False', 'through': "orm['rsr.InternalOrganisationID']", 'to': "orm['rsr.Organisation']"}), + 'language': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'en'", 'max_length': '2'}), + 'last_modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'linkedin': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'logo': (u'sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'blank': 'True'}), + 'long_name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'blank': 'True'}), + 'mobile': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '20', 'blank': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'db_index': 'True'}), + 'new_organisation_type': ('django.db.models.fields.IntegerField', [], {'default': '22', 'db_index': 'True'}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'organisation_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'db_index': 'True'}), + 'partner_types': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['rsr.PartnerType']", 'symmetrical': 'False'}), + 'phone': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '20', 'blank': 'True'}), + 'primary_location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.OrganisationLocation']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'twitter': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'rsr.organisationaccount': { + 'Meta': {'object_name': 'OrganisationAccount'}, + 'account_level': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'archived'", 'max_length': '12'}), + 'organisation': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['rsr.Organisation']", 'unique': 'True', 'primary_key': 'True'}) + }, + 'rsr.organisationlocation': { + 'Meta': {'ordering': "['id']", 'object_name': 'OrganisationLocation'}, + 'address_1': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'address_2': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Country']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'latitude': ('akvo.rsr.fields.LatitudeField', [], {'default': '0', 'db_index': 'True'}), + 'location_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'locations'", 'null': 'True', 'to': "orm['rsr.Organisation']"}), + 'longitude': ('akvo.rsr.fields.LongitudeField', [], {'default': '0', 'db_index': 'True'}), + 'postcode': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '10', 'blank': 'True'}), + 'state': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'rsr.partnership': { + 'Meta': {'ordering': "['partner_type']", 'object_name': 'Partnership'}, + 'funding_amount': ('django.db.models.fields.DecimalField', [], {'db_index': 'True', 'null': 'True', 'max_digits': '10', 'decimal_places': '2', 'blank': 'True'}), + 'iati_activity_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'db_index': 'True', 'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'iati_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'internal_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'db_index': 'True', 'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'organisation': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'partnerships'", 'to': "orm['rsr.Organisation']"}), + 'partner_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '8', 'db_index': 'True'}), + 'partner_type_extra': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'partnerships'", 'to': "orm['rsr.Project']"}), + 'related_activity_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'rsr.partnersite': { + 'Meta': {'ordering': "('organisation__name',)", 'object_name': 'PartnerSite'}, + 'about_box': ('akvo.rsr.fields.ValidXMLTextField', [], {'max_length': '500', 'blank': 'True'}), + 'about_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), + 'cname': ('akvo.rsr.fields.NullCharField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'custom_css': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'custom_favicon': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'custom_logo': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'custom_return_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'custom_return_url_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "''", 'max_length': '50', 'blank': 'True'}), + 'default_language': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'en'", 'max_length': '5'}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'exclude_keywords': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'facebook_app_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'facebook_button': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'google_translation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'hostname': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '50'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'partnersites'", 'blank': 'True', 'to': "orm['rsr.Keyword']"}), + 'last_modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Organisation']"}), + 'partner_projects': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'piwik_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'twitter_button': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ui_translation': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'rsr.partnertype': { + 'Meta': {'ordering': "('label',)", 'object_name': 'PartnerType'}, + 'id': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '8', 'primary_key': 'True'}), + 'label': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'rsr.paymentgatewayselector': { + 'Meta': {'object_name': 'PaymentGatewaySelector'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mollie_gateway': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['rsr.MollieGateway']"}), + 'paypal_gateway': ('django.db.models.fields.related.ForeignKey', [], {'default': '1', 'to': "orm['rsr.PayPalGateway']"}), + 'project': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['rsr.Project']", 'unique': 'True'}) + }, + 'rsr.paypalgateway': { + 'Meta': {'object_name': 'PayPalGateway'}, + 'account_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}), + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'EUR'", 'max_length': '3'}), + 'description': ('akvo.rsr.fields.ValidXMLTextField', [], {'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'locale': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'US'", 'max_length': '2'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255'}), + 'notification_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}) + }, + 'rsr.planneddisbursement': { + 'Meta': {'object_name': 'PlannedDisbursement'}, + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'period_end': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'period_start': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'planned_disbursements'", 'to': "orm['rsr.Project']"}), + 'type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'updated': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '2', 'blank': 'True'}), + 'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) + }, + 'rsr.policymarker': { + 'Meta': {'object_name': 'PolicyMarker'}, + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'policy_marker': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'policy_markers'", 'to': "orm['rsr.Project']"}), + 'significance': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'rsr.project': { + 'Meta': {'ordering': "['-id']", 'object_name': 'Project'}, + 'background': ('akvo.rsr.fields.ProjectLimitedTextField', [], {'blank': 'True'}), + 'budget': ('django.db.models.fields.DecimalField', [], {'decimal_places': '2', 'default': '0', 'max_digits': '10', 'blank': 'True', 'null': 'True', 'db_index': 'True'}), + 'capital_spend_percentage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '4', 'decimal_places': '1', 'blank': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'projects'", 'blank': 'True', 'to': "orm['rsr.Category']"}), + 'collaboration_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'EUR'", 'max_length': '3'}), + 'current_image': (u'sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'blank': 'True'}), + 'current_image_caption': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'current_image_credit': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'current_status': ('akvo.rsr.fields.ProjectLimitedTextField', [], {'blank': 'True'}), + 'date_end_actual': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_end_planned': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_start_actual': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'date_start_planned': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today'}), + 'default_aid_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'default_finance_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'default_flow_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'default_tied_status': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'donate_button': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'funds': ('django.db.models.fields.DecimalField', [], {'decimal_places': '2', 'default': '0', 'max_digits': '10', 'blank': 'True', 'null': 'True', 'db_index': 'True'}), + 'funds_needed': ('django.db.models.fields.DecimalField', [], {'decimal_places': '2', 'default': '0', 'max_digits': '10', 'blank': 'True', 'null': 'True', 'db_index': 'True'}), + 'goals_overview': ('akvo.rsr.fields.ProjectLimitedTextField', [], {}), + 'hierarchy': ('django.db.models.fields.PositiveIntegerField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}), + 'iati_activity_id': ('akvo.rsr.fields.ValidXMLCharField', [], {'db_index': 'True', 'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keywords': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'projects'", 'blank': 'True', 'to': "orm['rsr.Keyword']"}), + 'language': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'en'", 'max_length': '2'}), + 'last_modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'last_update': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'the_project'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['rsr.ProjectUpdate']"}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'partners': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'projects'", 'symmetrical': 'False', 'through': "orm['rsr.Partnership']", 'to': "orm['rsr.Organisation']"}), + 'primary_location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.ProjectLocation']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'project_plan': ('akvo.rsr.fields.ValidXMLTextField', [], {'blank': 'True'}), + 'project_plan_summary': ('akvo.rsr.fields.ProjectLimitedTextField', [], {}), + 'project_rating': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'project_scope': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'status': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'N'", 'max_length': '1', 'db_index': 'True'}), + 'subtitle': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75'}), + 'sustainability': ('akvo.rsr.fields.ValidXMLTextField', [], {}), + 'sync_owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Organisation']", 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'sync_owner_secondary_reporter': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'target_group': ('akvo.rsr.fields.ProjectLimitedTextField', [], {'blank': 'True'}), + 'title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '45', 'db_index': 'True'}) + }, + 'rsr.projectcomment': { + 'Meta': {'ordering': "('-id',)", 'object_name': 'ProjectComment'}, + 'comment': ('akvo.rsr.fields.ValidXMLTextField', [], {}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'comments'", 'to': "orm['rsr.Project']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.User']"}) + }, + 'rsr.projectcondition': { + 'Meta': {'object_name': 'ProjectCondition'}, + 'attached': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'conditions'", 'to': "orm['rsr.Project']"}), + 'text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}) + }, + 'rsr.projectcontact': { + 'Meta': {'object_name': 'ProjectContact'}, + 'country': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'contacts'", 'null': 'True', 'to': "orm['rsr.Country']"}), + 'department': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'job_title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'mailing_address': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'organisation': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'person_name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'contacts'", 'to': "orm['rsr.Project']"}), + 'state': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'telephone': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '15', 'blank': 'True'}), + 'type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'rsr.projectdocument': { + 'Meta': {'ordering': "['-id']", 'object_name': 'ProjectDocument'}, + 'category': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'document': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}), + 'format': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['rsr.Project']"}), + 'title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'title_language': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}) + }, + 'rsr.projectlocation': { + 'Meta': {'ordering': "['id']", 'object_name': 'ProjectLocation'}, + 'activity_description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'address_1': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'address_2': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'administrative_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'blank': 'True'}), + 'administrative_level': ('django.db.models.fields.PositiveSmallIntegerField', [], {'max_length': '1', 'null': 'True', 'blank': 'True'}), + 'administrative_vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'city': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Country']"}), + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'exactness': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'feature_designation': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '5', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'latitude': ('akvo.rsr.fields.LatitudeField', [], {'default': '0', 'db_index': 'True'}), + 'location_class': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'location_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'blank': 'True'}), + 'location_reach': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'location_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'locations'", 'null': 'True', 'to': "orm['rsr.Project']"}), + 'longitude': ('akvo.rsr.fields.LongitudeField', [], {'default': '0', 'db_index': 'True'}), + 'name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'postcode': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '10', 'blank': 'True'}), + 'reference': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'state': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}) + }, + 'rsr.projectupdate': { + 'Meta': {'ordering': "['-id']", 'object_name': 'ProjectUpdate'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'en'", 'max_length': '2'}), + 'last_modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'db_index': 'True', 'blank': 'True'}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'photo': (u'sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'blank': 'True'}), + 'photo_caption': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'blank': 'True'}), + 'photo_credit': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'blank': 'True'}), + 'primary_location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.ProjectUpdateLocation']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['rsr.Project']"}), + 'text': ('akvo.rsr.fields.ValidXMLTextField', [], {'blank': 'True'}), + 'title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'db_index': 'True'}), + 'update_method': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'W'", 'max_length': '1', 'db_index': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.User']"}), + 'user_agent': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "''", 'max_length': '200', 'blank': 'True'}), + 'uuid': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "''", 'max_length': '40', 'db_index': 'True', 'blank': 'True'}), + 'video': ('embed_video.fields.EmbedVideoField', [], {'max_length': '200', 'blank': 'True'}), + 'video_caption': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '75', 'blank': 'True'}), + 'video_credit': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'blank': 'True'}) + }, + 'rsr.projectupdatelocation': { + 'Meta': {'ordering': "['id']", 'object_name': 'ProjectUpdateLocation'}, + 'address_1': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'address_2': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'city': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rsr.Country']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'latitude': ('akvo.rsr.fields.LatitudeField', [], {'default': '0', 'db_index': 'True'}), + 'location_target': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'locations'", 'null': 'True', 'to': "orm['rsr.ProjectUpdate']"}), + 'longitude': ('akvo.rsr.fields.LongitudeField', [], {'default': '0', 'db_index': 'True'}), + 'postcode': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '10', 'blank': 'True'}), + 'state': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}) + }, + 'rsr.publishingstatus': { + 'Meta': {'ordering': "('-status', 'project')", 'object_name': 'PublishingStatus'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['rsr.Project']", 'unique': 'True'}), + 'status': ('akvo.rsr.fields.ValidXMLCharField', [], {'default': "'unpublished'", 'max_length': '30', 'db_index': 'True'}) + }, + 'rsr.recipientcountry': { + 'Meta': {'object_name': 'RecipientCountry'}, + 'country': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percentage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '4', 'decimal_places': '1', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipient_countries'", 'to': "orm['rsr.Project']"}), + 'text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'rsr.recipientregion': { + 'Meta': {'object_name': 'RecipientRegion'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percentage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '4', 'decimal_places': '1', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'recipient_regions'", 'to': "orm['rsr.Project']"}), + 'region': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'region_vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'rsr.relatedproject': { + 'Meta': {'ordering': "['project']", 'object_name': 'RelatedProject'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'related_projects'", 'to': "orm['rsr.Project']"}), + 'related_project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'related_to_projects'", 'to': "orm['rsr.Project']"}), + 'relation': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1'}) + }, + 'rsr.result': { + 'Meta': {'object_name': 'Result'}, + 'aggregation_status': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'description_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'results'", 'to': "orm['rsr.Project']"}), + 'title': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}) + }, + 'rsr.sector': { + 'Meta': {'object_name': 'Sector'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'percentage': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '4', 'decimal_places': '1', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sectors'", 'to': "orm['rsr.Project']"}), + 'sector_code': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '5', 'blank': 'True'}), + 'text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'vocabulary': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '5', 'blank': 'True'}) + }, + 'rsr.transaction': { + 'Meta': {'object_name': 'Transaction'}, + 'aid_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'aid_type_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'currency': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'description': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '255', 'blank': 'True'}), + 'disbursement_channel': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'disbursement_channel_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'finance_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '3', 'blank': 'True'}), + 'finance_type_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'flow_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'flow_type_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': "orm['rsr.Project']"}), + 'provider_organisation': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'provider_organisation_activity': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'provider_organisation_ref': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'receiver_organisation': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'receiver_organisation_activity': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'receiver_organisation_ref': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '50', 'blank': 'True'}), + 'reference': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '25', 'blank': 'True'}), + 'tied_status': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '1', 'blank': 'True'}), + 'tied_status_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'transaction_date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'transaction_type': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '2', 'blank': 'True'}), + 'transaction_type_text': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '100', 'blank': 'True'}), + 'value': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '11', 'decimal_places': '1', 'blank': 'True'}), + 'value_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}) + }, + 'rsr.user': { + 'Meta': {'ordering': "['username']", 'object_name': 'User'}, + 'avatar': (u'sorl.thumbnail.fields.ImageField', [], {'max_length': '100', 'null': 'True'}), + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '254'}), + 'first_name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_admin': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('akvo.rsr.fields.ValidXMLCharField', [], {'max_length': '30', 'blank': 'True'}), + 'notes': ('akvo.rsr.fields.ValidXMLTextField', [], {'default': "''", 'blank': 'True'}), + 'organisations': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'users'", 'blank': 'True', 'through': "orm['rsr.Employment']", 'to': "orm['rsr.Organisation']"}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('akvo.rsr.fields.ValidXMLCharField', [], {'unique': 'True', 'max_length': '254'}) + } + } + + complete_apps = ['rsr'] \ No newline at end of file diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py index 36907584a9..0bfcfb540c 100644 --- a/akvo/rsr/models/project.py +++ b/akvo/rsr/models/project.py @@ -265,6 +265,7 @@ def get_absolute_url(self): return ('project-main', (), {'project_id': self.pk}) def accepts_donations(self): + """This is slow.""" if not self.donate_button: return False if self in Project.objects.active() and self.funds_needed > 0: diff --git a/akvo/rsr/models/publishing_status.py b/akvo/rsr/models/publishing_status.py index c84cad7739..22426b7915 100644 --- a/akvo/rsr/models/publishing_status.py +++ b/akvo/rsr/models/publishing_status.py @@ -24,7 +24,9 @@ class PublishingStatus(models.Model): ) project = models.OneToOneField('Project',) - status = ValidXMLCharField(max_length=30, choices=PUBLISHING_STATUS, default=STATUS_UNPUBLISHED) + status = ValidXMLCharField(max_length=30, + choices=PUBLISHING_STATUS, + db_index=True, default=STATUS_UNPUBLISHED) class Meta: app_label = 'rsr' diff --git a/akvo/rsr/views/project.py b/akvo/rsr/views/project.py index 78b95bd58a..96acd6348f 100644 --- a/akvo/rsr/views/project.py +++ b/akvo/rsr/views/project.py @@ -1,25 +1,125 @@ # -*- coding: utf-8 -*- +""" +Akvo RSR is covered by the GNU Affero General Public License. -"""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 >. +Akvo RSR module. For additional details on the GNU license please see +< http://www.gnu.org/licenses/agpl.html >. """ import json +from sorl.thumbnail import get_thumbnail +from django.contrib.auth.decorators import login_required +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from django.shortcuts import get_object_or_404, redirect, render + from ..forms import ProjectUpdateForm from ..filters import remove_empty_querydict_items, ProjectFilter from ..models import Invoice, Project, ProjectUpdate from ...utils import pagination, filter_query_string -from django.contrib.auth.decorators import login_required -from django.core.exceptions import PermissionDenied -from django.http import Http404 -from django.shortcuts import get_object_or_404, redirect, render -from django.template import RequestContext -from sorl.thumbnail import get_thumbnail +############################################################################### +# Project directory +############################################################################### + + +def _all_projects(): + """Return all active projects.""" + print "_all_projects" + return Project.objects.select_related( + 'partners', + 'categories', + 'primary_location', + 'primary_location__country', + 'sync_owner').active().prefetch_related('categories', + 'partners').order_by('-id') + + +def _orgs_projects(organisation): + """Return active projects from organisation.""" + print "_orgs_projects" + return organisation.active_projects().select_related( + 'partners', + 'categories', + 'primary_location', + 'primary_location__country', + 'sync_owner').active().prefetch_related('categories', + 'partners').order_by('-id') + + +def _apply_keywords(page, projects): + """Apply keywords. + + If keywords exist, check if they should be used for filtering or exclusion. + """ + print "_apply_keywords" + if not page.keywords.all(): + return projects + + if page.exclude_keywords: + return projects.exclude(keywords__in=page.keywords.all()) + else: + return projects.filter(keywords__in=page.keywords.all()) + + +def _page_projects(page): + """Dig out the list of projects to use.""" + print "_page_projects" + projects = _orgs_projects() if page.partner_projects else _all_projects() + return _apply_keywords(projects) + + +def _project_directory_coll(request): + """Dig out and pass correct projects to the view.""" + page = request.rsr_page + if not page: + return _all_projects() + return _page_projects(page) + + +def directory(request): + """The project list view.""" + qs = remove_empty_querydict_items(request.GET) + + # Set show_filters to "in" if any filter is selected + show_filters = "in" # To simplify template use bootstrap class + available_filters = ['location', 'status', 'organisation', 'sector', ] + if frozenset(qs.keys()).isdisjoint(available_filters): + show_filters = "" + + # Prepare sorting + available_sorting = ['last_modified_at', '-last_modified_at', 'title', + '-title', 'budget', '-budget', ] + sort_by = request.GET.get('sort_by', '-last_modified_at') + sorting = sort_by if sort_by in available_sorting else '-last_modified_at' + + # Yank project collection + f = ProjectFilter(qs, queryset=_project_directory_coll(request)) + sorted_projects = f.qs.distinct().order_by(sorting) + + # Build page + page = request.GET.get('page') + page, paginator, page_range = pagination(page, sorted_projects, 10) + + context = { + 'project_count': sorted_projects.count(), + 'filter': f, + 'page': page, + 'page_range': page_range, + 'paginator': paginator, + 'show_filters': show_filters, + 'q': filter_query_string(qs), + 'sorting': sorting, + } + return render(request, 'project_directory.html', context) + + +############################################################################### +# Project main +############################################################################### def _get_accordion_data(project): @@ -101,9 +201,13 @@ def _get_carousel_data(project): }) return {"photos": photos} + def _get_hierarchy_row(max_rows, projects): - """Returns a column for the project hierarchy with a division. - E.g. with a max_rows of 4 and one project, it will return [False, , False, False].""" + """Return a column for the project hierarchy with a division. + + E.g. with a max_rows of 4 and one project, it will return [False, + , False, False]. + """ project_count = projects.count() if max_rows == project_count: return [project for project in projects] @@ -155,44 +259,8 @@ def _get_project_partners(project): return partners -def directory(request): - qs = remove_empty_querydict_items(request.GET) - projects = RequestContext(request)['projects_qs'] if request.rsr_page else Project.objects.published() - f = ProjectFilter(qs, queryset=projects) - - # Instead of true or false, adhere to bootstrap3 class names to simplify - show_filters = "in" - available_filters = ['location', 'status', 'organisation', 'sector', ] - if frozenset(qs.keys()).isdisjoint(available_filters): - show_filters = "" - - # Sorting of projects - available_sorting = ['last_modified_at', '-last_modified_at', 'title', '-title', 'budget', '-budget', ] - sort_param = request.GET.get('sort_by', '-last_modified_at') - sorting = sort_param if sort_param in available_sorting else '-last_modified_at' - - sorted_projects = f.qs.distinct().order_by(sorting) - - page = request.GET.get('page') - page, paginator, page_range = pagination(page, sorted_projects, 10) - - # sector_count = SectorCategory.objects.all().count() - - context = { - 'project_count': sorted_projects.count(), - # 'sector_count': sector_count, - 'filter': f, - 'page': page, - 'page_range': page_range, - 'paginator': paginator, - 'show_filters': show_filters, - 'q': filter_query_string(qs), - 'sorting': sorting, - } - return render(request, 'project_directory.html', context) - - def main(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) # Non-editors are not allowed to view unpublished projects @@ -221,7 +289,13 @@ def main(request, project_id): return render(request, 'project_main.html', context) +############################################################################### +# Project hierarchy +############################################################################### + + def hierarchy(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) # Non-editors are not allowed to view unpublished projects @@ -241,7 +315,13 @@ def hierarchy(request, project_id): return render(request, 'project_hierarchy.html', context) +############################################################################### +# Project report +############################################################################### + + def report(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) # Non-editors are not allowed to view unpublished projects @@ -255,7 +335,13 @@ def report(request, project_id): return render(request, 'project_report.html', context) +############################################################################### +# Project widgets +############################################################################### + + def widgets(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) selected_widget = request.GET.get('widget', None) @@ -279,6 +365,7 @@ def widgets(request, project_id): @login_required() def set_update(request, project_id, edit_mode=False, form_class=ProjectUpdateForm, update_id=None): + """.""" project = get_object_or_404(Project, id=project_id) # Non-editors are not allowed to view unpublished projects @@ -323,12 +410,15 @@ def set_update(request, project_id, edit_mode=False, form_class=ProjectUpdateFor return render(request, 'update_add.html', context) + def search(request): + """.""" context = {'projects': Project.objects.published()} return render(request, 'project_search.html', context) def finance(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) context = { 'project': project @@ -337,10 +427,12 @@ def finance(request, project_id): def donations_disabled(project): + """.""" return not project.donate_button def can_accept_donations(project): + """.""" if project in Project.objects.active() and project.funds_needed > 0: return True else: @@ -348,6 +440,7 @@ def can_accept_donations(project): def donate(request, project_id): + """.""" project = get_object_or_404(Project, pk=project_id) if not project.accepts_donations(): diff --git a/akvo/settings/10-base.conf b/akvo/settings/10-base.conf index e97c9ab130..d3476d5047 100644 --- a/akvo/settings/10-base.conf +++ b/akvo/settings/10-base.conf @@ -19,7 +19,7 @@ INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.humanize', - 'django.contrib.messages', + # 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.staticfiles', @@ -65,7 +65,7 @@ AUTH_USER_MODEL = 'rsr.User' MIDDLEWARE_CLASSES = ( 'akvo.rsr.middleware.PagesRouterMiddleware', - 'akvo.rsr.middleware.PagesLocaleMiddleware', + # 'akvo.rsr.middleware.PagesLocaleMiddleware', # 'django.middleware.gzip.GZipMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', @@ -75,7 +75,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', + # 'django.contrib.messages.middleware.MessageMiddleware', 'akvo.rsr.middleware.ExceptionLoggingMiddleware', 'akvo.rsr.middleware.RSRVersionHeaderMiddleware', ) diff --git a/akvo/templates/partials/project_budget.html b/akvo/templates/partials/project_budget.html index 54f7e83002..cee82080fc 100644 --- a/akvo/templates/partials/project_budget.html +++ b/akvo/templates/partials/project_budget.html @@ -14,8 +14,10 @@
Total Budget:
{{project.get_currency_display}}{{project.budget|floatformat|intcomma}}
+{% comment %} {% if project.accepts_donations %} {% endif %} +{% endcomment %} From 2205e85821824eb5022118208f2a5a0dfbda03a3 Mon Sep 17 00:00:00 2001 From: Daniel Karlsson Date: Wed, 11 Mar 2015 12:31:07 +0100 Subject: [PATCH 3/9] [#1258] More optimizations - Rewrote organisation directory view - Rewrote project update view - Cleaned up syntax to score better with static analyzers - Commented out a exception logger that is for development in map tag - Moved show_filter_class to view utils - Fixed template bug in org directory. - Removed old code --- akvo/rsr/context_processors.py | 15 ------ akvo/rsr/templatetags/maps.py | 61 +++++++++++++----------- akvo/rsr/views/organisation.py | 71 +++++++++++++++++++++------- akvo/rsr/views/project.py | 50 ++++---------------- akvo/rsr/views/project_update.py | 64 ++++++++++++++++++------- akvo/rsr/views/utils.py | 39 +++++++++++++++ akvo/settings/10-base.conf | 4 +- akvo/templates/update_directory.html | 2 +- 8 files changed, 184 insertions(+), 122 deletions(-) create mode 100644 akvo/rsr/views/utils.py diff --git a/akvo/rsr/context_processors.py b/akvo/rsr/context_processors.py index 1758ef99ec..819d2a5f54 100644 --- a/akvo/rsr/context_processors.py +++ b/akvo/rsr/context_processors.py @@ -45,20 +45,6 @@ def extra_pages_context(request): """Add context information of an RSR Page.""" if request.rsr_page: page = request.rsr_page - - # Check if only projects of the partner should be shown or all projects - # if page.partner_projects: - # projects = page.organisation.published_projects().prefetch_related('locations') - # else: - # projects = Project.objects.all().published().prefetch_related('locations') - # - # # Check if keywords have been specified for the partner site and filter projects based on keywords if so - # if page.keywords.all(): - # if page.exclude_keywords: - # projects = projects.exclude(keywords__in=page.keywords.all()) - # else: - # projects = projects.filter(keywords__in=page.keywords.all()) - return { 'rsr_page': page, 'favicon': page.favicon, @@ -69,7 +55,6 @@ def extra_pages_context(request): 'stylesheet': page.stylesheet, 'akvoapp_root_url': request.akvoapp_root_url, 'domain_url': request.domain_url, - # 'projects_qs': projects.latest_update_fields().order_by('-id'), 'no_facebook': not page.facebook_button, 'facebook_app_id': page.facebook_app_id, 'no_twitter': not page.twitter_button, diff --git a/akvo/rsr/templatetags/maps.py b/akvo/rsr/templatetags/maps.py index 25a2dedfbd..b75a9cd548 100644 --- a/akvo/rsr/templatetags/maps.py +++ b/akvo/rsr/templatetags/maps.py @@ -1,9 +1,10 @@ # -*- 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 >. +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 >. """ import os @@ -17,9 +18,13 @@ register = template.Library() -PROJECT_MARKER_ICON = getattr(settings, 'GOOGLE_MAPS_PROJECT_MARKER_ICON', '') -PROJECT_UPDATE_MARKER_ICON = getattr(settings, 'GOOGLE_MAPS_PROJECT_UPDATE_MARKER_ICON', '') -ORGANISATION_MARKER_ICON = getattr(settings, 'GOOGLE_MAPS_ORGANISATION_MARKER_ICON', '') +PROJECT_MARKER_ICON = getattr(settings, + 'GOOGLE_MAPS_PROJECT_MARKER_ICON', '') +PROJECT_UPDATE_MARKER_ICON = getattr(settings, + 'GOOGLE_MAPS_PROJECT_UPDATE_MARKER_ICON', + '') +ORGANISATION_MARKER_ICON = getattr(settings, + 'GOOGLE_MAPS_ORGANISATION_MARKER_ICON', '') MEDIA_URL = getattr(settings, 'MEDIA_URL', '/media/') @@ -28,7 +33,8 @@ def avatar(item, geometry='60x60', quality=99): - """ + """Build user avatar. + Digs out the url to the visual representation from object. If no one exists defaults to empty string. """ @@ -36,15 +42,15 @@ def avatar(item, geometry='60x60', quality=99): try: if isinstance(item, Project): url = get_thumbnail(item.current_image, geometry, - crop='center', quality=quality).url + crop='center', quality=quality).url elif isinstance(item, Organisation): url = get_thumbnail(item.logo, geometry, crop='center', quality=quality).url elif isinstance(item, ProjectUpdate): url = get_thumbnail(item.photo, geometry, crop='center', quality=quality).url - except Exception, e: - print e + except Exception: # , e: + # print e pass return url @@ -52,9 +58,7 @@ def avatar(item, geometry='60x60', quality=99): @register.inclusion_tag('inclusion_tags/map.html') def coll_map(coll, width='100%', height='100%', dynamic='dynamic'): - """ - ... - """ + """.""" if dynamic != 'dynamic': dynamic = False map_id = 'akvo_map_%s' % os.urandom(8).encode('hex') @@ -72,12 +76,10 @@ def coll_map(coll, width='100%', height='100%', dynamic='dynamic'): item_type = 'project' icon = PROJECT_MARKER_ICON text = item.title.encode('utf8') - elif isinstance(item, Organisation): item_type = 'organisation' icon = ORGANISATION_MARKER_ICON text = item.name.encode('utf8') - elif isinstance(item, ProjectUpdate): item_type = 'projectUpdate' icon = PROJECT_UPDATE_MARKER_ICON @@ -93,7 +95,7 @@ def coll_map(coll, width='100%', height='100%', dynamic='dynamic'): 'pk': str(item.pk), 'text': text}) except Exception, e: - # print e + print e pass return { @@ -107,7 +109,6 @@ def coll_map(coll, width='100%', height='100%', dynamic='dynamic'): 'partnersite_widget': False} - def get_location(item): """...""" try: @@ -148,6 +149,7 @@ def get_location(item): @register.inclusion_tag('inclusion_tags/map.html') def primary_location_map(item, width='100%', height='100%', dynamic='dynamic'): + """.""" if dynamic != 'dynamic': dynamic = False map_id = 'akvo_map_{}'.format(os.urandom(8).encode('hex')) @@ -166,6 +168,7 @@ def primary_location_map(item, width='100%', height='100%', dynamic='dynamic'): @register.inclusion_tag('inclusion_tags/map.html') def locations_map(item, width='100%', height='100%', dynamic='dynamic'): + """.""" if dynamic != 'dynamic': dynamic = False map_id = 'akvo_map_{}'.format(os.urandom(8).encode('hex')) @@ -175,7 +178,6 @@ def locations_map(item, width='100%', height='100%', dynamic='dynamic'): if isinstance(item, Project): locations.append(get_location(item)) - return { 'map_id': map_id, 'width': width, @@ -187,13 +189,14 @@ def locations_map(item, width='100%', height='100%', dynamic='dynamic'): @register.inclusion_tag('inclusion_tags/maps.html') def project_map(id, width, height, dynamic='dynamic'): - """ + """Project map. + params: id: integer, id of project or organisation. width, height: the dimensions of the map. - dynamic: 'dynamic' (default) or 'static', map is scrollable and clickable if 'dynamic'. + dynamic: 'dynamic' (default) or 'static', map is scrollable and + clickable if 'dynamic'. """ - if dynamic != 'dynamic': dynamic = False @@ -248,13 +251,14 @@ def project_map(id, width, height, dynamic='dynamic'): @register.inclusion_tag('inclusion_tags/maps.html') def organisation_map(id, width, height, dynamic='dynamic'): - """ + """Organisation map. + params: id: integer, id of project or organisation. width, height: the dimensions of the map. - dynamic: 'dynamic' (default) or 'static', map is scrollable and clickable if 'dynamic'. + dynamic: 'dynamic' (default) or 'static', map is scrollable and + clickable if 'dynamic'. """ - if dynamic != 'dynamic': dynamic = False @@ -285,12 +289,13 @@ def organisation_map(id, width, height, dynamic='dynamic'): @register.inclusion_tag('inclusion_tags/maps.html') def global_project_map(width, height, dynamic='dynamic'): - """ + """Global project map. + params: width, height: the dimensions of the map. - dynamic: 'dynamic' (default) or 'static', map is scrollable and clickable if 'dynamic'. + dynamic: 'dynamic' (default) or 'static', map is scrollable and + clickable if 'dynamic'. """ - if dynamic != 'dynamic': dynamic = False diff --git a/akvo/rsr/views/organisation.py b/akvo/rsr/views/organisation.py index a15a84a30c..2f851572a3 100644 --- a/akvo/rsr/views/organisation.py +++ b/akvo/rsr/views/organisation.py @@ -1,48 +1,83 @@ # -*- 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.shortcuts import get_object_or_404, render + from ..filters import remove_empty_querydict_items, OrganisationFilter from ..models import Organisation from ...utils import pagination, filter_query_string +from .utils import apply_keywords, org_projects, show_filter_class + +############################################################################### +# Organisation directory +############################################################################### + + +def _all_organisations(): + """Return all project updates.""" + return Organisation.objects.select_related().order_by('name') + + +def _page_organisations(page): + """Dig out the list or organisations to use.""" + org = page.organisation + if page.partner_projects: + projects = apply_keywords(page, org_projects(org)) + return projects.all_partners() + else: + return _all_organisations() -from django.shortcuts import get_object_or_404, render -from django.template import RequestContext + +def _organisation_directory_coll(request): + """Dig out and pass correct organisations to the view.""" + page = request.rsr_page + if not page: + return _all_organisations() + return _page_organisations(page) def directory(request): + """The Organisation list view.""" qs = remove_empty_querydict_items(request.GET) - if request.rsr_page: - organisations = RequestContext(request)['projects_qs'].all_partners() - else: - organisations = Organisation.objects.all() - f = OrganisationFilter(qs, queryset=organisations) - # Instead of true or false, adhere to bootstrap3 class names to simplify - show_filters = "in" - available_filters = ['continent', ] - if frozenset(qs.keys()).isdisjoint(available_filters): - show_filters = "" + # Set show_filters to "in" if any filter is selected + filter_class = show_filter_class(qs, ['location', ]) + # Yank Organisation collection + f = OrganisationFilter(qs, queryset=_organisation_directory_coll(request)) + + # Build page page = request.GET.get('page') page, paginator, page_range = pagination(page, f.qs.distinct(), 10) - context = { + return render(request, 'organisation_directory.html', { 'orgs_count': f.qs.distinct().count(), 'filter': f, 'page': page, 'paginator': paginator, 'page_range': page_range, - 'show_filters': show_filters, + 'show_filters': filter_class, 'q': filter_query_string(qs) - } - return render(request, 'organisation_directory.html', context) + }) + # return render(request, 'organisation_directory.html', context) + + +############################################################################### +# Organisation main +############################################################################### def main(request, organisation_id): - context = {'organisation': get_object_or_404(Organisation, pk=organisation_id)} - return render(request, 'organisation_main.html', context) + """The organisation main view.""" + return render(request, 'organisation_main.html', { + 'organisation': get_object_or_404(Organisation, pk=organisation_id)}) + +# def main(request, organisation_id): +# context = {'organisation': get_object_or_404(Organisation, pk=organisation_id)} +# return render(request, 'organisation_main.html', context) diff --git a/akvo/rsr/views/project.py b/akvo/rsr/views/project.py index 96acd6348f..e1c91f0455 100644 --- a/akvo/rsr/views/project.py +++ b/akvo/rsr/views/project.py @@ -19,6 +19,7 @@ from ..filters import remove_empty_querydict_items, ProjectFilter from ..models import Invoice, Project, ProjectUpdate from ...utils import pagination, filter_query_string +from .utils import apply_keywords, org_projects ############################################################################### @@ -28,48 +29,17 @@ def _all_projects(): """Return all active projects.""" - print "_all_projects" - return Project.objects.select_related( - 'partners', - 'categories', - 'primary_location', - 'primary_location__country', - 'sync_owner').active().prefetch_related('categories', - 'partners').order_by('-id') - - -def _orgs_projects(organisation): - """Return active projects from organisation.""" - print "_orgs_projects" - return organisation.active_projects().select_related( - 'partners', - 'categories', - 'primary_location', - 'primary_location__country', - 'sync_owner').active().prefetch_related('categories', - 'partners').order_by('-id') - - -def _apply_keywords(page, projects): - """Apply keywords. - - If keywords exist, check if they should be used for filtering or exclusion. - """ - print "_apply_keywords" - if not page.keywords.all(): - return projects - - if page.exclude_keywords: - return projects.exclude(keywords__in=page.keywords.all()) - else: - return projects.filter(keywords__in=page.keywords.all()) - + return Project.objects.published().select_related().prefetch_related( + 'partners').order_by('-id') def _page_projects(page): - """Dig out the list of projects to use.""" - print "_page_projects" - projects = _orgs_projects() if page.partner_projects else _all_projects() - return _apply_keywords(projects) + """Dig out the list of projects to use. + First get a list based on page settings (orgs or all projects). Then apply + keywords filtering / exclusion. + """ + org = page.organisation + projects = org_projects(org) if page.partner_projects else _all_projects() + return apply_keywords(page, projects) def _project_directory_coll(request): diff --git a/akvo/rsr/views/project_update.py b/akvo/rsr/views/project_update.py index c731bb8ca8..eb0d0023f6 100644 --- a/akvo/rsr/views/project_update.py +++ b/akvo/rsr/views/project_update.py @@ -1,47 +1,75 @@ # -*- coding: utf-8 -*- +""" +Akvo RSR is covered by the GNU Affero General Public License. -"""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 >. +Akvo RSR module. For additional details on the GNU license please see +< http://www.gnu.org/licenses/agpl.html >. """ + +from django.shortcuts import get_object_or_404, render + from ..filters import remove_empty_querydict_items, ProjectUpdateFilter from ..models import ProjectUpdate, Project from ...utils import pagination, filter_query_string +from .utils import apply_keywords, org_projects, show_filter_class -from django.shortcuts import get_object_or_404, render -from django.template import RequestContext + +############################################################################### +# Projectupdate directory +############################################################################### + + +def _all_updates(): + """Return all project updates.""" + return ProjectUpdate.objects.select_related().order_by('-id') + + +def _page_updates(page): + """Dig out the list or project updates to use.""" + org = page.organisation + if page.partner_projects: + projects = apply_keywords(page, org_projects(org)) + return projects.all_updates() + else: + return _all_updates() + + +def _update_directory_coll(request): + """Dig out and pass correct projects to the view.""" + page = request.rsr_page + if not page: + return _all_updates() + return _page_updates(page) def directory(request): + """The projectupdate list view.""" qs = remove_empty_querydict_items(request.GET) - if request.rsr_page: - updates = RequestContext(request)['projects_qs'].all_updates() - else: - updates = ProjectUpdate.objects.all() - f = ProjectUpdateFilter(qs, queryset=updates) - show_filters = "in" - available_filters = ['continent', 'status', 'organisation', 'focus_area', ] - if frozenset(qs.keys()).isdisjoint(available_filters): - show_filters = "" + # Set show_filters to "in" if any filter is selected + filter_class = show_filter_class(qs, ['location', 'partner', 'sector', ]) + # Yank projectupdate collection + f = ProjectUpdateFilter(qs, queryset=_update_directory_coll(request)) + + # Build page page = request.GET.get('page') page, paginator, page_range = pagination(page, f.qs.distinct(), 10) - context = { + return render(request, 'update_directory.html', { 'updates_count': f.qs.distinct().count(), 'filter': f, 'page': page, 'page_range': page_range, 'paginator': paginator, - 'show_filters': show_filters, + 'show_filters': filter_class, 'q': filter_query_string(qs) - } - return render(request, 'update_directory.html', context) + }) def main(request, project_id, update_id): + """The projectupdate main view.""" project = get_object_or_404(Project, pk=project_id) update = get_object_or_404(ProjectUpdate, pk=update_id, project=project_id) other_updates = project.updates_desc().exclude(pk=update_id)[:5] diff --git a/akvo/rsr/views/utils.py b/akvo/rsr/views/utils.py new file mode 100644 index 0000000000..d504b58d47 --- /dev/null +++ b/akvo/rsr/views/utils.py @@ -0,0 +1,39 @@ +# -*- 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 >. +""" + + +def apply_keywords(page, coll): + """Apply keywords. + + If keywords exist, check if they should be used for filtering or exclusion. + """ + if not page.keywords.all(): + return coll + + if page.exclude_keywords: + return coll.exclude(keywords__in=page.keywords.all()) + else: + return coll.filter(keywords__in=page.keywords.all()) + + +def org_projects(organisation): + """.""" + return organisation.published_projects().select_related( + 'partners', + 'categories', + 'primary_location', + 'primary_location__country', + 'sync_owner' + ).prefetch_related('categories', + 'partners').order_by('-id') + + +def show_filter_class(qs, filters): + """To simplify template, instead of bool adhere to bootstrap class name.""" + return "" if frozenset(qs.keys()).isdisjoint(filters) else "in" diff --git a/akvo/settings/10-base.conf b/akvo/settings/10-base.conf index d3476d5047..54a1297aa0 100644 --- a/akvo/settings/10-base.conf +++ b/akvo/settings/10-base.conf @@ -19,7 +19,7 @@ INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.humanize', - # 'django.contrib.messages', + 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.staticfiles', @@ -75,7 +75,7 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware', - # 'django.contrib.messages.middleware.MessageMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', 'akvo.rsr.middleware.ExceptionLoggingMiddleware', 'akvo.rsr.middleware.RSRVersionHeaderMiddleware', ) diff --git a/akvo/templates/update_directory.html b/akvo/templates/update_directory.html index c1c1f98d17..c194722a55 100644 --- a/akvo/templates/update_directory.html +++ b/akvo/templates/update_directory.html @@ -30,7 +30,7 @@
-