diff --git a/democracy/migrations/0012_auto_20160502_1014.py b/democracy/migrations/0012_auto_20160502_1014.py new file mode 100644 index 00000000..59fb6720 --- /dev/null +++ b/democracy/migrations/0012_auto_20160502_1014.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9 on 2016-05-02 10:14 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('democracy', '0011_contentless_comments'), + ] + + operations = [ + migrations.CreateModel( + name='Organization', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now, editable=False, verbose_name='time of creation')), + ('modified_at', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='time of last modification')), + ('published', models.BooleanField(db_index=True, default=True, verbose_name='public')), + ('deleted', models.BooleanField(db_index=True, default=False, editable=False, verbose_name='deleted')), + ('name', models.CharField(max_length=255, unique=True, verbose_name='name')), + ('admin_users', models.ManyToManyField(blank=True, related_name='admin_organizations', to=settings.AUTH_USER_MODEL)), + ('created_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_created', to=settings.AUTH_USER_MODEL, verbose_name='created by')), + ('modified_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_modified', to=settings.AUTH_USER_MODEL, verbose_name='last modified by')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='hearing', + name='organization', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='hearings', to='democracy.Organization', verbose_name='organization'), + ), + ] diff --git a/democracy/models/base.py b/democracy/models/base.py index 77bb2a6b..2dd1d3a1 100644 --- a/democracy/models/base.py +++ b/democracy/models/base.py @@ -13,7 +13,6 @@ ORDERING_HELP = _("The ordering position for this object. Objects with smaller numbers appear first.") - def generate_id(): return get_random_string(32) @@ -116,6 +115,13 @@ class Meta: abstract = True +class Organization(BaseModel): + name = models.CharField(verbose_name=_('name'), max_length=255, unique=True) + admin_users = models.ManyToManyField( + settings.AUTH_USER_MODEL, blank=True, related_name='admin_organizations' + ) + + class Commentable(models.Model): """ Mixin for models which can be commented. diff --git a/democracy/models/hearing.py b/democracy/models/hearing.py index 041003c7..32ecee89 100644 --- a/democracy/models/hearing.py +++ b/democracy/models/hearing.py @@ -13,7 +13,7 @@ from democracy.models.comment import recache_on_save from democracy.utils.hmac_hash import get_hmac_b64_encoded -from .base import Commentable, StringIdBaseModel +from .base import Commentable, StringIdBaseModel, Organization from .comment import BaseComment from .images import BaseImage @@ -27,6 +27,7 @@ class Hearing(Commentable, StringIdBaseModel): borough = models.CharField(verbose_name=_('borough'), blank=True, default='', max_length=200) servicemap_url = models.CharField(verbose_name=_('service map URL'), default='', max_length=255, blank=True) geojson = GeometryField(blank=True, null=True, verbose_name=_('area')) + organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name="hearings", blank=True, null=True) labels = models.ManyToManyField("Label", verbose_name=_('labels'), blank=True) followers = models.ManyToManyField( diff --git a/democracy/tests/test_hearing.py b/democracy/tests/test_hearing.py index 47cfc87f..1af02f31 100644 --- a/democracy/tests/test_hearing.py +++ b/democracy/tests/test_hearing.py @@ -8,6 +8,7 @@ from democracy.models import ( Hearing, HearingComment, HearingImage, Label, Section, SectionComment, SectionImage, SectionType ) +from democracy.models.base import Organization from democracy.models.utils import copy_hearing from democracy.tests.utils import ( assert_datetime_fuzzy_equal, get_data_from_response, get_geojson, get_hearing_detail_url @@ -120,7 +121,7 @@ def test_8_get_detail_check_properties(api_client, default_hearing): assert set(data.keys()) >= { 'abstract', 'borough', 'close_at', 'closed', 'created_at', 'id', 'images', 'labels', 'n_comments', 'open_at', 'sections', 'servicemap_url', - 'title' + 'title', 'organization' } @@ -212,6 +213,25 @@ def test_8_get_detail_labels(api_client): assert label_one.label in data['labels'] +@pytest.mark.django_db +def test_8_get_detail_organization(api_client): + hearing = Hearing() + hearing.save() + + organization = Organization(name='The department for squirrel welfare') + organization.save() + + hearing.organization = organization + hearing.save() + + response = api_client.get(get_detail_url(hearing.id)) + + data = get_data_from_response(response) + + assert 'results' not in data + assert data['organization'] == organization.name + + @pytest.mark.django_db def test_7_get_detail_servicemap(api_client): hearing = Hearing( diff --git a/democracy/views/hearing.py b/democracy/views/hearing.py index 759d9d21..e827b933 100644 --- a/democracy/views/hearing.py +++ b/democracy/views/hearing.py @@ -51,6 +51,10 @@ class HearingSerializer(serializers.ModelSerializer): comments = HearingCommentSerializer.get_field_serializer(many=True, read_only=True) commenting = EnumField(enum_type=Commenting) geojson = JSONField() + organization = serializers.SlugRelatedField( + read_only=True, + slug_field='name' + ) def get_sections(self, hearing): queryset = hearing.sections.all() @@ -68,7 +72,7 @@ class Meta: 'commenting', 'published', 'labels', 'open_at', 'close_at', 'created_at', 'servicemap_url', 'images', 'sections', 'images', - 'closed', 'comments', 'geojson' + 'closed', 'comments', 'geojson', 'organization' ]