From bf0863d910b68cd27d26b4b2a1e9b2e8cf9979d5 Mon Sep 17 00:00:00 2001 From: Will MacLean Date: Thu, 30 Jan 2025 15:29:27 -0600 Subject: [PATCH 1/6] view and model update --- api/models/friendship.py | 19 +++++++++++++++++ api/views/friendship.py | 46 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 api/views/friendship.py diff --git a/api/models/friendship.py b/api/models/friendship.py index 0bd9bf47..e70d3f2b 100644 --- a/api/models/friendship.py +++ b/api/models/friendship.py @@ -27,6 +27,21 @@ def save(self, *args, **kwargs): if self.requesting == self.requested: raise ValidationError("User cannot send friendship requests to themselves.") super().save(*args, **kwargs) + + def accept(self): + accepted_status = StatusTypes.objects.get(status='ACCEPTED') # pylint: disable=no-member + self.status = accepted_status + self.save() + + def reject(self): + rejected_status = StatusTypes.objects.get(status='REJECTED') # pylint: disable=no-member + self.status = rejected_status + self.save() + + def terminate(self): + terminated_status = StatusTypes.objects.get(status='TERMINATED') # pylint: disable=no-member + self.status = terminated_status + self.save() class Meta: constraints = [ @@ -38,4 +53,8 @@ class Meta: fields=['requested_id', 'requesting_id'], name='unique_reverse_friendship' ) + ] + indexes = [ + models.Index(fields=['status'], name='friendship_status_idx'), + models.Index(fields=['requesting', 'requested'], name='friendship_users_idx') ] \ No newline at end of file diff --git a/api/views/friendship.py b/api/views/friendship.py new file mode 100644 index 00000000..9be0aedb --- /dev/null +++ b/api/views/friendship.py @@ -0,0 +1,46 @@ +from django.http import HttpResponseServerError +import datetime +from rest_framework.viewsets import ViewSet +from rest_framework.response import Response +from rest_framework import serializers +from rest_framework import status +from api.models import Friendship, statustypes + +class FriendshipSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Friendship + url = serializers.HyperlinkedIdentityField( + view_name='friendship_detail', lookup_field='id' + ) + fields= ('requesting', 'requested', 'created_at', 'updated_at', 'status') + depth = 1 + +class Friendships(ViewSet): + def retrieve(self, request, pk=None): + try: + friendship = Friendship.objects.get(pk=pk) # pylint: disable=no-member + serializer = FriendshipSerializer(friendship, context={"request": request}) + return Response(serializer.data) + except Exception as ex: + return HttpResponseServerError(ex) + def list(self, request): + try: + friendship = Friendship.objects.all() # pylint: disable=no-member + serializer = FriendshipSerializer(friendship, many=True, context={"request": request}) + return Response(serializer.data) + except Exception as ex: + return HttpResponseServerError(ex) + def create(self, request): + new_friendship= Friendship() + new_friendship.requesting = request.auth.user + new_friendship.requested = request.data["requested"] + new_friendship.created_at = datetime.datetime.now() + new_friendship.updated_at = None + new_friendship.status = 'PENDING' + new_friendship.save() + + serializer = FriendshipSerializer( + new_friendship, context={"request": request} + ) + + return Response(serializer.data, status=status.HTTP_201_CREATED) \ No newline at end of file From bf4abc13c18e4e3c058ab928a8e860f363acc470 Mon Sep 17 00:00:00 2001 From: Will MacLean Date: Wed, 5 Feb 2025 20:53:35 -0600 Subject: [PATCH 2/6] poetry installed with toml file --- api/models/__init__.py | 3 +- api/views/__init__.py | 4 +- api/views/friendship.py | 30 ++--- poetry.lock | 254 ++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 46 ++++++++ requirements.txt | 6 - 6 files changed, 317 insertions(+), 26 deletions(-) create mode 100644 poetry.lock create mode 100644 pyproject.toml delete mode 100644 requirements.txt diff --git a/api/models/__init__.py b/api/models/__init__.py index a870ecb3..354f5809 100644 --- a/api/models/__init__.py +++ b/api/models/__init__.py @@ -3,4 +3,5 @@ from .notification import Notification from .friendship import Friendship from .like import Like -from .profile import Profile \ No newline at end of file +from .profile import Profile +from .statustypes import StatusTypes \ No newline at end of file diff --git a/api/views/__init__.py b/api/views/__init__.py index 80fe0aea..ea49e2e7 100644 --- a/api/views/__init__.py +++ b/api/views/__init__.py @@ -1,5 +1,5 @@ -## ///////////START CODE ADDED BY EDWIN MOZ ## from .profile import Profiles from .user import Users from .test import test_view -## END CODE ADDED BY EDWIN MOZ/////////// ## \ No newline at end of file +from .friendship import Friendships + diff --git a/api/views/friendship.py b/api/views/friendship.py index 9be0aedb..79fb5bad 100644 --- a/api/views/friendship.py +++ b/api/views/friendship.py @@ -1,21 +1,31 @@ from django.http import HttpResponseServerError +from django.db.models import Q import datetime from rest_framework.viewsets import ViewSet from rest_framework.response import Response from rest_framework import serializers from rest_framework import status -from api.models import Friendship, statustypes +from api.models import Friendship, StatusTypes -class FriendshipSerializer(serializers.HyperlinkedModelSerializer): +class FriendshipSerializer(serializers.ModelSerializer): class Meta: model = Friendship url = serializers.HyperlinkedIdentityField( view_name='friendship_detail', lookup_field='id' ) - fields= ('requesting', 'requested', 'created_at', 'updated_at', 'status') + fields = ('id', 'requesting', 'requested', 'created_at', 'updated_at', 'status') + read_only_fields = ('requesting', 'created_at', 'updated_at') depth = 1 class Friendships(ViewSet): + def get_queryset(self): + """ + Filter friendships based on the authenticated user + """ + user = self.request.user + return Friendship.objects.filter( # pylint: disable=no-member + Q(requesting=user) | Q(requested=user) + ) def retrieve(self, request, pk=None): try: friendship = Friendship.objects.get(pk=pk) # pylint: disable=no-member @@ -30,17 +40,3 @@ def list(self, request): return Response(serializer.data) except Exception as ex: return HttpResponseServerError(ex) - def create(self, request): - new_friendship= Friendship() - new_friendship.requesting = request.auth.user - new_friendship.requested = request.data["requested"] - new_friendship.created_at = datetime.datetime.now() - new_friendship.updated_at = None - new_friendship.status = 'PENDING' - new_friendship.save() - - serializer = FriendshipSerializer( - new_friendship, context={"request": request} - ) - - return Response(serializer.data, status=status.HTTP_201_CREATED) \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..3e62cda1 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,254 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. + +[[package]] +name = "asgiref" +version = "3.8.1" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.8" +files = [ + {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, + {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, +] + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "django" +version = "5.1.6" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = ">=3.10" +files = [ + {file = "Django-5.1.6-py3-none-any.whl", hash = "sha256:8d203400bc2952fbfb287c2bbda630297d654920c72a73cc82a9ad7926feaad5"}, + {file = "Django-5.1.6.tar.gz", hash = "sha256:1e39eafdd1b185e761d9fab7a9f0b9fa00af1b37b25ad980a8aa0dac13535690"}, +] + +[package.dependencies] +asgiref = ">=3.8.1,<4" +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "djangorestframework" +version = "3.15.2" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.8" +files = [ + {file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"}, + {file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"}, +] + +[package.dependencies] +django = ">=4.2" + +[[package]] +name = "pillow" +version = "10.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"}, + {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"}, + {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"}, + {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"}, + {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"}, + {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"}, + {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"}, + {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"}, + {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"}, + {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"}, + {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"}, + {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"}, + {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"}, + {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"}, + {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"}, + {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"}, + {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"}, + {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"}, + {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"}, + {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"}, + {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"}, + {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"}, + {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"}, + {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"}, + {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"}, + {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"}, + {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"}, + {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"}, + {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"}, + {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"}, + {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"}, + {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"}, + {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"}, + {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"}, + {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"}, + {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"}, + {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"}, + {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"}, + {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "psycopg2-binary" +version = "2.9.10" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.8" +files = [ + {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-win32.whl", hash = "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b"}, + {file = "psycopg2_binary-2.9.10-cp310-cp310-win_amd64.whl", hash = "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-win32.whl", hash = "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392"}, + {file = "psycopg2_binary-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64"}, + {file = "psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567"}, + {file = "psycopg2_binary-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863"}, + {file = "psycopg2_binary-2.9.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-win32.whl", hash = "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8"}, + {file = "psycopg2_binary-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5"}, +] + +[[package]] +name = "sqlparse" +version = "0.5.3" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"}, + {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"}, +] + +[package.extras] +dev = ["build", "hatch"] +doc = ["sphinx"] + +[[package]] +name = "tzdata" +version = "2025.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, + {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.12,<4.0" +content-hash = "27f8165e265ab038008663d6b68b0b74ad52e4e91e5aef662446c1041675a1a6" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6bb6ef66 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +# [tool.poetry] +# name = "teamuno-python" +# version = "0.1.0" +# description = "" +# authors = ["Your Name "] +# readme = "README.md" + + + + +# [build-system] +# requires = ["poetry-core"] +# build-backend = "poetry.core.masonry.api" + +# [project] +# name = "teamuno_python" +# requires-python = ">=3.12" +# dependencies = [ +# "asgiref==3.*", +# "Django==5.*", +# "djangorestframework==3.*", +# "psycopg2==2.*", +# "sqlparse==0.*", +# "Pillow==11.*" +# ] + +[tool.poetry] +name = "teamuno_python" +version = "0.1.0" +description = "Your project description" +authors = ["Your Name "] +license = "MIT" + +[tool.poetry.dependencies] +python = ">=3.12,<4.0" +asgiref = ">=3.0,<4.0" +Django = ">=5.0,<6.0" +djangorestframework = ">=3.0,<4.0" +psycopg2-binary = ">=2.9,<3.0" # or use psycopg2 if compiling from source +sqlparse = ">=0.4.0,<1.0" +Pillow = ">=10.0,<11.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index a1969ceb..00000000 --- a/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -asgiref==3.8.1 -Django==5.1.4 -djangorestframework==3.15.2 -psycopg2==2.9.10 -sqlparse==0.5.3 -Pillow==11.1.0 \ No newline at end of file From 6432b617426ce41300ebbfa18f73eec75be86824 Mon Sep 17 00:00:00 2001 From: Will MacLean Date: Wed, 12 Feb 2025 17:19:09 -0600 Subject: [PATCH 3/6] working on it --- api/views/friendship.py | 55 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/api/views/friendship.py b/api/views/friendship.py index 79fb5bad..b06cfdf0 100644 --- a/api/views/friendship.py +++ b/api/views/friendship.py @@ -40,3 +40,58 @@ def list(self, request): return Response(serializer.data) except Exception as ex: return HttpResponseServerError(ex) + def create(self, request): + try: + # Get or create pending status + pending_status = StatusTypes.objects.get(status='PENDING') # pylint: disable=no-member + + friendship = Friendship( + requesting=request.user, + requested_id=request.data.get('requested'), + status=pending_status + ) + + # This will trigger the model's save validation + friendship.save() + + serializer = self.get_serializer(friendship) + return Response(serializer.data, status=status.HTTP_201_CREATED) + + except IntegrityError: + return Response( + {"detail": "Friendship request already exists"}, + status=status.HTTP_400_BAD_REQUEST + ) + except ValidationError as e: + return Response( + {"detail": str(e)}, + status=status.HTTP_400_BAD_REQUEST + ) + except Exception as e: + return Response( + {"detail": "Failed to create friendship request"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) + def update(self, request, pk=None): + friendship = get_object_or_404(Friendship, pk=pk) + + # Only the requested user can update the friendship status + if request.user != friendship.requested: + return Response( + {"detail": "Only the requested user can update friendship status"}, + status=status.HTTP_403_FORBIDDEN + ) + + try: + new_status = StatusTypes.objects.get(status=request.data.get('status', '').upper()) + friendship.status = new_status + friendship.save() + + serializer = self.get_serializer(friendship) + return Response(serializer.data) + + except StatusTypes.DoesNotExist: + return Response( + {"detail": "Invalid status"}, + status=status.HTTP_400_BAD_REQUEST + ) \ No newline at end of file From eedaf691bf380a97a3d932c3b1ec23d6b8a7dfdc Mon Sep 17 00:00:00 2001 From: Will MacLean Date: Sun, 23 Feb 2025 21:42:52 -0600 Subject: [PATCH 4/6] testing --- api/views/friendship.py | 147 ++++++++++++++++++++-------- codefire_python_backend/settings.py | 24 +++-- 2 files changed, 123 insertions(+), 48 deletions(-) diff --git a/api/views/friendship.py b/api/views/friendship.py index b06cfdf0..830db961 100644 --- a/api/views/friendship.py +++ b/api/views/friendship.py @@ -1,61 +1,72 @@ -from django.http import HttpResponseServerError -from django.db.models import Q -import datetime -from rest_framework.viewsets import ViewSet +from django.db.models import Q +from django.shortcuts import get_object_or_404 +from rest_framework import viewsets, status from rest_framework.response import Response -from rest_framework import serializers -from rest_framework import status +from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError +from django.db import IntegrityError from api.models import Friendship, StatusTypes +from rest_framework import serializers class FriendshipSerializer(serializers.ModelSerializer): class Meta: model = Friendship - url = serializers.HyperlinkedIdentityField( - view_name='friendship_detail', lookup_field='id' - ) fields = ('id', 'requesting', 'requested', 'created_at', 'updated_at', 'status') read_only_fields = ('requesting', 'created_at', 'updated_at') - depth = 1 -class Friendships(ViewSet): + def to_representation(self, instance): + representation = super().to_representation(instance) + # Add additional user details + representation['requesting'] = { + 'id': instance.requesting.id, + 'username': instance.requesting.username, + } + representation['requested'] = { + 'id': instance.requested.id, + 'username': instance.requested.username, + } + return representation + +class Friendships(viewsets.ModelViewSet): + queryset = Friendship.objects.all() # pylint: disable=no-member + serializer_class = FriendshipSerializer + def get_queryset(self): """ Filter friendships based on the authenticated user """ user = self.request.user - return Friendship.objects.filter( # pylint: disable=no-member + + # Filter by status if provided in query params + status_filter = self.request.query_params.get('status', None) + queryset = Friendship.objects.filter( # pylint: disable=no-member Q(requesting=user) | Q(requested=user) - ) - def retrieve(self, request, pk=None): - try: - friendship = Friendship.objects.get(pk=pk) # pylint: disable=no-member - serializer = FriendshipSerializer(friendship, context={"request": request}) - return Response(serializer.data) - except Exception as ex: - return HttpResponseServerError(ex) - def list(self, request): - try: - friendship = Friendship.objects.all() # pylint: disable=no-member - serializer = FriendshipSerializer(friendship, many=True, context={"request": request}) - return Response(serializer.data) - except Exception as ex: - return HttpResponseServerError(ex) + ) + + if status_filter: + queryset = queryset.filter(status__status=status_filter.upper()) + + return queryset + def create(self, request): try: - # Get or create pending status - pending_status = StatusTypes.objects.get(status='PENDING') # pylint: disable=no-member + # Get pending status + pending_status = StatusTypes.objects.get(status='PENDING') # pylint: disable=no-member + + # Validate the input data + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) - friendship = Friendship( + # Save with the requesting user and status + friendship = serializer.save( requesting=request.user, - requested_id=request.data.get('requested'), status=pending_status ) - # This will trigger the model's save validation - friendship.save() - - serializer = self.get_serializer(friendship) - return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response( + self.get_serializer(friendship).data, + status=status.HTTP_201_CREATED + ) except IntegrityError: return Response( @@ -67,13 +78,19 @@ def create(self, request): {"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST ) + except StatusTypes.DoesNotExist: # pylint: disable=no-member + return Response( + {"detail": "Pending status type not found"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR + ) except Exception as e: return Response( - {"detail": "Failed to create friendship request"}, + {"detail": f"Failed to create friendship request: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) + def update(self, request, pk=None): - friendship = get_object_or_404(Friendship, pk=pk) + friendship = self.get_object() # Only the requested user can update the friendship status if request.user != friendship.requested: @@ -83,15 +100,63 @@ def update(self, request, pk=None): ) try: - new_status = StatusTypes.objects.get(status=request.data.get('status', '').upper()) + new_status = StatusTypes.objects.get(status=request.data.get('status', '').upper()) # pylint: disable=no-member friendship.status = new_status friendship.save() serializer = self.get_serializer(friendship) return Response(serializer.data) - except StatusTypes.DoesNotExist: + except StatusTypes.DoesNotExist: # pylint: disable=no-member return Response( {"detail": "Invalid status"}, status=status.HTTP_400_BAD_REQUEST - ) \ No newline at end of file + ) + + @action(detail=True, methods=['post']) + def accept(self, request, pk=None): + """Accept a friend request""" + friendship = self.get_object() + if request.user != friendship.requested: + return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) + + try: + accepted_status = StatusTypes.objects.get(status='ACCEPTED') # pylint: disable=no-member + friendship.status = accepted_status + friendship.save() + serializer = self.get_serializer(friendship) + return Response(serializer.data) + except Exception as e: + return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) + + @action(detail=True, methods=['post']) + def reject(self, request, pk=None): + """Reject a friend request""" + friendship = self.get_object() + if request.user != friendship.requested: + return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) + + try: + rejected_status = StatusTypes.objects.get(status='REJECTED') # pylint: disable=no-member + friendship.status = rejected_status + friendship.save() + serializer = self.get_serializer(friendship) + return Response(serializer.data) + except Exception as e: + return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) + + @action(detail=True, methods=['post']) + def terminate(self, request, pk=None): + """Terminate an existing friendship""" + friendship = self.get_object() + if request.user not in [friendship.requesting, friendship.requested]: + return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) + + try: + terminated_status = StatusTypes.objects.get(status='TERMINATED') # pylint: disable=no-member + friendship.status = terminated_status + friendship.save() + serializer = self.get_serializer(friendship) + return Response(serializer.data) + except Exception as e: + return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/codefire_python_backend/settings.py b/codefire_python_backend/settings.py index b41c0ec1..227e2327 100644 --- a/codefire_python_backend/settings.py +++ b/codefire_python_backend/settings.py @@ -9,10 +9,20 @@ For the full list of settings and their values, see https://docs.djangoproject.com/en/5.1/ref/settings/ """ - +import environ +import os from pathlib import Path from decouple import config +BASE_DIR = Path(__file__).resolve().parent.parent + +# Initialize environment variables +env = environ.Env() + +# Read the .env.local file +environ.Env.read_env(os.path.join(BASE_DIR, ".env.local")) + + # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -78,12 +88,12 @@ DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': config('DATABASE_NAME', default=''), - 'USER': config('DATABASE_USER', default=''), - 'PASSWORD': config('DATABASE_PASSWORD', default=''), - 'HOST': config('DATABASE_HOST', default='localhost'), - 'PORT': config('DATABASE_PORT', default='5432'), + 'ENGINE': env("DB_ENGINE", default="django.db.backends.postgresql"), + 'NAME': env("DB_NAME"), + 'USER': env("DB_USER"), + 'PASSWORD': env("DB_PASSWORD"), + 'HOST': env("DB_HOST", default="localhost"), + 'PORT': env("DB_PORT", default="5432"), } } # database connection populated with .env From 48df740733e406ffecf2fc82936429b7e23c72a7 Mon Sep 17 00:00:00 2001 From: Keaton Law Date: Fri, 28 Feb 2025 20:57:33 -0600 Subject: [PATCH 5/6] tested endpoints, added status to readonly so its automatically set --- api/urls.py | 2 ++ api/views/friendship.py | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/api/urls.py b/api/urls.py index 7c5c1542..1979a8f1 100644 --- a/api/urls.py +++ b/api/urls.py @@ -9,6 +9,7 @@ from api import views from api.views import PostViewSet, Profiles, Users, test_view #Edwin Moz added Post import from .views.auth import AuthViewSet +from .views.friendship import Friendships router = routers.DefaultRouter(trailing_slash=False) @@ -18,6 +19,7 @@ ## ///////////END CODE ADDED BY EDWIN MOZ ## router.register(r"profiles", Profiles, "profile") router.register(r'auth', AuthViewSet, basename='auth') +router.register(r'friendships', Friendships, basename='friendship') urlpatterns = [ path('test/', views.test_view), diff --git a/api/views/friendship.py b/api/views/friendship.py index 830db961..719a9734 100644 --- a/api/views/friendship.py +++ b/api/views/friendship.py @@ -12,7 +12,7 @@ class FriendshipSerializer(serializers.ModelSerializer): class Meta: model = Friendship fields = ('id', 'requesting', 'requested', 'created_at', 'updated_at', 'status') - read_only_fields = ('requesting', 'created_at', 'updated_at') + read_only_fields = ('requesting', 'created_at', 'updated_at', 'status') def to_representation(self, instance): representation = super().to_representation(instance) @@ -136,6 +136,9 @@ def reject(self, request, pk=None): if request.user != friendship.requested: return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) + if friendship.status.status != "PENDING": + return Response({"detail": "This request is not pending."}, status=status.HTTP_400_BAD_REQUEST) + try: rejected_status = StatusTypes.objects.get(status='REJECTED') # pylint: disable=no-member friendship.status = rejected_status @@ -152,6 +155,9 @@ def terminate(self, request, pk=None): if request.user not in [friendship.requesting, friendship.requested]: return Response({"detail": "Not authorized"}, status=status.HTTP_403_FORBIDDEN) + if friendship.status.status != "ACCEPTED": + return Response({"detail": "Only accepted friendships can be terminated."}, status=status.HTTP_400_BAD_REQUEST) + try: terminated_status = StatusTypes.objects.get(status='TERMINATED') # pylint: disable=no-member friendship.status = terminated_status From 45524a9c045f44737675be7689c0f8dc823f5aa5 Mon Sep 17 00:00:00 2001 From: Keaton Law Date: Sun, 2 Mar 2025 11:48:57 -0600 Subject: [PATCH 6/6] added permission classes to friend requests --- api/views/friendship.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/views/friendship.py b/api/views/friendship.py index 719a9734..230d5701 100644 --- a/api/views/friendship.py +++ b/api/views/friendship.py @@ -6,7 +6,7 @@ from rest_framework.exceptions import ValidationError from django.db import IntegrityError from api.models import Friendship, StatusTypes -from rest_framework import serializers +from rest_framework import serializers, permissions class FriendshipSerializer(serializers.ModelSerializer): class Meta: @@ -28,6 +28,7 @@ def to_representation(self, instance): return representation class Friendships(viewsets.ModelViewSet): + permission_classes = [permissions.IsAuthenticated] queryset = Friendship.objects.all() # pylint: disable=no-member serializer_class = FriendshipSerializer