internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: akvo/akvo-rsr

Issue:
API endpoint to trigger Project.import_results()
Add an endpoint to the API that calls the Project.import_results() method.

Path: /rest/v1/project/<project ID>/import_results/ Method: POST


Discussion:
After more discussions with Philibert we need to add an endpoint that imports a particular indicator.
This is undergoing tests by Philibert right now. Leaving open.
As a precursor to this, I changed the parent_... fields on the IndicatorSerializer and the IndicatorPeriodSerializer. That PR should be reverted: #3416

Resolving patch:
diff --git a/akvo/rest/views/project_editor.py b/akvo/rest/views/project_editor.py
--- a/akvo/rest/views/project_editor.py
+++ b/akvo/rest/views/project_editor.py
@@ -295,17 +295,17 @@ def project_editor_import_indicator(request, project_pk, parent_indicator_id):
         return HttpResponseBadRequest()
 
     user = request.user
-    if not (user.is_superuser or
-            user.can_import_results() and user.has_perm('rsr.change_project', project)):
+    if not (user.can_import_results() and user.has_perm('rsr.change_project', project)):
         return HttpResponseForbidden()
 
     try:
-        project.import_indicator(parent_indicator_id)
+        indicator = project.import_indicator(parent_indicator_id)
     except (Project.DoesNotExist, Project.MultipleObjectsReturned, Indicator.DoesNotExist,
             Indicator.MultipleObjectsReturned, ValidationError) as e:
         raise RestValidationError(e.message)
 
-    return Response(data=None, status=http_status.HTTP_201_CREATED)
+    data = {'indicator_id': indicator.pk, 'import_success': True}
+    return Response(data=data, status=http_status.HTTP_201_CREATED)
 
 
 @api_view(['POST'])
diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py
--- a/akvo/rsr/models/project.py
+++ b/akvo/rsr/models/project.py
@@ -1209,9 +1209,9 @@ def import_indicator(self, parent_indicator_id):
         # Check that we have a parent project and that project of parent indicator is that parent
         parents = self.parents_all()
         if parents.count() == 0:
-            raise Project.DoesNotExist, "Project has no parent"
+            raise Project.DoesNotExist("Project has no parent")
         elif parents.count() > 1:
-            raise Project.MultipleObjectsReturned, "Project has multiple parents"
+            raise Project.MultipleObjectsReturned("Project has multiple parents")
         else:
             parent_project = parents[0]
 


Test patch:
Repository: akvo/akvo-rsr

Issue:
Indicators and dimensions randomly re-order themselves when saved
When I add an indicator framework and save it, for each result indicators seem to randomly re-order themselves. This is difficult, because there is a certain logic to them (for example outcomes on top, outputs below). The same happen for indicator dimensions (which are not able to move anymore after being saved in the wrong order either). Can someone of the technical team take a look at this, please?



Discussion:

Resolving patch:
diff --git a/akvo/rest/views/project_editor_utils.py b/akvo/rest/views/project_editor_utils.py
--- a/akvo/rest/views/project_editor_utils.py
+++ b/akvo/rest/views/project_editor_utils.py
@@ -569,7 +569,7 @@ def sort_keys(x):
         Model, _ = RELATED_OBJECTS_MAPPING[Model]
     if Model in MANY_TO_MANY_FIELDS or Model != Project:
         level += 1
-    return level
+    return (level, key_parts.ids)
 
 
 def create_or_update_objects_from_data(project, data):


Test patch:
diff --git a/akvo/rsr/tests/rest/test_project_editor.py b/akvo/rsr/tests/rest/test_project_editor.py
--- a/akvo/rsr/tests/rest/test_project_editor.py
+++ b/akvo/rsr/tests/rest/test_project_editor.py
@@ -996,6 +996,9 @@ def test_creating_project_attirbute_hierarchy(self):
         self.assertEqual(result_2.type, result_type_2)
         self.assertEqual(result_2.aggregation_status, result_aggregation_2 == '1')
 
+        # Verify that ordering is maintained
+        self.assertLess(result.id, result_2.id)
+
         indicator = Indicator.objects.get(result=result)
         self.assertEqual(indicator.title, indicator_title)
         self.assertEqual(indicator.description, indicator_description)
Repository: akvo/akvo-rsr

Issue:
Change dimension field labels
https://akvo.zulipchat.com/#narrow/stream/131441-rsr/topic/Change.20dimension.20field.20labels

