Skip to content

Commit

Permalink
Merge pull request #200 from Georiviere/feat_add_linked_object_contri…
Browse files Browse the repository at this point in the history
…butions

Feat add linked object contributions
  • Loading branch information
LePetitTim authored Jul 26, 2023
2 parents a97a134 + 3d1a171 commit a6a3208
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 5 deletions.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ CHANGELOG
* Add contribution status
* Send mail to managers when contribution is created
* Send mail to contributor when contribution is created
* Add linked objects on contributions

1.1.0 (2023-13-06)
-------------------------
Expand Down
28 changes: 25 additions & 3 deletions georiviere/contribution/forms.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
from crispy_forms.layout import Div
from crispy_forms.layout import Div, Field
from dal import autocomplete
from django.utils.translation import gettext_lazy as _

from geotrek.common.forms import CommonForm

from georiviere.contribution.models import Contribution
from georiviere.knowledge.models import FollowUp, Knowledge
from georiviere.maintenance.models import Intervention


class ContributionForm(CommonForm):
class ContributionForm(autocomplete.FutureModelForm, CommonForm):
can_delete = False
geomfields = ['geom']

linked_object = autocomplete.Select2GenericForeignKeyModelField(
model_choice=[
(Knowledge, 'name'),
(Intervention, 'name'),
(FollowUp, 'name')
],
label=_('Linked object'),
required=False,
initial=None,
)

fieldslayout = [
Div(
"description",
Expand All @@ -19,12 +34,13 @@ class ContributionForm(CommonForm):
"email_author",
"assigned_user",
"status_contribution",
Field('linked_object', css_class="chosen-select"),
)
]

class Meta(CommonForm):
fields = ["description", "severity", "published", "portal", "email_author", "geom", "assigned_user",
"status_contribution", "validated"]
"status_contribution", "validated", "linked_object"]
model = Contribution

def __init__(self, *args, **kwargs):
Expand All @@ -35,3 +51,9 @@ def __init__(self, *args, **kwargs):

def clean_portal(self):
return self.instance.portal

def clean_linked_object(self):
linked_object = self.cleaned_data['linked_object']
if linked_object == "":
return None
return linked_object
25 changes: 25 additions & 0 deletions georiviere/contribution/migrations/0007_auto_20230725_1541.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 3.1.14 on 2023-07-25 15:41

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('contribution', '0006_contribution_publication_date'),
]

