Skip to content

Commit

Permalink
Merge pull request #66 from City-of-Turku/develop
Browse files Browse the repository at this point in the history
Release tku-v1.10
  • Loading branch information
SanttuA authored Jan 11, 2023
2 parents cdfa1c0 + d26590c commit 7ce9926
Show file tree
Hide file tree
Showing 20 changed files with 439 additions and 8 deletions.
72 changes: 72 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Kerrokantasi tests

on:
push:
branches:
- release
- develop
pull_request:
branches:
- '**'

jobs:
build:
runs-on: [ ubuntu-20.04 ]
services:
postgres:
image: postgis/postgis:11-2.5
env:
POSTGRES_USER: kerrokantasi
POSTGRES_PASSWORD: kerrokantasi
POSTGRES_DB: kerrokantasi
SECRET_KEY: kerrokantasi_secret
DEBUG: true
POSTGRES_HOST_AUTH_METHOD: trust
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9"]
steps:
- uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install Ubuntu packages
run: |
sudo apt-get update
sudo apt-get install gettext python-dev libpq-dev gdal-bin -y
- name: Install requirements
run: |
python3 -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Apply migrations
env:
DATABASE_URL: postgis://kerrokantasi:kerrokantasi@localhost/kerrokantasi
DEBUG: true
SECRET_KEY: kerrokantasi_secret
run: |
python3 manage.py migrate
- name: Compile translations
env:
DATABASE_URL: postgis://kerrokantasi:kerrokantasi@localhost/kerrokantasi
DEBUG: true
SECRET_KEY: kerrokantasi_secret
run: |
python3 manage.py compilemessages
- name: Run tests
env:
DATABASE_URL: postgis://kerrokantasi:kerrokantasi@localhost/kerrokantasi
DEBUG: true
SECRET_KEY: kerrokantasi_secret
HEARING_REPORT_PUBLIC_AUTHOR_NAMES: false
GITHUB_ACTION_TEST: 'true'
run: |
pytest
37 changes: 36 additions & 1 deletion config_dev.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,39 @@ DEFAULT_MAP_ZOOM=11
HEARING_REPORT_PUBLIC_AUTHOR_NAMES=True

# Hearing report theming. Default is whitelabel
# HEARING_REPORT_THEME=whitelabel
# HEARING_REPORT_THEME=whitelabel

# USER_EXPIRATION_DAYS:
# How long until user account is considered expired,
# Expired users are polled by command: clean_expired_users
# Default: 365
USER_EXPIRATION_DAYS=365

# Email settings for Kerrokantasi
#
#

# EMAIL_HOST:
# SMTP Server
# Default: None
#EMAIL_HOST=smtp.server.address

# EMAIL_PORT:
# SMTP Port
# Default: 25
EMAIL_PORT=25

# EMAIL_ENABLED:
# Send emails
# Default: False
EMAIL_ENABLED=False

# EMAIL_FROM:
# From email
# Default: 'noreply@site.domain'
#EMAIL_FROM=noreply@site.domain

# USE_DJANGO_SMTP_BACKEND:
# Use Django provided SMTP backend or print to console
# Default: False
USE_DJANGO_SMTP_BACKEND=False
26 changes: 26 additions & 0 deletions democracy/admin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
from django.urls import reverse
from django_admin_inline_paginator.admin import TabularInlinePaginated
from ckeditor_uploader.widgets import CKEditorUploadingWidget
from djgeojson.fields import GeoJSONFormField
from leaflet.admin import LeafletGeoAdmin
Expand All @@ -27,6 +28,7 @@
from democracy import models
from democracy.admin.widgets import Select2SelectMultiple, ShortTextAreaWidget
from democracy.enums import InitialSectionType
from democracy.models.organization import Organization, OrganizationLog
from democracy.models.utils import copy_hearing
from democracy.plugins import get_implementation

Expand Down Expand Up @@ -295,14 +297,38 @@ class SectionTypeAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).exclude_initial()

class OrganizationLogInline(TabularInlinePaginated):
per_page = 5
model = OrganizationLog
extra = 0
fields = (
'created_at',
'action_by',
'action',
)
ordering = ('-created_at', )
readonly_fields = fields
can_delete = False

def has_add_permission(self, request, obj):
return False