@AkvoAnt  did a quick informal survey of the key partnership team peeps and all are in favour of changes the field names in the section 5 from 'dimension' to 'disaggregation'; with a simple help text notifying that in IATI terminology disaggregations are 'dimensions'.


Discussion:

Resolving patch:
diff --git a/akvo/rsr/migrations/0150_auto_20190524_1115.py b/akvo/rsr/migrations/0150_auto_20190524_1115.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/migrations/0150_auto_20190524_1115.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.20 on 2019-05-24 11:15
+from __future__ import unicode_literals
+
+import akvo.rsr.fields
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('rsr', '0149_indicator_export_to_iati'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='indicatordimension',
+            options={'ordering': ['id'], 'verbose_name': 'indicator disaggregation', 'verbose_name_plural': 'indicator disaggregations'},
+        ),
+        migrations.AlterField(
+            model_name='indicatordimension',
+            name='name',
+            field=akvo.rsr.fields.ValidXMLCharField(blank=True, help_text='The name of a category to be used when disaggregating (e.g "Age")', max_length=100, verbose_name='disaggregation name'),
+        ),
+        migrations.AlterField(
+            model_name='indicatordimension',
+            name='value',
+            field=akvo.rsr.fields.ValidXMLCharField(blank=True, help_text='A value in the category being disaggregated (e.g. "Older than 60 years").', max_length=100, verbose_name='disaggregation value'),
+        ),
+    ]
diff --git a/akvo/rsr/models/result/indicator_dimension.py b/akvo/rsr/models/result/indicator_dimension.py
--- a/akvo/rsr/models/result/indicator_dimension.py
+++ b/akvo/rsr/models/result/indicator_dimension.py
@@ -22,16 +22,16 @@ class IndicatorDimension(models.Model):
                                          verbose_name=_(u'parent dimension'),
                                          related_name='child_dimensions')
     name = ValidXMLCharField(
-        _(u'dimension name'), blank=True, max_length=100,
+        _(u'disaggregation name'), blank=True, max_length=100,
         help_text=_(u'The name of a category to be used when disaggregating (e.g "Age")'))
     value = ValidXMLCharField(
-        _(u'dimension value'), blank=True, max_length=100,
+        _(u'disaggregation value'), blank=True, max_length=100,
         help_text=_(u'A value in the category being disaggregated (e.g. "Older than 60 years").'))
 
     class Meta:
         app_label = 'rsr'
-        verbose_name = _(u'indicator dimension')
-        verbose_name_plural = _(u'indicator dimensions')
+        verbose_name = _(u'indicator disaggregation')
+        verbose_name_plural = _(u'indicator disaggregations')
         ordering = ['id']
 
     def __unicode__(self):


Test patch:
Repository: akvo/akvo-rsr

Issue:
Organisation report shown in project reports page
The "Project overview" report is displayed on the project report page, which is an organisation report  and should not be displayed on the project report page.


Discussion:

Resolving patch:
diff --git a/akvo/rest/views/report.py b/akvo/rest/views/report.py
--- a/akvo/rest/views/report.py
+++ b/akvo/rest/views/report.py
@@ -58,7 +58,7 @@ def project_reports(request, project_pk):
 
     project = get_object_or_404(Project, pk=project_pk)
     reports = Report.objects.prefetch_related('formats', 'organisations')\
-                            .filter(url__icontains='project')
+                            .filter(url__icontains='{project}')
 
     user = request.user
     if not user.has_perm('rsr.view_project', project):


Test patch:
diff --git a/akvo/rsr/tests/base.py b/akvo/rsr/tests/base.py
--- a/akvo/rsr/tests/base.py
+++ b/akvo/rsr/tests/base.py
@@ -60,8 +60,9 @@ def create_project(title, published=True, public=True):
         return project
 
     @staticmethod
-    def create_report(report_name, organisation=None, is_org_report=False):
-        url = '/{organisation}/?format={format}' if is_org_report else '/{project}/?format={format}'
+    def create_report(report_name, organisation=None, is_org_report=False, url=None):
+        if url is None:
+            url = '/{organisation}/?format={format}' if is_org_report else '/{project}/?format={format}'
         report = Report.objects.create(
             name=report_name, title=report_name, url=url)
         if organisation is not None:
diff --git a/akvo/rsr/tests/rest/test_project_reports.py b/akvo/rsr/tests/rest/test_project_reports.py
--- a/akvo/rsr/tests/rest/test_project_reports.py
+++ b/akvo/rsr/tests/rest/test_project_reports.py
@@ -114,6 +114,10 @@ def test_only_reports_with_project_parameter_are_shown(self):
         org1 = self.create_organisation('org-1')
         self.create_report('report-1')
         self.create_report('report-2', is_org_report=True)