operations = [
migrations.AddField(
model_name='contribution',
name='linked_object_id',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.AddField(
model_name='contribution',
name='linked_object_type',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype'),
),
]
15 changes: 15 additions & 0 deletions georiviere/contribution/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.db import models
from django.core.mail import mail_managers
from django.template.loader import render_to_string
Expand Down Expand Up @@ -99,6 +101,9 @@ class Contribution(BasePublishableMixin, TimeStampedModelMixin, WatershedPropert
)
validated = models.BooleanField(verbose_name=_("Validated"), default=False,
help_text=_("Validate the contribution"))
linked_object_type = models.ForeignKey(ContentType, null=True, on_delete=models.CASCADE)
linked_object_id = models.PositiveIntegerField(blank=True, null=True)
linked_object = GenericForeignKey('linked_object_type', 'linked_object_id')

class Meta:
verbose_name = _("Contribution")
Expand All @@ -108,6 +113,16 @@ class Meta:
def category_verbose_name(cls):
return _("Name")

@classproperty
def linked_object_verbose_name(cls):
return _("Linked object")

@property
def linked_object_model_name(self):
if self.linked_object:
return self.linked_object._meta.verbose_name
return None

def __str__(self):
if hasattr(self, 'potential_damage'):
return f'{self.email_author} {ContributionPotentialDamage._meta.verbose_name.title()} ' \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
<th>{{ object|verbose:"status_contribution" }}</th>
<td>{{ object.status_contribution|default:"" }}</td>
</tr>
<tr>
<th>{{ object|verbose:"linked_object" }}</th>
<td>{% if object.linked_object_model_name %}{{ object.linked_object_model_name }} : {% endif %}{{ object.linked_object|default:"" }}</td>
</tr>
{% if object.potential_damage %}
<tr>
<th>{{ object.potential_damage|verbose:"type" }}</th>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

{% block extrabody %}
{{ block.super }}
<script type="text/javascript" src="https://code.jquery.com/ui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript">
$("select#id_category").on("change", function(event, status) {
var selectedCategories = [];
Expand Down
28 changes: 27 additions & 1 deletion georiviere/contribution/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from django.contrib.contenttypes.models import ContentType

from georiviere.tests import CommonRiverTest
from . import factories
from georiviere.contribution import models as contribution_models
from georiviere.knowledge.models import Knowledge
from georiviere.knowledge.tests.factories import KnowledgeFactory
from georiviere.portal.tests.factories import PortalFactory


Expand All @@ -26,18 +30,23 @@ def get_expected_json_attrs(self):
'status_contribution': self.obj.status_contribution,
'validated': False,
'publication_date': self.obj.publication_date,
'linked_object_type': None,
'linked_object_id': None,
}

def get_good_data(self):
portal = PortalFactory.create()
severity = factories.SeverityTypeTypeFactory()
temp_data = self.modelfactory.build(portal=portal)
knowledge = KnowledgeFactory.create()
return {
'email_author': temp_data.email_author,
'portal': portal.pk,
'geom': temp_data.geom.ewkt,
'severity': severity.pk,
'description': 'New_description'
'description': 'New_description',
'linked_object_id': knowledge.pk,
'linked_object_type': ContentType.objects.get_for_model(Knowledge)
}

def test_distance_to_source_is_available(self):
Expand All @@ -60,10 +69,27 @@ def test_crud_status(self):
response = self.client.get(obj.get_detail_url())
self.assertEqual(response.status_code, 200)

obj.linked_object = KnowledgeFactory.create()
obj.save()

response = self.client.get(obj.get_detail_url())
self.assertEqual(response.status_code, 200)

response = self.client.get(obj.get_update_url())
self.assertEqual(response.status_code, 200)
self._post_update_form(obj)

response = self.client.get(obj.get_update_url())
self.assertEqual(response.status_code, 200)
good_data_without_linked_object = self.get_good_data()
good_data_without_linked_object['linked_object'] = ""
response = self.client.post(obj.get_update_url(), good_data_without_linked_object)
if response.status_code != 302:
form = self.get_form(response)
self.assertEqual(form.errors, []) # this will show form errors

self.assertEqual(response.status_code, 302) # success, redirects to detail view

url = obj.get_detail_url()
obj.delete()
response = self.client.get(url)
Expand Down
2 changes: 2 additions & 0 deletions georiviere/contribution/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class ContributionList(MapEntityList):
class ContributionLayer(MapEntityLayer):
queryset = Contribution.objects.all()
model = Contribution
filterform = ContributionFilterSet
columns = ['category', ]


class ContributionJsonList(MapEntityJsonList, ContributionList):
Expand Down
11 changes: 11 additions & 0 deletions georiviere/knowledge/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,12 @@ class Knowledge(WatershedPropertiesMixin, TimeStampedModelMixin, ZoningPropertie
object_id_field='target_id',
)

contributions = GenericRelation(
'contribution.Contribution',
content_type_field='linked_object_type',
object_id_field='linked_object_id',
)

class Meta:
verbose_name = _("Knowledge")
verbose_name_plural = _("Knowledges")
Expand Down Expand Up @@ -450,6 +456,11 @@ class FollowUp(TimeStampedModelMixin, WatershedPropertiesMixin, ZoningProperties
length = models.FloatField(default=0.0, blank=True, null=True, verbose_name=_("Length"))
width = models.FloatField(default=0.0, blank=True, null=True, verbose_name=_("Width"))
height = models.FloatField(default=0.0, blank=True, null=True, verbose_name=_("Height"))
contributions = GenericRelation(
'contribution.Contribution',
content_type_field='linked_object_type',
object_id_field='linked_object_id',
)

# generic relations
administrative_operations = GenericRelation(AdministrativeOperation)
Expand Down
5 changes: 5 additions & 0 deletions georiviere/maintenance/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ class Intervention(TimeStampedModelMixin, WatershedPropertiesMixin, ZoningProper

# generic relations
administrative_operations = GenericRelation(AdministrativeOperation)
contributions = GenericRelation(
'contribution.Contribution',
content_type_field='linked_object_type',
object_id_field='linked_object_id',
)

class Meta:
verbose_name = _("Intervention")
Expand Down

0 comments on commit a6a3208

Please sign in to comment.