class OrganizationAdmin(admin.ModelAdmin):
inlines = (
OrganizationLogInline,
)

formfield_overrides = {
ManyToManyField: {'widget': FilteredSelectMultiple("ylläpitäjät", is_stacked=False)},
}
exclude = ('published', )


def save_model(self, request, obj, form, change):
obj.modified_by = request.user
return super().save_model(request, obj, form, change)


class ContactPersonAdmin(TranslatableAdmin, admin.ModelAdmin):
list_display = ('name', 'title', 'organization', 'phone', 'email')
exclude = ('published',)
Expand Down
3 changes: 3 additions & 0 deletions democracy/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
class DemocracyAppConfig(AppConfig):
name = 'democracy'
verbose_name = _("Participatory Democracy")

def ready(self):
from . import signals
20 changes: 19 additions & 1 deletion democracy/locale/fi/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -254,4 +254,22 @@ msgid "multiple-choice"
msgstr "monivalintakysymys"

msgid "total answers"
msgstr "vastauksia yhteensä"
msgstr "vastauksia yhteensä"

msgid "Kerrokantasi account expiration"
msgstr "Kerrokantasi tilin vanheneminen"

msgid "Kerrokantasi account will expire %(date)s."
msgstr "Kerrokantasi-tunnuksesi ovat vanhenemassa %(date)s."

msgid "You must login to the service before the expiration date to prevent account removal."
msgstr "Kirjaudu palveluun sitä ennen, jos haluat, että tunnuksesi pysyvät voimassa."

msgid "Best regards, Kerrokantasi"
msgstr "Terveisin, Kerrokantasi"

msgid "Added: %(email)s"
msgstr "Lisätty: %(email)s"

msgid "Removed: %(email)s"
msgstr "Poistettu: %(email)s"
32 changes: 32 additions & 0 deletions democracy/migrations/0059_add_organization_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 2.2.28 on 2022-08-31 09:01

from django.conf import settings
import django.core.files.storage
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', '0058_poll_answer_cascade'),
]

operations = [
migrations.CreateModel(
name='OrganizationLog',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('action', models.TextField(verbose_name='Action')),
('created_at', models.DateTimeField(db_index=True, default=django.utils.timezone.now, editable=False, verbose_name='time of creation')),
('action_by', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='organizationlog_action_by', to=settings.AUTH_USER_MODEL, verbose_name='action by')),
('organization', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organization_log', to='democracy.Organization', verbose_name='organization')),
],
options={
'verbose_name': 'organization log',
'verbose_name_plural': 'organization logs',
},
),
]
3 changes: 2 additions & 1 deletion democracy/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from .label import Label
from .section import Section, SectionComment, SectionImage, SectionFile, SectionType
from .section import SectionPoll, SectionPollOption, SectionPollAnswer
from .organization import ContactPerson, Organization
from .organization import ContactPerson, Organization, OrganizationLog
from .project import Project, ProjectPhase

__all__ = [
Expand All @@ -17,6 +17,7 @@
"SectionPollOption",
"SectionPollAnswer",
"Organization",
"OrganizationLog",
"Project",
"ProjectPhase",
"SectionFile",
Expand Down
25 changes: 24 additions & 1 deletion democracy/models/organization.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from .base import StringIdBaseModel
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from parler.models import TranslatedFields, TranslatableModel


class Organization(StringIdBaseModel):
name = models.CharField(verbose_name=_('name'), max_length=255, unique=True)
admin_users = models.ManyToManyField(
Expand All @@ -21,6 +21,26 @@ def __str__(self):
return self.name


class OrganizationLog(models.Model):
action = models.TextField(verbose_name=_('Action'))
organization = models.ForeignKey(Organization, verbose_name=_('organization'),
related_name='organization_log', on_delete=models.CASCADE)
created_at = models.DateTimeField(
verbose_name=_('time of creation'), default=timezone.now, editable=False, db_index=True
)
action_by = models.ForeignKey(
settings.AUTH_USER_MODEL, verbose_name=_('action by'),
null=True, blank=True, related_name="%(class)s_action_by",
editable=False, on_delete=models.SET_NULL
)

class Meta:
verbose_name = _('organization log')
verbose_name_plural = _('organization logs')

def __str__(self):
return 'Organization Log'

class ContactPerson(TranslatableModel, StringIdBaseModel):
organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name='contact_persons',
blank=True, null=True, on_delete=models.PROTECT)
Expand All @@ -38,3 +58,6 @@ class Meta:

def __str__(self):
return '%s, %s / %s' % (self.name, self.title, self.organization)



28 changes: 28 additions & 0 deletions democracy/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from django.db.models.signals import m2m_changed
from democracy.models.organization import Organization, OrganizationLog
from kerrokantasi.models import User
from django.utils.translation import gettext_lazy as _



def organization_log_signal(sender, **kwargs):
instance = kwargs.get('instance', None)
action = kwargs.get('action')
pk_set = kwargs.get('pk_set')
if any(isinstance(_id, str) for _id in pk_set):
return
users = User.objects.filter(pk__in=pk_set)
if action == 'post_add':
OrganizationLog.objects.create(
organization=instance,
action=_('Added: %(email)s') % ({ 'email': ', '.join([user.email for user in users]) }),
action_by=instance.modified_by
)
elif action == 'post_remove':
OrganizationLog.objects.create(
organization=instance,
action=_('Removed: %(email)s') % ({ 'email': ', '.join([user.email for user in users]) }),
action_by=instance.modified_by
)

m2m_changed.connect(organization_log_signal, sender=Organization.admin_users.through)
48 changes: 48 additions & 0 deletions democracy/tests/test_hearing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import datetime
import json
import os

import pytest
from django.utils.encoding import force_text
Expand Down Expand Up @@ -1088,6 +1089,52 @@ def test_POST_hearing_with_existing_project(valid_hearing_json, default_project,
assert phase['id'] in default_project_phase_ids, "Phase ids return must match original default_project phase ids"


@pytest.mark.django_db
def test_POST_hearing_with_attachment(valid_hearing_json, section_file_orphan,
john_smith_api_client):
"""
Tests that a hearing can be created with an attachment
"""
# file is created as orphaned file before linking it to a hearing
file = sectionfile_base64_test_data()
file_id = section_file_orphan.id
file.update({'id': file_id})
sections = valid_hearing_json['sections']
sections[2].update({'files': [file]})
valid_hearing_json.update({'sections': sections, 'isNew': True})

response = john_smith_api_client.post(endpoint, data=valid_hearing_json, format='json')
data = get_data_from_response(response, status_code=201)
section_files = data['sections'][2]['files']
assert len(section_files) == 1
assert section_files[0]['id'] == file_id


@pytest.mark.django_db
def test_PUT_hearing_with_attachment(valid_hearing_json, section_file_orphan,
john_smith_api_client):
"""
Tests that a hearing can be updated with an attachment
"""
response_post = john_smith_api_client.post(endpoint, data=valid_hearing_json, format='json')
data_post = get_data_from_response(response_post, status_code=201)
# file is created as orphaned file before linking it to a hearing
file = sectionfile_base64_test_data()
file_id = section_file_orphan.id
file.update({'id': file_id})
sections = data_post['sections']
sections[2].update({'files': [file]})
data_post.update({'sections': sections})

response_put = john_smith_api_client.put(
'%s%s/' % (endpoint, data_post['id']), data=data_post, format='json'
)
data_put = get_data_from_response(response_put, status_code=200)
section_files = data_put['sections'][2]['files']
assert len(section_files) == 1
assert section_files[0]['id'] == file_id


@pytest.mark.django_db
def test_POST_hearing_with_updated_project(valid_hearing_json, default_project, default_project_json, john_smith_api_client):
updated_title = 'updated title'
Expand Down Expand Up @@ -1121,6 +1168,7 @@ def test_POST_hearing_with_updated_project(valid_hearing_json, default_project,
assert phase['schedule']['en'] == 'new schedule'


@pytest.mark.skipif(os.environ.get('GITHUB_ACTION_TEST') == 'true', reason="does not work with github actions")
@pytest.mark.django_db
def test_POST_hearing_with_updated_project_add_translation(valid_hearing_json, default_project, default_project_json, john_smith_api_client):
# replace English with Finnish translation in project
Expand Down
Loading

0 comments on commit 7ce9926

Please sign in to comment.