Skip to content

Commit

Permalink
Implement lending system
Browse files Browse the repository at this point in the history
  • Loading branch information
Bestem0r authored and jonasdeluna committed May 24, 2024
1 parent 75848dc commit afc32a3
Show file tree
Hide file tree
Showing 58 changed files with 997 additions and 296 deletions.
12 changes: 6 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
master_doc = "index"

# General information about the project.
project = u"LEGO"
copyright = u"2014 - 2020, Abakus Webkom"
project = "LEGO"
copyright = "2014 - 2020, Abakus Webkom"

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -197,7 +197,7 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
("index", "LEGO.tex", u"LEGO Documentation", u"Abakus Webkom", "manual")
("index", "LEGO.tex", "LEGO Documentation", "Abakus Webkom", "manual")
]

# The name of an image file (relative to this directory) to place at the top of
Expand All @@ -224,7 +224,7 @@

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [("index", "lego", u"LEGO Documentation", [u"Abakus Webkom"], 1)]
man_pages = [("index", "lego", "LEGO Documentation", ["Abakus Webkom"], 1)]

# If true, show URL addresses after external links.
# man_show_urls = False
Expand All @@ -238,8 +238,8 @@
(
"index",
"LEGO",
u"LEGO Documentation",
u"Abakus Webkom",
"LEGO Documentation",
"Abakus Webkom",
"LEGO",
"One line description of project.",
"Miscellaneous",
Expand Down
5 changes: 3 additions & 2 deletions lego/api/v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
from lego.apps.gallery.views import GalleryPictureViewSet, GalleryViewSet
from lego.apps.ical.viewsets import ICalTokenViewset, ICalViewset
from lego.apps.joblistings.views import JoblistingViewSet
from lego.apps.lending.urls import urlpatterns as lending_urls
from lego.apps.lending.views import LendableObjectViewSet, LendingInstanceViewSet
from lego.apps.meetings.views import (
MeetingInvitationTokenViewSet,
MeetingInvitationViewSet,
Expand Down Expand Up @@ -78,8 +80,6 @@
from lego.apps.users.views.registration import UserRegistrationRequestViewSet
from lego.apps.users.views.user_delete import UserDeleteViewSet
from lego.apps.users.views.users import UsersViewSet
from lego.apps.lending.views import LendableObjectViewSet, LendingInstanceViewSet

from lego.utils.views import SiteMetaViewSet

router = routers.DefaultRouter()
Expand Down Expand Up @@ -215,4 +215,5 @@
urlpatterns = [
path("", include(router.urls)),
path("forums/", include((forums_urls, "forums"))),
path("", include((lending_urls, "lending"))),
]
28 changes: 27 additions & 1 deletion lego/apps/comments/models.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models

from lego.apps.comments.permissions import CommentPermissionHandler
from lego.apps.content.fields import ContentField
from lego.apps.reactions.models import Reaction
from lego.utils.managers import BasisModelManager
from lego.utils.models import BasisModel

Expand All @@ -19,6 +20,7 @@ class Comment(BasisModel):
object_id = models.PositiveIntegerField(db_index=True)
content_object = GenericForeignKey()
parent = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)
reactions = GenericRelation(Reaction)

objects = CommentManager() # type: ignore

Expand All @@ -34,5 +36,29 @@ def delete(self):
else:
super(Comment, self).delete(force=True)

def get_reactions_grouped(self, user):
grouped = {}
for reaction in self.reactions.all():
if reaction.emoji.pk not in grouped:
grouped[reaction.emoji.pk] = {
"emoji": reaction.emoji.pk,
"unicode_string": reaction.emoji.unicode_string,
"count": 0,
"has_reacted": False,
"reaction_id": None,
}

grouped[reaction.emoji.pk]["count"] += 1

if reaction.created_by == user:
grouped[reaction.emoji.pk]["has_reacted"] = True
grouped[reaction.emoji.pk]["reaction_id"] = reaction.id

return sorted(grouped.values(), key=lambda kv: kv["count"], reverse=True)

def __str__(self):
return self.text

@property
def content_target_self(self):
return f"{self._meta.app_label}.{self._meta.model_name}-{self.pk}"
11 changes: 10 additions & 1 deletion lego/apps/comments/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework.fields import DateTimeField
from rest_framework.fields import CharField, DateTimeField

from lego.apps.comments.models import Comment
from lego.apps.content.fields import ContentSerializerField
Expand All @@ -14,6 +15,8 @@ class CommentSerializer(BasisModelSerializer):
updated_at = DateTimeField(read_only=True)
content_target = GenericRelationField(source="content_object")
text = ContentSerializerField()
reactions_grouped = serializers.SerializerMethodField()
content_target_self = CharField(read_only=True)

class Meta:
model = Comment
Expand All @@ -22,11 +25,17 @@ class Meta:
"text",
"author",
"content_target",
"content_target_self",
"created_at",
"updated_at",
"parent",
"reactions_grouped",
)

def get_reactions_grouped(self, obj):
user = self.context["request"].user
return obj.get_reactions_grouped(user)

def validate(self, attrs):
content_target = attrs.get("content_object")

Expand Down
56 changes: 56 additions & 0 deletions lego/apps/comments/tests/test_reactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from lego.apps.articles.models import Article
from lego.apps.comments.models import Comment
from lego.apps.emojis.models import Emoji
from lego.apps.reactions.models import Reaction
from lego.apps.users.models import User
from lego.utils.test_utils import BaseTestCase


class CommentReactionsTestCase(BaseTestCase):
fixtures = [
"test_abakus_groups.yaml",
"test_users.yaml",
"test_emojis.yaml",
"test_articles.yaml",
]

def setUp(self):
self.comment = Comment.objects.create(
created_by_id=0,
text="first comment",
content_object=Article.objects.all().first(),
)
self.user = User.objects.all().first()
self.comment.created_by = self.user
self.comment.save()

self.emoji = Emoji.objects.first()

def test_add_reaction(self):
test_reaction = Reaction.objects.create(
content_object=self.comment, emoji=self.emoji
)
test_reaction.created_by = self.user
test_reaction.save()

reactions_grouped = self.comment.get_reactions_grouped(self.user)
self.assertEqual(len(reactions_grouped), 1)
self.assertEqual(reactions_grouped[0]["count"], 1)
self.assertEqual(reactions_grouped[0]["has_reacted"], True)
self.assertEqual(
reactions_grouped[0]["unicode_string"], self.emoji.unicode_string
)

def test_remove_reaction(self):
test_reaction = Reaction.objects.create(
content_object=self.comment, emoji=self.emoji
)

reactions_grouped = self.comment.get_reactions_grouped(self.user)
self.assertEqual(len(reactions_grouped), 1)
self.assertEqual(reactions_grouped[0]["count"], 1)

test_reaction.delete()

reactions_grouped = self.comment.get_reactions_grouped(self.user)
self.assertEqual(len(reactions_grouped), 0)
25 changes: 17 additions & 8 deletions lego/apps/email/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
from django_filters.rest_framework import BooleanFilter, CharFilter, FilterSet

from lego.apps.email.models import EmailList
from lego.apps.users.constants import GROUP_COMMITTEE, GROUP_GRADE
from lego.apps.users.constants import (
GROUP_BOARD,
GROUP_COMMITTEE,
GROUP_GRADE,
GROUP_ORDAINED,
GROUP_SUB,
)
from lego.apps.users.filters import CharInFilter
from lego.apps.users.models import User


Expand All @@ -21,8 +28,8 @@ class EmailUserFilterSet(FilterSet):
email = CharFilter(field_name="internal_email__email", lookup_expr="icontains")
userUsername = CharFilter(field_name="username", lookup_expr="icontains")
userFullname = CharFilter(field_name="userFullname", method="fullname")
userCommittee = CharFilter(field_name="userCommitee", method="commitee")
userGrade = CharFilter(field_name="abakus_groups", method="grade")
userGroups = CharInFilter(field_name="userGroups", method="groups")
enabled = BooleanFilter(field_name="internal_email_enabled")

def grade(self, queryset, name, value):
Expand All @@ -34,13 +41,15 @@ def grade(self, queryset, name, value):
)
return queryset

def commitee(self, queryset, name, value):
if value == "-":
return queryset.exclude(abakus_groups__type=GROUP_COMMITTEE)
def groups(self, queryset, name, value):
relevant_types = [GROUP_COMMITTEE, GROUP_BOARD, GROUP_ORDAINED, GROUP_SUB]

if value == ["-"]:
return queryset.exclude(abakus_groups__type__in=relevant_types)
if value:
return queryset.filter(
abakus_groups__name__icontains=value,
abakus_groups__type=GROUP_COMMITTEE,
abakus_groups__name__in=value,
abakus_groups__type__in=relevant_types,
)
return queryset

Expand All @@ -59,5 +68,5 @@ class Meta:
"userUsername",
"userFullname",
"userGrade",
"userCommittee",
"userGroups",
)
31 changes: 30 additions & 1 deletion lego/apps/email/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any

from django.core.exceptions import ValidationError
from rest_framework import exceptions, serializers

Expand All @@ -7,7 +9,7 @@
PublicUserListField,
PublicUserWithGroupsField,
)
from lego.apps.users.models import User
from lego.apps.users.models import Membership, User

from .fields import EmailAddressField

Expand Down Expand Up @@ -44,6 +46,33 @@ class Meta:
"additional_emails",
)

def validate(self, attrs: Any) -> Any:
# Use existing values where missing to support patch requests
get = (
lambda index: attrs[index]
if index in attrs
else getattr(self.instance, index)
)
length = (
lambda value: len(value) if hasattr(value, "__len__") else value.count()
)
# Require at least one receiver of the email list
users_len, emails_len = length(get("users")), length(get("additional_emails"))
if users_len == 0 and emails_len == 0:
groups = (
attrs["groups"] if "groups" in attrs else self.instance.groups.all()
)
group_roles = get("group_roles")
memberships = Membership.objects.filter(abakus_group__in=groups)

if length(group_roles) > 0:
memberships = memberships.filter(role__in=group_roles)

if not memberships.exists():
raise ValidationError("Cannot create a mail list without receivers")

return super().validate(attrs)


class EmailListDetailSerializer(EmailListSerializer):
users = PublicUserListField({"read_only": True})
Expand Down
Loading

0 comments on commit afc32a3

Please sign in to comment.