diff --git a/client/src/components/ui/goBack.tsx b/client/src/components/ui/goBack.tsx
new file mode 100644
index 00000000..4273aad5
--- /dev/null
+++ b/client/src/components/ui/goBack.tsx
@@ -0,0 +1,34 @@
+import Link from "next/link";
+
+const ButtonGallery = () => {
+ return (
+
+
+
+ );
+};
+
+export default ButtonGallery;
diff --git a/client/src/components/ui/imageFrame.tsx b/client/src/components/ui/imageFrame.tsx
new file mode 100644
index 00000000..6d66335c
--- /dev/null
+++ b/client/src/components/ui/imageFrame.tsx
@@ -0,0 +1,30 @@
+import Image from "next/image";
+import React from "react";
+
+interface CardProps {
+ imageSrc?: string;
+ imageAlt?: string;
+ children?: React.ReactNode;
+}
+
+const Card = ({ imageSrc, imageAlt = "Artwork", children }: CardProps) => {
+ return (
+
+
+ {imageSrc ? (
+
+ ) : (
+ children || No Image
+ )}
+
+
+ );
+};
+
+export default Card;
diff --git a/client/src/hooks/useArtworkData.ts b/client/src/hooks/useArtworkData.ts
new file mode 100644
index 00000000..44d0136c
--- /dev/null
+++ b/client/src/hooks/useArtworkData.ts
@@ -0,0 +1,49 @@
+import { Art } from "@/types/art";
+
+export const generateMockArtworks = (count: number): Art[] => {
+ const artworks: Art[] = [];
+ for (let i = 1; i <= count; i++) {
+ artworks.push({
+ id: i,
+ name: `Artwork ${i}`,
+ description: "Mock artwork description",
+ //source_game: "Mock Game",
+ path_to_media: "",
+ active: true,
+ contributors: [],
+ //created_at: new Date().toISOString(),
+ });
+ }
+ return artworks;
+};
+
+export const generateMockArtwork = (id: string): Art => {
+ return {
+ id: Number(id),
+ name: "Mock Artwork Title",
+ description:
+ "Lorem ipsum dolor sit amet. Non numquam dicta nam autem dicta 33 error molestias et repellat consequatur eum iste expedita est dolorem libero et quas provident!",
+ //source_game: "Mock Game",
+ path_to_media: "",
+ active: true,
+ //created_at: new Date().toISOString(),
+ contributors: [
+ {
+ id: 1,
+ art_id: Number(id),
+ member_name: "Contributor 1",
+ role: "user1",
+ discord_url: "https://discord.com",
+ instagram_url: "",
+ },
+ {
+ id: 2,
+ art_id: Number(id),
+ member_name: "Contributor 2",
+ role: "user2",
+ discord_url: "",
+ instagram_url: "https://instagram.com",
+ },
+ ],
+ };
+};
diff --git a/client/src/pages/artwork/[id].tsx b/client/src/pages/artwork/[id].tsx
index 95adacb5..ac8ecc3a 100644
--- a/client/src/pages/artwork/[id].tsx
+++ b/client/src/pages/artwork/[id].tsx
@@ -1,8 +1,9 @@
import { GetServerSideProps } from "next";
import Image from "next/image";
-import Link from "next/link";
import { JSX } from "react";
+import ButtonGallery from "@/components/ui/goBack";
+import { generateMockArtwork } from "@/hooks/useArtworkData";
import api from "@/lib/api";
import { Art } from "@/types/art";
@@ -102,13 +103,13 @@ export default function ArtworkPage({ artwork }: ArtworkPageProps) {
-
+ {artwork.path_to_media ? (
+
+ ) : (
+ // in case fail to load image or no image in db yet
+
+ )}
= async (
context,
) => {
const { id } = context.params as { id: string };
- const artResponse = await api.get
(`game-dev/arts/${id}`);
- const artwork = artResponse.data;
- return { props: { artwork } };
+ try {
+ const artResponse = await api.get(`game-dev/arts/${id}`);
+ const artwork = artResponse.data;
+ return { props: { artwork } };
+ } catch {
+ // Return mock data when API fails or DB is empty
+ const mockArtwork = generateMockArtwork(id);
+ return { props: { artwork: mockArtwork } };
+ }
};
diff --git a/client/src/pages/artwork/index.tsx b/client/src/pages/artwork/index.tsx
index 8a3586f5..e34fbddf 100644
--- a/client/src/pages/artwork/index.tsx
+++ b/client/src/pages/artwork/index.tsx
@@ -1,13 +1,15 @@
import { GetServerSideProps } from "next";
-import Image from "next/image";
import Link from "next/link";
+import { Button } from "@/components/ui/button";
+import Card from "@/components/ui/imageFrame";
+import { generateMockArtworks } from "@/hooks/useArtworkData";
import api from "@/lib/api";
import { Art } from "@/types/art";
import { PageResult } from "@/types/page-response";
interface ArtworksPageProps {
- pages: PageResult;
+ artworks: PageResult;
}
const PLACEHOLDER_ICON = (
@@ -31,29 +33,22 @@ function renderArtworkCard(artwork: Art) {
return (
-
+ {!artwork.path_to_media && PLACEHOLDER_ICON}
+
);
}
-export default function ArtworksPage({ pages }: ArtworksPageProps) {
+export default function ArtworksPage({ artworks }: ArtworksPageProps) {
return (
- {pages.results.map((artwork) => renderArtworkCard(artwork))}
+ {artworks.results.map((artwork) => renderArtworkCard(artwork))}
= async () => {
- const res = await api.get
>("game-dev/arts");
- const pages = res.data;
- return { props: { pages } };
+ try {
+ const res = await api.get>("game-dev/arts");
+ return { props: { artworks: res.data } };
+ } catch {
+ // return {
+ // props: {
+ // artworks: {
+ // count: 0,
+ // next: null as unknown as string,
+ // previous: null as unknown as string,
+ // results: [] as Art[],
+ // },
+ // },
+ // }; ==> use when successfully populate db
+ const mockArtworks = generateMockArtworks(12);
+ return {
+ props: {
+ artworks: {
+ count: mockArtworks.length,
+ next: "",
+ previous: "",
+ results: mockArtworks,
+ },
+ },
+ };
+ }
};
diff --git a/client/src/styles/globals.css b/client/src/styles/globals.css
index 53eb51bc..43a74218 100644
--- a/client/src/styles/globals.css
+++ b/client/src/styles/globals.css
@@ -34,11 +34,11 @@
--radius: 0.5rem;
- --dark-2: #090A19;
- --neutral-1: #1B1F4C;
- --light-1: #FFFFFF;
- --light-2: #CED1FE;
- --light-3: #9CA4FD;
+ --dark-2: #090a19;
+ --neutral-1: #1b1f4c;
+ --light-1: #ffffff;
+ --light-2: #ced1fe;
+ --light-3: #9ca4fd;
}
}
@@ -68,4 +68,4 @@
}
.border-light-2 {
border-color: var(--light-2);
-}
\ No newline at end of file
+}
diff --git a/server/api/settings.py b/server/api/settings.py
index 4664e69a..46902758 100644
--- a/server/api/settings.py
+++ b/server/api/settings.py
@@ -158,4 +158,4 @@
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100,
-}
\ No newline at end of file
+}
diff --git a/server/api/urls.py b/server/api/urls.py
index c6a16187..073e0e7d 100644
--- a/server/api/urls.py
+++ b/server/api/urls.py
@@ -21,5 +21,5 @@
urlpatterns = [
path("admin/", admin.site.urls),
path("api/healthcheck/", include("api.healthcheck.urls")),
- path("api/game-dev/", include("game_dev.urls")),
-]
\ No newline at end of file
+ path("api/game-dev/", include("game_dev.urls")),
+]
diff --git a/server/game_dev/admin.py b/server/game_dev/admin.py
index 2cfeab2e..b148cace 100644
--- a/server/game_dev/admin.py
+++ b/server/game_dev/admin.py
@@ -2,23 +2,8 @@
from .models import Member, Art, ArtContributor
-@admin.register(Member)
-class MemberAdmin(admin.ModelAdmin):
- list_display = ['name', 'active', 'pronouns']
- list_filter = ['active']
- search_fields = ['name']
+admin.site.register(Member)
+admin.site.register(Art)
-@admin.register(Art)
-class ArtAdmin(admin.ModelAdmin):
- list_display = ['name', 'active']
- list_filter = ['active']
- search_fields = ['name']
-
-
-@admin.register(ArtContributor)
-class ArtContributorAdmin(admin.ModelAdmin):
- list_display = ['art', 'member', 'role']
- list_filter = ['role']
- search_fields = ['member__name', 'art__name']
- autocomplete_fields = ['art', 'member']
\ No newline at end of file
+admin.site.register(ArtContributor)
diff --git a/server/game_dev/models.py b/server/game_dev/models.py
index ea132d6b..0735d3f3 100644
--- a/server/game_dev/models.py
+++ b/server/game_dev/models.py
@@ -22,14 +22,16 @@ class Art(models.Model):
def __str__(self):
return str(self.name)
+
class ArtContributor(models.Model):
art = models.ForeignKey('Art', on_delete=models.CASCADE, related_name='contributors')
member = models.ForeignKey('Member', on_delete=models.CASCADE, related_name='art_contributions')
role = models.CharField(max_length=100)
+
class Meta:
unique_together = ('art', 'member')
verbose_name = 'Art Contributor'
verbose_name_plural = 'Art Contributors'
-
+
def __str__(self):
return f"{self.member.name} - {self.art.name} ({self.role})"
diff --git a/server/game_dev/serializers.py b/server/game_dev/serializers.py
index 48901998..ae3a0d67 100644
--- a/server/game_dev/serializers.py
+++ b/server/game_dev/serializers.py
@@ -4,17 +4,20 @@
class ArtContributorSerializer(serializers.ModelSerializer):
member_name = serializers.CharField(source='member.name', read_only=True)
-
+
class Meta:
model = ArtContributor
fields = ['id', 'art', 'member', 'member_name', 'role']
+
class ArtSerializer(serializers.ModelSerializer):
- contributors = ArtContributorSerializer(many = True, read_only = True)
+ contributors = ArtContributorSerializer(many=True, read_only=True)
+
class Meta:
model = Art
fields = ['id', 'name', 'description', 'path_to_media', 'active', 'contributors']
+
class MemberSerializer(serializers.ModelSerializer):
class Meta:
model = Member
diff --git a/server/game_dev/urls.py b/server/game_dev/urls.py
index b6f94c27..7fb8d68d 100644
--- a/server/game_dev/urls.py
+++ b/server/game_dev/urls.py
@@ -9,4 +9,4 @@
urlpatterns = [
path('', include(router.urls)),
-]
\ No newline at end of file
+]
diff --git a/server/game_dev/views.py b/server/game_dev/views.py
index 86d0f0ad..78790797 100644
--- a/server/game_dev/views.py
+++ b/server/game_dev/views.py
@@ -8,12 +8,14 @@ class ArtContributorViewSet(viewsets.ModelViewSet):
queryset = ArtContributor.objects.all()
serializer_class = ArtContributorSerializer
filter_backends = [DjangoFilterBackend]
- filterset_fields=['art']
+ filterset_fields = ['art']
+
class ArtViewSet(viewsets.ModelViewSet):
queryset = Art.objects.all()
serializer_class = ArtSerializer
+
class MemberViewSet(viewsets.ModelViewSet):
queryset = Member.objects.all()
- serializer_class = MemberSerializer
\ No newline at end of file
+ serializer_class = MemberSerializer
diff --git a/server/poetry.lock b/server/poetry.lock
index c6b7d5ff..bb17707c 100644
--- a/server/poetry.lock
+++ b/server/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
[[package]]
name = "asgiref"
@@ -6,7 +6,6 @@ version = "3.8.1"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"},
{file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"},
@@ -21,7 +20,6 @@ version = "2.15.8"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.7.2"
-groups = ["dev"]
files = [
{file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
{file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
@@ -37,8 +35,6 @@ version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-groups = ["dev"]
-markers = "sys_platform == \"win32\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
@@ -46,18 +42,17 @@ files = [
[[package]]
name = "django"
-version = "5.1.14"
+version = "5.2.9"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
-groups = ["main"]
files = [
- {file = "django-5.1.14-py3-none-any.whl", hash = "sha256:2a4b9c20404fd1bf50aaaa5542a19d860594cba1354f688f642feb271b91df27"},
- {file = "django-5.1.14.tar.gz", hash = "sha256:b98409fb31fdd6e8c3a6ba2eef3415cc5c0020057b43b21ba7af6eff5f014831"},
+ {file = "django-5.2.9-py3-none-any.whl", hash = "sha256:3a4ea88a70370557ab1930b332fd2887a9f48654261cdffda663fef5976bb00a"},
+ {file = "django-5.2.9.tar.gz", hash = "sha256:16b5ccfc5e8c27e6c0561af551d2ea32852d7352c67d452ae3e76b4f6b2ca495"},
]
[package.dependencies]
-asgiref = ">=3.8.1,<4"
+asgiref = ">=3.8.1"
sqlparse = ">=0.3.1"
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
@@ -71,7 +66,6 @@ version = "4.4.0"
description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)."
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "django_cors_headers-4.4.0-py3-none-any.whl", hash = "sha256:5c6e3b7fe870876a1efdfeb4f433782c3524078fa0dc9e0195f6706ce7a242f6"},
{file = "django_cors_headers-4.4.0.tar.gz", hash = "sha256:92cf4633e22af67a230a1456cb1b7a02bb213d6536d2dcb2a4a24092ea9cebc2"},
@@ -87,7 +81,6 @@ version = "3.2.3"
description = "Extensions for Django"
optional = false
python-versions = ">=3.6"
-groups = ["main"]
files = [
{file = "django-extensions-3.2.3.tar.gz", hash = "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a"},
{file = "django_extensions-3.2.3-py3-none-any.whl", hash = "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401"},
@@ -96,13 +89,29 @@ files = [
[package.dependencies]
Django = ">=3.2"
+[[package]]
+name = "django-filter"
+version = "25.2"
+description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
+optional = false
+python-versions = ">=3.10"
+files = [
+ {file = "django_filter-25.2-py3-none-any.whl", hash = "sha256:9c0f8609057309bba611062fe1b720b4a873652541192d232dd28970383633e3"},
+ {file = "django_filter-25.2.tar.gz", hash = "sha256:760e984a931f4468d096f5541787efb8998c61217b73006163bf2f9523fe8f23"},
+]
+
+[package.dependencies]
+Django = ">=5.2"
+
+[package.extras]
+drf = ["djangorestframework"]
+
[[package]]
name = "djangorestframework"
version = "3.15.2"
description = "Web APIs for Django, made easy."
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "djangorestframework-3.15.2-py3-none-any.whl", hash = "sha256:2b8871b062ba1aefc2de01f773875441a961fefbf79f5eed1e32b2f096944b20"},
{file = "djangorestframework-3.15.2.tar.gz", hash = "sha256:36fe88cd2d6c6bec23dca9804bab2ba5517a8bb9d8f47ebc68981b56840107ad"},
@@ -117,7 +126,6 @@ version = "6.1.0"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.8.1"
-groups = ["dev"]
files = [
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"},
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"},
@@ -134,7 +142,6 @@ version = "1.4"
description = "Plugin to catch bad style specific to Django Projects."
optional = false
python-versions = ">=3.7.2,<4.0.0"
-groups = ["dev"]
files = [
{file = "flake8_django-1.4.tar.gz", hash = "sha256:4debba883084191568e3187416d1d6bdd4abd826da988f197a3c36572e9f30de"},
]
@@ -149,7 +156,6 @@ version = "1.5.1"
description = "Let your Python tests travel through time"
optional = false
python-versions = ">=3.7"
-groups = ["main"]
files = [
{file = "freezegun-1.5.1-py3-none-any.whl", hash = "sha256:bf111d7138a8abe55ab48a71755673dbaa4ab87f4cff5634a4442dfec34c15f1"},
{file = "freezegun-1.5.1.tar.gz", hash = "sha256:b29dedfcda6d5e8e083ce71b2b542753ad48cfec44037b3fc79702e2980a89e9"},
@@ -164,7 +170,6 @@ version = "23.0.0"
description = "WSGI HTTP Server for UNIX"
optional = false
python-versions = ">=3.7"
-groups = ["main"]
files = [
{file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
{file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
@@ -186,7 +191,6 @@ version = "2.0.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.7"
-groups = ["dev"]
files = [
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
@@ -198,7 +202,6 @@ version = "1.10.0"
description = "A fast and thorough lazy object proxy."
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "lazy-object-proxy-1.10.0.tar.gz", hash = "sha256:78247b6d45f43a52ef35c25b5581459e85117225408a4128a3daf8bf9648ac69"},
{file = "lazy_object_proxy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:855e068b0358ab916454464a884779c7ffa312b8925c6f7401e952dcf3b89977"},
@@ -245,7 +248,6 @@ version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
@@ -257,7 +259,6 @@ version = "24.1"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
-groups = ["main", "dev"]
files = [
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
@@ -269,7 +270,6 @@ version = "11.3.0"
description = "Python Imaging Library (Fork)"
optional = false
python-versions = ">=3.9"
-groups = ["main"]
files = [
{file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"},
{file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"},
@@ -385,7 +385,7 @@ fpx = ["olefile"]
mic = ["olefile"]
test-arrow = ["pyarrow"]
tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"]
-typing = ["typing-extensions ; python_version < \"3.10\""]
+typing = ["typing-extensions"]
xmp = ["defusedxml"]
[[package]]
@@ -394,7 +394,6 @@ version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
@@ -410,7 +409,6 @@ version = "2.11.1"
description = "Python style guide checker"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
{file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
@@ -422,7 +420,6 @@ version = "3.1.0"
description = "passive checker of Python programs"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"},
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"},
@@ -434,7 +431,6 @@ version = "8.3.2"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
-groups = ["dev"]
files = [
{file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"},
{file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"},
@@ -455,7 +451,6 @@ version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-groups = ["main"]
files = [
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
@@ -470,7 +465,6 @@ version = "1.0.1"
description = "Read key-value pairs from a .env file and set them as environment variables"
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"},
{file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"},
@@ -485,7 +479,6 @@ version = "1.16.0"
description = "Python 2 and 3 compatibility utilities"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-groups = ["main"]
files = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
{file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
@@ -497,7 +490,6 @@ version = "0.5.1"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.8"
-groups = ["main"]
files = [
{file = "sqlparse-0.5.1-py3-none-any.whl", hash = "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4"},
{file = "sqlparse-0.5.1.tar.gz", hash = "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e"},
@@ -513,8 +505,6 @@ version = "2024.1"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
-groups = ["main"]
-markers = "sys_platform == \"win32\""
files = [
{file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
@@ -526,7 +516,6 @@ version = "1.16.0"
description = "Module for decorators, wrappers and monkey patching."
optional = false
python-versions = ">=3.6"
-groups = ["dev"]
files = [
{file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
{file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
@@ -601,6 +590,6 @@ files = [
]
[metadata]
-lock-version = "2.1"
+lock-version = "2.0"
python-versions = "^3.12"
-content-hash = "f804c2f3998772b91e34ad214e5fcafe900bec97675f73046d3bcc79aba0f7db"
+content-hash = "c5308f28a61ca954f8c322f9d9c4ae9412b82c10eb7a262c8ee438554aeed16f"
diff --git a/server/pyproject.toml b/server/pyproject.toml
index 5ec547cb..dd0e67ee 100644
--- a/server/pyproject.toml
+++ b/server/pyproject.toml
@@ -16,6 +16,7 @@ gunicorn = "^23.0.0"
python-dotenv = "^1.0.1"
django-extensions = "^3.2.3"
pillow = "^11.3.0"
+django-filter = "^25.2"
[tool.poetry.group.dev.dependencies]