+        self.create_report(
+            'projects-overview',
+            url='/en/reports/project_overview/{organisation}?format={format}&download=true'
+        )
 
         self.make_partner(proj1, org1)
         user = self.create_user('foo@example.com', 'secret')
Repository: akvo/akvo-rsr

Issue:
Add JSON editor to Django admin for OrganisationCodelist
The codelists are currently updated by hand, and making changes to it is a pain. It would make life a lot easier if we add the codelist model to the django admin, and also add a json editor. 


Discussion:

Resolving patch:
diff --git a/akvo/rsr/admin.py b/akvo/rsr/admin.py
--- a/akvo/rsr/admin.py
+++ b/akvo/rsr/admin.py
@@ -21,6 +21,7 @@
 from django.contrib.auth.models import Group
 from django.db import models, transaction
 from django.apps import apps
+from django.contrib.postgres.fields import JSONField
 from django.forms.utils import ErrorList
 from django.forms import TextInput
 from django.utils.decorators import method_decorator
@@ -28,6 +29,7 @@
 from django.views.decorators.csrf import csrf_protect
 from django.utils.encoding import force_text
 
+from prettyjson import PrettyJSONWidget
 from sorl.thumbnail.fields import ImageField
 from embed_video.admin import AdminVideoMixin
 import os.path
@@ -1579,3 +1581,12 @@ class UserProjectsAdmin(admin.ModelAdmin):
     model = UserProjects
 
 admin.site.register(UserProjects, UserProjectsAdmin)
+
+
+class OrganisationCodeList(admin.ModelAdmin):
+    list_display = (u'slug',)
+    formfield_overrides = {
+        JSONField: {'widget': PrettyJSONWidget}
+    }
+
+admin.site.register(apps.get_model('rsr', 'organisationcodelist'), OrganisationCodeList)


Test patch:
Repository: akvo/akvo-rsr

Issue:
Restrict user API endpoint



Discussion:

Resolving patch:
diff --git a/akvo/rest/views/user.py b/akvo/rest/views/user.py
--- a/akvo/rest/views/user.py
+++ b/akvo/rest/views/user.py
@@ -37,6 +37,15 @@ class UserViewSet(BaseRSRViewSet):
     )
     serializer_class = UserSerializer
 
+    def get_queryset(self):
+        user = self.request.user
+        if user.is_anonymous():
+            raise PermissionDenied()
+        return super(UserViewSet, self).get_queryset()
+
+    def list(self, request, *args, **kwargs):
+        raise PermissionDenied()
+
 
 @api_view(['POST'])
 def change_password(request, pk=None):


Test patch:
diff --git a/akvo/rsr/tests/rest/migration_data.py b/akvo/rsr/tests/rest/migration_data.py
--- a/akvo/rsr/tests/rest/migration_data.py
+++ b/akvo/rsr/tests/rest/migration_data.py
@@ -40,10 +40,6 @@
     # akvo/rsr/front-end/scripts-src/my-projects.js
     '/rest/v1/project/?format=json',
 
-    # akvo/scripts/cordaid/organisation_upload.py
-    '/api/v1/user/?format=json&api_key={api_key}&username={username}&user__username={username}&do_format=1',
-    # XXX: /rest/v1/internal_organisation_id/?recording_org={recording_org}&identifier={identifier}&format=json,
-
     # akvo/rest/filters.py doc examples
     "/rest/v1/project/?filter={'title__icontains':'water','currency':'EUR'}&format=json",
     "/rest/v1/project/?filter={'title__icontains':'fiber'}&exclude={'currency':'EUR'}&format=json",
diff --git a/akvo/rsr/tests/rest/test_user.py b/akvo/rsr/tests/rest/test_user.py
--- a/akvo/rsr/tests/rest/test_user.py
+++ b/akvo/rsr/tests/rest/test_user.py
@@ -43,6 +43,15 @@ def setUp(self):
             version=version
         )
 
+    def test_request_to_list_user_are_forbidden(self):
+        response = self.c.get('/rest/v1/user/?format=json')
+        self.assertEqual(response.status_code, 403)
+
+    def test_unauthenticated_request_for_user_detail_should_be_forbidden(self):
+        self.c.logout()
+        response = self.c.get('/rest/v1/user/{}/?format=json'.format(self.user.id))
+        self.assertEqual(response.status_code, 403)
+
     def test_request_organisation_simple(self):
         # Given
         data = {'organisation': self.org.id}
