24 Commits

Author SHA1 Message Date
Aarni Halinen 1fa1d0c019 Fix lint 2022-08-06 17:48:48 +03:00
Aarni Halinen 4419f1cf2c Fix nginx_jwt_resp HTTP responses 2022-08-06 17:46:29 +03:00
Aarni Halinen 6e74548206 Fix tests 2022-08-06 17:46:27 +03:00
Aarni Halinen 5b9b4021d3 Fix serializer 2022-08-06 17:46:25 +03:00
Aarni Halinen 05279ae900 Implement filter for publishAt 2022-08-06 17:46:22 +03:00
Aarni Halinen 9b450f94a5 Rewrite Event and JobAd get_queryset 2022-08-06 17:46:17 +03:00
Aarni Halinen 7ffce4e929 Rewrite Feed get_queryset 2022-08-06 17:46:12 +03:00
Aarni Halinen ca8937d9f6 Rename BaseFeed fields 2022-08-06 17:46:04 +03:00
Aarni Halinen 92f744f39c Re-order views and serializers 2022-08-06 17:45:38 +03:00
Aarni Halinen 7c9a627d41 Require explicit publishing from creator 2022-08-06 17:45:35 +03:00
Aarni Halinen a35b86af43 Rename BaseFeed fields 2022-08-06 17:45:01 +03:00
Aarni Halinen 9651725bb3 Audit log register for TemplateQuestions 2022-08-06 17:44:10 +03:00
Aarni Halinen ac017bfb82 Remove created_at from JobAd 2022-08-06 17:44:08 +03:00
Aarni Halinen f923511a72 Re-order models 2022-08-06 17:43:18 +03:00
Aarni Halinen 78092ce734 Fix field name 2022-08-06 17:40:00 +03:00
Aarni Halinen ae136aebae Remove OldJobAd model 2022-08-06 17:39:59 +03:00
Aarni Halinen 1eb5e7e10c Add missing fields for new JobAd 2022-08-06 17:38:43 +03:00
Aarni Halinen 74d0765eb2 Data migration for JobAds 2022-08-06 17:38:41 +03:00
Aarni Halinen 1cab37dbcf Add new JobAd model 2022-08-06 17:38:02 +03:00
Aarni Halinen cf673c32c5 Rename old JobAd model 2022-08-06 17:37:28 +03:00
Aarni Halinen a2e6a4754e Remove duplicate code 2022-08-06 17:36:36 +03:00
Aarni Halinen 6ccb1d01cf Rename and remove moved fields 2022-08-06 17:36:34 +03:00
Aarni Halinen cd708a469d Add new base fields 2022-08-06 17:33:37 +03:00
Aarni Halinen 831f15d0ff Reorder fields 2022-08-06 17:32:53 +03:00
19 changed files with 624 additions and 342 deletions
@@ -0,0 +1,35 @@
# Generated by Django 3.2.15 on 2022-08-06 14:33
from django.db import migrations, models
import django.utils.timezone
import webapp.utils
class Migration(migrations.Migration):
dependencies = [
("webapp", "0082_delete_baserole"),
]
operations = [
migrations.AddField(
model_name="basefeed",
name="base_autohide",
field=models.DateTimeField(default=webapp.utils.month_from_now),
),
migrations.AddField(
model_name="basefeed",
name="base_autohide_enabled",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="basefeed",
name="base_deleted",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="basefeed",
name="base_publish_time",
field=models.DateTimeField(default=django.utils.timezone.now),
),
]
@@ -0,0 +1,31 @@
# Generated by Django 2.2.28 on 2022-07-26 18:12
from django.db import migrations
def copyOldDataToNewFields(apps, schema_editor):
Event = apps.get_model("webapp", "Event")
Feed = apps.get_model("webapp", "Feed")
for event in Event.objects.all():
event.base_deleted = event.deleted
event.save()
for post in Feed.objects.all():
post.base_deleted = post.deleted
post.base_publish_time = post.publish_time
post.base_autohide = post.autohide
post.base_autohide_enabled = post.autohide_enabled
post.save()
class Migration(migrations.Migration):
dependencies = [
("webapp", "0083_auto_20220806_1733"),
]
operations = [
migrations.RunPython(
copyOldDataToNewFields, reverse_code=migrations.RunPython.noop
),
]
@@ -0,0 +1,58 @@
# Generated by Django 2.2.28 on 2022-07-26 18:28
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0084_auto_20220726_2112"),
]
operations = [
migrations.RenameField(
model_name="event",
old_name="deleted",
new_name="old_deleted",
),
migrations.RenameField(
model_name="feed",
old_name="autohide",
new_name="old_autohide",
),
migrations.RenameField(
model_name="feed",
old_name="autohide_enabled",
new_name="old_autohide_enabled",
),
migrations.RenameField(
model_name="feed",
old_name="deleted",
new_name="old_deleted",
),
migrations.RenameField(
model_name="feed",
old_name="publish_time",
new_name="old_publish_time",
),
migrations.RenameField(
model_name="basefeed",
old_name="base_autohide",
new_name="autohide",
),
migrations.RenameField(
model_name="basefeed",
old_name="base_autohide_enabled",
new_name="autohide_enabled",
),
migrations.RenameField(
model_name="basefeed",
old_name="base_deleted",
new_name="deleted",
),
migrations.RenameField(
model_name="basefeed",
old_name="base_publish_time",
new_name="publish_time",
),
]
@@ -0,0 +1,33 @@
# Generated by Django 2.2.28 on 2022-07-26 18:29
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0085_auto_20220726_2128"),
]
operations = [
migrations.RemoveField(
model_name="event",
name="old_deleted",
),
migrations.RemoveField(
model_name="feed",
name="old_autohide",
),
migrations.RemoveField(
model_name="feed",
name="old_autohide_enabled",
),
migrations.RemoveField(
model_name="feed",
name="old_deleted",
),
migrations.RemoveField(
model_name="feed",
name="old_publish_time",
),
]
@@ -0,0 +1,17 @@
# Generated by Django 2.2.28 on 2022-07-26 19:26
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0086_auto_20220726_2129"),
]
operations = [
migrations.RenameModel(
old_name="JobAd",
new_name="RemoveJobAd",
),
]
+37
View File
@@ -0,0 +1,37 @@
# Generated by Django 2.2.28 on 2022-07-26 19:49
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("webapp", "0087_auto_20220726_2226"),
]
operations = [
migrations.CreateModel(
name="JobAd",
fields=[
(
"basefeed_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="webapp.BaseFeed",
),
),
("created_at", models.DateTimeField(default=django.utils.timezone.now)),
],
options={
"verbose_name": "JobAd",
"verbose_name_plural": "JobAds",
},
bases=("webapp.basefeed",),
),
]
@@ -0,0 +1,35 @@
# Generated by Django 2.2.28 on 2022-07-26 19:29
from django.db import migrations
def copyOldDataToNewFields(apps, schema_editor):
Old = apps.get_model("webapp", "RemoveJobAd")
New = apps.get_model("webapp", "JobAd")
for jobAd in Old.objects.all():
New.objects.create(
id=jobAd.id,
title=jobAd.title,
tags=jobAd.tags,
visible=jobAd.visible,
deleted=jobAd.deleted,
publish_time=jobAd.publish_time,
autohide=jobAd.autohide_at,
autohide_enabled=jobAd.autohide_enabled,
description=jobAd.description,
content=jobAd.content,
created_at=jobAd.created_at,
)
class Migration(migrations.Migration):
dependencies = [
("webapp", "0088_jobad"),
]
operations = [
migrations.RunPython(
copyOldDataToNewFields, reverse_code=migrations.RunPython.noop
),
]
@@ -0,0 +1,16 @@
# Generated by Django 2.2.28 on 2022-07-26 19:52
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0089_auto_20220726_2229"),
]
operations = [
migrations.DeleteModel(
name="RemoveJobAd",
),
]
@@ -0,0 +1,17 @@
# Generated by Django 2.2.28 on 2022-07-26 20:11
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0090_delete_removejobad"),
]
operations = [
migrations.RemoveField(
model_name="jobad",
name="created_at",
),
]
@@ -0,0 +1,33 @@
# Generated by Django 2.2.28 on 2022-07-26 21:16
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("webapp", "0091_remove_jobad_created_at"),
]
operations = [
migrations.RenameField(
model_name="basefeed",
old_name="autohide_enabled",
new_name="autoUnpublish",
),
migrations.RenameField(
model_name="basefeed",
old_name="visible",
new_name="isPublished",
),
migrations.RenameField(
model_name="basefeed",
old_name="publish_time",
new_name="publishAt",
),
migrations.RenameField(
model_name="basefeed",
old_name="autohide",
new_name="unpublishAt",
),
]
@@ -0,0 +1,18 @@
# Generated by Django 2.2.28 on 2022-07-26 21:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0092_auto_20220727_0016"),
]
operations = [
migrations.AlterField(
model_name="basefeed",
name="isPublished",
field=models.BooleanField(default=False),
),
]
+77 -122
View File
@@ -24,15 +24,15 @@ EMAIL_REGEX = r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)"
class Tag(models.Model):
"""Model for tag."""
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
id = models.AutoField(primary_key=True)
slug = models.SlugField(unique=True)
name = models.CharField(max_length=127)
icon = models.ImageField()
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
def __str__(self):
return _("Tag: {}").format(self.slug)
@@ -41,89 +41,85 @@ class BaseFeed(models.Model):
"""Model containing something showing on some info feed."""
id = models.AutoField(primary_key=True)
tags = models.ManyToManyField(Tag, related_name="feeds", blank=True)
visible = models.BooleanField(default=True)
deleted = models.BooleanField(default=False)
title = models.CharField(max_length=255)
description = models.CharField(max_length=255)
content = models.TextField()
image = models.ImageField(blank=True, null=True)
tags = models.ManyToManyField(Tag, related_name="feeds", blank=True)
# Require explicit publishing from creator
isPublished = models.BooleanField(default=False)
# Automatically publish after this time, unless still in draft (!isPublished)
publishAt = models.DateTimeField(default=timezone.now)
autoUnpublish = models.BooleanField(default=False)
# Automatically unpublish after this if auto_unpublish==True
unpublishAt = models.DateTimeField(default=month_from_now)
webhookUrl = ""
hookType = ""
wasPublishedBefore = False
def __init__(self, *args, **kwargs):
super(BaseFeed, self).__init__(*args, **kwargs)
self.wasPublishedBefore = self.isPublished
def __str__(self):
delete_str = _("Deleted: ") if self.deleted else ""
return _("{}{}: {}").format(delete_str, self._meta.verbose_name, self.title)
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(BaseFeed, self).save(force_insert, force_update, *args, **kwargs)
if self.isPublished and (created or not self.wasPublishedBefore):
self.refresh_from_db() # Fetch so we can use primary key
url = f"{self.webhookUrl}/{self.pk}"
processHooks(
message=generateMessage(
f"Uusi {self._meta.verbose_name}", self.title, self.description, url
),
eventType=self.hookType,
)
self.wasPublishedBefore = self.isPublished
class Feed(BaseFeed):
"""Model representing feed."""
webhookUrl = f"https://{FRONTEND_URL}/feed"
hookType = "feed"
class Meta:
verbose_name = _("Feed")
verbose_name_plural = _("Feeds")
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):
delete_str = _("Deleted: ") if self.deleted else ""
return _("{}Feed: {}").format(delete_str, self.title)
__previousVisible = False
def __init__(self, *args, **kwargs):
super(Feed, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(Feed, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/feed/{self.pk}"
processHooks(
message=generateMessage(
"Uusi uutinen", self.title, self.description, url
),
eventType="feed",
)
self.__previousVisible = self.visible
class Event(BaseFeed):
"""Model for event in guild calendar"""
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now)
location = models.CharField(max_length=255, blank=True)
signupForm = models.ManyToManyField("SignupForm", blank=True, related_name="event")
webhookUrl = f"https://{FRONTEND_URL}/events"
hookType = "event"
class Meta:
verbose_name = _("Event")
verbose_name_plural = _("Events")
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):
delete_str = _("Deleted: ") if self.deleted else ""
return _("{}Event: {}").format(delete_str, self.title)
class JobAd(BaseFeed):
"""Job advertisements shown on Corporate relations page"""
__previousVisible = False
webhookUrl = f"https://{FRONTEND_URL}/jobads"
hookType = "jobad"
def __init__(self, *args, **kwargs):
super(Event, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(Event, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/events/{self.pk}"
processHooks(
message=generateMessage(
"Uusi tapahtuma", self.title, self.description, url
),
eventType="event",
)
self.__previousVisible = self.visible
class Meta:
verbose_name = _("JobAd")
verbose_name_plural = _("JobAds")
class TemplateQuestion(models.Model):
@@ -131,15 +127,15 @@ class TemplateQuestion(models.Model):
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")
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
questions = JSONField()
deleted = models.BooleanField(default=False)
class Meta:
verbose_name = _("Template question")
verbose_name_plural = _("Template questions")
def __str__(self):
return _("Template questions: {}").format(self.name)
@@ -147,20 +143,20 @@ class TemplateQuestion(models.Model):
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")
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
deleted = models.BooleanField(default=False)
visible = models.BooleanField(default=True)
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)
class Meta:
verbose_name = _("Signup form")
verbose_name_plural = _("Signup forms")
def __str__(self):
delete_str = _("Deleted: ") if self.deleted else ""
@@ -181,12 +177,9 @@ class Signup(models.Model):
Actual signup into any SignupForm. Deletes are soft.
"""
class Meta:
verbose_name = _("Sign-up")
verbose_name_plural = _("Sign-ups")
id = models.AutoField(primary_key=True)
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
deleted = models.BooleanField(default=False)
time = models.DateTimeField(default=timezone.now)
answer = JSONField()
# Answer we use in signupForm signups field. Frontend uses first questions answer as this value.
@@ -195,7 +188,11 @@ class Signup(models.Model):
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)
signupForm = models.ForeignKey("SignupForm", on_delete=models.CASCADE)
class Meta:
verbose_name = _("Sign-up")
verbose_name_plural = _("Sign-ups")
def __str__(self):
delete_str = _("Deleted: ") if self.deleted else ""
@@ -222,49 +219,6 @@ def email_on_signup(sender, instance, created, **kwargs):
)
class JobAd(models.Model):
"""Job advertisements shown on Corporate relations page"""
class Meta:
verbose_name = _("JobAd")
verbose_name_plural = _("JobAds")
id = models.AutoField(primary_key=True)
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):
delete_str = _("Deleted: ") if self.deleted else ""
return f"{delete_str}{self.title}"
__previousVisible = False
def __init__(self, *args, **kwargs):
super(JobAd, self).__init__(*args, **kwargs)
self.__previousVisible = self.visible
def save(self, force_insert=False, force_update=False, *args, **kwargs):
created = self.pk is None
super(JobAd, self).save(force_insert, force_update, *args, **kwargs)
if self.visible and (created or not self.__previousVisible):
self.refresh_from_db() # Fetch so we can use primary key
url = f"https://{FRONTEND_URL}/jobads/{self.pk}"
processHooks(
message=generateMessage(
"Uusi työpaikkailmoitus", self.title, self.description, url
),
eventType="jobad",
)
self.__previousVisible = self.visible
def generateMessage(heading: str, title: str, description: str, url: str):
return render_to_string(
"webapp/tg_message.tpl",
@@ -347,8 +301,9 @@ class TelegramHook(BaseWebhook):
auditlog.register(Tag)
auditlog.register(Feed)
auditlog.register(Event)
auditlog.register(JobAd)
auditlog.register(TemplateQuestion)
auditlog.register(SignupForm)
auditlog.register(Signup)
auditlog.register(JobAd)
auditlog.register(GenericWebhook)
auditlog.register(TelegramHook)
+72 -64
View File
@@ -2,6 +2,14 @@ from rest_framework import serializers
from webapp.models import *
class SavedQuestionsSerializer(serializers.ModelSerializer):
questions = serializers.JSONField()
class Meta:
model = TemplateQuestion
fields = ("id", "name", "questions")
class SignupSerializer(serializers.ModelSerializer):
signupForm_id = serializers.PrimaryKeyRelatedField(
source="signupForm", queryset=SignupForm.objects.all()
@@ -68,11 +76,54 @@ class SignupFormSerializer(serializers.ModelSerializer):
)
class EventSerializer(serializers.ModelSerializer):
signupForm = SignupFormSerializer(
source="filtered_signup_forms",
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ("id", "slug", "name_fi", "name_en", "icon")
class FeedSerializer(serializers.ModelSerializer):
tagId = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
many=True,
read_only=True,
write_only=True,
)
class Meta:
model = Feed
fields = (
"id",
"title_fi",
"title_en",
"description_fi",
"description_en",
"content_fi",
"content_en",
"image",
"tags",
"tagId",
"isPublished",
"publishAt",
"autoUnpublish",
"unpublishAt",
)
read_only_fields = ["tags"]
depth = 1
def create(self, validated_data):
tags_data = validated_data.pop("tagId")
feed = Feed.objects.create(**validated_data)
for tag in tags_data:
feed.tags.add(tag)
feed.save()
return feed
class EventSerializer(serializers.ModelSerializer):
tagId = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
many=True,
write_only=True,
)
signup_id = serializers.PrimaryKeyRelatedField(
@@ -80,26 +131,30 @@ class EventSerializer(serializers.ModelSerializer):
many=True,
write_only=True,
)
tag_id = serializers.PrimaryKeyRelatedField(
queryset=Tag.objects.all(),
signupForm = SignupFormSerializer(
source="filtered_signup_forms",
many=True,
write_only=True,
read_only=True,
)
class Meta:
model = Event
fields = (
"id",
"tag_id",
"tags",
"visible",
"image",
"title_fi",
"title_en",
"description_fi",
"description_en",
"content_fi",
"content_en",
"image",
"tags",
"tagId",
"isPublished",
"publishAt",
"autoUnpublish",
"unpublishAt",
"start_time",
"end_time",
"location_fi",
@@ -112,7 +167,7 @@ class EventSerializer(serializers.ModelSerializer):
def create(self, validated_data):
signupForms = validated_data.pop("signup_id", [])
tags = validated_data.pop("tag_id")
tags = validated_data.pop("tagId")
event = Event.objects.create(**validated_data)
for form in signupForms:
event.signupForm.add(form)
@@ -123,7 +178,7 @@ class EventSerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
signupForms = validated_data.pop("signup_id", [])
tags = validated_data.pop("tag_id")
tags = validated_data.pop("tagId")
instance.signupForm.clear()
instance.tags.clear()
for form in signupForms:
@@ -134,54 +189,6 @@ class EventSerializer(serializers.ModelSerializer):
return instance
class SavedQuestionsSerializer(serializers.ModelSerializer):
questions = serializers.JSONField()
class Meta:
model = TemplateQuestion
fields = ("id", "name", "questions")
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ("id", "slug", "name_fi", "name_en", "icon")
class FeedSerializer(serializers.ModelSerializer):
tag_id = serializers.PrimaryKeyRelatedField(
many=True, source="tags", queryset=Tag.objects.all()
)
class Meta:
model = Feed
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):
tags_data = validated_data.pop("tags")
feed = Feed.objects.create(**validated_data)
for tag in tags_data:
feed.tags.add(tag)
feed.save()
return feed
class JobAdSerializer(serializers.ModelSerializer):
class Meta:
model = JobAd
@@ -193,7 +200,8 @@ class JobAdSerializer(serializers.ModelSerializer):
"description_en",
"content_fi",
"content_en",
"visible",
"autohide_at",
"autohide_enabled",
"isPublished",
"publishAt",
"autoUnpublish",
"unpublishAt",
)
+6 -6
View File
@@ -5,7 +5,7 @@ from webapp.utils import month_from_now
def createEventObject(
name="Testitapahtuma1",
visible=True,
isPublished=True,
start_time=timezone.now(),
end_time=month_from_now(),
tag_id=[],
@@ -14,7 +14,7 @@ def createEventObject(
return Event.objects.create(
title_fi=name,
title_en=f"title_en {name}",
visible=visible,
isPublished=isPublished,
description_fi=f"desc_fi {name}",
description_en=f"desc_en {name}",
content_fi=f"content_fi {name}",
@@ -27,15 +27,15 @@ def createEventObject(
def createEventJSON(
name="POST1",
visible=True,
isPublished=True,
start_time=timezone.now(),
end_time=month_from_now(),
tag_id=[],
tagId=[],
signup_id=[],
):
return {
"tag_id": tag_id,
"visible": visible,
"tagId": tagId,
"visible": isPublished,
"title_fi": f"title_fi {name}",
"title_en": f"title_en {name}",
"description_fi": f"desc_fi {name}",
+5 -5
View File
@@ -21,20 +21,20 @@ class EventTestCase(APITestCase):
# Invisible but relevant
createEventObject(
"Testitapahtuma2",
visible=False,
isPublished=False,
start_time=timezone.datetime(2018, 11, 9, 12, 0, 0),
)
# Visible but unrelevant
test2 = createEventObject(
"Testitapahtuma3",
visible=True,
isPublished=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,
isPublished=True,
start_time=timezone.datetime(2018, 12, 9, 12, 0, 0),
)
# Add some tags
@@ -122,7 +122,7 @@ class EventTestCase(APITestCase):
self.client.force_authenticate(user=self.authClient)
response = self.client.post(
URL,
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
createEventJSON(tagId=[self.testTagId], signup_id=[self.signupFormId]),
format="json",
)
@@ -132,7 +132,7 @@ class EventTestCase(APITestCase):
def test_post_event_unauth(self):
response = self.client.post(
URL,
createEventJSON(tag_id=[self.testTagId], signup_id=[self.signupFormId]),
createEventJSON(tagId=[self.testTagId], signup_id=[self.signupFormId]),
format="json",
)
+3 -3
View File
@@ -16,7 +16,7 @@ class FeedTestCase(APITestCase):
feed = Feed.objects.create(
title="TestFeed",
visible=True,
isPublished=True,
description="diidadaapa",
content="lorem ipsum",
)
@@ -51,10 +51,10 @@ class FeedTestCase(APITestCase):
tag2_id = tagBuilder("test2").id
data = {
"tag_id": [tag1_id, tag2_id],
"tagId": [tag1_id, tag2_id],
"title_fi": "testtitle",
"title_en": "testtitle",
"visible": "True",
"isPublished": "True",
"description_fi": "liirumlaarum",
"description_en": "liirumlaarum",
"content_fi": "lorem ipsum",
+3 -3
View File
@@ -13,7 +13,7 @@ class JobAdTestCase(APITestCase):
self.prefilled_jobad = JobAd.objects.create(
title_fi="ABB Test",
title_en="ABB Test",
visible=True,
isPublished=True,
description_fi="desc",
description_en="desc",
content_fi="lorem",
@@ -35,12 +35,12 @@ class JobAdTestCase(APITestCase):
data = {
"title_fi": "testtitle",
"title_en": "testtitle",
"visible": "True",
"isPublished": "True",
"description_fi": "liirumlaarum",
"description_en": "liirumlaarum",
"content_fi": "lorem ipsum",
"content_en": "lorem ipsum",
"autohide_enabled": "True",
"autoUnpublish": "True",
}
# Try post without authentication
+23 -23
View File
@@ -4,28 +4,37 @@ from modeltranslation.translator import register, TranslationOptions
from webapp.models import *
@register(BaseFeed)
class BaseFeedTranslationOptions(TranslationOptions):
fields = ("title", "description", "content")
@register(Feed)
class FeedTranslationOptions(TranslationOptions):
fields = ()
@register(Tag)
class TagTranslationOptions(TranslationOptions):
fields = ("name",)
@register(BaseFeed)
class BaseFeedTranslationOptions(TranslationOptions):
fields = (
"title",
"description",
"content",
)
@register(Feed)
class FeedTranslationOptions(TranslationOptions):
fields = ()
@register(Event)
class EventTranslationOptions(TranslationOptions):
fields = ("location",)
@register(Signup)
class SignupTranslationOptions(TranslationOptions):
@register(JobAd)
class JobAdTranslationOptions(TranslationOptions):
fields = ()
@register(TemplateQuestion)
class TemplateQuestionTranslationOptions(TranslationOptions):
fields = ()
@@ -34,20 +43,11 @@ class SignupFormTranslationOptions(TranslationOptions):
fields = ("title",)
@register(TemplateQuestion)
class TemplateQuestionTranslationOptions(TranslationOptions):
@register(Signup)
class SignupTranslationOptions(TranslationOptions):
fields = ()
@register(JobAd)
class JobAdTranslationOptions(TranslationOptions):
fields = (
"title",
"description",
"content",
)
@register(BaseWebhook)
class BaseWebhookOptions(TranslationOptions):
fields = ()
+101 -112
View File
@@ -9,7 +9,7 @@ from django.http import HttpResponse, JsonResponse
from django.shortcuts import 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.db.models import Prefetch, Q
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import routers
from rest_framework.response import Response
@@ -46,23 +46,58 @@ class RootView(routers.APIRootView):
permission_classes = [IsAuthenticatedOrReadOnly]
class TagsViewSet(ReadOnlyModelViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
class FeedViewSet(ModelViewSet):
queryset = Feed.objects.filter(deleted=False)
serializer_class = FeedSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
filter_fields = ("id", "tags", "isPublished")
search_fields = ("id", "tags", "isPublished")
def get_queryset(self):
# If admin page...
if self.request.user.is_authenticated:
# Return all objects expect those that are (soft) deleted
# Soft deleted objects can be edited (and completely deleted) via Django admin (for superadmins)
return Feed.objects.filter(deleted=False).order_by("-publishAt")
now = timezone.now()
# Hide deleted and unpublished objects...
query = Q(deleted=False, isPublished=True, publishAt__lte=now)
# and hide objects that are automatically unpublished
hideQuery = Q(autoUnpublish=False) | Q(unpublishAt__gt=now)
query.add(hideQuery, Q.AND)
return Feed.objects.filter(query).order_by("-publishAt")
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 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 = ("id", "tags", "visible", "signupForm")
search_fields = ("id", "tags", "visible", "signupForm")
filter_fields = ("id", "tags", "isPublished", "signupForm")
search_fields = ("id", "tags", "isPublished", "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"
):
# TODO: For create and update, this returns old data in signupForm field (prefetched at the start of request)...
if self.request.user.is_authenticated:
return Event.objects.filter(deleted=False).prefetch_related(
Prefetch(
"signupForm",
@@ -71,32 +106,26 @@ class EventViewSet(ModelViewSet):
)
)
now = timezone.now()
# Hide deleted and unpublished objects...
query = Q(deleted=False, isPublished=True, publishAt__lte=now)
# and hide objects that are automatically unpublished
hideQuery = Q(autoUnpublish=False) | Q(unpublishAt__gt=timezone.now())
query.add(hideQuery, Q.AND)
since = self.request.query_params.get("since", None)
if since:
return (
Event.objects.filter(deleted=False, visible=True, end_time__gt=since)
.order_by("start_time")
.prefetch_related(
query.add(Q(end_time__gt=since), Q.AND)
else:
query.add(Q(end_time__gt=now), Q.AND)
return Event.objects.filter(query).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",
)
)
)
def create(self, request, *args, **kwargs):
raw_image = request.data.get("image", None)
@@ -122,6 +151,48 @@ class EventViewSet(ModelViewSet):
return JsonResponse(status=404, data={"error": f"Event {pk} not found"})
class JobAdViewSet(ModelViewSet):
queryset = JobAd.objects.filter(deleted=False)
serializer_class = JobAdSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
if self.request.user.is_authenticated:
return JobAd.objects.filter(deleted=False).order_by("-publishAt")
now = timezone.now()
query = Q(deleted=False, isPublished=True, publishAt__lte=now)
hideQuery = Q(autoUnpublish=False) | Q(unpublishAt__gt=timezone.now())
query.add(hideQuery, Q.AND)
return JobAd.objects.filter(query).order_by("-publishAt")
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"})
class SavedQuestionsViewSet(ModelViewSet):
queryset = TemplateQuestion.objects.filter(deleted=False)
serializer_class = SavedQuestionsSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def destroy(self, request, pk=None, *args, **kwargs):
try:
question = self.get_object()
question.deleted = True
question.save()
return JsonResponse(status=200, data={"message": "OK"})
except ObjectDoesNotExist:
return JsonResponse(
status=404, data={"error": f"Template question {pk} not found"}
)
class SignupFormViewSet(ModelViewSet):
queryset = SignupForm.objects.filter(deleted=False)
ordering = ["start_time"]
@@ -264,99 +335,17 @@ class SignupViewSet(ModelViewSet):
return JsonResponse(status=404, data={"error": f"Signup {pk} not found"})
class SavedQuestionsViewSet(ModelViewSet):
queryset = TemplateQuestion.objects.filter(deleted=False)
serializer_class = SavedQuestionsSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def destroy(self, request, pk=None, *args, **kwargs):
try:
question = self.get_object()
question.deleted = True
question.save()
return JsonResponse(status=200, data={"message": "OK"})
except ObjectDoesNotExist:
return JsonResponse(
status=404, data={"error": f"Template question {pk} not found"}
)
class FeedViewSet(ModelViewSet):
queryset = Feed.objects.filter(deleted=False)
serializer_class = FeedSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
filter_backends = (filters.DjangoFilterBackend, SearchFilter, OrderingFilter)
filter_fields = ("id", "tags", "visible")
search_fields = ("id", "tags", "visible")
def get_queryset(self):
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:
if obj.autohide > timezone.now():
result_ids.append(obj.id)
else:
result_ids.append(obj.id)
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 TagsViewSet(ReadOnlyModelViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
class JobAdViewSet(ModelViewSet):
queryset = JobAd.objects.filter(deleted=False)
serializer_class = JobAdSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
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"])
def nginx_jwt_resp(request, *args, **kwargs):
accessKey = request.COOKIES.get("jwt_access", None)
if not accessKey:
return HttpResponse("", status=401)
return HttpResponse("No valid access token", status=401)
try:
# This also verifies the signature.
# See https://pyjwt.readthedocs.io/en/latest/usage.html#reading-the-claimset-without-validation
token = decode(accessKey, settings.SECRET_KEY, algorithms=["HS256"])
except InvalidTokenError:
return HttpResponse("", status=403)
return HttpResponse("Invalid access token", status=401)
user = "admin" if token.get("username", "") == "admin" else "moderator"
resp = HttpResponse("", status=200)
resp["X-FBrowser-User"] = user