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

Issue:
Period.actual_comment clean-up.
Ignore period update without comment


Discussion:

Resolving patch:
diff --git a/akvo/rsr/management/commands/period_actual_comment_cleanup.py b/akvo/rsr/management/commands/period_actual_comment_cleanup.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/management/commands/period_actual_comment_cleanup.py
@@ -0,0 +1,19 @@
+# -*- 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 django.db.models import Q
+from akvo.rsr.models import IndicatorPeriod
+
+
+class Command(BaseCommand):
+    help = "Clean up period.actual_comment that contains only period update dates"
+
+    def handle(self, *args, **options):
+        periods = IndicatorPeriod.objects.filter(~Q(actual_comment=''))
+        for period in periods:
+            period.update_actual_comment()
diff --git a/akvo/rsr/models/result/indicator_period.py b/akvo/rsr/models/result/indicator_period.py
--- a/akvo/rsr/models/result/indicator_period.py
+++ b/akvo/rsr/models/result/indicator_period.py
@@ -272,6 +272,7 @@ def update_actual_comment(self, save=True):
         update_texts = [
             u'{}: {}'.format(update.last_modified_at.strftime('%d-%m-%Y'), update.text)
             for update in self.approved_updates.order_by('-created_at')
+            if update.text.strip()
         ]
         actual_comment = u' | '.join(update_texts)
         if len(actual_comment) >= 2000:  # max_size


Test patch:
diff --git a/akvo/rsr/tests/models/test_indicator_period.py b/akvo/rsr/tests/models/test_indicator_period.py
--- a/akvo/rsr/tests/models/test_indicator_period.py
+++ b/akvo/rsr/tests/models/test_indicator_period.py
@@ -61,6 +61,21 @@ def test_period_data_updates_actual_comment(self):
         period = IndicatorPeriod.objects.get(id=period.id)
         self.assertIn(data.text, period.actual_comment)
 
+    def test_approved_period_data_with_empty_text_does_not_update_actual_comment(self):
+        # Given
+        period = self.period
+        user = self.user
+
+        # When
+        IndicatorPeriodData.objects.create(text=' ',
+                                           period=period,
+                                           user=user,
+                                           status=IndicatorPeriodData.STATUS_APPROVED_CODE)
+
+        # Then
+        period = IndicatorPeriod.objects.get(id=period.id)
+        self.assertEqual('', period.actual_comment)
+
     def test_period_data_non_ascii_updates_actual_comment(self):
         """Test that adding IndicatorPeriodData with unicode text works."""
Repository: akvo/akvo-rsr

Issue:
Show only relevant updates in typeahead on Akvo pages
Currently, all updates can be searched for on partner site updates typeahead. 


Discussion:

Resolving patch:
diff --git a/akvo/rest/views/typeahead.py b/akvo/rest/views/typeahead.py
--- a/akvo/rest/views/typeahead.py
+++ b/akvo/rest/views/typeahead.py
@@ -134,7 +134,8 @@ def typeahead_impact_projects(request):
 
 @api_view(['GET'])
 def typeahead_projectupdate(request):
-    updates = ProjectUpdate.objects.all()
+    page = request.rsr_page
+    updates = page.updates() if page else ProjectUpdate.objects.all()
     return Response(
         rejig(updates, TypeaheadProjectUpdateSerializer(updates, many=True))
     )


Test patch:
Repository: akvo/akvo-rsr

Issue:
Project update admin interface search doesn't work



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
@@ -1254,7 +1254,7 @@ class ProjectUpdateAdmin(TimestampsAdminDisplayMixin, AdminVideoMixin, admin.Mod
 
     list_display = ('title', 'project', 'user', 'text', 'language', 'event_date', 'created_at', 'img',)
     list_filter = ('created_at', 'project', )
-    search_fields = ('project__id', 'project__title', 'user__first_name', 'user__last_name',)
+    search_fields = ('title', 'id', 'project__id', 'project__title', 'user__first_name', 'user__last_name',)
     inlines = (ProjectUpdateLocationInline,)
     # created_at and last_modified_at MUST be readonly since they have the auto_now/_add attributes
     readonly_fields = ('created_at', 'last_modified_at')


Test patch:
Repository: akvo/akvo-rsr

Issue:
Don't entirely remove existing reporting partner on EUTF projects
EUTF wants the main EUTF organisation to be set as the reporting organisation for all their projects. Previously, we removed the existing reporting organisation, if any. This causes problems with permissions - ideally, we'd want to change the existing partnership to an implementing partner role, or something like that. 


Discussion:

Resolving patch:
diff --git a/akvo/rsr/eutf_signals.py b/akvo/rsr/eutf_signals.py
--- a/akvo/rsr/eutf_signals.py
+++ b/akvo/rsr/eutf_signals.py
@@ -54,5 +54,11 @@ def set_eutf_as_reporting_organisation(project):
         defaults=dict(organisation=eutf)
     )
     if not created and partnership.organisation != eutf:
+        old_reporting_organisation = partnership.organisation
         partnership.organisation = eutf
         partnership.save(update_fields=['organisation'])
+        Partnership.objects.create(
+            project=project,
+            organisation=old_reporting_organisation,
+            iati_organisation_role=Partnership.IATI_IMPLEMENTING_PARTNER,
+        )


Test patch:
diff --git a/akvo/rsr/tests/test_eutf_signals.py b/akvo/rsr/tests/test_eutf_signals.py
--- a/akvo/rsr/tests/test_eutf_signals.py
+++ b/akvo/rsr/tests/test_eutf_signals.py
@@ -14,6 +14,7 @@ class EUTFSignalsTestCase(BaseTestCase):
     def setUp(self):
         super(EUTFSignalsTestCase, self).setUp()
         self.eutf = self.create_organisation('EUTF')
+        self.delegation = self.create_organisation('EUTF Delegation')
         self.root_project = self.create_project('Root Project')
         ProjectHierarchy.objects.create(
             root_project=self.root_project, organisation=self.eutf, max_depth=2)
@@ -32,6 +33,27 @@ def test_adding_project_to_hierarchy_sets_reporting_org(self):
             project.reporting_partner.iati_organisation_role,
             Partnership.IATI_REPORTING_ORGANISATION)
 
+    def test_existing_reporting_org_made_implementing_partner(self):
+        # Given
+        project = self.create_project('New Project')
+        Partnership.objects.create(
+            project=project,
+            organisation=self.delegation,
+            iati_organisation_role=Partnership.IATI_REPORTING_ORGANISATION,
+        )
+
+        # When
+        with self.settings(EUTF_ROOT_PROJECT=self.root_project.id, EUTF_ORG_ID=self.eutf.id):
+            self.make_parent(self.root_project, project)
+
+        # Then
+        self.assertEqual(project.reporting_partner.organisation, self.eutf)
+        self.assertEqual(
+            project.reporting_partner.iati_organisation_role,
+            Partnership.IATI_REPORTING_ORGANISATION)
+        implementing_partnership = project.partnerships.get(organisation=self.delegation)
+        self.assertEqual(implementing_partnership.iati_organisation_role, Partnership.IATI_IMPLEMENTING_PARTNER)
+
     def test_adding_project_outside_hierarchy_does_not_set_reporting_org(self):
         # Given
         project = self.create_project('New Project')
Repository: akvo/akvo-rsr

Issue:
Users should have permissions on projects in content owned organisations



Discussion:

Resolving patch:
diff --git a/akvo/rsr/permissions.py b/akvo/rsr/permissions.py
--- a/akvo/rsr/permissions.py
+++ b/akvo/rsr/permissions.py
@@ -200,7 +200,7 @@ def user_has_perm(user, employments, project_id):
     # hierarchy, and restrict access based on whether the owner has enabled
     # restrictions or not.
     if hierarchy_org is None or not hierarchy_org.enable_restrictions:
-        all_projects = organisations.all_projects()
+        all_projects = organisations.content_owned_organisations().all_projects()
 
     else:
         collaborator_ids = get_organisation_collaborator_org_ids(hierarchy_org.id)


Test patch:
diff --git a/akvo/rsr/tests/test_permissions.py b/akvo/rsr/tests/test_permissions.py
--- a/akvo/rsr/tests/test_permissions.py
+++ b/akvo/rsr/tests/test_permissions.py
@@ -790,3 +790,23 @@ def test_hierarchy_published_only_projects_listed_for_managed_users(self):
         self.assertFalse(project.is_published())
         self.assertNotIn(project, projects)
         self.assertIn(self.project, projects)