diff --git a/akvo/rsr/tests/up/test_up.py b/akvo/rsr/tests/up/test_up.py
--- a/akvo/rsr/tests/up/test_up.py
+++ b/akvo/rsr/tests/up/test_up.py
@@ -119,6 +119,7 @@ def test_get_updates_information(self):
         all_updates = results.findall('list-item')
         self.assertGreater(len(all_updates), 0)
 
+        self.c.login(username='TestUser', password='TestPassword')
         for update in all_updates:
             user_element = update.find('user')
             self.assertNotEqual(user_element, None)
Repository: akvo/akvo-rsr

Issue:
Search on project id in django admin



Discussion:

Resolving patch:
diff --git a/akvo/rsr/admin.py b/akvo/rsr/admin.py
--- a/akvo/rsr/admin.py
+++ b/akvo/rsr/admin.py
@@ -991,9 +991,9 @@ class ProjectAdmin(TimestampsAdminDisplayMixin, ObjectPermissionsModelAdmin, Nes
         }),
     )
     filter_horizontal = ('keywords',)
-    list_display = ('title', 'status', 'project_plan_summary', 'last_update',
+    list_display = ('id', 'title', 'status', 'project_plan_summary', 'last_update',
                     'show_current_image', 'is_published', 'show_keywords')
-    search_fields = ('title', 'subtitle', 'project_plan_summary', 'iati_activity_id',)
+    search_fields = ('id', 'title', 'subtitle', 'project_plan_summary', 'iati_activity_id',)
     list_filter = ('currency', 'status', 'keywords',)
     # created_at and last_modified_at MUST be readonly since they have the auto_now/_add attributes
     readonly_fields = ('budget', 'funds', 'funds_needed', 'created_at', 'last_modified_at',


Test patch:
Repository: akvo/akvo-rsr

Issue:
Allow patching indicator with empty list of dimension names



Discussion:

Resolving patch:
diff --git a/akvo/rest/serializers/indicator.py b/akvo/rest/serializers/indicator.py
--- a/akvo/rest/serializers/indicator.py
+++ b/akvo/rest/serializers/indicator.py
@@ -7,7 +7,7 @@
 from akvo.rest.serializers.indicator_period import IndicatorPeriodFrameworkSerializer
 from akvo.rest.serializers.indicator_dimension_name import IndicatorDimensionNameSerializer
 from akvo.rest.serializers.rsr_serializer import BaseRSRSerializer
-from akvo.rsr.models import Indicator
+from akvo.rsr.models import Indicator, IndicatorDimensionName
 
 from rest_framework import serializers
 
@@ -17,6 +17,8 @@ class IndicatorSerializer(BaseRSRSerializer):
     result_unicode = serializers.ReadOnlyField(source='result.__unicode__')
     measure_label = serializers.ReadOnlyField(source='iati_measure_unicode')
     children_aggregate_percentage = serializers.ReadOnlyField()
+    dimension_names = serializers.PrimaryKeyRelatedField(
+        many=True, queryset=IndicatorDimensionName.objects.all())
 
     class Meta:
         model = Indicator


Test patch:
diff --git a/akvo/rsr/tests/rest/test_indicator.py b/akvo/rsr/tests/rest/test_indicator.py
--- a/akvo/rsr/tests/rest/test_indicator.py
+++ b/akvo/rsr/tests/rest/test_indicator.py
@@ -131,6 +131,26 @@ def test_indicator_framework_dimension_names_patch(self):
         # Then
         self.assertEqual(data['dimension_names'], response.data['dimension_names'])
 
+    def test_indicator_framework_dimension_names_empty_patch(self):
+        # Given
+        result = Result.objects.create(project=self.project)
+        indicator = Indicator.objects.create(result=result)
+        IndicatorPeriod.objects.create(indicator=indicator)
+        dimension = IndicatorDimensionName.objects.create(project=self.project, name='Age')
+        indicator.dimension_names.add(dimension)
+        self.c.login(username=self.user.username, password="password")
+        url = '/rest/v1/indicator/{}/?format=json'.format(indicator.id)
+        content_type = 'application/json'
+        response = self.c.get(url)
+        self.assertIn(dimension.id, response.data['dimension_names'])
+        data = {'dimension_names': []}
+
+        # When
+        response = self.c.patch(url, data=json.dumps(data), content_type=content_type)
+
+        # Then
+        self.assertEqual(data['dimension_names'], response.data['dimension_names'])
+
     def get_indicator_periods(self, project_id):
         periods = []
         next_url = '/rest/v1/indicator_period/?format=json&indicator__result__project={}&limit=50'.format(project_id)
Repository: akvo/akvo-rsr

Issue:
Add a script to delete all contact information for a list of projects



Discussion:

Resolving patch:
diff --git a/akvo/rsr/management/commands/remove_project_contacts.py b/akvo/rsr/management/commands/remove_project_contacts.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/management/commands/remove_project_contacts.py
@@ -0,0 +1,32 @@
+# -*- 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 >.
+
+
+from django.core.management.base import BaseCommand
+
+from akvo.rsr.models import ProjectContact
+
+
+class Command(BaseCommand):
+    help = u"Remove ProjectContacts for all the given projects."
+
+    def add_arguments(self, parser):
+        parser.add_argument(
+            'project_ids',
+            action='store',
+            nargs='+',
+            help='IDs of projects where contacts should be deleted'
+        )
+
+    def handle(self, *args, **options):
+        project_ids = options['project_ids']
+        contacts = ProjectContact.objects.filter(project_id__in=project_ids)
+        count = contacts.count()
+        if count > 0:
+            print('Deleting {} contacts'.format(count))
+            contacts.delete()
+        else:
+            print('No contacts found to delete')


Test patch:
Repository: akvo/akvo-rsr

Issue:
Creating new dimension name and adding it to an indicator should create values in all children



Discussion:

Resolving patch:
diff --git a/akvo/rsr/models/project.py b/akvo/rsr/models/project.py
--- a/akvo/rsr/models/project.py
+++ b/akvo/rsr/models/project.py
@@ -1313,6 +1313,8 @@ def copy_dimension_name(self, source_dimension_name, set_parent=True):
         for dimension_value in source_dimension_name.dimension_values.all():
             self.copy_dimension_value(dimension_name, dimension_value, set_parent=set_parent)
 
+        return dimension_name
+
     def copy_dimension_value(self, dimension_name, source_dimension_value, set_parent=True):
         IndicatorDimensionValue = apps.get_model('rsr', 'IndicatorDimensionValue')
         defaults = dict(parent_dimension_value=source_dimension_value)
diff --git a/akvo/rsr/models/result/indicator.py b/akvo/rsr/models/result/indicator.py
--- a/akvo/rsr/models/result/indicator.py
+++ b/akvo/rsr/models/result/indicator.py
@@ -239,10 +239,7 @@ def add_dimension_names_to_children(sender, instance, action, **kwargs):
 
     dimension_name = kwargs['model'].objects.filter(id__in=kwargs['pk_set']).first()
     for indicator in instance.child_indicators.all():
-        child_dimension_name, _ = dimension_name.child_dimension_names.get_or_create(
-            name=dimension_name.name,
-            parent_dimension_name=dimension_name,
-            project=indicator.result.project)
+        child_dimension_name = indicator.result.project.copy_dimension_name(dimension_name, set_parent=True)
 
         if action == 'post_add':
             indicator.dimension_names.add(child_dimension_name)


Test patch:
diff --git a/akvo/rsr/tests/results_framework/test_results_framework.py b/akvo/rsr/tests/results_framework/test_results_framework.py
--- a/akvo/rsr/tests/results_framework/test_results_framework.py
+++ b/akvo/rsr/tests/results_framework/test_results_framework.py
@@ -896,3 +896,23 @@ def test_adding_or_removing_indicator_dimension_names_changes_children(self):
 
         # Then
         self.assertEqual(child_indicator.dimension_names.count(), 1)
+
+    def test_adding_new_dimension_name_and_values_to_indicator_changes_children(self):
+        # Given
+        name = IndicatorDimensionName.objects.create(name='Colour', project=self.parent_project)
+        colours = {'Red', 'Blue', 'Green'}
+        for colour in colours:
+            IndicatorDimensionValue.objects.create(name=name, value=colour)
+        indicator = self.indicator
+        child_indicator = Indicator.objects.get(
+            parent_indicator=indicator, result__project=self.child_project)
+        self.assertEqual(child_indicator.dimension_names.count(), 1)
+
+        # When
+        indicator.dimension_names.add(name)
+
+        # Then
+        self.assertEqual(child_indicator.dimension_names.count(), 2)
+        colour = child_indicator.dimension_names.get(name='Colour')
+        child_values = set(colour.dimension_values.values_list('value', flat=True))
+        self.assertEqual(colours, child_values)