Merge branch 'develop' into feature-nobot
This commit is contained in:
+2
-5
@@ -1,8 +1,7 @@
|
||||
"""File containing webapp app admin registers."""
|
||||
|
||||
from django.contrib import admin
|
||||
from webapp.models import Official, Role, Committee
|
||||
from webapp.models import Feed, Tag, BaseFeed, Event, Signup, SignupForm, TemplateQuestion
|
||||
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd
|
||||
from modeltranslation.admin import TranslationAdmin
|
||||
from django.contrib.auth.models import Permission
|
||||
# this is needed so that the models get registered for translation
|
||||
@@ -16,6 +15,4 @@ admin.site.register(Event, TranslationAdmin)
|
||||
admin.site.register(SignupForm, TranslationAdmin)
|
||||
admin.site.register(Signup, TranslationAdmin)
|
||||
admin.site.register(TemplateQuestion, TranslationAdmin)
|
||||
admin.site.register(Official)
|
||||
admin.site.register(Role)
|
||||
admin.site.register(Committee)
|
||||
admin.site.register(JobAd, TranslationAdmin)
|
||||
|
||||
+1
-1
@@ -10,4 +10,4 @@ class WebappConfig(AppConfig):
|
||||
|
||||
def ready(self):
|
||||
"""Import translations."""
|
||||
import webapp.translations
|
||||
import webapp.translation
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
"""File containing webapp forms."""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.1.5 on 2019-09-26 17:48
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('webapp', '0054_auto_20190313_1642'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='official'
|
||||
)
|
||||
]
|
||||
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 2.1.5 on 2019-09-26 17:51
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import phonenumber_field.modelfields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('webapp', '0055_auto_20190926_2048'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Occupation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('start_date', models.DateField(verbose_name='Start date')),
|
||||
('end_date', models.DateField(verbose_name='End date')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Occupation',
|
||||
'verbose_name_plural': 'Occupations',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Official',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(max_length=30, verbose_name='First name')),
|
||||
('last_name', models.CharField(max_length=150, verbose_name='Last name')),
|
||||
('email', models.EmailField(max_length=254, verbose_name='Email address')),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, verbose_name='Phone number')),
|
||||
('role_history', models.ManyToManyField(blank=True, to='webapp.Occupation')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Official',
|
||||
'verbose_name_plural': 'Officials',
|
||||
},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='role',
|
||||
name='end_date',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='role',
|
||||
name='start_date',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='occupation',
|
||||
name='role',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='webapp.Role'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.1.5 on 2019-09-26 18:02
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0056_auto_20190926_2051'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='occupation',
|
||||
name='role',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='webapp.Role'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2019-10-10 15:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0057_auto_20190926_2102'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='official',
|
||||
name='role_history',
|
||||
field=models.ManyToManyField(blank=True, related_name='officials', to='webapp.Occupation'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 2.1.5 on 2019-10-10 16:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0058_auto_20191010_1837'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='committee',
|
||||
name='name_en',
|
||||
field=models.CharField(max_length=255, null=True, verbose_name='Name'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='committee',
|
||||
name='name_fi',
|
||||
field=models.CharField(max_length=255, null=True, verbose_name='Name'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2019-10-10 16:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0059_auto_20191010_1900'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='official',
|
||||
name='image',
|
||||
field=models.ImageField(null=True, upload_to=''),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-10 18:24
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0060_official_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='official',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to=''),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='signup',
|
||||
name='answer',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='signupform',
|
||||
name='questions',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.1.5 on 2019-11-10 19:17
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0061_auto_20191110_2024'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='templatequestion',
|
||||
name='question',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.1.5 on 2020-06-16 18:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0062_auto_20191110_2117'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signup',
|
||||
name='list_name',
|
||||
field=models.CharField(default='', max_length=255, verbose_name='Name'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.1.5 on 2020-06-22 15:42
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0063_signup_list_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signup',
|
||||
name='uuid',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-06-22 18:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0064_signup_uuid'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signup',
|
||||
name='email',
|
||||
field=models.EmailField(blank=True, max_length=254, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-06-22 20:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0065_signup_email'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='signupForm',
|
||||
field=models.ManyToManyField(blank=True, related_name='event', to='webapp.SignupForm'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-07-22 14:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0066_auto_20200622_2302'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='basefeed',
|
||||
name='image',
|
||||
field=models.ImageField(blank=True, null=True, upload_to=''),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-07-22 17:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0067_basefeed_image'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='quota',
|
||||
field=models.PositiveIntegerField(blank=True, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Generated by Django 2.1.5 on 2020-07-23 19:18
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0068_signupform_quota'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='schema',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(default=[]),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
# Generated by Django 2.1.5 on 2020-10-04 15:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0069_signupform_schema'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='location_en',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='location_fi',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='email_content',
|
||||
field=models.TextField(default=''),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='title_en',
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='title_fi',
|
||||
field=models.CharField(max_length=255, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-10-06 14:49
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0070_auto_20201004_1820'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='signupform',
|
||||
name='email_content',
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 2.1.5 on 2020-11-03 15:38
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
import webapp.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0071_auto_20201006_1749'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='JobAd',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('title_fi', models.CharField(max_length=255, null=True)),
|
||||
('title_en', models.CharField(max_length=255, null=True)),
|
||||
('description', models.CharField(max_length=255)),
|
||||
('description_fi', models.CharField(max_length=255, null=True)),
|
||||
('description_en', models.CharField(max_length=255, null=True)),
|
||||
('content', models.TextField()),
|
||||
('content_fi', models.TextField(null=True)),
|
||||
('content_en', models.TextField(null=True)),
|
||||
('visible', models.BooleanField(default=True)),
|
||||
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('autohide_at', models.DateTimeField(default=webapp.utils.month_from_now)),
|
||||
('autohide_enabled', models.BooleanField(default=False)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'JobAd',
|
||||
'verbose_name_plural': 'JobAds',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,60 @@
|
||||
# Generated by Django 2.1.5 on 2020-11-07 17:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0072_jobad'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='occupation',
|
||||
name='role',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='official',
|
||||
name='role_history',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='official',
|
||||
name='user',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='presetrole',
|
||||
name='baserole_ptr',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='role',
|
||||
name='committee',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='role',
|
||||
name='presetrole_ptr',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='baserole',
|
||||
name='name_en',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='baserole',
|
||||
name='name_fi',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Committee',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Occupation',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Official',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='PresetRole',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Role',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 2.1.5 on 2020-11-07 18:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0073_auto_20201107_1916'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='signup',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 2.1.5 on 2021-01-14 19:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('webapp', '0074_signup_deleted'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='feed',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='jobad',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='signupform',
|
||||
name='deleted',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
+115
-110
@@ -1,33 +1,35 @@
|
||||
"""Webapp app models."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
# from datetime import timedelta
|
||||
from django.contrib.auth.models import User
|
||||
from webapp.utils import month_from_now
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
from webapp.utils import month_from_now, send_signup_email
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
# from django.contrib.auth.models import User
|
||||
from auditlog.registry import auditlog
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
# from django.contrib.postgres.fields import JSONField
|
||||
|
||||
# import logging
|
||||
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from uuid import uuid4
|
||||
import logging
|
||||
|
||||
VERBOSE_NAME = _('Webapp')
|
||||
EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
"""Model for tag."""
|
||||
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(max_length=127)
|
||||
icon = models.ImageField()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Tag')
|
||||
verbose_name_plural = _('Tags')
|
||||
|
||||
slug = models.SlugField(unique=True)
|
||||
name = models.CharField(max_length=127)
|
||||
icon = models.ImageField()
|
||||
|
||||
def __str__(self):
|
||||
return _('Tag: {}').format(self.slug)
|
||||
|
||||
@@ -40,83 +42,129 @@ class BaseFeed(models.Model):
|
||||
title = models.CharField(max_length=255)
|
||||
description = models.CharField(max_length=255)
|
||||
content = models.TextField()
|
||||
image = models.ImageField(blank=True, null=True)
|
||||
|
||||
|
||||
class Feed(BaseFeed):
|
||||
"""Model representing feed."""
|
||||
|
||||
publish_time = models.DateTimeField(default=timezone.now)
|
||||
autohide = models.DateTimeField(default=month_from_now)
|
||||
autohide_enabled = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return _('Feed: {}').format(self.title)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Feed')
|
||||
verbose_name_plural = _('Feeds')
|
||||
|
||||
|
||||
class Event(BaseFeed):
|
||||
"""Model for event."""
|
||||
|
||||
start_time = models.DateTimeField(default=timezone.now)
|
||||
end_time = models.DateTimeField(default=timezone.now)
|
||||
signupForm = models.ManyToManyField(
|
||||
'SignupForm', blank=True)
|
||||
location = models.CharField(max_length=255, blank=True)
|
||||
publish_time = models.DateTimeField(default=timezone.now)
|
||||
autohide = models.DateTimeField(default=month_from_now)
|
||||
autohide_enabled = models.BooleanField(default=False)
|
||||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return _('Event: {}').format(self.title)
|
||||
delete_str = _("Deleted: ") if self.deleted else ""
|
||||
return _('{}Feed: {}').format(delete_str, self.title)
|
||||
|
||||
|
||||
class Event(BaseFeed):
|
||||
"""Model for event in guild calendar"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Event')
|
||||
verbose_name_plural = _('Events')
|
||||
|
||||
|
||||
class TemplateQuestion(models.Model):
|
||||
"""Stores template questions for signup forms as JSONB"""
|
||||
# question = JSONField()
|
||||
name = models.CharField(max_length=255)
|
||||
question = models.CharField(max_length=255)
|
||||
start_time = models.DateTimeField(default=timezone.now)
|
||||
end_time = models.DateTimeField(default=timezone.now)
|
||||
signupForm = models.ManyToManyField(
|
||||
'SignupForm', blank=True, related_name="event")
|
||||
location = models.CharField(max_length=255, blank=True)
|
||||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return _('Template questions: {}').format(self.name)
|
||||
delete_str = _("Deleted: ") if self.deleted else ""
|
||||
return _('{}Event: {}').format(delete_str, self.title)
|
||||
|
||||
|
||||
class TemplateQuestion(models.Model):
|
||||
"""
|
||||
NOT IMPLEMENTED!!!
|
||||
Stores template questions for signup forms as JSON format. Used in signup form creation
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Template question')
|
||||
verbose_name_plural = _('Template questions')
|
||||
|
||||
|
||||
class SignupForm(models.Model):
|
||||
"""Model for event signup form. Stores questions in JSONB."""
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
start_time = models.DateTimeField(default=timezone.now)
|
||||
end_time = models.DateTimeField(default=timezone.now)
|
||||
# question = JSONField()
|
||||
questions = models.TextField(default="[]")
|
||||
visible = models.BooleanField(default=True)
|
||||
name = models.CharField(max_length=255)
|
||||
question = JSONField()
|
||||
|
||||
def __str__(self):
|
||||
return _('#{} {}').format(self.id, self.title)
|
||||
return _('Template questions: {}').format(self.name)
|
||||
|
||||
|
||||
class SignupForm(models.Model):
|
||||
"""Model for event signup form. Stores questions in JSON format."""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Signup form')
|
||||
verbose_name_plural = _('Signup forms')
|
||||
|
||||
|
||||
class Signup(models.Model):
|
||||
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
|
||||
time = models.DateTimeField(default=timezone.now)
|
||||
answer = models.CharField(max_length=255)
|
||||
title = models.CharField(max_length=255)
|
||||
start_time = models.DateTimeField(default=timezone.now)
|
||||
end_time = models.DateTimeField(default=timezone.now)
|
||||
questions = JSONField()
|
||||
schema = JSONField()
|
||||
visible = models.BooleanField(default=True)
|
||||
quota = models.PositiveIntegerField(blank=True, null=True)
|
||||
email_content = models.TextField(blank=True)
|
||||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return _('Sign-ups: {}').format(self.signupForm)
|
||||
delete_str = _("Deleted: ") if self.deleted else ""
|
||||
return _('#{} {}{}').format(self.id, delete_str, self.title)
|
||||
|
||||
@property
|
||||
def signups(self):
|
||||
return Signup.objects.filter(signupForm=self, deleted=False).order_by('pk')
|
||||
|
||||
@property
|
||||
def isOpen(self):
|
||||
now = timezone.now()
|
||||
return self.start_time <= now and now < self.end_time
|
||||
|
||||
|
||||
class Signup(models.Model):
|
||||
"""
|
||||
Actual signup into any SignupForm. Deletes are soft.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Sign-up')
|
||||
verbose_name_plural = _('Sign-ups')
|
||||
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
|
||||
time = models.DateTimeField(default=timezone.now)
|
||||
answer = JSONField()
|
||||
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
|
||||
list_name = models.CharField(_('Name'), max_length=255)
|
||||
# If there is email in questions, we save it as own field
|
||||
email = models.EmailField(blank=True, null=True)
|
||||
# Random unique identifier. Used for signup editing by the user.
|
||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
delete_str = _("Deleted: ") if self.deleted else ""
|
||||
return f"{self.signupForm}: {delete_str}{self.list_name} ({self.pk})"
|
||||
|
||||
|
||||
@receiver(post_save, sender=Signup)
|
||||
def email_on_signup(sender, instance, created, **kwargs):
|
||||
if created and instance.email:
|
||||
# TODO: Possible bug due to many-to-many relationship with events and forms.
|
||||
# TODO: Subject field crashes with lazy loaded translations.
|
||||
try:
|
||||
# subject = _(f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}")
|
||||
subject = f"Olet ilmoittautunut tapahtumaan {instance.signupForm.event.first().title}"
|
||||
except AttributeError:
|
||||
# subject = _(f"Olet ilmoittautunut ilmoon {instance.signupForm.title}")
|
||||
subject = f"Olet ilmoittautunut ilmoon {instance.signupForm.title}"
|
||||
send_signup_email(instance.email, subject, instance.id, instance.uuid, instance.signupForm.email_content)
|
||||
|
||||
|
||||
class BaseRole(models.Model):
|
||||
@@ -130,73 +178,30 @@ class BaseRole(models.Model):
|
||||
return '{} ({})'.format(n, _('board member')) if self.is_board else n
|
||||
|
||||
|
||||
class PresetRole(BaseRole):
|
||||
"""Model representing a preset occupation in the guild."""
|
||||
|
||||
description = models.TextField(_('Description'))
|
||||
|
||||
|
||||
class Committee(models.Model):
|
||||
"""
|
||||
Committee model
|
||||
Has many Roles found under variable roles
|
||||
"""
|
||||
class JobAd(models.Model):
|
||||
"""Job advertisements shown on Corporate relations page"""
|
||||
|
||||
class Meta:
|
||||
"""Meta class for Committee class."""
|
||||
verbose_name = _('JobAd')
|
||||
verbose_name_plural = _('JobAds')
|
||||
|
||||
verbose_name = _('Committee')
|
||||
verbose_name_plural = _('Committees')
|
||||
title = models.CharField(max_length=255)
|
||||
description = models.CharField(max_length=255)
|
||||
content = models.TextField()
|
||||
visible = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(default=timezone.now)
|
||||
autohide_at = models.DateTimeField(default=month_from_now)
|
||||
autohide_enabled = models.BooleanField(default=False)
|
||||
deleted = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return _('Committee: {}').format(self.name)
|
||||
|
||||
name = models.CharField(_("Name"), max_length=255)
|
||||
|
||||
@property
|
||||
def current_roles(self):
|
||||
return self.roles.all().filter(end_date__gte=timezone.now()).filter(start_date__lte=timezone.now())
|
||||
|
||||
|
||||
class Role(PresetRole):
|
||||
"""
|
||||
Model for Role.
|
||||
|
||||
Model representing an active or historical occupation
|
||||
in an official's history.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""Meta class for Role model."""
|
||||
|
||||
verbose_name = _('Role')
|
||||
verbose_name_plural = _('Roles')
|
||||
|
||||
start_date = models.DateField(_('Start date'))
|
||||
end_date = models.DateField(_('End date'))
|
||||
committee = models.ForeignKey('Committee', related_name='roles', on_delete=models.SET_NULL, null=True)
|
||||
|
||||
|
||||
class Official(User):
|
||||
"""Model representing a guild official."""
|
||||
|
||||
class Meta:
|
||||
"""Meta class for Official class."""
|
||||
|
||||
verbose_name = _('Official')
|
||||
verbose_name_plural = _('Officials')
|
||||
|
||||
phone_number = PhoneNumberField(_('Phone number'))
|
||||
role = models.ManyToManyField('Role', related_name='official')
|
||||
|
||||
def __str__(self):
|
||||
return '{} {}'.format(self.first_name, self.last_name)
|
||||
delete_str = _("Deleted: ") if self.deleted else ""
|
||||
return f'{delete_str}{self.title}'
|
||||
|
||||
|
||||
auditlog.register(Tag)
|
||||
auditlog.register(Feed)
|
||||
auditlog.register(Event)
|
||||
auditlog.register(SignupForm)
|
||||
auditlog.register(Signup)
|
||||
auditlog.register(PresetRole)
|
||||
auditlog.register(Role)
|
||||
auditlog.register(Official)
|
||||
auditlog.register(JobAd)
|
||||
|
||||
+87
-40
@@ -2,34 +2,88 @@ from rest_framework import serializers
|
||||
from webapp.models import *
|
||||
|
||||
|
||||
class SignupFormSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = SignupForm
|
||||
fields = ('id', 'title', 'start_time', 'end_time', 'questions')
|
||||
|
||||
|
||||
class EventSerializer(serializers.HyperlinkedModelSerializer):
|
||||
signupForm = SignupFormSerializer(many=True, read_only=True, required=False)
|
||||
signup_id = serializers.PrimaryKeyRelatedField(
|
||||
many=True,
|
||||
class SignupSerializer(serializers.ModelSerializer):
|
||||
signupForm_id = serializers.PrimaryKeyRelatedField(
|
||||
source="signupForm",
|
||||
queryset=SignupForm.objects.all()
|
||||
)
|
||||
tag_id = serializers.PrimaryKeyRelatedField(
|
||||
list_name = serializers.CharField(read_only=True)
|
||||
|
||||
def add_extra_fields(self, validated_data):
|
||||
questions = validated_data["signupForm"].questions
|
||||
name_ids = list(filter(lambda x: x["type"] == "name", questions))
|
||||
email_ids = list(filter(lambda x: x["type"] == "email", questions))
|
||||
|
||||
# Send email to first email field in the form
|
||||
if (len(email_ids) > 0):
|
||||
id = email_ids[0]["id"]
|
||||
email_value = validated_data["answer"].get(id)
|
||||
validated_data["email"] = email_value
|
||||
# Combine all name fields to list_name
|
||||
if (len(name_ids) > 0):
|
||||
# name_value = validated_data["answer"].get(name_fields[0]["id"], None)
|
||||
all_names = map(lambda x: validated_data["answer"].get(x["id"]), name_ids)
|
||||
validated_data["list_name"] = " ".join(all_names)
|
||||
|
||||
def create(self, validated_data):
|
||||
self.add_extra_fields(validated_data)
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
self.add_extra_fields(validated_data)
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
class Meta:
|
||||
model = Signup
|
||||
fields = ('id', 'signupForm_id', 'answer', 'list_name')
|
||||
extra_kwargs = {
|
||||
'url': {
|
||||
'view_name': 'signup-detail',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SignupFormSerializer(serializers.ModelSerializer):
|
||||
signups = serializers.SlugRelatedField(
|
||||
slug_field="list_name",
|
||||
many=True,
|
||||
source="tags",
|
||||
queryset=Tag.objects.all()
|
||||
read_only=True,
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = SignupForm
|
||||
fields = ('id', 'title_fi', 'title_en', 'visible', 'isOpen', 'start_time', 'end_time', 'email_content', 'questions', 'schema', 'signups', 'quota')
|
||||
|
||||
|
||||
class EventSerializer(serializers.ModelSerializer):
|
||||
signupForm = SignupFormSerializer(
|
||||
source='filtered_signup_forms',
|
||||
many=True,
|
||||
read_only=True,
|
||||
)
|
||||
|
||||
signup_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=SignupForm.objects.all(),
|
||||
many=True,
|
||||
write_only=True,
|
||||
)
|
||||
tag_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Tag.objects.all(),
|
||||
many=True,
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ('id', 'tag_id', 'tags', 'visible', 'title', 'description',
|
||||
'content', 'start_time', 'end_time', 'location', 'signup_id', 'signupForm')
|
||||
fields = ('id', 'tag_id', 'tags', 'visible', 'image', 'title_fi', 'title_en', 'description_fi', 'description_en',
|
||||
'content_fi', 'content_en', 'start_time', 'end_time', 'location_fi', 'location_en', 'signup_id', 'signupForm')
|
||||
read_only_fields = ['tags', 'signupForm']
|
||||
depth = 1
|
||||
|
||||
def create(self, validated_data):
|
||||
signupForms = validated_data.pop('signupForm')
|
||||
tags = validated_data.pop('tags')
|
||||
signupForms = validated_data.pop('signup_id', [])
|
||||
tags = validated_data.pop('tag_id')
|
||||
event = Event.objects.create(**validated_data)
|
||||
for form in signupForms:
|
||||
event.signupForm.add(form)
|
||||
@@ -39,27 +93,21 @@ class EventSerializer(serializers.HyperlinkedModelSerializer):
|
||||
return event
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
signupForms = validated_data.pop('signupForm')
|
||||
instance = super(EventSerializer, self).update(instance, validated_data)
|
||||
signupForms = validated_data.pop('signup_id', [])
|
||||
tags = validated_data.pop('tag_id')
|
||||
instance.signupForm.clear()
|
||||
for form_data in signupForms:
|
||||
# form_qs = SignupForms.objects.filter(id=form['id'])
|
||||
instance.signupForm.add(form_data)
|
||||
instance.tags.clear()
|
||||
for form in signupForms:
|
||||
instance.signupForm.add(form)
|
||||
for tag in tags:
|
||||
instance.tags.add(tag)
|
||||
instance = super(EventSerializer, self).update(instance, validated_data)
|
||||
return instance
|
||||
|
||||
|
||||
class SignupSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Signup
|
||||
fields = ('id', 'signupForm', 'answer')
|
||||
extra_kwargs = {
|
||||
'url': {
|
||||
'view_name': 'signup-detail',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SavedQuestionsSerializer(serializers.ModelSerializer):
|
||||
question = serializers.JSONField()
|
||||
|
||||
class Meta:
|
||||
model = TemplateQuestion
|
||||
fields = ('id', 'name', 'question')
|
||||
@@ -68,7 +116,7 @@ class SavedQuestionsSerializer(serializers.ModelSerializer):
|
||||
class TagSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = ('id', 'slug', 'name', 'icon')
|
||||
fields = ('id', 'slug', 'name_fi', 'name_en', 'icon')
|
||||
|
||||
|
||||
class FeedSerializer(serializers.ModelSerializer):
|
||||
@@ -80,8 +128,8 @@ class FeedSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Feed
|
||||
fields = ('id', 'tags', 'tag_id', 'visible', 'title', 'description',
|
||||
'content', 'publish_time', 'autohide', 'autohide_enabled')
|
||||
fields = ('id', 'tags', 'tag_id', 'visible', 'image', 'title_fi', 'title_en', 'description_fi', 'description_en',
|
||||
'content_fi', 'content_en', 'publish_time', 'autohide', 'autohide_enabled')
|
||||
depth = 1
|
||||
|
||||
def create(self, validated_data):
|
||||
@@ -93,8 +141,7 @@ class FeedSerializer(serializers.ModelSerializer):
|
||||
return feed
|
||||
|
||||
|
||||
class ContactsSerializer(serializers.ModelSerializer):
|
||||
class JobAdSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Official
|
||||
fields = ('id', 'first_name', 'last_name', 'phone_number', 'role')
|
||||
depth = 2
|
||||
model = JobAd
|
||||
fields = ('id', 'title_fi', 'title_en', 'description_fi', 'description_en', 'content_fi', 'content_en', 'visible', 'autohide_at', 'autohide_enabled')
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import django_tables2 as tables
|
||||
from django.db.models import Count, Q
|
||||
from django.utils.translation import ugettext as _
|
||||
@@ -0,0 +1,9 @@
|
||||
{% autoescape off %}
|
||||
{{ content }}
|
||||
{% endautoescape %}
|
||||
|
||||
<p>Voit muokata ilmoittautumistasi lomakkeen olleessa avoinna alla olevasta linkistä:</p>
|
||||
|
||||
<a href={{ url }}>{{url}}</a>
|
||||
|
||||
<p>Hädässä ota yhteyttä sik-vtmk@list.ayy.fi</p>
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
"""Tests for webapp."""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.core.files import File
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework import status
|
||||
from rest_framework.test import force_authenticate
|
||||
from webapp.models import Tag, Feed
|
||||
from webapp.serializers import TagSerializer, FeedSerializer
|
||||
|
||||
from collections import OrderedDict
|
||||
from itertools import islice
|
||||
import tempfile
|
||||
|
||||
|
||||
class TagsTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.icon = tempfile.NamedTemporaryFile(suffix=".jpg").name
|
||||
Tag.objects.create(slug='Party', name='Bileet', icon=self.icon)
|
||||
|
||||
def test_get_single_tag(self):
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
response = self.client.get('/api/tags/', format='json')
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
# We dont care about icon, so response is sliced
|
||||
sliced_response = OrderedDict(islice(response.data['results'][0].items(), 3))
|
||||
tag1 = Tag.objects.get(slug="Party")
|
||||
self.assertEqual(sliced_response, {'id': tag1.id, 'slug': 'Party', 'name': 'Bileet'})
|
||||
|
||||
def test_get_single_tag_serializer(self):
|
||||
response = self.client.get('/api/tags/', format='json')
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
tags = Tag.objects.all()
|
||||
serializer = TagSerializer(tags, many=True)
|
||||
# Icon on serializer is returned without protocol and domain
|
||||
# Assert these individually
|
||||
resp_icon = response.data['results'][0].pop('icon')
|
||||
serial_icon = serializer.data[0].pop('icon')
|
||||
self.assertEqual(response.data['results'], serializer.data)
|
||||
self.assertEqual(resp_icon, "http://testserver" + serial_icon)
|
||||
|
||||
def test_get_multiple_tags(self):
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
Tag.objects.create(slug='Freshmen', name='Fuksit', icon=self.icon)
|
||||
Tag.objects.create(slug='International', name='Ulkkarit', icon=self.icon)
|
||||
self.assertEqual(Tag.objects.count(), 3)
|
||||
|
||||
response = self.client.get('/api/tags/', format='json')
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
# We dont care about icon, so response is sliced
|
||||
tag1 = Tag.objects.get(slug="Party")
|
||||
sliced_response = OrderedDict(islice(response.data['results'][0].items(), 3))
|
||||
self.assertEqual(sliced_response, {'id': tag1.id, 'slug': 'Party', 'name': 'Bileet'})
|
||||
sliced_response = OrderedDict(islice(response.data['results'][1].items(), 3))
|
||||
tag2 = Tag.objects.get(slug="Freshmen")
|
||||
self.assertEqual(sliced_response, {'id': tag2.id, 'slug': 'Freshmen', 'name': 'Fuksit'})
|
||||
sliced_response = OrderedDict(islice(response.data['results'][2].items(), 3))
|
||||
tag3 = Tag.objects.get(slug="International")
|
||||
self.assertEqual(sliced_response, {'id': tag3.id, 'slug': 'International', 'name': 'Ulkkarit'})
|
||||
|
||||
def test_create_tag(self):
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
response = self.client.post('/api/tags/', {'slug': 'Test', 'name': 'Testinimi', 'icon': self.icon}, format='multipart')
|
||||
self.assertFalse(status.is_success(response.status_code))
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
|
||||
def test_invalid_tag(self):
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
response = self.client.get('/api/tags/15', format='json', follow=True)
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
|
||||
class FeedTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.icon = tempfile.NamedTemporaryFile(suffix=".jpg").name
|
||||
Tag.objects.create(slug='testtag1', name='test1', icon=self.icon)
|
||||
tag1 = Tag.objects.get(slug="testtag1")
|
||||
Tag.objects.create(slug="testtag2", name='test2', icon=self.icon)
|
||||
tag2 = Tag.objects.get(slug="testtag2")
|
||||
self.assertEqual(Tag.objects.count(), 2)
|
||||
|
||||
Feed.objects.create(title="TestFeed", visible=True, description="diidadaapa", content="lorem ipsum")
|
||||
Feed.objects.get(title="TestFeed").tags.add(tag1)
|
||||
Feed.objects.get(title="TestFeed").tags.add(tag2)
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
self.assertEqual(Feed.objects.all()[0].tags.count(), 2)
|
||||
|
||||
username, password = 'test_admin', 'password123'
|
||||
self.authClient = User.objects.create_superuser(username, 'myemail@test.com', password)
|
||||
|
||||
def test_get_feed(self):
|
||||
response = self.client.get('/api/feed/', format='json')
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
feeds = Feed.objects.all()
|
||||
serializer = FeedSerializer(feeds, many=True)
|
||||
|
||||
# DRF extends path given by serializer with the protocol and domain for icon
|
||||
# Ignore tag on serializer and response. This is tested on TagTestCase.
|
||||
# Note that we assume the length here to be 1
|
||||
response.data['results'][0].pop('tags')
|
||||
serializer.data[0].pop('tags')
|
||||
self.assertEqual(response.data['results'], serializer.data)
|
||||
|
||||
def test_post_feed(self):
|
||||
Tag.objects.create(slug="test1", name="testsds")
|
||||
Tag.objects.create(slug="test2", name="testsdsd")
|
||||
tag1_id = Tag.objects.get(slug="test1").id
|
||||
tag2_id = Tag.objects.get(slug="test2").id
|
||||
|
||||
data = {'tags': [tag1_id, tag2_id], 'title': 'testtitle', 'visible': 'True', 'description': 'liirumlaarum', 'content': 'lorem ipsum'}
|
||||
# Try post without authentication
|
||||
response = self.client.post('/api/feed/', data, format='multipart')
|
||||
self.assertTrue(status.is_client_error(response.status_code))
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post('/api/feed/', data, format='multipart')
|
||||
# Return success and check object was created
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
self.assertEqual(Feed.objects.count(), 2)
|
||||
|
||||
created = Feed.objects.get(title="testtitle")
|
||||
print(created.tags)
|
||||
# self.assertEqual(created.tags.count(), 2)
|
||||
@@ -0,0 +1,35 @@
|
||||
from django.utils import timezone
|
||||
from webapp.models import Event
|
||||
from webapp.utils import month_from_now
|
||||
|
||||
|
||||
def createEventObject(name="Testitapahtuma1", visible=True, start_time=timezone.now(), end_time=month_from_now(), tag_id=[], signup_id=[]):
|
||||
return Event.objects.create(
|
||||
title_fi=name,
|
||||
title_en=f"title_en {name}",
|
||||
visible=visible,
|
||||
description_fi=f"desc_fi {name}",
|
||||
description_en=f"desc_en {name}",
|
||||
content_fi=f"content_fi {name}",
|
||||
content_en=f"content_en {name}",
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
location=f"loc {name}"
|
||||
)
|
||||
|
||||
|
||||
def createEventJSON(name="POST1", visible=True, start_time=timezone.now(), end_time=month_from_now(), tag_id=[], signup_id=[]):
|
||||
return {
|
||||
"tag_id": tag_id,
|
||||
"visible": visible,
|
||||
"title_fi": f"title_fi {name}",
|
||||
"title_en": f"title_en {name}",
|
||||
"description_fi": f"desc_fi {name}",
|
||||
"description_en": f"desc_en {name}",
|
||||
"content_fi": f"content_fi {name}",
|
||||
"content_en": f"content_en {name}",
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
"location": f"loc {name}",
|
||||
"signup_id": signup_id
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
from webapp.models import Signup, SignupForm
|
||||
from django.utils import timezone
|
||||
from webapp.utils import month_from_now
|
||||
|
||||
ALL_QUESTION_TYPES = [{"id": "-naY2R1-h", "name": "Nimi", "type": "text", "options": []}, {"id": "5t1oN2Qev", "name": "Testi", "type": "info", "options": "teskstii"}, {"id": "MYaaAiOiU", "name": "Email", "type": "email", "options": []}, {"id": "Wb5tmyvki", "name": "Radio", "type": "radiobutton", "options": ["Yes", "no", "maybe"]}, {"id": "U41Zp9x0L", "name": "Checkbox", "type": "checkbox", "options": ["A", "B", "C"]}, {"id": "TnDqrvXKf", "name": "Numero", "type": "integer", "options": ["1", "100"]}]
|
||||
|
||||
ALL_QUESTION_TYPES_ANSWER = {"-naY2R1-h": "Testi", "MYaaAiOiU": "test-spam@sahkoinsinoorikilta.fi", "Wb5tmyvki": "maybe", "U41Zp9x0L": ["B", "C"], "TnDqrvXKf": 5}
|
||||
|
||||
|
||||
ALL_QUESTIONS_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["-naY2R1-h", "MYaaAiOiU", "Wb5tmyvki", "U41Zp9x0L", "TnDqrvXKf"],
|
||||
"properties": {"-naY2R1-h": {"type": "string"}, "MYaaAiOiU": {"type": "string", "format": "email", "pattern": "^[a-zA-Z0-9.!#$%&\u2019*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"}, "TnDqrvXKf": {"type": "number", "title": "Numero (1 -- 100)", "maximum": 100, "minimum": 1, "multipleOf": 1}, "U41Zp9x0L": {"type": "array", "items": {"type": "string", "pattern": "^A$|^B$|^C$"}, "maxItems": 3, "uniqueItems": True}, "Wb5tmyvki": {"type": "string", "pattern": "^Yes$|^no$|^maybe$"}},
|
||||
"minProperties": 5
|
||||
}
|
||||
|
||||
TEXT_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["-naY2R1-h"],
|
||||
"properties": {
|
||||
"-naY2R1-h": ALL_QUESTIONS_SCHEMA["properties"]["-naY2R1-h"]
|
||||
},
|
||||
"minProperties": 1
|
||||
}
|
||||
RADIO_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["Wb5tmyvki"],
|
||||
"properties": {
|
||||
"Wb5tmyvki": ALL_QUESTIONS_SCHEMA["properties"]["Wb5tmyvki"]
|
||||
},
|
||||
"minProperties": 1
|
||||
}
|
||||
CBOX_SCHEMA = {
|
||||
"type": "object",
|
||||
"required": ["U41Zp9x0L"],
|
||||
"properties": {
|
||||
"U41Zp9x0L": ALL_QUESTIONS_SCHEMA["properties"]["U41Zp9x0L"]
|
||||
},
|
||||
"minProperties": 1
|
||||
}
|
||||
|
||||
|
||||
def createSignupForm(name="Form1", start_time=timezone.now(), end_time=month_from_now(), questions=ALL_QUESTION_TYPES, schema=ALL_QUESTIONS_SCHEMA, visible=True, quota=1):
|
||||
return SignupForm.objects.create(
|
||||
title=name,
|
||||
start_time=start_time,
|
||||
end_time=end_time,
|
||||
questions=questions,
|
||||
visible=visible,
|
||||
schema=schema,
|
||||
quota=quota
|
||||
)
|
||||
|
||||
|
||||
def createSignupObject(list_name, form, answer):
|
||||
return Signup.objects.create(
|
||||
list_name=list_name,
|
||||
signupForm=form,
|
||||
answer=answer
|
||||
)
|
||||
|
||||
|
||||
def createSignupRequest(list_name, form_id, answer):
|
||||
return {
|
||||
"list_name": list_name,
|
||||
"signupForm_id": form_id,
|
||||
"answer": answer
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
from webapp.models import Tag
|
||||
import tempfile
|
||||
|
||||
|
||||
def createTagIcon():
|
||||
return tempfile.NamedTemporaryFile(suffix=".jpg").name
|
||||
|
||||
|
||||
def tagBuilder(slug="Tag1", icon=createTagIcon()):
|
||||
return Tag.objects.create(
|
||||
slug=slug,
|
||||
name_fi=slug + " name_fi",
|
||||
name_en=slug + " name_en",
|
||||
icon=icon
|
||||
)
|
||||
@@ -0,0 +1,177 @@
|
||||
from django.contrib.auth.models import User, AnonymousUser
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIRequestFactory
|
||||
|
||||
from webapp.models import Event
|
||||
from webapp.serializers import EventSerializer
|
||||
from webapp.tests.tag_fixture import tagBuilder
|
||||
from webapp.tests.event_fixture import createEventObject, createEventJSON
|
||||
from webapp.tests.signup_fixture import createSignupForm
|
||||
|
||||
URL = "/api/events/"
|
||||
|
||||
|
||||
class EventTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
# Visible and relevant
|
||||
test1 = createEventObject(
|
||||
"Testitapahtuma1",
|
||||
start_time=timezone.datetime(2019, 11, 9, 12, 0, 0))
|
||||
# Invisible but relevant
|
||||
createEventObject(
|
||||
"Testitapahtuma2",
|
||||
visible=False,
|
||||
start_time=timezone.datetime(2018, 11, 9, 12, 0, 0))
|
||||
# Visible but unrelevant
|
||||
test2 = createEventObject(
|
||||
"Testitapahtuma3",
|
||||
visible=True,
|
||||
start_time=timezone.datetime(2018, 12, 9, 12, 0, 0),
|
||||
end_time=timezone.datetime(2018, 12, 9, 13, 0, 0))
|
||||
# Visible and relevant
|
||||
createEventObject(
|
||||
"Testitapahtuma4",
|
||||
visible=True,
|
||||
start_time=timezone.datetime(2018, 12, 9, 12, 0, 0))
|
||||
# Add some tags
|
||||
tag1 = tagBuilder()
|
||||
tag2 = tagBuilder("testtag2")
|
||||
self.testTagId = tag1.id
|
||||
test1.tags.add(tag1)
|
||||
test2.tags.add(tag2)
|
||||
self.testEventId = test1.id
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
|
||||
self.signupFormId = createSignupForm().id
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get_current_events(self):
|
||||
# Get from API
|
||||
response = self.client.get(URL, format="json")
|
||||
# Response 200
|
||||
self.assertTrue(response.status_code, status.HTTP_200_OK)
|
||||
# Response should not have old events and invisible
|
||||
self.assertEqual(len(response.data["results"]), 2)
|
||||
# Check that serialized data is equal to received response
|
||||
req = APIRequestFactory().get(r"http://testserver/api/events/")
|
||||
req.user = AnonymousUser()
|
||||
serializer = EventSerializer(
|
||||
Event.objects.filter(title_fi__in=("Testitapahtuma1", "Testitapahtuma4")).order_by("start_time"),
|
||||
many=True,
|
||||
context={
|
||||
"request": req
|
||||
}
|
||||
)
|
||||
expected = serializer.data
|
||||
# TODO: Couldn't figure out how to fill filtered_signup_forms used by prefetch for the test...
|
||||
for e in expected:
|
||||
e["signupForm"] = []
|
||||
self.assertEqual(response.data["results"], expected)
|
||||
|
||||
def test_get_events_since(self):
|
||||
response = self.client.get(f"{URL}?since=2018-01-01", format="json")
|
||||
self.assertTrue(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data["results"]), 3)
|
||||
|
||||
req = APIRequestFactory().get(r"http://testserver/api/events/")
|
||||
req.user = AnonymousUser()
|
||||
serializer = EventSerializer(
|
||||
Event.objects.filter(title_fi__in=("Testitapahtuma1", "Testitapahtuma3", "Testitapahtuma4")).order_by("start_time"),
|
||||
many=True,
|
||||
context={
|
||||
"request": req
|
||||
}
|
||||
)
|
||||
expected = serializer.data
|
||||
# TODO: Couldn't figure out how to fill filtered_signup_forms used by prefetch for the test...
|
||||
for e in expected:
|
||||
e["signupForm"] = []
|
||||
self.assertEqual(response.data["results"], expected)
|
||||
|
||||
def test_get_single_event(self):
|
||||
response = self.client.get(f"{URL}{self.testEventId}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
req = APIRequestFactory().get(r"http://testserver/api/events/")
|
||||
req.user = AnonymousUser()
|
||||
serializer = EventSerializer(
|
||||
Event.objects.get(title_fi="Testitapahtuma1"),
|
||||
context={
|
||||
"request": req,
|
||||
},
|
||||
)
|
||||
expected = serializer.data
|
||||
# TODO: Couldn't figure out how to fill filtered_signup_forms used by prefetch for the test...
|
||||
expected["signupForm"] = []
|
||||
self.assertEqual(response.data, expected)
|
||||
|
||||
def test_get_invalid_event(self):
|
||||
response = self.client.get(f"{URL}200/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_post_event(self):
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post(
|
||||
URL,
|
||||
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
|
||||
format="json"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(Event.objects.count(), 5)
|
||||
|
||||
def test_post_event_unauth(self):
|
||||
response = self.client.post(
|
||||
URL,
|
||||
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
|
||||
format="json"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
|
||||
def test_update_event(self):
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
event = Event.objects.get(id=self.testEventId)
|
||||
new = createEventJSON(name="Update1", signup_id=[self.signupFormId])
|
||||
response = self.client.put(
|
||||
f"{URL}{self.testEventId}/",
|
||||
new,
|
||||
format="json"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
event = Event.objects.get(id=self.testEventId)
|
||||
self.assertEqual(event.title_fi, "title_fi Update1")
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
|
||||
def test_update_event_unauth(self):
|
||||
response = self.client.put(
|
||||
f"{URL}{self.testEventId}/",
|
||||
createEventJSON(name="Update1"),
|
||||
format="json"
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
event = Event.objects.get(id=self.testEventId)
|
||||
self.assertEqual(event.title_fi, "Testitapahtuma1")
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
|
||||
def test_delete_event(self):
|
||||
response = self.client.delete(f"{URL}{self.testEventId}/",)
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.delete(f"{URL}{self.testEventId}/")
|
||||
# Soft delete
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(Event.objects.count(), 4)
|
||||
self.assertEqual(Event.objects.get(id=self.testEventId).deleted, True)
|
||||
@@ -0,0 +1,78 @@
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIRequestFactory
|
||||
|
||||
from webapp.models import Feed
|
||||
from webapp.serializers import FeedSerializer
|
||||
from webapp.tests.tag_fixture import tagBuilder
|
||||
|
||||
URL = "/api/feed/"
|
||||
|
||||
|
||||
class FeedTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
tag1 = tagBuilder()
|
||||
tag2 = tagBuilder("testtag2")
|
||||
|
||||
feed = Feed.objects.create(title="TestFeed", visible=True, description="diidadaapa", content="lorem ipsum")
|
||||
feed.tags.add(tag1)
|
||||
feed.tags.add(tag2)
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
self.assertEqual(Feed.objects.all()[0].tags.count(), 2)
|
||||
|
||||
self.feedId = feed.id
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get_feed(self):
|
||||
response = self.client.get(URL, format="json")
|
||||
self.assertTrue(status.is_success(response.status_code))
|
||||
|
||||
feeds = Feed.objects.all()
|
||||
serializer = FeedSerializer(feeds, many=True, context={
|
||||
"request": APIRequestFactory().get(r"http://testserver/api/feed/")
|
||||
})
|
||||
self.assertEqual(response.data["results"], serializer.data)
|
||||
|
||||
def test_post_feed(self):
|
||||
tag1_id = tagBuilder("test1").id
|
||||
tag2_id = tagBuilder("test2").id
|
||||
|
||||
data = {
|
||||
"tag_id": [tag1_id, tag2_id],
|
||||
"title_fi": "testtitle",
|
||||
"title_en": "testtitle",
|
||||
"visible": "True",
|
||||
"description_fi": "liirumlaarum",
|
||||
"description_en": "liirumlaarum",
|
||||
"content_fi": "lorem ipsum",
|
||||
"content_en": "lorem ipsum"
|
||||
}
|
||||
# Try post without authentication
|
||||
response = self.client.post(URL, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post(URL, data, format="json")
|
||||
# Return success and check object was created
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(Feed.objects.count(), 2)
|
||||
|
||||
created = Feed.objects.get(title_fi="testtitle")
|
||||
self.assertEqual(created.tags.count(), 2)
|
||||
|
||||
def test_post_delete(self):
|
||||
response = self.client.delete(f"{URL}{self.feedId}/",)
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.delete(f"{URL}{self.feedId}/")
|
||||
# Soft delete
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(Feed.objects.count(), 1)
|
||||
self.assertEqual(Feed.objects.get(id=self.feedId).deleted, True)
|
||||
@@ -0,0 +1,54 @@
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIRequestFactory
|
||||
|
||||
from webapp.models import JobAd
|
||||
from webapp.serializers import JobAdSerializer
|
||||
|
||||
API = "/api/jobads/"
|
||||
|
||||
|
||||
class JobAdTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.prefilled_jobad = JobAd.objects.create(
|
||||
title_fi="ABB Test",
|
||||
title_en="ABB Test",
|
||||
visible=True,
|
||||
description_fi="desc",
|
||||
description_en="desc",
|
||||
content_fi="lorem",
|
||||
content_en="lorem"
|
||||
)
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get_jobads(self):
|
||||
response = self.client.get(API, format="json")
|
||||
expected = JobAdSerializer(self.prefilled_jobad).data
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["results"][0], expected)
|
||||
|
||||
def test_post_jobad(self):
|
||||
data = {
|
||||
"title_fi": "testtitle",
|
||||
"title_en": "testtitle",
|
||||
"visible": "True",
|
||||
"description_fi": "liirumlaarum",
|
||||
"description_en": "liirumlaarum",
|
||||
"content_fi": "lorem ipsum",
|
||||
"content_en": "lorem ipsum",
|
||||
"autohide_enabled": "True"
|
||||
}
|
||||
|
||||
# Try post without authentication
|
||||
response = self.client.post(API, data, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(JobAd.objects.count(), 1)
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post(API, data, format="json")
|
||||
# Return success and check object was created
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(JobAd.objects.count(), 2)
|
||||
@@ -0,0 +1,99 @@
|
||||
from unittest import skip
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
from webapp.serializers import SignupSerializer
|
||||
from webapp.models import Signup
|
||||
from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER
|
||||
|
||||
URL = "/api/signup/"
|
||||
|
||||
|
||||
class SignupTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.signupForm = createSignupForm()
|
||||
self.hiddenForm = createSignupForm(visible=False)
|
||||
|
||||
self.signup1 = createSignupObject("1", self.signupForm, ALL_QUESTION_TYPES)
|
||||
self.signup2 = createSignupObject("2", self.signupForm, ALL_QUESTION_TYPES)
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get_signups(self):
|
||||
expected = SignupSerializer(
|
||||
self.signupForm.signup_set.all(),
|
||||
many=True
|
||||
)
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.get(URL, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["results"], expected.data)
|
||||
|
||||
def test_get_single_signup(self):
|
||||
id = self.signup1.id
|
||||
expected = SignupSerializer(
|
||||
Signup.objects.get(id=id)
|
||||
)
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.get(f"{URL}{id}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, expected.data)
|
||||
|
||||
def test_create_signup(self):
|
||||
new = createSignupRequest("asd", self.signupForm.id, ALL_QUESTION_TYPES_ANSWER)
|
||||
response = self.client.post(URL, new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(Signup.objects.count(), 3)
|
||||
|
||||
# Can signup to a hidden form
|
||||
def test_create_signup_hidden(self):
|
||||
new = createSignupRequest("asd", self.hiddenForm.id, ALL_QUESTION_TYPES_ANSWER)
|
||||
response = self.client.post(URL, new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(Signup.objects.count(), 3)
|
||||
|
||||
def test_delete_as_admin(self):
|
||||
id = self.signup1.id
|
||||
no_auth_response = self.client.delete(f"{URL}{id}/", format="json")
|
||||
self.assertEqual(no_auth_response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.delete(f"{URL}{id}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(Signup.objects.get(id=id).deleted, True)
|
||||
|
||||
@skip("NotImplemented")
|
||||
def test_get_hidden_forms_admin(self):
|
||||
pass
|
||||
|
||||
def test_update_signup_token(self):
|
||||
id = self.signup1.id
|
||||
uuid = self.signup1.uuid
|
||||
clone = ALL_QUESTION_TYPES_ANSWER.copy()
|
||||
clone["-naY2R1-h"] = "Edited Testi"
|
||||
new = createSignupRequest("asd", self.signupForm.id, clone)
|
||||
response = self.client.put(f"{URL}{id}/edit/?uuid={uuid}", new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(Signup.objects.get(id=id).answer["-naY2R1-h"], "Edited Testi")
|
||||
|
||||
@skip("NotImplemented")
|
||||
def test_delete_signup_token(self):
|
||||
pass
|
||||
|
||||
# TODO: Use some mocking library and check that mailjet is actually called
|
||||
def test_signupee_sendemail(self):
|
||||
form = self.signupForm
|
||||
emailURL = f"/api/signupForm/{form.id}/sendemail/"
|
||||
payload = {
|
||||
"subject": "Email subject",
|
||||
"content": "Markdown",
|
||||
"mode": "actual"
|
||||
}
|
||||
no_auth_response = self.client.post(emailURL, payload, format="json")
|
||||
self.assertEqual(no_auth_response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post(emailURL, payload, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
@@ -0,0 +1,104 @@
|
||||
from django.utils import timezone
|
||||
from django.contrib.auth.models import User
|
||||
from unittest import skip
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
from webapp.models import Signup
|
||||
from webapp.tests.signup_fixture import createSignupForm, createSignupObject, createSignupRequest, ALL_QUESTION_TYPES, ALL_QUESTION_TYPES_ANSWER, TEXT_SCHEMA, RADIO_SCHEMA, CBOX_SCHEMA
|
||||
|
||||
URL = "/api/signup/"
|
||||
|
||||
|
||||
class SignupErrorTestCase(APITestCase):
|
||||
def setUp(self):
|
||||
self.signupForm = createSignupForm()
|
||||
self.signupFormText = createSignupForm(name="Form2", questions=[ALL_QUESTION_TYPES[0]], schema=TEXT_SCHEMA)
|
||||
self.signupFormRadio = createSignupForm(name="Form3", questions=[ALL_QUESTION_TYPES[1]], schema=RADIO_SCHEMA)
|
||||
self.signupFormCheck = createSignupForm(name="Form4", questions=[ALL_QUESTION_TYPES[2]], schema=CBOX_SCHEMA)
|
||||
self.hiddenForm = createSignupForm(visible=False)
|
||||
day_from_now = timezone.now() + timezone.timedelta(days=1)
|
||||
day_before_now = timezone.now() + timezone.timedelta(days=-1)
|
||||
self.signupFormNotStarted = createSignupForm(start_time=day_from_now, end_time=day_from_now)
|
||||
self.signupFormEnded = createSignupForm(start_time=day_before_now, end_time=timezone.now())
|
||||
|
||||
self.signup1 = createSignupObject("1", self.signupForm, ALL_QUESTION_TYPES)
|
||||
self.signup2 = createSignupObject("2", self.signupForm, ALL_QUESTION_TYPES)
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get_all_unauthorized(self):
|
||||
response = self.client.get(URL, format="json")
|
||||
self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
def test_get_single_unauthorized(self):
|
||||
id = self.signup1.id
|
||||
response = self.client.get(f"{URL}{id}/", format="json")
|
||||
self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
def test_create_signup_404(self):
|
||||
new = createSignupRequest("asd", 3001, ALL_QUESTION_TYPES_ANSWER)
|
||||
response = self.client.post(URL, new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertEqual(Signup.objects.count(), 2)
|
||||
|
||||
def test_create_signup_not_started(self):
|
||||
new = createSignupRequest("asd", self.signupFormNotStarted.id, ALL_QUESTION_TYPES_ANSWER)
|
||||
response = self.client.post(URL, new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertEqual(Signup.objects.count(), 2)
|
||||
|
||||
def test_create_signup_ended(self):
|
||||
new = createSignupRequest("asd", self.signupFormEnded.id, ALL_QUESTION_TYPES_ANSWER)
|
||||
response = self.client.post(URL, new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
self.assertEqual(Signup.objects.count(), 2)
|
||||
|
||||
def test_create_empty_body(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupForm.id, {}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_array_body(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupForm.id, []), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@skip("We allow extra signup body because of info fields")
|
||||
def test_create_extra_body(self):
|
||||
testInput = ALL_QUESTION_TYPES_ANSWER.copy()
|
||||
testInput["newId"] = "Oon extraa"
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupForm.id, testInput), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_id(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormText.id, {"malformed": "Tekstiä"}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_type_text(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormText.id, {"j5CeRZDvl": 123}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_data_checkbox(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormCheck.id, {
|
||||
"i10d426d5": ["D"]
|
||||
}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_type_checkbox(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormCheck.id, {
|
||||
"i10d426d5": {"j5CeRZDvl": {"asd": "123"}}
|
||||
}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_radio(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormRadio.id, {
|
||||
"RHJhSoaLD": []
|
||||
}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_create_bad_type_radio(self):
|
||||
response = self.client.post(URL, createSignupRequest("", self.signupFormRadio.id, {
|
||||
"RHJhSoaLD": {"asd": "123"}
|
||||
}), format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
self.assertEqual(Signup.objects.count(), 2)
|
||||
@@ -0,0 +1,79 @@
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, APIRequestFactory, force_authenticate
|
||||
|
||||
from webapp.models import Tag
|
||||
from webapp.serializers import TagSerializer
|
||||
|
||||
|
||||
from webapp.tests.tag_fixture import tagBuilder, createTagIcon
|
||||
|
||||
|
||||
class TagsTestCase(APITestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.icon = createTagIcon()
|
||||
tag = tagBuilder("Party", icon=self.icon)
|
||||
self.tag_id = tag.id
|
||||
|
||||
tagBuilder("Off")
|
||||
self.assertEqual(Tag.objects.count(), 2)
|
||||
|
||||
username, password = 'test_admin', 'password123'
|
||||
self.authClient = User.objects.create_superuser(username, 'myemail@test.com', password)
|
||||
|
||||
def test_get_multiple_tags(self):
|
||||
tagBuilder("Fuksi")
|
||||
tagBuilder("Inter")
|
||||
|
||||
expected = TagSerializer(
|
||||
Tag.objects.all(), many=True,
|
||||
context={
|
||||
"request": APIRequestFactory().get(r"http://testserver/api/events/")
|
||||
}).data
|
||||
|
||||
response = self.client.get('/api/tags/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(len(response.data['results']), 4)
|
||||
|
||||
self.assertEqual(
|
||||
response.data['results'],
|
||||
expected
|
||||
)
|
||||
|
||||
def test_get_single_tag(self):
|
||||
response = self.client.get(f"/api/tags/{self.tag_id}/", format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
||||
serializer = TagSerializer(
|
||||
Tag.objects.get(id=self.tag_id),
|
||||
context={
|
||||
"request": APIRequestFactory().get(r"http://testserver/api/events/")
|
||||
})
|
||||
self.assertEqual(response.data, serializer.data)
|
||||
|
||||
def test_get_invalid_tag(self):
|
||||
response = self.client.get('/api/tags/15/', format='json')
|
||||
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# READ ONLY API! Modify result code and count
|
||||
def test_create_tag(self):
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post(
|
||||
"/api/tags/",
|
||||
{
|
||||
"slug": "Test",
|
||||
"name": "Testinimi",
|
||||
"name_fi": "Testinimi",
|
||||
"name_en": "Test name",
|
||||
"icon": self.icon
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
|
||||
# Method Not allowed!
|
||||
self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
|
||||
# Not created
|
||||
self.assertEqual(Tag.objects.count(), 2)
|
||||
@@ -0,0 +1,80 @@
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase, force_authenticate
|
||||
|
||||
from webapp.models import TemplateQuestion
|
||||
from webapp.serializers import SavedQuestionsSerializer
|
||||
from webapp.tests.signup_fixture import ALL_QUESTION_TYPES
|
||||
import json
|
||||
|
||||
|
||||
class TemplateQuestionCase(APITestCase):
|
||||
def setUp(self):
|
||||
self.questions = [
|
||||
TemplateQuestion.objects.create(
|
||||
name="Testi1",
|
||||
question=ALL_QUESTION_TYPES
|
||||
),
|
||||
TemplateQuestion.objects.create(
|
||||
name="Testi2",
|
||||
question=ALL_QUESTION_TYPES
|
||||
)
|
||||
]
|
||||
|
||||
username, password = "test_admin", "password123"
|
||||
self.authClient = User.objects.create_superuser(username, "myemail@test.com", password)
|
||||
|
||||
def test_get(self):
|
||||
response = self.client.get("/api/questions/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["results"], SavedQuestionsSerializer(self.questions, many=True).data)
|
||||
response = self.client.get(f"/api/questions/{self.questions[0].id}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data, SavedQuestionsSerializer(self.questions[0]).data)
|
||||
|
||||
def test_post(self):
|
||||
new = {
|
||||
"name": "testi3",
|
||||
"question": json.dumps(ALL_QUESTION_TYPES)
|
||||
}
|
||||
response = self.client.post("/api/questions/", new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 2)
|
||||
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.post("/api/questions/", new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 3)
|
||||
|
||||
def test_update(self):
|
||||
new = {
|
||||
"name": "uusi testi2",
|
||||
"question": json.dumps({})
|
||||
}
|
||||
response = self.client.put(f"/api/questions/{self.questions[0].id}/", new, format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 2)
|
||||
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.put(f"/api/questions/{self.questions[0].id}/", new, format="json")
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 2)
|
||||
self.assertEqual(
|
||||
TemplateQuestion.objects.get(id=self.questions[0].id).name,
|
||||
"uusi testi2"
|
||||
)
|
||||
|
||||
def test_delete(self):
|
||||
response = self.client.delete(f"/api/questions/{self.questions[0].id}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 2)
|
||||
|
||||
# Authenticate
|
||||
self.client.force_authenticate(user=self.authClient)
|
||||
response = self.client.delete(f"/api/questions/{self.questions[0].id}/", format="json")
|
||||
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
|
||||
self.assertEqual(TemplateQuestion.objects.count(), 1)
|
||||
+6
-30
@@ -1,68 +1,44 @@
|
||||
"""Translation classes."""
|
||||
|
||||
from modeltranslation.translator import register, TranslationOptions
|
||||
from webapp.models import BaseFeed, Feed, Tag, Event, Signup, SignupForm, TemplateQuestion
|
||||
from webapp.models import PresetRole, BaseRole
|
||||
from webapp.models import *
|
||||
|
||||
|
||||
@register(BaseFeed)
|
||||
class BaseFeedTranslationOptions(TranslationOptions):
|
||||
"""Class for base feed translation options."""
|
||||
|
||||
fields = ('title', 'description', 'content')
|
||||
|
||||
|
||||
@register(Feed)
|
||||
class FeedTranslationOptions(TranslationOptions):
|
||||
"""Class for feed translation options."""
|
||||
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(Tag)
|
||||
class TagTranslationOptions(TranslationOptions):
|
||||
"""Class for tag translation options."""
|
||||
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
@register(Event)
|
||||
class EventTranslationOptions(TranslationOptions):
|
||||
"""Class for event translation options."""
|
||||
|
||||
fields = ()
|
||||
fields = ('location',)
|
||||
|
||||
|
||||
@register(Signup)
|
||||
class SignupTranslationOptions(TranslationOptions):
|
||||
"""Class for registration translation options."""
|
||||
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(SignupForm)
|
||||
class SignupFormTranslationOptions(TranslationOptions):
|
||||
"""Class for registration translation options."""
|
||||
|
||||
fields = ()
|
||||
fields = ('title',)
|
||||
|
||||
|
||||
@register(TemplateQuestion)
|
||||
class TemplateQuestionTranslationOptions(TranslationOptions):
|
||||
"""Class for registration translation options."""
|
||||
|
||||
fields = ()
|
||||
|
||||
|
||||
@register(BaseRole)
|
||||
class BaseRoleTranslationOptions(TranslationOptions):
|
||||
"""Class for base role translation options"""
|
||||
|
||||
fields = ('name',)
|
||||
|
||||
|
||||
@register(PresetRole)
|
||||
class PresetRoleTranslationOptions(TranslationOptions):
|
||||
"""Class for PresetRole translation options."""
|
||||
|
||||
fields = ('description',)
|
||||
@register(JobAd)
|
||||
class JobAdTranslationOptions(TranslationOptions):
|
||||
fields = ('title', 'description', 'content',)
|
||||
|
||||
+4
-56
@@ -3,25 +3,10 @@
|
||||
from django.conf.urls import url, include
|
||||
from rest_framework import routers
|
||||
from rest_framework_jwt.views import obtain_jwt_token, verify_jwt_token
|
||||
from webapp.views import about_view, nginx_jwt_resp
|
||||
|
||||
# from rest_framework.urlpatterns import format_suffix_patterns
|
||||
# from django.conf import settings
|
||||
# from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
# from webapp.views import main_index
|
||||
# from webapp.views import login_view
|
||||
# from webapp.views import logout_view
|
||||
from webapp.views import about_view
|
||||
# from webapp.views import guild_view
|
||||
# from webapp.views import freshmen_view
|
||||
# from webapp.views import jobs_view
|
||||
# from webapp.views import event_calendar_view
|
||||
# from webapp.views import international_view
|
||||
# from webapp.views import sosso_view
|
||||
# from webapp.views import contact_view
|
||||
|
||||
from webapp.views import EventViewSet, SignupFormViewSet, SignupViewSet,\
|
||||
FeedViewSet, ContactsViewSet, SavedQuestionsViewSet, RootView, TagsViewSet
|
||||
from webapp.views import *
|
||||
|
||||
|
||||
class APIRouter(routers.DefaultRouter):
|
||||
@@ -33,53 +18,16 @@ router.register(r'events', EventViewSet)
|
||||
router.register(r'signupForm', SignupFormViewSet)
|
||||
router.register(r'signup', SignupViewSet)
|
||||
router.register(r'feed', FeedViewSet)
|
||||
router.register(r'contacts', ContactsViewSet)
|
||||
router.register(r'questions', SavedQuestionsViewSet)
|
||||
router.register(r'tags', TagsViewSet)
|
||||
router.register(r'jobads', JobAdViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^api/', include(router.urls)),
|
||||
url(r'^api/api-token-auth/', obtain_jwt_token),
|
||||
url(r'^api/api-token-verify/', verify_jwt_token),
|
||||
url('nb/', include("nobotapp.urls")),
|
||||
|
||||
# login stuff
|
||||
# url(r'^login$', login_view),
|
||||
# url(r'^logout$', logout_view),
|
||||
|
||||
# git revision
|
||||
url(r'^about', about_view),
|
||||
url(r'^jwt_nginx', nginx_jwt_resp),
|
||||
]
|
||||
# urlpatterns = [
|
||||
# # main
|
||||
# url(r'^$', main_index),
|
||||
# url(r'^api/events/$', EventList.as_view(), name='event-list'),
|
||||
# url(r'^api/events/(?P<pk>[0-9]+)/$', EventDetail.as_view(), name='event-detail'),
|
||||
# url(r'^api/signup/$', SignupFormList.as_view(), name='signupform-list'),
|
||||
# url(r'^api/signup/(?P<pk>[0-9]+)/$', SignupFormDetail.as_view(), name='signup-detail'),
|
||||
|
||||
# url(r'^api/signup/create$', SignupFormCreate.as_view(), name='signupform-create'),
|
||||
|
||||
# # url(r'^signupform/$', SignupFormList.as_view(), name='signupform-list'),
|
||||
# # url(r'^signupform/(?P<pk>[0-9]+)/$', SignupFormDetail.as_view(), name='signupform-detail'),
|
||||
|
||||
# # login stuff
|
||||
# url(r'^login$', login_view),
|
||||
# url(r'^logout$', logout_view),
|
||||
|
||||
# # pages
|
||||
# url(r'^guild', guild_view),
|
||||
# url(r'^freshmen', freshmen_view),
|
||||
# url(r'^event_calendar', event_calendar_view),
|
||||
# url(r'^international', international_view),
|
||||
# url(r'^sosso', sosso_view),
|
||||
# url(r'^contact', contact_view),
|
||||
|
||||
# # corporate
|
||||
# url(r'^jobs', jobs_view),
|
||||
# ]
|
||||
|
||||
|
||||
# if settings.DEBUG:
|
||||
# from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
# urlpatterns += staticfiles_urlpatterns()
|
||||
|
||||
+94
-11
@@ -1,11 +1,51 @@
|
||||
"""Webapp utils."""
|
||||
|
||||
from django.utils import timezone
|
||||
from django.core.mail import send_mail
|
||||
# from django.core.mail import send_mail
|
||||
import os
|
||||
from mailjet_rest import Client
|
||||
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.template.loader import render_to_string
|
||||
from django.core.files.base import ContentFile
|
||||
import base64
|
||||
import uuid
|
||||
from sikweb.settings import FRONTEND_URL, URL, EMAIL_API_KEY, EMAIL_API_SECRET, DEFAULT_EMAIL_FROM, DEFAULT_EMAIL_FROM_ADDR, ENABLE_AUTOMATIC_EMAILS
|
||||
import imghdr
|
||||
import markdown
|
||||
|
||||
|
||||
def get_file_extension(file_name, decoded_file):
|
||||
extension = imghdr.what(file_name, decoded_file)
|
||||
extension = "jpg" if extension == "jpeg" else extension
|
||||
return extension
|
||||
|
||||
|
||||
def decode_base64_file(data):
|
||||
# Check if this is a base64 string
|
||||
if isinstance(data, str):
|
||||
# Check if the base64 string is in the "data:" format
|
||||
if 'data:' in data and ';base64,' in data:
|
||||
# Break out the header from the base64 content
|
||||
header, data = data.split(';base64,')
|
||||
|
||||
# Try to decode the file. Return validation error if it fails.
|
||||
try:
|
||||
decoded_file = base64.b64decode(data)
|
||||
except TypeError:
|
||||
TypeError('invalid_image')
|
||||
|
||||
# Generate file name:
|
||||
file_name = str(uuid.uuid4())
|
||||
|
||||
# Get the file name extension:
|
||||
file_extension = get_file_extension(file_name, decoded_file)
|
||||
|
||||
complete_file_name = "%s.%s" % (file_name, file_extension, )
|
||||
|
||||
return ContentFile(decoded_file, name=complete_file_name)
|
||||
|
||||
|
||||
def month_from_now():
|
||||
@@ -13,17 +53,60 @@ def month_from_now():
|
||||
return timezone.now() + timedelta(days=30)
|
||||
|
||||
|
||||
def send_email(to, subject, body):
|
||||
def send_email(to, subject, body, html=False):
|
||||
if not ENABLE_AUTOMATIC_EMAILS:
|
||||
logging.debug("Skipping email")
|
||||
logging.debug(f"to: {to}")
|
||||
logging.debug(f"subject: {subject}")
|
||||
logging.debug(f"body: {body}")
|
||||
return
|
||||
try:
|
||||
success = send_mail(
|
||||
subject,
|
||||
body,
|
||||
settings.DEFAULT_EMAIL_FROM,
|
||||
[to],
|
||||
fail_silently=False,
|
||||
)
|
||||
if success == 0:
|
||||
raise Exception('Failed to send email!')
|
||||
mailjet = Client(auth=(EMAIL_API_KEY, EMAIL_API_SECRET), version='v3.1')
|
||||
|
||||
data = {
|
||||
'Messages': [
|
||||
{
|
||||
"From": {
|
||||
"Email": DEFAULT_EMAIL_FROM_ADDR,
|
||||
"Name": DEFAULT_EMAIL_FROM
|
||||
},
|
||||
"To": [
|
||||
{
|
||||
"Email": to,
|
||||
"Name": "You"
|
||||
}
|
||||
],
|
||||
"Subject": subject
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
if (html):
|
||||
data["Messages"][0]["HTMLPart"] = body
|
||||
else:
|
||||
data["Messages"][0]["TextPart"] = body
|
||||
|
||||
success = mailjet.send.create(data=data)
|
||||
|
||||
# For some reason returns 200 OK instead of 201 Created...
|
||||
if success.status_code != 200:
|
||||
raise Exception(f'Failed to send email: {success.json()}')
|
||||
|
||||
except Exception as ex:
|
||||
logging.exception('Failed to send email.')
|
||||
|
||||
|
||||
def send_signup_email(to, subject, id, uuid, content):
|
||||
message = render_to_string(
|
||||
'webapp:signup_email.html', {
|
||||
'url': f"https://{FRONTEND_URL}/signup/edit/{id}/{uuid}",
|
||||
'content': markdown.markdown(content),
|
||||
}
|
||||
)
|
||||
|
||||
return send_email(to, subject, message, True)
|
||||
|
||||
|
||||
def admin_send_email_signupees(list, subject, content):
|
||||
for to in list:
|
||||
send_email(to, subject, markdown.markdown(content), True)
|
||||
|
||||
+229
-108
@@ -1,32 +1,31 @@
|
||||
"""Webapp views."""
|
||||
|
||||
# from django.db.models import Count
|
||||
from django.shortcuts import render, redirect
|
||||
from django.contrib.auth import login, logout, authenticate
|
||||
from django.views.decorators.http import require_http_methods
|
||||
# from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
# from django.http import HttpResponse, HttpResponseRedirect
|
||||
# from django.contrib.auth.decorators import permission_required, login_required
|
||||
# from django.conf import settings
|
||||
# from django.utils import timezone
|
||||
from rest_framework import viewsets, routers
|
||||
from rest_framework.permissions import IsAuthenticatedOrReadOnly
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
from django_filters import rest_framework as filters
|
||||
from rest_framework.filters import SearchFilter, OrderingFilter
|
||||
from rest_framework import permissions
|
||||
# import logging
|
||||
# import requests
|
||||
from jwt import decode
|
||||
from jwt.exceptions import InvalidSignatureError
|
||||
from django.utils import timezone
|
||||
from dealer.git import git
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django_filters import rest_framework as filters
|
||||
from django.db.models import Prefetch
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from rest_framework import routers
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission, AllowAny, IsAuthenticated
|
||||
from jsonschema import validate
|
||||
from jsonschema.exceptions import ValidationError
|
||||
|
||||
from webapp.models import Event, SignupForm, Signup, TemplateQuestion, Feed,\
|
||||
Committee, Official, Tag
|
||||
from webapp.models import *
|
||||
from webapp.serializers import *
|
||||
from members.views.utils import *
|
||||
from webapp.utils import admin_send_email_signupees, decode_base64_file
|
||||
|
||||
|
||||
class IsPostOrIsAuthenticated(permissions.BasePermission):
|
||||
class SignupPermission(BasePermission):
|
||||
|
||||
def has_permission(self, request, view):
|
||||
if request.method == 'POST':
|
||||
@@ -39,63 +38,203 @@ class RootView(routers.APIRootView):
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
|
||||
class EventViewSet(viewsets.ModelViewSet):
|
||||
queryset = Event.objects.all()
|
||||
class EventViewSet(ModelViewSet):
|
||||
queryset = Event.objects.filter(deleted=False)
|
||||
ordering = ["start_time"]
|
||||
serializer_class = EventSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
|
||||
filter_fields = '__all__'
|
||||
search_fields = '__all__'
|
||||
filter_fields = ('id', 'tags', 'visible', 'signupForm')
|
||||
search_fields = ('id', 'tags', 'visible', 'signupForm')
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
# TODO: For create and update, this return old data in signupForm field (prefetched)...
|
||||
if self.request.user.is_authenticated or \
|
||||
self.request.method == 'POST' or \
|
||||
self.request.method == 'PUT':
|
||||
return Event.objects.filter(deleted=False).prefetch_related(
|
||||
Prefetch('signupForm', queryset=SignupForm.objects.filter(deleted=False), to_attr='filtered_signup_forms')
|
||||
)
|
||||
|
||||
since = self.request.query_params.get('since', None)
|
||||
if since:
|
||||
return Event.objects.filter(visible=True, end_time__gt=since).order_by('start_time')
|
||||
return Event.objects.filter(deleted=False, visible=True, end_time__gt=since).order_by('start_time').prefetch_related(
|
||||
Prefetch('signupForm', queryset=SignupForm.objects.filter(deleted=False, visible=True), to_attr='filtered_signup_forms')
|
||||
)
|
||||
return Event.objects.filter(deleted=False, visible=True, end_time__gt=timezone.now()).order_by('start_time').prefetch_related(
|
||||
Prefetch('signupForm', queryset=SignupForm.objects.filter(deleted=False, visible=True), to_attr='filtered_signup_forms')
|
||||
)
|
||||
|
||||
return Event.objects.filter(visible=True).order_by('start_time')
|
||||
def create(self, request, *args, **kwargs):
|
||||
raw_image = request.data.get("image", None)
|
||||
if raw_image is not None:
|
||||
image = decode_base64_file(raw_image)
|
||||
request.data.update({
|
||||
"image": image
|
||||
})
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
raw_image = request.data.get("image", None)
|
||||
if raw_image is not None:
|
||||
image = decode_base64_file(raw_image)
|
||||
request.data.update({
|
||||
"image": image
|
||||
})
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
def destroy(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
event = self.get_object()
|
||||
event.deleted = True
|
||||
event.save()
|
||||
return JsonResponse(status=200, data={"message": "OK"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"Event {pk} not found"})
|
||||
|
||||
|
||||
class SignupFormViewSet(viewsets.ModelViewSet):
|
||||
queryset = SignupForm.objects.all()
|
||||
class SignupFormViewSet(ModelViewSet):
|
||||
queryset = SignupForm.objects.filter(deleted=False)
|
||||
ordering = ["start_time"]
|
||||
serializer_class = SignupFormSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
|
||||
filter_fields = '__all__'
|
||||
search_fields = '__all__'
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
try:
|
||||
schema = {
|
||||
"type": "array",
|
||||
}
|
||||
validate(instance=request.data["questions"], schema=schema)
|
||||
return super().create(request, *args, **kwargs)
|
||||
except ValidationError as err:
|
||||
return JsonResponse(status=400, data={"error": err.message})
|
||||
|
||||
def get_queryset(self):
|
||||
return SignupForm.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time')
|
||||
if self.request.user.is_authenticated:
|
||||
return SignupForm.objects.filter(deleted=False).order_by('start_time')
|
||||
return SignupForm.objects.filter(deleted=False, visible=True, end_time__gt=timezone.now()).order_by('start_time')
|
||||
|
||||
def destroy(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
form = self.get_object()
|
||||
form.deleted = True
|
||||
form.save()
|
||||
return JsonResponse(status=200, data={"message": "OK"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"SignupForm {pk} not found"})
|
||||
|
||||
@action(detail=True, methods=['post'], permission_classes=[IsAuthenticated])
|
||||
def sendemail(self, request, pk=None, *args, **kwargs):
|
||||
subject = request.data["subject"]
|
||||
content = request.data["content"]
|
||||
mode = request.data["mode"]
|
||||
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
filter = {'pk': pk}
|
||||
signupForm = get_object_or_404(queryset, **filter)
|
||||
if (mode == "all"):
|
||||
admin_send_email_signupees(signupForm.signups, subject, content)
|
||||
return JsonResponse(status=201, data={"message": "Email sent"})
|
||||
elif (mode == "actual"):
|
||||
admin_send_email_signupees(signupForm.signups[:signupForm.quota], subject, content)
|
||||
return JsonResponse(status=201, data={"message": "Email sent"})
|
||||
elif (mode == "reserved"):
|
||||
admin_send_email_signupees(signupForm.signups[signupForm.quota:], subject, content)
|
||||
return JsonResponse(status=201, data={"message": "Email sent"})
|
||||
else:
|
||||
return JsonResponse(status=400, data={"error": f"Bad mode '{mode}'"})
|
||||
|
||||
@action(detail=True, methods=['get'], permission_classes=[IsAuthenticated])
|
||||
def signups(self, request, pk=None, *args, **kwargs):
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
filter = {'pk': pk}
|
||||
signupForm = get_object_or_404(queryset, **filter)
|
||||
return Response(SignupSerializer(signupForm.signups, many=True).data)
|
||||
|
||||
|
||||
class SignupViewSet(viewsets.ModelViewSet):
|
||||
queryset = Signup.objects.all()
|
||||
class SignupViewSet(ModelViewSet):
|
||||
queryset = Signup.objects.filter(deleted=False)
|
||||
serializer_class = SignupSerializer
|
||||
permission_classes = [IsPostOrIsAuthenticated]
|
||||
# filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
|
||||
# filter_fields = '__all__'
|
||||
# search_fields = '__all__'
|
||||
permission_classes = [SignupPermission]
|
||||
|
||||
# def get_queryset(self):
|
||||
# return Signup.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time')
|
||||
@action(detail=True, methods=['get', 'put'], permission_classes=[AllowAny])
|
||||
def edit(self, request, pk=None, *args, **kwargs):
|
||||
uuid = request.query_params.get("uuid", None)
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
filter = {'pk': pk, 'uuid': uuid}
|
||||
get_object_or_404(queryset, **filter)
|
||||
if request.method == 'GET':
|
||||
return self.retrieve(request, *args, **kwargs)
|
||||
elif request.method == 'PUT':
|
||||
return self.partial_update(request, *args, **kwargs)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
id = request.data["signupForm_id"]
|
||||
try:
|
||||
answer = request.data["answer"]
|
||||
form = SignupForm.objects.get(id=id)
|
||||
if (form.isOpen):
|
||||
# Throws ValidationError if not valid
|
||||
validate(instance=answer, schema=form.schema)
|
||||
return super().create(request, *args, **kwargs)
|
||||
except ValidationError as inst:
|
||||
return JsonResponse(status=400, data={"error": inst.message})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"})
|
||||
else:
|
||||
return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"})
|
||||
|
||||
def partial_update(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
# ID & UUID validated in edit @action for normal users.
|
||||
# This is otherwise open for authenticated users.
|
||||
signup = self.get_object()
|
||||
answer = request.data["answer"]
|
||||
form = SignupForm.objects.get(id=signup.signupForm_id)
|
||||
|
||||
if (form.visible):
|
||||
# Throws ValidationError if not valid
|
||||
validate(instance=answer, schema=form.schema)
|
||||
return super().partial_update(request, *args, **kwargs)
|
||||
except ValidationError as inst:
|
||||
return JsonResponse(status=400, data={"error": inst.message})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"})
|
||||
else:
|
||||
return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"})
|
||||
|
||||
def destroy(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
signup = self.get_object()
|
||||
signup.deleted = True
|
||||
signup.save()
|
||||
return JsonResponse(status=200, data={"message": "OK"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"Signup {pk} not found"})
|
||||
|
||||
|
||||
class SavedQuestionsViewSet(viewsets.ModelViewSet):
|
||||
class SavedQuestionsViewSet(ModelViewSet):
|
||||
queryset = TemplateQuestion.objects.all()
|
||||
serializer_class = SavedQuestionsSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
|
||||
class FeedViewSet(viewsets.ModelViewSet):
|
||||
queryset = Feed.objects.all()
|
||||
class FeedViewSet(ModelViewSet):
|
||||
queryset = Feed.objects.filter(deleted=False)
|
||||
serializer_class = FeedSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
|
||||
filter_fields = '__all__'
|
||||
search_fields = '__all__'
|
||||
filter_fields = ('id', 'tags', 'visible')
|
||||
search_fields = ('id', 'tags', 'visible')
|
||||
|
||||
def get_queryset(self):
|
||||
objs = Feed.objects.filter(visible=True).order_by('publish_time')
|
||||
if self.request.user.is_authenticated:
|
||||
return Feed.objects.filter(deleted=False).order_by('-publish_time')
|
||||
else:
|
||||
objs = Feed.objects.filter(deleted=False, visible=True).order_by('-publish_time')
|
||||
|
||||
# TODO: Bad filtering. Rewrite!
|
||||
result_ids = []
|
||||
for obj in objs:
|
||||
if obj.autohide_enabled:
|
||||
@@ -104,27 +243,42 @@ class FeedViewSet(viewsets.ModelViewSet):
|
||||
else:
|
||||
result_ids.append(obj.id)
|
||||
|
||||
return Feed.objects.filter(id__in=result_ids)
|
||||
return Feed.objects.filter(id__in=result_ids).order_by('-publish_time')
|
||||
|
||||
def destroy(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
post = self.get_object()
|
||||
post.deleted = True
|
||||
post.save()
|
||||
return JsonResponse(status=200, data={"message": "OK"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"Post {pk} not found"})
|
||||
|
||||
|
||||
class ContactsViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = Official.objects.all()
|
||||
serializer_class = ContactsSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
|
||||
class TagsViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
class TagsViewSet(ReadOnlyModelViewSet):
|
||||
queryset = Tag.objects.all()
|
||||
serializer_class = TagSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
|
||||
# -- OLD CODEBASE -- #
|
||||
class JobAdViewSet(ModelViewSet):
|
||||
queryset = JobAd.objects.filter(deleted=False)
|
||||
serializer_class = JobAdSerializer
|
||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def main_index(request, *args, **kwargs):
|
||||
"""Render main page."""
|
||||
return render(request, "index.html", {})
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_authenticated:
|
||||
return JobAd.objects.filter(deleted=False)
|
||||
return JobAd.objects.filter(deleted=False, visible=True, autohide_at__gt=timezone.now())
|
||||
|
||||
def destroy(self, request, pk=None, *args, **kwargs):
|
||||
try:
|
||||
ad = self.get_object()
|
||||
ad.deleted = True
|
||||
ad.save()
|
||||
return JsonResponse(status=200, data={"message": "OK"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse(status=404, data={"error": f"Job Ad {pk} not found"})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@@ -138,8 +292,8 @@ def about_view(request, *args, **kwargs):
|
||||
latest_commit = repo.git("rev-parse HEAD").decode('utf-8')
|
||||
latest_date = repo.git("show -s --format=%ci " + latest_commit).decode('utf-8')
|
||||
latest_tag = repo.git("describe --tags " + repo.git("rev-list --tags --max-count=1").decode('utf-8')).decode('utf-8')
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(f"Git failed:\n{e}")
|
||||
|
||||
context = {
|
||||
'commit': latest_commit,
|
||||
@@ -150,48 +304,15 @@ def about_view(request, *args, **kwargs):
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def guild_view(request, *args, **kwargs):
|
||||
"""Render "Guild" page."""
|
||||
return render(request, "guild.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def freshmen_view(request, *args, **kwargs):
|
||||
"""Render "Freshmen" page."""
|
||||
return render(request, "freshmen.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def jobs_view(request, *args, **kwargs):
|
||||
"""Render "Jobs" page."""
|
||||
return render(request, "jobs.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def event_calendar_view(request, *args, **kwargs):
|
||||
"""Render "Event calendar" page."""
|
||||
return render(request, "event_calendar.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def international_view(request, *args, **kwargs):
|
||||
"""Render "International" page."""
|
||||
return render(request, "international.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def sosso_view(request, *args, **kwargs):
|
||||
"""Render "Sössö" page."""
|
||||
return render(request, "sosso.html", {})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def contact_view(request, *args, **kwargs):
|
||||
"""Render "Contact" page."""
|
||||
committees = Committee.objects.order_by('name')
|
||||
|
||||
context = {
|
||||
"committees": committees
|
||||
}
|
||||
|
||||
return render(request, "contact.html", context)
|
||||
def nginx_jwt_resp(request, *args, **kwargs):
|
||||
cookie = request.COOKIES.get("jwt", None)
|
||||
if not cookie:
|
||||
return HttpResponse("", status=401)
|
||||
try:
|
||||
token = decode(cookie, settings.SECRET_KEY)
|
||||
except InvalidSignatureError:
|
||||
return HttpResponse("", status=403)
|
||||
user = 'admin' if token.get('username', '') == 'admin' else 'moderator'
|
||||
resp = HttpResponse("", status=200)
|
||||
resp['X-FBrowser-User'] = user
|
||||
return resp
|
||||
|
||||
Reference in New Issue
Block a user