+
+
+class ContentOwnedOrganisationsPermissionsTestCase(BaseTestCase):
+
+    def setUp(self):
+        super(ContentOwnedOrganisationsPermissionsTestCase, self).setUp()
+        self.user = self.create_user('foo@example.com', 'password')
+        self.org = self.create_organisation('EUTF')
+        self.org1 = self.create_organisation('EUTF Delegation 1')
+        self.project = self.create_project('EUTF Project')
+
+    def test_can_access_content_owned_org_projects(self):
+        # Given
+        self.make_employment(self.user, self.org, 'Admins')
+        self.org1.content_owner = self.org
+        self.org1.save()
+        self.make_partner(self.project, self.org1)
+
+        # When/Then
+        self.assertTrue(self.user.has_perm('rsr.change_project', self.project))
Repository: akvo/akvo-rsr

Issue:
Remove validation sets from the admin interface
The validation set rules stored in the backend are no longer used for validation with the new PE. Removing these validation sets from the backend would make it clear to the partnership team that editing these rules doesn't work any more. May be we also communicate with the partnership team separately, that they need to approach the dev team to modify the validation sets. 


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
@@ -1537,23 +1537,10 @@ def get_queryset(self, request):
 admin.site.register(apps.get_model('rsr', 'IatiExport'), IatiExportAdmin)
 
 
-class ValidationInline(admin.TabularInline):
-    model = apps.get_model('rsr', 'ProjectEditorValidation')
-    fields = ('validation', 'action', )
-    ordering = ('validation',)
-
-    def get_extra(self, request, obj=None, **kwargs):
-        if obj:
-            return 1 if obj.validations.count() == 0 else 0
-        else:
-            return 1
-
-
 class ValidationSetAdmin(admin.ModelAdmin):
     model = apps.get_model('rsr', 'ProjectEditorValidationSet')
     list_display = ('name', 'description')
     fields = ('name', 'description')
-    inlines = (ValidationInline, )
 
     def get_queryset(self, request):
         if request.user.is_admin or request.user.is_superuser:


Test patch:
Repository: akvo/akvo-rsr

Issue:
Project delete raises 500s



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
@@ -435,6 +435,13 @@ class Meta:
             ('post_updates', u'Can post updates'),
         )
 
+    def delete(self, using=None, keep_parents=False):
+        # Delete results on the project, before trying to delete the project,
+        # since the RelatedProject object on the project refuses to get deleted
+        # if there are existing results, causing the delete to raise 500s
+        self.results.all().delete()
+        return super(Project, self).delete(using=using, keep_parents=keep_parents)
+
     def save(self, *args, **kwargs):
         # Strip title of any trailing or leading spaces
         if self.title:


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
@@ -776,6 +776,23 @@ def test_prevent_deleting_parent_if_results_imported(self):
         with self.assertRaises(ParentChangeDisallowed):
             related_project.delete()
 
+    def test_allow_deleting_child(self):
+        # Given
+        child_project = self.create_project('New Child Project')
+        RelatedProject.objects.create(
+            project=child_project, related_project=self.parent_project, relation='1'
+        )
+        child_project.import_results()
+
+        # When
+        child_project.delete()
+
+        # Then
+        query = RelatedProject.objects.filter(
+            project=child_project, related_project=self.parent_project)
+        self.assertFalse(query.exists())
+        self.assertIsNone(child_project.pk)
+
     def test_allow_changing_parents_if_results_not_imported(self):
         # Given
         project = self.create_project(title='New Parent Project')
Repository: akvo/akvo-rsr

Issue:
Don't show Editor tab to users without permissions



Discussion:

Resolving patch:
diff --git a/akvo/rest/serializers/project.py b/akvo/rest/serializers/project.py
--- a/akvo/rest/serializers/project.py
+++ b/akvo/rest/serializers/project.py
@@ -60,7 +60,9 @@ class Meta:
     def get_editable(self, obj):
         """Method used by the editable SerializerMethodField"""
         user = self.context['request'].user
-        return user.has_perm('rsr.change_project', obj)
+        if not user.is_authenticated():
+            return False
+        return user.can_edit_project(obj)
 
 
 class ProjectDirectorySerializer(serializers.ModelSerializer):
@@ -200,7 +202,9 @@ def get_parent(self, obj):
     def get_editable(self, obj):
         """Method used by the editable SerializerMethodField"""
         user = self.context['request'].user
-        return user.has_perm('rsr.change_project', obj)
+        if not user.is_authenticated():
+            return False
+        return user.can_edit_project(obj)
 
     class Meta:
         model = Project
diff --git a/akvo/rsr/models/user.py b/akvo/rsr/models/user.py
--- a/akvo/rsr/models/user.py
+++ b/akvo/rsr/models/user.py
@@ -385,6 +385,10 @@ def can_import_results(self, project):
         """Check to see if the user can import results to the specified project."""
         return self.has_perm('rsr.import_results', project)
 
