prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Prevent automatic publishing of projects
  ## Test plan
  
  GIVEN a project that is going to be published
  WHEN the project is published
  THEN a notification mail will be sent to Kasper
  ## Issue description
  
  Somehow there are projects (mainly from Commonsites and Akvo) that get published automatically, even though they shouldn't be. This should be prevented and fixed.
  
  Note; I can't find the reason why this happens. I suspect the API, but then again, this only happens for Akvo and Commonsites projects. Therefore I'll monitor it for now.
  
  
  </issue>
  <code>
  [start of akvo/rsr/models/publishing_status.py]
  1 # -*- coding: utf-8 -*-
  2 
  3 # Akvo RSR is covered by the GNU Affero General Public License.
  4 # See more details in the license.txt file located at the root folder of the Akvo RSR module.
  5 # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
  6 
  7 from django.core.exceptions import ValidationError
  8 from django.db import models
  9 from django.utils.translation import ugettext_lazy as _
  10 
  11 from ..fields import ValidXMLCharField
  12 
  13 
  14 class PublishingStatus(models.Model):
  15     """Keep track of publishing status."""
  16     STATUS_PUBLISHED = 'published'
  17     STATUS_UNPUBLISHED = 'unpublished'
  18     PUBLISHING_STATUS = (
  19         (STATUS_UNPUBLISHED, _(u'Unpublished')),
  20         (STATUS_PUBLISHED, _(u'Published')),
  21     )
  22 
  23     project = models.OneToOneField('Project',)
  24     status = ValidXMLCharField(max_length=30,
  25                                choices=PUBLISHING_STATUS,
  26                                db_index=True, default=STATUS_UNPUBLISHED)
  27 
  28     def clean(self):
  29         """Projects can only be published, when several checks have been performed."""
  30         if self.status == 'published':
  31             validation_errors = []
  32 
  33             if not self.project.title:
  34                 validation_errors.append(
  35                     ValidationError(_('Project needs to have a title.'),
  36                                     code='title')
  37                 )
  38 
  39             if not self.project.subtitle:
  40                 validation_errors.append(
  41                     ValidationError(_('Project needs to have a subtitle.'),
  42                                     code='subtitle')
  43                 )
  44 
  45             if not self.project.project_plan_summary:
  46                 validation_errors.append(
  47                     ValidationError(_('Project needs to have the project plan summary filled in.'),
  48                                     code='summary')
  49                 )
  50 
  51             if not self.project.goals_overview:
  52                 validation_errors.append(
  53                     ValidationError(_('Project needs to have the goals overview field filled in.'),
  54                                     code='goals_overview')
  55                 )
  56 
  57             if not self.project.date_start_planned:
  58                 validation_errors.append(
  59                     ValidationError(
  60                         _('Project needs to have the planned start date field filled in.'),
  61                         code='goals_overview')
  62                 )
  63 
  64             if not self.project.partners:
  65                 validation_errors.append(
  66                     ValidationError(_('Project needs to have at least one valid partner.'),
  67                                     code='partners')
  68                 )
  69             elif not self.project.partnerships.filter(
  70                     partner_type__in=['field', 'funding', 'support']
  71             ).exists():
  72                 validation_errors.append(
  73                     ValidationError(
  74                         _('Project needs to have at least one field, funding or support partner.'),
  75                         code='partners'
  76                     )
  77                 )
  78             else:
  79                 for funding_partner in self.project.partnerships.filter(partner_type='funding'):
  80                     if not funding_partner.funding_amount:
  81                         validation_errors.append(
  82                             ValidationError(_('All funding partners should have a funding amount.'),
  83                                             code='partners'
  84                             )
  85                         )
  86                         break
  87 
  88             if not self.project.sync_owner:
  89                 validation_errors.append(
  90                     ValidationError(_('Project needs to have a reporting organisation.'),
  91                                     code='reporting_org')
  92                 )
  93 
  94             if not self.project.current_image:
  95                 validation_errors.append(
  96                     ValidationError(_('Project needs to have a photo.'),
  97                                     code='current_image')
  98                 )
  99 
  100             if not self.project.locations.all():
  101                 validation_errors.append(
  102                     ValidationError(_('Project needs to have at least one location.'),
  103                                     code='location')
  104                 )
  105             else:
  106                 for location in self.project.locations.all():
  107                     if not location.latitude or not location.longitude or not location.country:
  108                         validation_errors.append(
  109                             ValidationError(
  110                                 _('All locations need to have a latitude, longitude and country '
  111                                   'specified.'),
  112                                 code='location')
  113                         )
  114                         break
  115 
  116             if not self.project.budget_items.all():
  117                 validation_errors.append(
  118                     ValidationError(_('Project needs to have at least one budget item.'),
  119                                     code='budget_item')
  120                 )
  121             elif not self.project.budget_items.filter(amount__gt=0).exists():
  122                 validation_errors.append(
  123                     ValidationError(
  124                         _('Project needs to have at least one budget item with an amount.'),
  125                         code='budget_item'
  126                     )
  127                 )
  128 
  129             if not self.project.sectors.all():
  130                 validation_errors.append(
  131                     ValidationError(_('Project needs to have at least one sector.'),
  132                                     code='sector')
  133                 )
  134             else:
  135                 for sector in self.project.sectors.all():
  136                     if not sector.sector_code:
  137                         validation_errors.append(
  138                             ValidationError(_('All sectors need to have a sector code.'),
  139                                             code='sector')
  140                         )
  141                         break
  142 
  143             if validation_errors:
  144                 raise ValidationError(validation_errors)
  145 
  146     class Meta:
  147         app_label = 'rsr'
  148         verbose_name = _(u'publishing status')
  149         verbose_name_plural = _(u'publishing statuses')
  150         ordering = ('-status', 'project')
  151
  [end of akvo/rsr/models/publishing_status.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/akvo/rsr/models/publishing_status.py b/akvo/rsr/models/publishing_status.py
  --- a/akvo/rsr/models/publishing_status.py
  +++ b/akvo/rsr/models/publishing_status.py
  @@ -4,8 +4,12 @@
   # 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.core.exceptions import ValidationError
  +from django.core.mail import send_mail
   from django.db import models
  +from django.db.models.signals import post_save
  +from django.dispatch import receiver
   from django.utils.translation import ugettext_lazy as _
   
   from ..fields import ValidXMLCharField
  @@ -148,3 +152,15 @@
           verbose_name = _(u'publishing status')
           verbose_name_plural = _(u'publishing statuses')
           ordering = ('-status', 'project')
  +
  +
  +@receiver(post_save, sender=PublishingStatus)
  +def update_denormalized_project(sender, **kwargs):
  +    "Send notification that a project is published."
  +    publishing_status = kwargs['instance']
  +    if publishing_status.status == PublishingStatus.STATUS_PUBLISHED:
  +        send_mail(
  +            'Project %s has been published' % str(publishing_status.project.pk),
  +            '', getattr(settings, "DEFAULT_FROM_EMAIL", "noreply@akvo.org"),
  +            getattr(settings, "NOTIFY_PUBLISH", ["kasper@akvo.org"])
  +        )
  
verification_info: {"golden_diff": "diff --git a/akvo/rsr/models/publishing_status.py b/akvo/rsr/models/publishing_status.py\n--- a/akvo/rsr/models/publishing_status.py\n+++ b/akvo/rsr/models/publishing_status.py\n@@ -4,8 +4,12 @@\n # See more details in the license.txt file located at the root folder of the Akvo RSR module.\n # For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n \n+from django.conf import settings\n from django.core.exceptions import ValidationError\n+from django.core.mail import send_mail\n from django.db import models\n+from django.db.models.signals import post_save\n+from django.dispatch import receiver\n from django.utils.translation import ugettext_lazy as _\n \n from ..fields import ValidXMLCharField\n@@ -148,3 +152,15 @@\n         verbose_name = _(u'publishing status')\n         verbose_name_plural = _(u'publishing statuses')\n         ordering = ('-status', 'project')\n+\n+\n+@receiver(post_save, sender=PublishingStatus)\n+def update_denormalized_project(sender, **kwargs):\n+    \"Send notification that a project is published.\"\n+    publishing_status = kwargs['instance']\n+    if publishing_status.status == PublishingStatus.STATUS_PUBLISHED:\n+        send_mail(\n+            'Project %s has been published' % str(publishing_status.project.pk),\n+            '', getattr(settings, \"DEFAULT_FROM_EMAIL\", \"noreply@akvo.org\"),\n+            getattr(settings, \"NOTIFY_PUBLISH\", [\"kasper@akvo.org\"])\n+        )\n", "issue": "Prevent automatic publishing of projects\n## Test plan\n\nGIVEN a project that is going to be published\nWHEN the project is published\nTHEN a notification mail will be sent to Kasper\n## Issue description\n\nSomehow there are projects (mainly from Commonsites and Akvo) that get published automatically, even though they shouldn't be. This should be prevented and fixed.\n\nNote; I can't find the reason why this happens. I suspect the API, but then again, this only happens for Akvo and Commonsites projects. Therefore I'll monitor it for now.\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n# Akvo RSR is covered by the GNU Affero General Public License.\n# See more details in the license.txt file located at the root folder of the Akvo RSR module.\n# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.\n\nfrom django.core.exceptions import ValidationError\nfrom django.db import models\nfrom django.utils.translation import ugettext_lazy as _\n\nfrom ..fields import ValidXMLCharField\n\n\nclass PublishingStatus(models.Model):\n    \"\"\"Keep track of publishing status.\"\"\"\n    STATUS_PUBLISHED = 'published'\n    STATUS_UNPUBLISHED = 'unpublished'\n    PUBLISHING_STATUS = (\n        (STATUS_UNPUBLISHED, _(u'Unpublished')),\n        (STATUS_PUBLISHED, _(u'Published')),\n    )\n\n    project = models.OneToOneField('Project',)\n    status = ValidXMLCharField(max_length=30,\n                               choices=PUBLISHING_STATUS,\n                               db_index=True, default=STATUS_UNPUBLISHED)\n\n    def clean(self):\n        \"\"\"Projects can only be published, when several checks have been performed.\"\"\"\n        if self.status == 'published':\n            validation_errors = []\n\n            if not self.project.title:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have a title.'),\n                                    code='title')\n                )\n\n            if not self.project.subtitle:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have a subtitle.'),\n                                    code='subtitle')\n                )\n\n            if not self.project.project_plan_summary:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have the project plan summary filled in.'),\n                                    code='summary')\n                )\n\n            if not self.project.goals_overview:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have the goals overview field filled in.'),\n                                    code='goals_overview')\n                )\n\n            if not self.project.date_start_planned:\n                validation_errors.append(\n                    ValidationError(\n                        _('Project needs to have the planned start date field filled in.'),\n                        code='goals_overview')\n                )\n\n            if not self.project.partners:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have at least one valid partner.'),\n                                    code='partners')\n                )\n            elif not self.project.partnerships.filter(\n                    partner_type__in=['field', 'funding', 'support']\n            ).exists():\n                validation_errors.append(\n                    ValidationError(\n                        _('Project needs to have at least one field, funding or support partner.'),\n                        code='partners'\n                    )\n                )\n            else:\n                for funding_partner in self.project.partnerships.filter(partner_type='funding'):\n                    if not funding_partner.funding_amount:\n                        validation_errors.append(\n                            ValidationError(_('All funding partners should have a funding amount.'),\n                                            code='partners'\n                            )\n                        )\n                        break\n\n            if not self.project.sync_owner:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have a reporting organisation.'),\n                                    code='reporting_org')\n                )\n\n            if not self.project.current_image:\n                validation_errors.append(\n                    ValidationError(_('Project needs to have a photo.'),\n                                    code='current_image')\n                )\n\n            if not self.project.locations.all():\n                validation_errors.append(\n                    ValidationError(_('Project needs to have at least one location.'),\n                                    code='location')\n                )\n            else:\n                for location in self.project.locations.all():\n                    if not location.latitude or not location.longitude or not location.country:\n                        validation_errors.append(\n                            ValidationError(\n                                _('All locations need to have a latitude, longitude and country '\n                                  'specified.'),\n                                code='location')\n                        )\n                        break\n\n            if not self.project.budget_items.all():\n                validation_errors.append(\n                    ValidationError(_('Project needs to have at least one budget item.'),\n                                    code='budget_item')\n                )\n            elif not self.project.budget_items.filter(amount__gt=0).exists():\n                validation_errors.append(\n                    ValidationError(\n                        _('Project needs to have at least one budget item with an amount.'),\n                        code='budget_item'\n                    )\n                )\n\n            if not self.project.sectors.all():\n                validation_errors.append(\n                    ValidationError(_('Project needs to have at least one sector.'),\n                                    code='sector')\n                )\n            else:\n                for sector in self.project.sectors.all():\n                    if not sector.sector_code:\n                        validation_errors.append(\n                            ValidationError(_('All sectors need to have a sector code.'),\n                                            code='sector')\n                        )\n                        break\n\n            if validation_errors:\n                raise ValidationError(validation_errors)\n\n    class Meta:\n        app_label = 'rsr'\n        verbose_name = _(u'publishing status')\n        verbose_name_plural = _(u'publishing statuses')\n        ordering = ('-status', 'project')\n", "path": "akvo/rsr/models/publishing_status.py"}]}