Repository: akvo/akvo-rsr

Issue:
Update Project custom fields



Discussion:
## Initial investigation of the current implementation of custom fields

There are two models that support custom fields, OrganisationCustomField and ProjectCustomField. OrganisationCustomField serves as the template for an organisation's custom fields, and ProjectCustomField instantiates the set of fields for a project.

AFAICT custom fields are only added to a project when it's created in the project editor. This is done by calling [addCustomFieldsToProject()](https://github.com/akvo/akvo-rsr/blob/8fbbbb427e492d864c36df4a55b2b42e22f296ac/akvo/rsr/static/scripts-src/my-projects.js#L131) after the project has been created. addCustomFieldsToProject() in turn calls [addCustomFieldToProject()](https://github.com/akvo/akvo-rsr/blob/8fbbbb427e492d864c36df4a55b2b42e22f296ac/akvo/rsr/static/scripts-src/my-projects.js#L61) for each field. The list of fields is to be found in[defaultValues.new_project_custom_fields](https://github.com/akvo/akvo-rsr/blob/8fbbbb427e492d864c36df4a55b2b42e22f296ac/akvo/rsr/static/scripts-src/my-projects.js#L90) which is created in the [my_projects.html](https://github.com/akvo/akvo-rsr/blob/8fbbbb427e492d864c36df4a55b2b42e22f296ac/akvo/templates/myrsr/my_projects.html#L135) template.

The data models for custom fields should be revised, and here I think we have two choices.

It seems that an organisation's projects should have the same set of custom fields. If this is the case, then we only need to store the *values* of the fields for each project. Currently the ProjectCustomField is a full copy of OrganisationCustomField with the addition of the value field!

Alternatively each project can be customized from the template of custom fields that is copied from the more complex solution is that when the project is created. This is a more complex solution that requires the creation of an interface to be able to edit the custom fields for each project.

I suggest replacing the current very awkward way of adding custom fields with post_save signals on Project and Organisation to update the ProjectCustomField objects from the OrganisationCustomField template objects. With this method, a project's custom fields would automatically be updated whenever the templates are edited.

Resolving patch:
diff --git a/akvo/rsr/management/commands/update_custom_fields.py b/akvo/rsr/management/commands/update_custom_fields.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/management/commands/update_custom_fields.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Akvo Reporting 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 sys
+
+from django.core.management.base import BaseCommand
+from ...models import Project, OrganisationCustomField, ProjectCustomField
+
+
+class Command(BaseCommand):
+    help = u'''
+        Update the custom fields for projects of the organisations listed in the args.
+        Fields are only updated if there is a matching organisation custom field where the match is
+        determined by equality of the 'name' and 'section' fields.
+    '''
+
+    def handle(self, *args, **options):
+
+        if not args:
+            print(self.help)
+            sys.exit(1)
+
+        org_ids = map(int, args)
+
+        for org_id in org_ids:
+            org_fields = OrganisationCustomField.objects.filter(organisation__id=org_id)
+            for project in Project.objects.filter(primary_organisation=org_id).order_by('pk'):
+                print "Updating custom fields for project ID: {}".format(project.pk)
+                for org_field in org_fields:
+                    ProjectCustomField.objects.filter(
+                        project=project, name=org_field.name, section=org_field.section
+                    ).update(
+                        max_characters=org_field.max_characters,
+                        help_text=org_field.help_text,
+                        mandatory=org_field.mandatory,
+                        order=org_field.order,
+                    )


Test patch: