Merge branch 'develop' into 'master'
Azure deploy & Signup flow See merge request sahkoinsinoorikilta/vtmk/web2.0-backend!32
This commit is contained in:
+9
-3
@@ -5,15 +5,21 @@ from webapp.utils import month_from_now
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from auditlog.registry import auditlog
|
from auditlog.registry import auditlog
|
||||||
from phonenumber_field.modelfields import PhoneNumberField
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
|
from webapp.models import BaseRole
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import webapp.models
|
|
||||||
|
# TODO: Move BaseRole to Kaehmt App; will fuck up the DB since table is removed, if no data migration is done before-hand.
|
||||||
|
# Either reconstruct all kaehmy roles from scratch then, or do these migrations:
|
||||||
|
# 1. Create table here
|
||||||
|
# 2. Data migrate from webapp BaseRole to new kaehmy BaseRole
|
||||||
|
# 3. Delete webapp BaseRole table
|
||||||
|
|
||||||
VERBOSE_NAME = _('Kaehmy')
|
VERBOSE_NAME = _('Kaehmy')
|
||||||
|
|
||||||
|
|
||||||
class KaehmyBaseRole(webapp.models.BaseRole):
|
class KaehmyBaseRole(BaseRole):
|
||||||
|
"""ABC"""
|
||||||
|
|
||||||
CATEGORIES = (
|
CATEGORIES = (
|
||||||
('corporate', _('Corporate affairs')),
|
('corporate', _('Corporate affairs')),
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa konsultoida <a href="https://static.sika.sik.party/uus_webi/kahmyopas.pdf">kaehmyopasta</a>
|
Jos sinulla on kysyttävää mistä tahansa virasta, kannattaa konsultoida <a href="https://static.sika.sik.party/uus_webi/kahmyopas.pdf">kaehmyopasta</a>
|
||||||
tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan henkilöön.{% endblocktrans %}
|
tai olla yhteydessä kyseistä virkaa tänä vuonna toimittavaan henkilöön.{% endblocktrans %}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
{% blocktrans %}(HUOM! Kaehmytekstin maksimipituus on 300 merkkiä. Tarvittaessa voit kirjoittaa lisätietoja kommenteihin.){% endblocktrans %}
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{% blocktrans %}Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus
|
{% blocktrans %}Muista, että kaehmyn lähettäminen on kiinnostuksen ilmaus
|
||||||
eikä siis missään nimessä sitova ilmoittautumien mihinkään tehtävään!{% endblocktrans %}
|
eikä siis missään nimessä sitova ilmoittautumien mihinkään tehtävään!{% endblocktrans %}
|
||||||
|
|||||||
Binary file not shown.
+83
-2488
File diff suppressed because it is too large
Load Diff
Binary file not shown.
+85
-2521
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -22,7 +22,7 @@ nose-exclude==0.5.0
|
|||||||
psycopg2-binary==2.8.4
|
psycopg2-binary==2.8.4
|
||||||
django-bootstrap3==11.1.0
|
django-bootstrap3==11.1.0
|
||||||
django-tables2==1.6.1
|
django-tables2==1.6.1
|
||||||
pycodestyle==2.3.1
|
pycodestyle==2.6.0
|
||||||
dealer==2.0.5
|
dealer==2.0.5
|
||||||
django-modeltranslation==0.13b1
|
django-modeltranslation==0.13b1
|
||||||
django-auditlog==0.4.5
|
django-auditlog==0.4.5
|
||||||
|
|||||||
+23
-23
@@ -8,15 +8,15 @@ services:
|
|||||||
condition: on-failure
|
condition: on-failure
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER_FILE=/run/secrets/DJANGO_DB_USER
|
- POSTGRES_USER_FILE=/run/secrets/BACKEND_DB_USER
|
||||||
- POSTGRES_PASSWORD_FILE=/run/secrets/DJANGO_DB_PASSWD
|
- POSTGRES_PASSWORD_FILE=/run/secrets/BACKEND_DB_PASSWD
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
volumes:
|
volumes:
|
||||||
- dbdata:/var/lib/postgresql/data
|
- dbdata:/var/lib/postgresql/data
|
||||||
secrets:
|
secrets:
|
||||||
- DJANGO_DB_USER
|
- BACKEND_DB_USER
|
||||||
- DJANGO_DB_PASSWD
|
- BACKEND_DB_PASSWD
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend:latest
|
image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend:latest
|
||||||
@@ -42,38 +42,38 @@ services:
|
|||||||
target: /app/collected_static
|
target: /app/collected_static
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
- SECRET_KEY_FILE=/run/secrets/DJANGO_SECRET_KEY
|
- SECRET_KEY_FILE=/run/secrets/BACKEND_SECRET_KEY
|
||||||
- TG_BOT_TOKEN_FILE=/run/secrets/DJANGO_TG_BOT_TOKEN
|
- TG_BOT_TOKEN_FILE=/run/secrets/BACKEND_TG_BOT_TOKEN
|
||||||
- DB_USER_FILE=/run/secrets/DJANGO_DB_USER
|
- DB_USER_FILE=/run/secrets/BACKEND_DB_USER
|
||||||
- DB_PASSWD_FILE=/run/secrets/DJANGO_DB_PASSWD
|
- DB_PASSWD_FILE=/run/secrets/BACKEND_DB_PASSWD
|
||||||
- HOST=api.sika.sik.party
|
- HOST=api.sika.sik.party
|
||||||
- FRONTEND_URL=sika.sik.party
|
- FRONTEND_URL=sika.sik.party
|
||||||
- DB_HOST=db
|
- DB_HOST=db
|
||||||
- DB_PORT=5432
|
- DB_PORT=5432
|
||||||
- EMAIL_API_KEY_FILE=/run/secrets/DJANGO_EMAIL_API_KEY
|
- EMAIL_API_KEY_FILE=/run/secrets/BACKEND_EMAIL_API_KEY
|
||||||
- EMAIL_API_SECRET_FILE=/run/secrets/DJANGO_EMAIL_API_SECRET
|
- EMAIL_API_SECRET_FILE=/run/secrets/BACKEND_EMAIL_API_SECRET
|
||||||
|
|
||||||
secrets:
|
secrets:
|
||||||
- DJANGO_SECRET_KEY
|
- BACKEND_SECRET_KEY
|
||||||
- DJANGO_TG_BOT_TOKEN
|
- BACKEND_TG_BOT_TOKEN
|
||||||
- DJANGO_DB_USER
|
- BACKEND_DB_USER
|
||||||
- DJANGO_DB_PASSWD
|
- BACKEND_DB_PASSWD
|
||||||
- DJANGO_EMAIL_API_KEY
|
- BACKEND_EMAIL_API_KEY
|
||||||
- DJANGO_EMAIL_API_SECRET
|
- BACKEND_EMAIL_API_SECRET
|
||||||
secrets:
|
secrets:
|
||||||
DJANGO_SECRET_KEY:
|
BACKEND_SECRET_KEY:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_TG_BOT_TOKEN:
|
BACKEND_TG_BOT_TOKEN:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_DB_NAME:
|
BACKEND_DB_NAME:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_DB_USER:
|
BACKEND_DB_USER:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_DB_PASSWD:
|
BACKEND_DB_PASSWD:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_EMAIL_API_KEY:
|
BACKEND_EMAIL_API_KEY:
|
||||||
external: true
|
external: true
|
||||||
DJANGO_EMAIL_API_SECRET:
|
BACKEND_EMAIL_API_SECRET:
|
||||||
external: true
|
external: true
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+2
-6
@@ -1,8 +1,7 @@
|
|||||||
"""File containing webapp app admin registers."""
|
"""File containing webapp app admin registers."""
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from webapp.models import Official, Role, Committee, Occupation
|
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion, JobAd
|
||||||
from webapp.models import Feed, Tag, Event, Signup, SignupForm, TemplateQuestion
|
|
||||||
from modeltranslation.admin import TranslationAdmin
|
from modeltranslation.admin import TranslationAdmin
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
# this is needed so that the models get registered for translation
|
# this is needed so that the models get registered for translation
|
||||||
@@ -16,7 +15,4 @@ admin.site.register(Event, TranslationAdmin)
|
|||||||
admin.site.register(SignupForm, TranslationAdmin)
|
admin.site.register(SignupForm, TranslationAdmin)
|
||||||
admin.site.register(Signup, TranslationAdmin)
|
admin.site.register(Signup, TranslationAdmin)
|
||||||
admin.site.register(TemplateQuestion, TranslationAdmin)
|
admin.site.register(TemplateQuestion, TranslationAdmin)
|
||||||
admin.site.register(Committee, TranslationAdmin)
|
admin.site.register(JobAd, TranslationAdmin)
|
||||||
admin.site.register(Official)
|
|
||||||
admin.site.register(Occupation)
|
|
||||||
admin.site.register(Role)
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
+49
-135
@@ -22,14 +22,14 @@ EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
|
|||||||
class Tag(models.Model):
|
class Tag(models.Model):
|
||||||
"""Model for tag."""
|
"""Model for tag."""
|
||||||
|
|
||||||
slug = models.SlugField(unique=True)
|
|
||||||
name = models.CharField(max_length=127)
|
|
||||||
icon = models.ImageField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Tag')
|
verbose_name = _('Tag')
|
||||||
verbose_name_plural = _('Tags')
|
verbose_name_plural = _('Tags')
|
||||||
|
|
||||||
|
slug = models.SlugField(unique=True)
|
||||||
|
name = models.CharField(max_length=127)
|
||||||
|
icon = models.ImageField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _('Tag: {}').format(self.slug)
|
return _('Tag: {}').format(self.slug)
|
||||||
|
|
||||||
@@ -48,6 +48,10 @@ class BaseFeed(models.Model):
|
|||||||
class Feed(BaseFeed):
|
class Feed(BaseFeed):
|
||||||
"""Model representing feed."""
|
"""Model representing feed."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Feed')
|
||||||
|
verbose_name_plural = _('Feeds')
|
||||||
|
|
||||||
publish_time = models.DateTimeField(default=timezone.now)
|
publish_time = models.DateTimeField(default=timezone.now)
|
||||||
autohide = models.DateTimeField(default=month_from_now)
|
autohide = models.DateTimeField(default=month_from_now)
|
||||||
autohide_enabled = models.BooleanField(default=False)
|
autohide_enabled = models.BooleanField(default=False)
|
||||||
@@ -55,13 +59,13 @@ class Feed(BaseFeed):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _('Feed: {}').format(self.title)
|
return _('Feed: {}').format(self.title)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Feed')
|
|
||||||
verbose_name_plural = _('Feeds')
|
|
||||||
|
|
||||||
|
|
||||||
class Event(BaseFeed):
|
class Event(BaseFeed):
|
||||||
"""Model for event."""
|
"""Model for event in guild calendar"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Event')
|
||||||
|
verbose_name_plural = _('Events')
|
||||||
|
|
||||||
start_time = models.DateTimeField(default=timezone.now)
|
start_time = models.DateTimeField(default=timezone.now)
|
||||||
end_time = models.DateTimeField(default=timezone.now)
|
end_time = models.DateTimeField(default=timezone.now)
|
||||||
@@ -72,26 +76,30 @@ class Event(BaseFeed):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _('Event: {}').format(self.title)
|
return _('Event: {}').format(self.title)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Event')
|
|
||||||
verbose_name_plural = _('Events')
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateQuestion(models.Model):
|
class TemplateQuestion(models.Model):
|
||||||
"""Stores template questions for signup forms as JSONB"""
|
"""
|
||||||
|
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')
|
||||||
|
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
question = JSONField()
|
question = JSONField()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _('Template questions: {}').format(self.name)
|
return _('Template questions: {}').format(self.name)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Template question')
|
|
||||||
verbose_name_plural = _('Template questions')
|
|
||||||
|
|
||||||
|
|
||||||
class SignupForm(models.Model):
|
class SignupForm(models.Model):
|
||||||
"""Model for event signup form. Stores questions in JSONB."""
|
"""Model for event signup form. Stores questions in JSON format."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Signup form')
|
||||||
|
verbose_name_plural = _('Signup forms')
|
||||||
|
|
||||||
title = models.CharField(max_length=255)
|
title = models.CharField(max_length=255)
|
||||||
start_time = models.DateTimeField(default=timezone.now)
|
start_time = models.DateTimeField(default=timezone.now)
|
||||||
@@ -107,19 +115,22 @@ class SignupForm(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def signups(self):
|
def signups(self):
|
||||||
return Signup.objects.filter(signupForm=self).order_by('pk')
|
return Signup.objects.filter(signupForm=self, deleted=False).order_by('pk')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def isOpen(self):
|
def isOpen(self):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
return self.start_time <= now and now < self.end_time
|
return self.start_time <= now and now < self.end_time
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Signup form')
|
|
||||||
verbose_name_plural = _('Signup forms')
|
|
||||||
|
|
||||||
|
|
||||||
class Signup(models.Model):
|
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)
|
signupForm = models.ForeignKey('SignupForm', on_delete=models.CASCADE)
|
||||||
time = models.DateTimeField(default=timezone.now)
|
time = models.DateTimeField(default=timezone.now)
|
||||||
answer = JSONField()
|
answer = JSONField()
|
||||||
@@ -129,18 +140,14 @@ class Signup(models.Model):
|
|||||||
email = models.EmailField(blank=True, null=True)
|
email = models.EmailField(blank=True, null=True)
|
||||||
# Random unique identifier. Used for signup editing by the user.
|
# Random unique identifier. Used for signup editing by the user.
|
||||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||||
|
deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.signupForm}: {self.list_name} ({self.pk})"
|
return f"{self.signupForm}: {self.list_name} ({self.pk})"
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Sign-up')
|
|
||||||
verbose_name_plural = _('Sign-ups')
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Signup)
|
@receiver(post_save, sender=Signup)
|
||||||
def email_on_signup(sender, instance, created, **kwargs):
|
def email_on_signup(sender, instance, created, **kwargs):
|
||||||
"""Send email validation."""
|
|
||||||
if created and instance.email:
|
if created and instance.email:
|
||||||
# TODO: Possible bug due to many-to-many relationship with events and forms.
|
# TODO: Possible bug due to many-to-many relationship with events and forms.
|
||||||
# TODO: Subject field crashes with lazy loaded translations.
|
# TODO: Subject field crashes with lazy loaded translations.
|
||||||
@@ -164,118 +171,25 @@ class BaseRole(models.Model):
|
|||||||
return '{} ({})'.format(n, _('board member')) if self.is_board else n
|
return '{} ({})'.format(n, _('board member')) if self.is_board else n
|
||||||
|
|
||||||
|
|
||||||
class PresetRole(BaseRole):
|
class JobAd(models.Model):
|
||||||
"""Model representing a preset occupation in the guild."""
|
"""Job advertisements shown on Corporate relations page"""
|
||||||
|
|
||||||
description = models.TextField(_('Description'))
|
|
||||||
|
|
||||||
|
|
||||||
class Committee(models.Model):
|
|
||||||
"""
|
|
||||||
Committee model
|
|
||||||
Has many Roles found under variable roles
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class for Committee class."""
|
verbose_name = _('JobAd')
|
||||||
|
verbose_name_plural = _('JobAds')
|
||||||
|
|
||||||
verbose_name = _('Committee')
|
title = models.CharField(max_length=255)
|
||||||
verbose_name_plural = _('Committees')
|
description = models.CharField(max_length=255)
|
||||||
|
content = models.TextField()
|
||||||
def __str__(self):
|
visible = models.BooleanField(default=True)
|
||||||
return _('Committee: {}').format(self.name)
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
autohide_at = models.DateTimeField(default=month_from_now)
|
||||||
name = models.CharField(_("Name"), max_length=255)
|
autohide_enabled = models.BooleanField(default=False)
|
||||||
|
|
||||||
@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 the guild.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta class for Role model."""
|
|
||||||
|
|
||||||
verbose_name = _('Role')
|
|
||||||
verbose_name_plural = _('Roles')
|
|
||||||
|
|
||||||
committee = models.ForeignKey('Committee', related_name='roles', on_delete=models.SET_NULL, null=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{} (Hallitus: {}) ({})'.format(self.name, _("Yes") if self.is_board else _("No"), self.committee)
|
|
||||||
|
|
||||||
|
|
||||||
class Occupation(models.Model):
|
|
||||||
"""
|
|
||||||
Model for a occupation in guild.
|
|
||||||
|
|
||||||
Model links Official into a role he/she has or has had in the guild.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _('Occupation')
|
|
||||||
verbose_name_plural = _('Occupations')
|
|
||||||
|
|
||||||
start_date = models.DateField(_('Start date'))
|
|
||||||
end_date = models.DateField(_('End date'))
|
|
||||||
role = models.ForeignKey('Role', on_delete=models.SET_NULL, null=True)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def by_year(year):
|
|
||||||
return Occupation.objects.filter(
|
|
||||||
end_date__gte=timezone.datetime(year, 1, 1)).filter(
|
|
||||||
start_date__lte=timezone.datetime(year, 12, 31))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{}: {} - {}'.format(self.role.name, self.start_date, self.end_date)
|
|
||||||
|
|
||||||
|
|
||||||
class Official(models.Model):
|
|
||||||
"""Model representing a guild official."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta class for Official class."""
|
|
||||||
|
|
||||||
verbose_name = _('Official')
|
|
||||||
verbose_name_plural = _('Officials')
|
|
||||||
|
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
first_name = models.CharField(_('First name'), max_length=30)
|
|
||||||
last_name = models.CharField(_('Last name'), max_length=150)
|
|
||||||
email = models.EmailField(_('Email address'))
|
|
||||||
phone_number = PhoneNumberField(_('Phone number'))
|
|
||||||
role_history = models.ManyToManyField('Occupation', 'officials', blank=True)
|
|
||||||
image = models.ImageField(blank=True, null=True)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def by_year(year):
|
|
||||||
return Official.objects.filter(
|
|
||||||
role_history__in=Occupation.occupations_by_year(year)).distinct()
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return '{} {}'.format(self.first_name, self.last_name)
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Official)
|
|
||||||
def save_user_official(sender, instance, **kwargs):
|
|
||||||
instance.user.first_name = instance.first_name
|
|
||||||
instance.user.last_name = instance.last_name
|
|
||||||
instance.user.email = instance.email
|
|
||||||
instance.user.save()
|
|
||||||
|
|
||||||
|
|
||||||
auditlog.register(Tag)
|
auditlog.register(Tag)
|
||||||
auditlog.register(Feed)
|
auditlog.register(Feed)
|
||||||
auditlog.register(Event)
|
auditlog.register(Event)
|
||||||
|
auditlog.register(SignupForm)
|
||||||
auditlog.register(Signup)
|
auditlog.register(Signup)
|
||||||
auditlog.register(PresetRole)
|
auditlog.register(JobAd)
|
||||||
auditlog.register(Role)
|
|
||||||
auditlog.register(Official)
|
|
||||||
|
|||||||
+3
-27
@@ -141,31 +141,7 @@ class FeedSerializer(serializers.ModelSerializer):
|
|||||||
return feed
|
return feed
|
||||||
|
|
||||||
|
|
||||||
class CommitteeSerializer(serializers.ModelSerializer):
|
class JobAdSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Committee
|
model = JobAd
|
||||||
fields = ['name_fi', 'name_en']
|
fields = ('id', 'title_fi', 'title_en', 'description_fi', 'description_en', 'content_fi', 'content_en', 'visible', 'autohide_at', 'autohide_enabled')
|
||||||
|
|
||||||
|
|
||||||
class RoleSerializer(serializers.ModelSerializer):
|
|
||||||
committee = CommitteeSerializer(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Role
|
|
||||||
fields = ('name_fi', 'name_en', 'description_fi', 'description_en', 'committee', 'is_board')
|
|
||||||
|
|
||||||
|
|
||||||
class ContactsSerializer(serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Official
|
|
||||||
fields = ('first_name', 'last_name', 'email', 'phone_number', 'image')
|
|
||||||
depth = 2
|
|
||||||
|
|
||||||
|
|
||||||
class OccupationSerializer(serializers.ModelSerializer):
|
|
||||||
role = RoleSerializer(read_only=True)
|
|
||||||
officials = ContactsSerializer(many=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = Occupation
|
|
||||||
fields = ('role', 'start_date', 'end_date', 'officials')
|
|
||||||
|
|||||||
@@ -40,14 +40,15 @@ CBOX_SCHEMA = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def createSignupForm(name="Form1", start_time=timezone.now(), end_time=month_from_now(), questions=ALL_QUESTION_TYPES, schema=ALL_QUESTIONS_SCHEMA, visible=True):
|
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(
|
return SignupForm.objects.create(
|
||||||
title=name,
|
title=name,
|
||||||
start_time=start_time,
|
start_time=start_time,
|
||||||
end_time=end_time,
|
end_time=end_time,
|
||||||
questions=questions,
|
questions=questions,
|
||||||
visible=visible,
|
visible=visible,
|
||||||
schema=schema
|
schema=schema,
|
||||||
|
quota=quota
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.utils import timezone
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.test import APITestCase
|
|
||||||
|
|
||||||
from webapp.models import Official, Role, Occupation, Committee
|
|
||||||
from webapp.serializers import OccupationSerializer
|
|
||||||
|
|
||||||
|
|
||||||
URL = "/api/contacts/"
|
|
||||||
COMMITTEE = Committee.objects.create(
|
|
||||||
name_fi="Viestintä",
|
|
||||||
name_en="Communications"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def createRoleBoard():
|
|
||||||
return Role.objects.create(
|
|
||||||
name_fi="Metsuri",
|
|
||||||
name_en="The lumberjack",
|
|
||||||
is_board=True,
|
|
||||||
description_fi="Toimikunta PJ",
|
|
||||||
description_en="Committee Chair"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def createRoleNoBoard():
|
|
||||||
return Role.objects.create(
|
|
||||||
name_fi="Toimari",
|
|
||||||
name_en="Official",
|
|
||||||
is_board=False,
|
|
||||||
description_fi="Toimikunta jäbä",
|
|
||||||
description_en="Committee dude(tte)",
|
|
||||||
committee=COMMITTEE
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def createOccupation(year, role=createRoleNoBoard(), dummydata=1):
|
|
||||||
occupation = Occupation.objects.create(
|
|
||||||
start_date=timezone.datetime(year, 1, 1),
|
|
||||||
end_date=timezone.datetime(year, 12, 31),
|
|
||||||
role=role
|
|
||||||
)
|
|
||||||
|
|
||||||
occupation.officials.add(
|
|
||||||
createPerson(dummydata)
|
|
||||||
)
|
|
||||||
|
|
||||||
return occupation
|
|
||||||
|
|
||||||
|
|
||||||
def createPerson(name):
|
|
||||||
return Official.objects.create(
|
|
||||||
user=User.objects.create_user(f"testi{name}", "test@test.tld", "password123"),
|
|
||||||
first_name=f"first{name}",
|
|
||||||
last_name=f"last{name}",
|
|
||||||
email="test@test.tld",
|
|
||||||
phone_number="+358501234567",
|
|
||||||
image=""
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ContactsTestCase(APITestCase):
|
|
||||||
current_year = timezone.now().year
|
|
||||||
old_year = 1970
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
createOccupation(self.current_year, role=createRoleBoard(), dummydata=1)
|
|
||||||
createOccupation(self.current_year, dummydata=2)
|
|
||||||
createOccupation(self.old_year, role=createRoleBoard(), dummydata=3)
|
|
||||||
createOccupation(self.old_year, dummydata=4)
|
|
||||||
|
|
||||||
def test_get(self):
|
|
||||||
response = self.client.get(f"{URL}", format='json')
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
serializer = OccupationSerializer(
|
|
||||||
Occupation.by_year(self.current_year),
|
|
||||||
many=True
|
|
||||||
)
|
|
||||||
self.assertEqual(response.data["results"], serializer.data)
|
|
||||||
|
|
||||||
def test_get_by_year(self):
|
|
||||||
response = self.client.get(f"{URL}?year={self.old_year}", format='json')
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
|
|
||||||
serializer = OccupationSerializer(
|
|
||||||
Occupation.by_year(self.old_year),
|
|
||||||
many=True
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertEqual(response.data["results"], serializer.data)
|
|
||||||
|
|
||||||
def test_by_year_empty(self):
|
|
||||||
response = self.client.get(f"{URL}?year=1971")
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
|
||||||
self.assertEqual(response.data["results"], [])
|
|
||||||
@@ -27,11 +27,9 @@ class FeedTestCase(APITestCase):
|
|||||||
self.assertTrue(status.is_success(response.status_code))
|
self.assertTrue(status.is_success(response.status_code))
|
||||||
|
|
||||||
feeds = Feed.objects.all()
|
feeds = Feed.objects.all()
|
||||||
serializer = FeedSerializer(
|
serializer = FeedSerializer(feeds, many=True, context={
|
||||||
feeds, many=True,
|
"request": APIRequestFactory().get(r"http://testserver/api/feed/")
|
||||||
context={
|
})
|
||||||
"request": APIRequestFactory().get(r"http://testserver/api/events/")
|
|
||||||
})
|
|
||||||
self.assertEqual(response.data["results"], serializer.data)
|
self.assertEqual(response.data["results"], serializer.data)
|
||||||
|
|
||||||
def test_post_feed(self):
|
def test_post_feed(self):
|
||||||
@@ -50,13 +48,13 @@ class FeedTestCase(APITestCase):
|
|||||||
}
|
}
|
||||||
# Try post without authentication
|
# Try post without authentication
|
||||||
response = self.client.post("/api/feed/", data, format="json")
|
response = self.client.post("/api/feed/", data, format="json")
|
||||||
self.assertTrue(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||||
self.assertEqual(Feed.objects.count(), 1)
|
self.assertEqual(Feed.objects.count(), 1)
|
||||||
# Authenticate
|
# Authenticate
|
||||||
self.client.force_authenticate(user=self.authClient)
|
self.client.force_authenticate(user=self.authClient)
|
||||||
response = self.client.post("/api/feed/", data, format="json")
|
response = self.client.post("/api/feed/", data, format="json")
|
||||||
# Return success and check object was created
|
# Return success and check object was created
|
||||||
self.assertTrue(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(Feed.objects.count(), 2)
|
self.assertEqual(Feed.objects.count(), 2)
|
||||||
|
|
||||||
created = Feed.objects.get(title_fi="testtitle")
|
created = Feed.objects.get(title_fi="testtitle")
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -56,16 +56,44 @@ class SignupTestCase(APITestCase):
|
|||||||
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
self.assertEqual(Signup.objects.count(), 3)
|
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")
|
@skip("NotImplemented")
|
||||||
def test_get_hidden_forms_admin(self):
|
def test_get_hidden_forms_admin(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Update and Delete are available for super admin (Django Admin)
|
|
||||||
# and to the user that signed up (uid token)
|
|
||||||
@skip("NotImplemented")
|
|
||||||
def test_update_signup_token(self):
|
def test_update_signup_token(self):
|
||||||
pass
|
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")
|
@skip("NotImplemented")
|
||||||
def test_delete_signup_token(self):
|
def test_delete_signup_token(self):
|
||||||
pass
|
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)
|
||||||
|
|||||||
+3
-33
@@ -6,69 +6,39 @@ from webapp.models import *
|
|||||||
|
|
||||||
@register(BaseFeed)
|
@register(BaseFeed)
|
||||||
class BaseFeedTranslationOptions(TranslationOptions):
|
class BaseFeedTranslationOptions(TranslationOptions):
|
||||||
"""Class for base feed translation options."""
|
|
||||||
|
|
||||||
fields = ('title', 'description', 'content')
|
fields = ('title', 'description', 'content')
|
||||||
|
|
||||||
|
|
||||||
@register(Feed)
|
@register(Feed)
|
||||||
class FeedTranslationOptions(TranslationOptions):
|
class FeedTranslationOptions(TranslationOptions):
|
||||||
"""Class for feed translation options."""
|
|
||||||
|
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
@register(Tag)
|
@register(Tag)
|
||||||
class TagTranslationOptions(TranslationOptions):
|
class TagTranslationOptions(TranslationOptions):
|
||||||
"""Class for tag translation options."""
|
|
||||||
|
|
||||||
fields = ('name',)
|
fields = ('name',)
|
||||||
|
|
||||||
|
|
||||||
@register(Event)
|
@register(Event)
|
||||||
class EventTranslationOptions(TranslationOptions):
|
class EventTranslationOptions(TranslationOptions):
|
||||||
"""Class for event translation options."""
|
|
||||||
|
|
||||||
fields = ('location',)
|
fields = ('location',)
|
||||||
|
|
||||||
|
|
||||||
@register(Signup)
|
@register(Signup)
|
||||||
class SignupTranslationOptions(TranslationOptions):
|
class SignupTranslationOptions(TranslationOptions):
|
||||||
"""Class for registration translation options."""
|
|
||||||
|
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
@register(SignupForm)
|
@register(SignupForm)
|
||||||
class SignupFormTranslationOptions(TranslationOptions):
|
class SignupFormTranslationOptions(TranslationOptions):
|
||||||
"""Class for registration translation options."""
|
|
||||||
|
|
||||||
fields = ('title',)
|
fields = ('title',)
|
||||||
|
|
||||||
|
|
||||||
@register(TemplateQuestion)
|
@register(TemplateQuestion)
|
||||||
class TemplateQuestionTranslationOptions(TranslationOptions):
|
class TemplateQuestionTranslationOptions(TranslationOptions):
|
||||||
"""Class for registration translation options."""
|
|
||||||
|
|
||||||
fields = ()
|
fields = ()
|
||||||
|
|
||||||
|
|
||||||
@register(BaseRole)
|
@register(JobAd)
|
||||||
class BaseRoleTranslationOptions(TranslationOptions):
|
class JobAdTranslationOptions(TranslationOptions):
|
||||||
"""Class for base role translation options"""
|
fields = ('title', 'description', 'content',)
|
||||||
|
|
||||||
fields = ('name',)
|
|
||||||
|
|
||||||
|
|
||||||
@register(PresetRole)
|
|
||||||
class PresetRoleTranslationOptions(TranslationOptions):
|
|
||||||
"""Class for PresetRole translation options."""
|
|
||||||
|
|
||||||
fields = ('description',)
|
|
||||||
|
|
||||||
|
|
||||||
@register(Committee)
|
|
||||||
class CommitteeTranslationOptions(TranslationOptions):
|
|
||||||
"""Class for PresetRole translation options."""
|
|
||||||
|
|
||||||
fields = ('name',)
|
|
||||||
|
|||||||
+1
-2
@@ -18,10 +18,9 @@ router.register(r'events', EventViewSet)
|
|||||||
router.register(r'signupForm', SignupFormViewSet)
|
router.register(r'signupForm', SignupFormViewSet)
|
||||||
router.register(r'signup', SignupViewSet)
|
router.register(r'signup', SignupViewSet)
|
||||||
router.register(r'feed', FeedViewSet)
|
router.register(r'feed', FeedViewSet)
|
||||||
router.register(r'contacts', ContactsViewSet)
|
|
||||||
router.register(r'committees', CommitteeViewSet)
|
|
||||||
router.register(r'questions', SavedQuestionsViewSet)
|
router.register(r'questions', SavedQuestionsViewSet)
|
||||||
router.register(r'tags', TagsViewSet)
|
router.register(r'tags', TagsViewSet)
|
||||||
|
router.register(r'jobads', JobAdViewSet)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^api/', include(router.urls)),
|
url(r'^api/', include(router.urls)),
|
||||||
|
|||||||
@@ -55,6 +55,10 @@ def month_from_now():
|
|||||||
|
|
||||||
def send_email(to, subject, body, html=False):
|
def send_email(to, subject, body, html=False):
|
||||||
if not ENABLE_AUTOMATIC_EMAILS:
|
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
|
return
|
||||||
try:
|
try:
|
||||||
mailjet = Client(auth=(EMAIL_API_KEY, EMAIL_API_SECRET), version='v3.1')
|
mailjet = Client(auth=(EMAIL_API_KEY, EMAIL_API_SECRET), version='v3.1')
|
||||||
@@ -101,3 +105,8 @@ def send_signup_email(to, subject, id, uuid, content):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return send_email(to, subject, message, True)
|
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)
|
||||||
|
|||||||
+54
-25
@@ -12,18 +12,17 @@ from django_filters import rest_framework as filters
|
|||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
from rest_framework.response import Response
|
||||||
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
|
||||||
from rest_framework.filters import OrderingFilter, SearchFilter
|
from rest_framework.filters import OrderingFilter, SearchFilter
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission, AllowAny
|
from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission, AllowAny, IsAuthenticated
|
||||||
from jsonschema import validate
|
from jsonschema import validate
|
||||||
from jsonschema.exceptions import ValidationError
|
from jsonschema.exceptions import ValidationError
|
||||||
|
|
||||||
from webapp.models import Event, SignupForm, Signup, TemplateQuestion, Feed, Committee, Occupation, Tag
|
from webapp.models import *
|
||||||
from webapp.serializers import (EventSerializer, SignupFormSerializer, SignupSerializer,
|
from webapp.serializers import *
|
||||||
SavedQuestionsSerializer, FeedSerializer, CommitteeSerializer,
|
from webapp.utils import admin_send_email_signupees, decode_base64_file
|
||||||
OccupationSerializer, TagSerializer)
|
|
||||||
from webapp.utils import decode_base64_file
|
|
||||||
|
|
||||||
|
|
||||||
class SignupPermission(BasePermission):
|
class SignupPermission(BasePermission):
|
||||||
@@ -106,9 +105,37 @@ class SignupFormViewSet(ModelViewSet):
|
|||||||
return SignupForm.objects.all().order_by('start_time')
|
return SignupForm.objects.all().order_by('start_time')
|
||||||
return SignupForm.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time')
|
return SignupForm.objects.filter(visible=True, end_time__gt=timezone.now()).order_by('start_time')
|
||||||
|
|
||||||
|
@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(ModelViewSet):
|
class SignupViewSet(ModelViewSet):
|
||||||
queryset = Signup.objects.all()
|
queryset = Signup.objects.filter(deleted=False)
|
||||||
serializer_class = SignupSerializer
|
serializer_class = SignupSerializer
|
||||||
permission_classes = [SignupPermission]
|
permission_classes = [SignupPermission]
|
||||||
|
|
||||||
@@ -158,6 +185,15 @@ class SignupViewSet(ModelViewSet):
|
|||||||
else:
|
else:
|
||||||
return JsonResponse(status=404, data={"error": f"SignupForm {id} not found"})
|
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(ModelViewSet):
|
class SavedQuestionsViewSet(ModelViewSet):
|
||||||
queryset = TemplateQuestion.objects.all()
|
queryset = TemplateQuestion.objects.all()
|
||||||
@@ -191,30 +227,23 @@ class FeedViewSet(ModelViewSet):
|
|||||||
return Feed.objects.filter(id__in=result_ids)
|
return Feed.objects.filter(id__in=result_ids)
|
||||||
|
|
||||||
|
|
||||||
class ContactsViewSet(ReadOnlyModelViewSet):
|
|
||||||
queryset = Occupation.objects.all()
|
|
||||||
serializer_class = OccupationSerializer
|
|
||||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
year = self.request.query_params.get('year')
|
|
||||||
if not year:
|
|
||||||
return Occupation.by_year(timezone.now().year)
|
|
||||||
return Occupation.by_year(int(year))
|
|
||||||
|
|
||||||
|
|
||||||
class CommitteeViewSet(ReadOnlyModelViewSet):
|
|
||||||
queryset = Committee.objects.all()
|
|
||||||
serializer_class = CommitteeSerializer
|
|
||||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
|
||||||
|
|
||||||
|
|
||||||
class TagsViewSet(ReadOnlyModelViewSet):
|
class TagsViewSet(ReadOnlyModelViewSet):
|
||||||
queryset = Tag.objects.all()
|
queryset = Tag.objects.all()
|
||||||
serializer_class = TagSerializer
|
serializer_class = TagSerializer
|
||||||
permission_classes = [IsAuthenticatedOrReadOnly]
|
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
|
|
||||||
|
class JobAdViewSet(ModelViewSet):
|
||||||
|
queryset = JobAd.objects.all()
|
||||||
|
serializer_class = JobAdSerializer
|
||||||
|
permission_classes = [IsAuthenticatedOrReadOnly]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_authenticated:
|
||||||
|
return JobAd.objects.all()
|
||||||
|
return JobAd.objects.filter(visible=True, autohide_at__gt=timezone.now())
|
||||||
|
|
||||||
|
|
||||||
@require_http_methods(["GET"])
|
@require_http_methods(["GET"])
|
||||||
def about_view(request, *args, **kwargs):
|
def about_view(request, *args, **kwargs):
|
||||||
"""Render about page."""
|
"""Render about page."""
|
||||||
|
|||||||
Reference in New Issue
Block a user