Skip to content

Commit

Permalink
Feat(user)/user bio (#758)
Browse files Browse the repository at this point in the history
* Created model, serializer and view for user-bio

* Created user bio model and made migrations

* Created user bio serializer + viewsets + added new endpoint

* Tested create method + added bio serializer to user serializer

* Format

* Created update method and started testing

* Debugging test failures in user retrieve

* fixed model error

* Created user_bio_factory + started testing put method

* Created fixture for UserBio

* Created custom excpetion for duplicate user bio

* Added permissions and inherited from BaseModel

* Modularized serializer for bio

* Use correct serializers in viewset + added destroy method

* Finished testing bio viewset integration + format

* Changed environent file to .env to avoid pushing up keys

* Fix: Flipped assertion statement in test, since user bio should not be deleted

* skiped buggy test from kontres

* added mark to pytest.skip

* Moved keys to .env file and reverted docker variables

* Skip buggy kontres test

* format

* Added str method to user_bio

* Removed unused imports

* format

* Changed user relation to a OneToOne-field (same affect as ForeignKey(unique=True) + removed check for duplicate bio in serializer

* Migrations + changed assertion status code in duplicate bio test (could try catch in serializer to produce 400 status code)

* format

* format

* Changed limit for description 50 -> 500 + migrations

* Migrate

* added id to serializer

* merged leaf nodes in migrations

* format

---------

Co-authored-by: Ester2109 <126612066+Ester2109@users.noreply.github.com>
Co-authored-by: Mads Nylund <madsnyl@gmail.com>
Co-authored-by: Mads Nylund <73914541+MadsNyl@users.noreply.github.com>
Co-authored-by: Tam Le <tamle2107@hotmail.com>
  • Loading branch information
5 people committed Apr 16, 2024
1 parent 95ef58c commit bfa2299
Show file tree
Hide file tree
Showing 20 changed files with 457 additions and 7 deletions.
2 changes: 1 addition & 1 deletion .envs/.local
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ DATABASE_HOST=db
DATABASE_NAME=nettside-dev
DATABASE_PASSWORD=password
DATABASE_PORT=3306
DATABASE_USER=root
DATABASE_USER=root
1 change: 1 addition & 0 deletions app/content/factories/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
from app.content.factories.toddel_factory import ToddelFactory
from app.content.factories.priority_pool_factory import PriorityPoolFactory
from app.content.factories.qr_code_factory import QRCodeFactory
from app.content.factories.user_bio_factory import UserBioFactory
from app.content.factories.logentry_factory import LogEntryFactory
from app.content.factories.minute_factory import MinuteFactory
12 changes: 12 additions & 0 deletions app/content/factories/user_bio_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import factory
from factory.django import DjangoModelFactory

from app.content.factories.user_factory import UserFactory
from app.content.models.user_bio import UserBio


class UserBioFactory(DjangoModelFactory):
class Meta:
model = UserBio

user = factory.SubFactory(UserFactory)
43 changes: 43 additions & 0 deletions app/content/migrations/0059_userbio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 4.2.5 on 2024-01-29 17:51

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("content", "0058_merge_20231217_2155"),
]

operations = [
migrations.CreateModel(
name="UserBio",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("description", models.CharField(max_length=50)),
("gitHub_link", models.URLField(blank=True, max_length=300, null=True)),
(
"linkedIn_link",
models.URLField(blank=True, max_length=300, null=True),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="bio",
to=settings.AUTH_USER_MODEL,
),
),
],
),
]
18 changes: 18 additions & 0 deletions app/content/migrations/0060_alter_userbio_description.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-02-01 10:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("content", "0059_userbio"),
]

operations = [
migrations.AlterField(
model_name="userbio",
name="description",
field=models.CharField(blank=True, max_length=50, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.5 on 2024-02-19 16:09

from django.db import migrations, models
import django.utils.timezone


class Migration(migrations.Migration):

dependencies = [
("content", "0060_alter_userbio_description"),
]

operations = [
migrations.AddField(
model_name="userbio",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name="userbio",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
]
25 changes: 25 additions & 0 deletions app/content/migrations/0062_alter_userbio_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.5 on 2024-02-21 13:35

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("content", "0061_userbio_created_at_userbio_updated_at"),
]

operations = [
migrations.AlterField(
model_name="userbio",
name="user",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="bio",
to=settings.AUTH_USER_MODEL,
unique=True,
),
),
]
24 changes: 24 additions & 0 deletions app/content/migrations/0063_alter_userbio_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.5 on 2024-02-21 13:39

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
("content", "0062_alter_userbio_user"),
]

operations = [
migrations.AlterField(
model_name="userbio",
name="user",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="bio",
to=settings.AUTH_USER_MODEL,
),
),
]
18 changes: 18 additions & 0 deletions app/content/migrations/0064_alter_userbio_description.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-02-26 15:27

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("content", "0063_alter_userbio_user"),
]

operations = [
migrations.AlterField(
model_name="userbio",
name="description",
field=models.CharField(blank=True, max_length=500, null=True),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 4.2.5 on 2024-04-16 06:58

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("content", "0060_minute_tag"),
("content", "0064_alter_userbio_description"),
]

operations = []
45 changes: 45 additions & 0 deletions app/content/models/user_bio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from django.db import models

from app.common.enums import Groups
from app.common.permissions import BasePermissionModel, check_has_access
from app.content.models.user import User
from app.util.models import BaseModel


class UserBio(BaseModel, BasePermissionModel):
read_access = (Groups.TIHLDE,)
write_access = (Groups.TIHLDE,)

description = models.CharField(max_length=500, blank=True, null=True)

gitHub_link = models.URLField(max_length=300, blank=True, null=True)

linkedIn_link = models.URLField(max_length=300, blank=True, null=True)

user = models.OneToOneField(
User, on_delete=models.CASCADE, unique=True, related_name="bio"
)

def __str__(self):
bio_str = f"{self.user}"
if self.description:
bio_str += f" - {self.description}"
if self.gitHub_link:
bio_str += f" - {self.gitHub_link}"
if self.linkedIn_link:
bio_str += f" - {self.linkedIn_link}"
return bio_str

@classmethod
def has_update_permission(cls, request):
return check_has_access(cls.write_access, request)

@classmethod
def has_destroy_permission(cls, request):
return check_has_access(cls.write_access, request)

def has_object_update_permission(self, request):
return self.user == request.user

def has_object_destroy_permission(self, request):
return self.user == request.user
3 changes: 3 additions & 0 deletions app/content/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from app.common.enums import GroupType
from app.common.serializers import BaseModelSerializer
from app.content.models import User
from app.content.serializers.user_bio import UserBioSerializer
from app.group.models import Group, Membership


Expand Down Expand Up @@ -50,6 +51,7 @@ def get_studyyear(self, obj):
class UserSerializer(DefaultUserSerializer):
unread_notifications = serializers.SerializerMethodField()
unanswered_evaluations_count = serializers.SerializerMethodField()
bio = UserBioSerializer(read_only=True, required=False)

class Meta:
model = User
Expand All @@ -63,6 +65,7 @@ class Meta:
"slack_user_id",
"allows_photo_by_default",
"accepts_event_rules",
"bio",
)
read_only_fields = ("user_id",)

Expand Down
23 changes: 23 additions & 0 deletions app/content/serializers/user_bio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from app.common.serializers import BaseModelSerializer
from app.content.models.user_bio import UserBio


class UserBioSerializer(BaseModelSerializer):
class Meta:
model = UserBio
fields = ["id", "description", "gitHub_link", "linkedIn_link"]


class UserBioCreateSerializer(BaseModelSerializer):
class Meta:
model = UserBio
fields = ["description", "gitHub_link", "linkedIn_link"]

def create(self, validated_data):
return super().create(validated_data)


class UserBioUpdateSerializer(BaseModelSerializer):
class Meta:
model = UserBio
fields = ["description", "gitHub_link", "linkedIn_link"]
2 changes: 2 additions & 0 deletions app/content/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
ShortLinkViewSet,
StrikeViewSet,
ToddelViewSet,
UserBioViewset,
UserCalendarEvents,
UserViewSet,
accept_form,
Expand All @@ -30,6 +31,7 @@
router.register("short-links", ShortLinkViewSet, basename="short-link")
router.register("qr-codes", QRCodeViewSet, basename="qr-code")
router.register("users", UserViewSet, basename="user")
router.register("user-bios", UserBioViewset, basename="user-bio")
router.register(
r"events/(?P<event_id>\d+)/registrations",
RegistrationViewSet,
Expand Down
1 change: 1 addition & 0 deletions app/content/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
from app.content.views.strike import StrikeViewSet
from app.content.views.toddel import ToddelViewSet
from app.content.views.qr_code import QRCodeViewSet
from app.content.views.user_bio import UserBioViewset
from app.content.views.logentry import LogEntryViewSet
from app.content.views.minute import MinuteViewSet
20 changes: 14 additions & 6 deletions app/content/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,23 @@ def get_serializer_class(self):
return super().get_serializer_class()

def retrieve(self, request, pk, *args, **kwargs):
user = self._get_user(request, pk)

self.check_object_permissions(self.request, user)
try:
user = self._get_user(request, pk)

serializer = DefaultUserSerializer(user)
if is_admin_user(self.request) or user == request.user:
serializer = UserSerializer(user, context={"request": self.request})
self.check_object_permissions(self.request, user)

return Response(serializer.data, status=status.HTTP_200_OK)
serializer = DefaultUserSerializer(user)

if is_admin_user(self.request) or user == request.user:
serializer = UserSerializer(user, context={"request": self.request})

return Response(serializer.data, status=status.HTTP_200_OK)

except Exception:
return Response(
{"message": "Error"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
)

def create(self, request, *args, **kwargs):
serializer = UserCreateSerializer(data=self.request.data)
Expand Down
Loading

0 comments on commit bfa2299

Please sign in to comment.