+    def can_edit_project(self, project):
+        """Check if the user can edit a project."""
+        return self.has_perm('rsr.change_project', project)
+
     def employments_dict(self, org_list):
         """
         Represent User as dict with employments.
diff --git a/akvo/rsr/views/my_rsr.py b/akvo/rsr/views/my_rsr.py
--- a/akvo/rsr/views/my_rsr.py
+++ b/akvo/rsr/views/my_rsr.py
@@ -640,6 +640,7 @@ def my_project(request, project_id, template='myrsr/my_project.html'):
         'user_is_me_manager': json.dumps(user_is_me_manager),
         'show_narrative_reports': json.dumps(show_narrative_reports),
         'show_results': json.dumps(show_results),
+        'can_edit_project': json.dumps(user.can_edit_project(project)),
     }
 
     context = project.project_hierarchy_context(context)


Test patch:
Repository: akvo/akvo-rsr

Issue:
Add management command to mark users as inactive and export active user list



Discussion:

Resolving patch:
diff --git a/akvo/rsr/management/commands/hubspot_user_list.py b/akvo/rsr/management/commands/hubspot_user_list.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/management/commands/hubspot_user_list.py
@@ -0,0 +1,62 @@
+# -*- 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 __future__ import print_function
+
+import csv
+import datetime
+from os.path import abspath, dirname, join
+
+from django.core.management.base import BaseCommand
+
+from akvo.rsr.models import User
+
+HERE = dirname(abspath(__file__))
+OUTPUT_PATH = abspath(join(HERE, '..', '..', '..', '..', 'data', 'user-list.csv'))
+
+
+def mark_users_as_inactive(fd_csv):
+    user_data = csv.reader(fd_csv, delimiter=',', quotechar='"')
+    header = next(user_data)
+    email_column = header.index('Email')
+    emails = [row[email_column] for row in user_data]
+    print('Looking for {} emails'.format(len(emails)))
+    users = User.objects.filter(email__in=emails)
+    print('Setting {} users as inactive'.format(users.count()))
+    users.update(is_active=False)
+
+
+def export(n_years=100):
+    today = datetime.datetime.today()
+    last_login_cutoff = today - datetime.timedelta(days=365 * n_years)
+    users = User.objects.filter(
+        is_active=True, employers__gt=0, last_login__gt=last_login_cutoff
+    ).distinct()
+    print('Exporting {} users to {}'.format(users.count(), OUTPUT_PATH))
+    with open(OUTPUT_PATH, 'w') as f:
+        for user in users:
+            f.write('"{}",{}\n'.format(user.get_full_name().encode('utf8'), user.email))
+
+
+class Command(BaseCommand):
+    help = 'Script to manage user list for using with hubspot'
+
+    def add_arguments(self, parser):
+        subparsers = parser.add_subparsers()
+
+        set_inactive = subparsers.add_parser('set_inactive', cmd=self)
+        set_inactive.add_argument('user_list_csv', type=open, help='path to the csv with user list')
+
+        export = subparsers.add_parser('export', cmd=self)
+        export.add_argument('-n', type=int,
+                            help='limit for number of years since last login',
+                            default=100)
+
+    def handle(self, *args, **options):
+        if 'user_list_csv' in options:
+            mark_users_as_inactive(options['user_list_csv'])
+        else:
+            export(options['n'])


Test patch:
Repository: akvo/akvo-rsr

Issue:
Response to Project POST doesn't contain correct primary_organisation



Discussion:

Resolving patch:
diff --git a/akvo/rest/serializers/project.py b/akvo/rest/serializers/project.py
--- a/akvo/rest/serializers/project.py
+++ b/akvo/rest/serializers/project.py
@@ -64,6 +64,13 @@ def get_editable(self, obj):
             return False
         return user.can_edit_project(obj)
 
+    def create(self, validated_data):
+        project = super(ProjectSerializer, self).create(validated_data)
+        user = self.context['request'].user
+        Project.new_project_created(project.id, user)
+        project.refresh_from_db()
+        return project
+
 
 class ProjectDirectorySerializer(serializers.ModelSerializer):
 
diff --git a/akvo/rest/views/project.py b/akvo/rest/views/project.py
--- a/akvo/rest/views/project.py
+++ b/akvo/rest/views/project.py
@@ -70,13 +70,6 @@ def get_queryset(self):
             ).distinct()
         return super(ProjectViewSet, self).get_queryset()
 
-    def create(self, request, *args, **kwargs):
-        response = super(ProjectViewSet, self).create(request, *args, **kwargs)
-        user = request.user
-        project_id = response.data['id']
-        Project.new_project_created(project_id, user)
-        return response
-
 
 class MyProjectsViewSet(PublicProjectViewSet):
     """Viewset providing listing of projects a user can edit."""


Test patch:
diff --git a/akvo/rsr/tests/rest/test_project.py b/akvo/rsr/tests/rest/test_project.py
--- a/akvo/rsr/tests/rest/test_project.py
+++ b/akvo/rsr/tests/rest/test_project.py
@@ -461,6 +461,7 @@ def test_project_creation(self):
         )
         for key in data:
             self.assertEqual(data[key], response.data[key])
+        self.assertEqual(response.data['primary_organisation'], partnership.organisation.pk)
 
     def test_reporting_org_set(self):
         # When
Repository: akvo/akvo-rsr

Issue:
IndicatorPeriod.recalculate_period raised TypeError
TypeError: conversion from NoneType to Decimal is not supported


Discussion:

Resolving patch:
diff --git a/akvo/rsr/models/result/indicator_period.py b/akvo/rsr/models/result/indicator_period.py
--- a/akvo/rsr/models/result/indicator_period.py
+++ b/akvo/rsr/models/result/indicator_period.py
@@ -205,7 +205,7 @@ def recalculate_period(self, save=True, only_self=False):
                     prev_val = str(calculate_percentage(float(prev_num), float(prev_den)))
                 else:
                     prev_val = str(Decimal(prev_val) + Decimal(update.value))
-            except InvalidOperation:
+            except (InvalidOperation, TypeError):
                 # If not possible, the update data or previous value is a normal string
                 if self.indicator.measure == PERCENTAGE_MEASURE:
                     prev_num = update.numerator


Test patch:
diff --git a/akvo/rsr/tests/models/test_results_aggregation.py b/akvo/rsr/tests/models/test_results_aggregation.py
--- a/akvo/rsr/tests/models/test_results_aggregation.py
+++ b/akvo/rsr/tests/models/test_results_aggregation.py
@@ -104,6 +104,26 @@ def test_actual_value_percentages_with_one_approved_updates(self):
         self.assertEqual(actual_value.numerator, Decimal("3.14"))
         self.assertEqual(actual_value.denominator, 17)
 
+    def test_recalculate_period_when_update_numerator_and_denominator_are_NoneType_should_not_raised_TypeError(self):
+        # Given
+        self.indicator.measure = PERCENTAGE_MEASURE
+        self.indicator.save()
+        raised = False
+
+        # When
+        try:
+            IndicatorPeriodData.objects.create(
+                period=self.period,
+                user=self.user,
+                status=IndicatorPeriodData.STATUS_APPROVED_CODE,
+                value=1
+            )
+        except TypeError:
+            raised = True
+
+        # Then
+        self.assertFalse(raised, 'TypeError raised')
+
 
 class PeriodDisaggregationTestCase(TestCase):
     """Tests for the indicator period model"""
Repository: akvo/akvo-rsr

Issue:
Add result type to /project/:id/results/
We want to display whether the result is an Outcome, Impact, etc...


Discussion:

Resolving patch:
diff --git a/akvo/rest/views/project_overview.py b/akvo/rest/views/project_overview.py
--- a/akvo/rest/views/project_overview.py
+++ b/akvo/rest/views/project_overview.py
@@ -27,7 +27,12 @@ def project_results(request, pk):
         'id': project.id,
         'title': project.title,
         'results': [
-            {'id': r.id, 'title': r.title, 'indicator_count': r.indicators.count()}
+            {
+                'id': r.id,
+                'title': r.title,
+                'indicator_count': r.indicators.count(),
+                'type': r.iati_type().name if r.type else None
+            }
             for r in project.results.all()
         ],
     }


Test patch:
Repository: akvo/akvo-rsr

Issue:
All transactions are not shown in project editor



Discussion:

Resolving patch:
diff --git a/akvo/rest/pagination.py b/akvo/rest/pagination.py
--- a/akvo/rest/pagination.py
+++ b/akvo/rest/pagination.py
@@ -11,7 +11,7 @@
 class StandardSizePageNumberPagination(pagination.PageNumberPagination):
     page_size = 30
     page_size_query_param = 'limit'
-    max_page_size = 100
+    max_page_size = 1000
 
 
 class LargeSizePageNumberPagination(pagination.PageNumberPagination):


Test patch: