From 197842796fa42b9ef8aee4e003b2b6e32f26cd30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sosnowski?= Date: Sun, 11 Jan 2026 11:52:02 +0100 Subject: [PATCH 1/4] Uzuniecie paginacji dla api locations - ograniczalo wyniki --- cityfeel/api/views.py | 5 +++-- cityfeel/static/js/map.js | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cityfeel/api/views.py b/cityfeel/api/views.py index 4404664..4231880 100644 --- a/cityfeel/api/views.py +++ b/cityfeel/api/views.py @@ -42,10 +42,10 @@ class EmotionPointViewSet(ModelViewSet): class LocationViewSet(ReadOnlyModelViewSet): """ ViewSet dla endpointu /api/locations/ (READ-ONLY). - + Zwraca lokalizacje z agregowaną średnią wartością emocjonalną (avg_emotional_value). Średnia liczy ze WSZYSTKICH emotion_points (zarówno publicznych jak i prywatnych). - + Filtrowanie: - ?name=Gdańsk - filtrowanie po nazwie (icontains) - ?lat=54.35&lon=18.64&radius=1000 - filtrowanie po promieniu (metry) @@ -55,6 +55,7 @@ class LocationViewSet(ReadOnlyModelViewSet): permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend] filterset_class = LocationFilter + pagination_class = None # Wyłącz paginację - wszystkie lokalizacje w bounding box def get_queryset(self): return ( diff --git a/cityfeel/static/js/map.js b/cityfeel/static/js/map.js index d209dcf..b9589af 100644 --- a/cityfeel/static/js/map.js +++ b/cityfeel/static/js/map.js @@ -221,7 +221,9 @@ function initFilters() { return response.json(); }) .then(data => { - displayLocations(data.results || []); + // Obsługa odpowiedzi bez paginacji (tablica) i z paginacją (obiekt z results) + const locations = Array.isArray(data) ? data : (data.results || []); + displayLocations(locations); }) .catch(error => { console.error('Error fetching locations:', error); From c3b5da750aeae140e2308e5e89e1d08fdeeafc60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sosnowski?= Date: Sun, 11 Jan 2026 11:52:21 +0100 Subject: [PATCH 2/4] Dane testowe --- cityfeel/fixtures/cityfeel_data.json | 1854 ++++++++++++++++++++++++ cityfeel/fixtures/comment_templates.py | 100 ++ cityfeel/fixtures/generate_fixtures.py | 346 +++++ cityfeel/fixtures/locations_data.py | 95 ++ 4 files changed, 2395 insertions(+) create mode 100644 cityfeel/fixtures/cityfeel_data.json create mode 100644 cityfeel/fixtures/comment_templates.py create mode 100644 cityfeel/fixtures/generate_fixtures.py create mode 100644 cityfeel/fixtures/locations_data.py diff --git a/cityfeel/fixtures/cityfeel_data.json b/cityfeel/fixtures/cityfeel_data.json new file mode 100644 index 0000000..79db419 --- /dev/null +++ b/cityfeel/fixtures/cityfeel_data.json @@ -0,0 +1,1854 @@ +[ + { + "model": "map.location", + "pk": 1, + "fields": { + "name": "Molo w Sopocie", + "coordinates": "SRID=4326;POINT(18.5695 54.4438)" + } + }, + { + "model": "map.location", + "pk": 2, + "fields": { + "name": "Krzywy Domek", + "coordinates": "SRID=4326;POINT(18.5672 54.442)" + } + }, + { + "model": "map.location", + "pk": 3, + "fields": { + "name": "Plaża Sopocka", + "coordinates": "SRID=4326;POINT(18.57 54.4415)" + } + }, + { + "model": "map.location", + "pk": 4, + "fields": { + "name": "Ulica Monte Cassino", + "coordinates": "SRID=4326;POINT(18.5665 54.4425)" + } + }, + { + "model": "map.location", + "pk": 5, + "fields": { + "name": "Łazienki Północne Sopot", + "coordinates": "SRID=4326;POINT(18.568 54.4445)" + } + }, + { + "model": "map.location", + "pk": 6, + "fields": { + "name": "Kawiarnia na Żeromskiego", + "coordinates": "SRID=4326;POINT(18.5668 54.4432)" + } + }, + { + "model": "map.location", + "pk": 7, + "fields": { + "name": "Park Północny Sopot", + "coordinates": "SRID=4326;POINT(18.5675 54.445)" + } + }, + { + "model": "map.location", + "pk": 8, + "fields": { + "name": "Opera Leśna", + "coordinates": "SRID=4326;POINT(18.562 54.438)" + } + }, + { + "model": "map.location", + "pk": 9, + "fields": { + "name": "Sopockie Muszle", + "coordinates": "SRID=4326;POINT(18.5688 54.4412)" + } + }, + { + "model": "map.location", + "pk": 10, + "fields": { + "name": "Restauracja Przystań Sopot", + "coordinates": "SRID=4326;POINT(18.571 54.4428)" + } + }, + { + "model": "map.location", + "pk": 11, + "fields": { + "name": "Długi Targ", + "coordinates": "SRID=4326;POINT(18.6538 54.3489)" + } + }, + { + "model": "map.location", + "pk": 12, + "fields": { + "name": "Bazylika Mariacka", + "coordinates": "SRID=4326;POINT(18.653 54.349)" + } + }, + { + "model": "map.location", + "pk": 13, + "fields": { + "name": "Fontanna Neptuna", + "coordinates": "SRID=4326;POINT(18.6536 54.3486)" + } + }, + { + "model": "map.location", + "pk": 14, + "fields": { + "name": "Złota Brama", + "coordinates": "SRID=4326;POINT(18.6525 54.3505)" + } + }, + { + "model": "map.location", + "pk": 15, + "fields": { + "name": "Ulica Mariacka", + "coordinates": "SRID=4326;POINT(18.6545 54.3485)" + } + }, + { + "model": "map.location", + "pk": 16, + "fields": { + "name": "Gdańskie Koło Widokowe", + "coordinates": "SRID=4326;POINT(18.6555 54.3492)" + } + }, + { + "model": "map.location", + "pk": 17, + "fields": { + "name": "Muzeum Bursztynu", + "coordinates": "SRID=4326;POINT(18.652 54.351)" + } + }, + { + "model": "map.location", + "pk": 18, + "fields": { + "name": "Kawiarnia Drukarnia", + "coordinates": "SRID=4326;POINT(18.6528 54.3495)" + } + }, + { + "model": "map.location", + "pk": 19, + "fields": { + "name": "Molo w Gdyni", + "coordinates": "SRID=4326;POINT(18.5515 54.5195)" + } + }, + { + "model": "map.location", + "pk": 20, + "fields": { + "name": "Bulwary Gdyńskie", + "coordinates": "SRID=4326;POINT(18.5505 54.518)" + } + }, + { + "model": "map.location", + "pk": 21, + "fields": { + "name": "Akwarium Gdyńskie", + "coordinates": "SRID=4326;POINT(18.552 54.5188)" + } + }, + { + "model": "map.location", + "pk": 22, + "fields": { + "name": "Plaża Miejska Gdynia", + "coordinates": "SRID=4326;POINT(18.5495 54.517)" + } + }, + { + "model": "map.location", + "pk": 23, + "fields": { + "name": "Skwer Kościuszki", + "coordinates": "SRID=4326;POINT(18.554 54.519)" + } + }, + { + "model": "map.location", + "pk": 24, + "fields": { + "name": "Marina Gdynia", + "coordinates": "SRID=4326;POINT(18.5525 54.5205)" + } + }, + { + "model": "map.location", + "pk": 25, + "fields": { + "name": "Kamienica Żeromskiego", + "coordinates": "SRID=4326;POINT(18.5535 54.5175)" + } + }, + { + "model": "map.location", + "pk": 26, + "fields": { + "name": "Parking Zaspa", + "coordinates": "SRID=4326;POINT(18.6125 54.3755)" + } + }, + { + "model": "map.location", + "pk": 27, + "fields": { + "name": "Przystanek PKM Zaspa", + "coordinates": "SRID=4326;POINT(18.6145 54.3765)" + } + }, + { + "model": "map.location", + "pk": 28, + "fields": { + "name": "Sklep Biedronka Zaspa", + "coordinates": "SRID=4326;POINT(18.613 54.375)" + } + }, + { + "model": "map.location", + "pk": 29, + "fields": { + "name": "Osiedle Chełm", + "coordinates": "SRID=4326;POINT(18.598 54.395)" + } + }, + { + "model": "map.location", + "pk": 30, + "fields": { + "name": "Parking Przymorze", + "coordinates": "SRID=4326;POINT(18.6025 54.405)" + } + }, + { + "model": "map.location", + "pk": 31, + "fields": { + "name": "Dworzec PKS Wrzeszcz", + "coordinates": "SRID=4326;POINT(18.6085 54.382)" + } + }, + { + "model": "map.location", + "pk": 32, + "fields": { + "name": "Galeria Metropolia", + "coordinates": "SRID=4326;POINT(18.601 54.402)" + } + }, + { + "model": "map.location", + "pk": 33, + "fields": { + "name": "Rondo Sobótki", + "coordinates": "SRID=4326;POINT(18.6095 54.374)" + } + }, + { + "model": "map.location", + "pk": 34, + "fields": { + "name": "Dworzec PKP Gdynia Główna", + "coordinates": "SRID=4326;POINT(18.5385 54.521)" + } + }, + { + "model": "map.location", + "pk": 35, + "fields": { + "name": "Parking Chwarzno", + "coordinates": "SRID=4326;POINT(18.5245 54.5025)" + } + }, + { + "model": "map.location", + "pk": 36, + "fields": { + "name": "Osiedle Witomino", + "coordinates": "SRID=4326;POINT(18.5185 54.5425)" + } + }, + { + "model": "map.location", + "pk": 37, + "fields": { + "name": "Centrum Handlowe Klif", + "coordinates": "SRID=4326;POINT(18.5325 54.5295)" + } + }, + { + "model": "map.location", + "pk": 38, + "fields": { + "name": "Sklep Żabka Chylonia", + "coordinates": "SRID=4326;POINT(18.5105 54.495)" + } + }, + { + "model": "map.location", + "pk": 39, + "fields": { + "name": "Park Oliwski", + "coordinates": "SRID=4326;POINT(18.571 54.4075)" + } + }, + { + "model": "map.location", + "pk": 40, + "fields": { + "name": "Westerplatte", + "coordinates": "SRID=4326;POINT(18.672 54.4065)" + } + }, + { + "model": "map.location", + "pk": 41, + "fields": { + "name": "Ogród Botaniczny UG", + "coordinates": "SRID=4326;POINT(18.5795 54.3955)" + } + }, + { + "model": "map.location", + "pk": 42, + "fields": { + "name": "Parking Oliwa", + "coordinates": "SRID=4326;POINT(18.5725 54.4085)" + } + }, + { + "model": "map.location", + "pk": 43, + "fields": { + "name": "Plaża Stogi", + "coordinates": "SRID=4326;POINT(18.682 54.3615)" + } + }, + { + "model": "map.location", + "pk": 44, + "fields": { + "name": "PKS Gdańsk Oliwa", + "coordinates": "SRID=4326;POINT(18.5705 54.409)" + } + }, + { + "model": "map.location", + "pk": 45, + "fields": { + "name": "Jarmark Dominikański", + "coordinates": "SRID=4326;POINT(18.6585 54.3515)" + } + }, + { + "model": "map.location", + "pk": 46, + "fields": { + "name": "Aquapark Sopot", + "coordinates": "SRID=4326;POINT(18.5455 54.4295)" + } + }, + { + "model": "map.location", + "pk": 47, + "fields": { + "name": "Park Brodwino", + "coordinates": "SRID=4326;POINT(18.5555 54.4525)" + } + }, + { + "model": "map.location", + "pk": 48, + "fields": { + "name": "SKM Sopot Kamienny Potok", + "coordinates": "SRID=4326;POINT(18.5525 54.4365)" + } + }, + { + "model": "map.location", + "pk": 49, + "fields": { + "name": "Ergo Arena", + "coordinates": "SRID=4326;POINT(18.5815 54.4145)" + } + }, + { + "model": "map.location", + "pk": 50, + "fields": { + "name": "Lidl Sopot Karlikowo", + "coordinates": "SRID=4326;POINT(18.5485 54.4335)" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 1, + "fields": { + "user": 1, + "location": 1, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-11-17T15:03:57.855602", + "updated_at": "2025-11-17T15:03:57.855602" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 2, + "fields": { + "user": 8, + "location": 1, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-11-26T09:40:57.855639", + "updated_at": "2025-11-26T09:40:57.855639" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 3, + "fields": { + "user": 8, + "location": 2, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-11-17T03:47:57.855648", + "updated_at": "2025-11-17T03:47:57.855648" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 4, + "fields": { + "user": 1, + "location": 3, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-01T00:32:57.855654", + "updated_at": "2025-12-01T00:32:57.855654" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 5, + "fields": { + "user": 9, + "location": 4, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-23T12:43:57.855659", + "updated_at": "2025-12-23T12:43:57.855659" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 6, + "fields": { + "user": 1, + "location": 6, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-17T02:43:57.855664", + "updated_at": "2025-12-17T02:43:57.855664" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 7, + "fields": { + "user": 9, + "location": 6, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-11-18T10:28:57.855668", + "updated_at": "2025-11-18T10:28:57.855668" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 8, + "fields": { + "user": 10, + "location": 6, + "emotional_value": 4, + "privacy_status": "private", + "created_at": "2025-12-16T03:38:57.855671", + "updated_at": "2025-12-16T03:38:57.855671" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 9, + "fields": { + "user": 8, + "location": 7, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-09T22:46:57.855675", + "updated_at": "2025-12-09T22:46:57.855675" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 10, + "fields": { + "user": 9, + "location": 7, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2026-01-02T03:55:57.855678", + "updated_at": "2026-01-02T03:55:57.855678" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 11, + "fields": { + "user": 9, + "location": 9, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-12-16T00:28:57.855683", + "updated_at": "2025-12-16T00:28:57.855683" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 12, + "fields": { + "user": 10, + "location": 9, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2026-01-05T09:47:57.855686", + "updated_at": "2026-01-05T09:47:57.855686" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 13, + "fields": { + "user": 1, + "location": 10, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-11-20T14:15:57.855690", + "updated_at": "2025-11-20T14:15:57.855690" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 14, + "fields": { + "user": 1, + "location": 11, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-11T19:26:57.855694", + "updated_at": "2025-12-11T19:26:57.855694" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 15, + "fields": { + "user": 8, + "location": 11, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2026-01-02T13:46:57.855698", + "updated_at": "2026-01-02T13:46:57.855698" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 16, + "fields": { + "user": 9, + "location": 12, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-14T06:13:57.855701", + "updated_at": "2025-12-14T06:13:57.855701" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 17, + "fields": { + "user": 1, + "location": 13, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-11-23T06:10:57.855707", + "updated_at": "2025-11-23T06:10:57.855707" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 18, + "fields": { + "user": 1, + "location": 15, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-12-08T16:30:57.855712", + "updated_at": "2025-12-08T16:30:57.855712" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 19, + "fields": { + "user": 9, + "location": 15, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-30T17:53:57.855715", + "updated_at": "2025-12-30T17:53:57.855715" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 20, + "fields": { + "user": 1, + "location": 16, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2026-01-02T23:46:57.855719", + "updated_at": "2026-01-02T23:46:57.855719" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 21, + "fields": { + "user": 9, + "location": 16, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-25T17:37:57.855723", + "updated_at": "2025-12-25T17:37:57.855723" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 22, + "fields": { + "user": 1, + "location": 17, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-11-22T17:53:57.855727", + "updated_at": "2025-11-22T17:53:57.855727" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 23, + "fields": { + "user": 8, + "location": 18, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-06T06:26:57.855731", + "updated_at": "2025-12-06T06:26:57.855731" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 24, + "fields": { + "user": 10, + "location": 19, + "emotional_value": 4, + "privacy_status": "private", + "created_at": "2025-12-15T14:01:57.855735", + "updated_at": "2025-12-15T14:01:57.855735" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 25, + "fields": { + "user": 1, + "location": 19, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-07T21:35:57.855738", + "updated_at": "2025-12-07T21:35:57.855738" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 26, + "fields": { + "user": 8, + "location": 21, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-06T04:05:57.855743", + "updated_at": "2025-12-06T04:05:57.855743" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 27, + "fields": { + "user": 1, + "location": 21, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-11-25T15:39:57.855746", + "updated_at": "2025-11-25T15:39:57.855746" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 28, + "fields": { + "user": 9, + "location": 21, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2026-01-08T01:38:57.855749", + "updated_at": "2026-01-08T01:38:57.855749" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 29, + "fields": { + "user": 8, + "location": 22, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-27T18:34:57.855753", + "updated_at": "2025-12-27T18:34:57.855753" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 30, + "fields": { + "user": 10, + "location": 23, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-12-29T08:36:57.855757", + "updated_at": "2025-12-29T08:36:57.855757" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 31, + "fields": { + "user": 1, + "location": 23, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-12-14T20:47:57.855761", + "updated_at": "2025-12-14T20:47:57.855761" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 32, + "fields": { + "user": 1, + "location": 24, + "emotional_value": 5, + "privacy_status": "private", + "created_at": "2025-11-20T08:27:57.855765", + "updated_at": "2025-11-20T08:27:57.855765" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 33, + "fields": { + "user": 9, + "location": 24, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-13T07:15:57.855768", + "updated_at": "2025-12-13T07:15:57.855768" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 34, + "fields": { + "user": 10, + "location": 24, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-26T09:14:57.855772", + "updated_at": "2025-12-26T09:14:57.855772" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 35, + "fields": { + "user": 8, + "location": 24, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-07T11:37:57.855775", + "updated_at": "2025-12-07T11:37:57.855775" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 36, + "fields": { + "user": 8, + "location": 25, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-11-15T22:45:57.855781", + "updated_at": "2025-11-15T22:45:57.855781" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 37, + "fields": { + "user": 1, + "location": 25, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2026-01-09T23:26:57.855784", + "updated_at": "2026-01-09T23:26:57.855784" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 38, + "fields": { + "user": 10, + "location": 25, + "emotional_value": 5, + "privacy_status": "public", + "created_at": "2025-11-26T11:52:57.855787", + "updated_at": "2025-11-26T11:52:57.855787" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 39, + "fields": { + "user": 10, + "location": 26, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2026-01-06T16:55:57.855791", + "updated_at": "2026-01-06T16:55:57.855791" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 40, + "fields": { + "user": 1, + "location": 26, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2026-01-07T10:05:57.855795", + "updated_at": "2026-01-07T10:05:57.855795" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 41, + "fields": { + "user": 10, + "location": 27, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2025-12-09T08:48:57.855799", + "updated_at": "2025-12-09T08:48:57.855799" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 42, + "fields": { + "user": 9, + "location": 27, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2026-01-05T13:47:57.855802", + "updated_at": "2026-01-05T13:47:57.855802" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 43, + "fields": { + "user": 8, + "location": 28, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-11-14T17:27:57.855806", + "updated_at": "2025-11-14T17:27:57.855806" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 44, + "fields": { + "user": 1, + "location": 29, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2025-12-03T17:09:57.855810", + "updated_at": "2025-12-03T17:09:57.855810" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 45, + "fields": { + "user": 9, + "location": 30, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-16T07:00:57.855814", + "updated_at": "2025-12-16T07:00:57.855814" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 46, + "fields": { + "user": 8, + "location": 30, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-11-12T09:42:57.855817", + "updated_at": "2025-11-12T09:42:57.855817" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 47, + "fields": { + "user": 1, + "location": 30, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-05T08:38:57.855820", + "updated_at": "2025-12-05T08:38:57.855820" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 48, + "fields": { + "user": 8, + "location": 31, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-11-15T08:46:57.855825", + "updated_at": "2025-11-15T08:46:57.855825" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 49, + "fields": { + "user": 9, + "location": 31, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2025-12-30T20:49:57.855828", + "updated_at": "2025-12-30T20:49:57.855828" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 50, + "fields": { + "user": 10, + "location": 31, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-11-19T15:09:57.855831", + "updated_at": "2025-11-19T15:09:57.855831" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 51, + "fields": { + "user": 1, + "location": 32, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2026-01-04T07:26:57.855837", + "updated_at": "2026-01-04T07:26:57.855837" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 52, + "fields": { + "user": 9, + "location": 32, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-11-23T18:33:57.855840", + "updated_at": "2025-11-23T18:33:57.855840" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 53, + "fields": { + "user": 9, + "location": 33, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-12-27T14:02:57.855844", + "updated_at": "2025-12-27T14:02:57.855844" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 54, + "fields": { + "user": 8, + "location": 33, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-25T10:37:57.855847", + "updated_at": "2025-12-25T10:37:57.855847" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 55, + "fields": { + "user": 10, + "location": 34, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2026-01-01T15:26:57.855852", + "updated_at": "2026-01-01T15:26:57.855852" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 56, + "fields": { + "user": 8, + "location": 34, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-12-05T13:15:57.855855", + "updated_at": "2025-12-05T13:15:57.855855" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 57, + "fields": { + "user": 1, + "location": 34, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2025-11-14T12:45:57.855858", + "updated_at": "2025-11-14T12:45:57.855858" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 58, + "fields": { + "user": 8, + "location": 35, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-12-03T18:33:57.855862", + "updated_at": "2025-12-03T18:33:57.855862" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 59, + "fields": { + "user": 10, + "location": 36, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2025-11-21T09:45:57.855866", + "updated_at": "2025-11-21T09:45:57.855866" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 60, + "fields": { + "user": 1, + "location": 36, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-25T14:36:57.855870", + "updated_at": "2025-12-25T14:36:57.855870" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 61, + "fields": { + "user": 9, + "location": 36, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2025-11-14T22:03:57.855873", + "updated_at": "2025-11-14T22:03:57.855873" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 62, + "fields": { + "user": 8, + "location": 36, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-12-26T05:51:57.855876", + "updated_at": "2025-12-26T05:51:57.855876" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 63, + "fields": { + "user": 8, + "location": 37, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2025-12-14T13:47:57.855880", + "updated_at": "2025-12-14T13:47:57.855880" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 64, + "fields": { + "user": 10, + "location": 37, + "emotional_value": 1, + "privacy_status": "public", + "created_at": "2025-11-20T13:36:57.855884", + "updated_at": "2025-11-20T13:36:57.855884" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 65, + "fields": { + "user": 1, + "location": 37, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2025-11-16T20:28:57.855887", + "updated_at": "2025-11-16T20:28:57.855887" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 66, + "fields": { + "user": 9, + "location": 37, + "emotional_value": 1, + "privacy_status": "private", + "created_at": "2025-12-12T00:23:57.855890", + "updated_at": "2025-12-12T00:23:57.855890" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 67, + "fields": { + "user": 8, + "location": 39, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-16T01:25:57.855895", + "updated_at": "2025-12-16T01:25:57.855895" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 68, + "fields": { + "user": 1, + "location": 39, + "emotional_value": 2, + "privacy_status": "private", + "created_at": "2025-12-24T00:01:57.855898", + "updated_at": "2025-12-24T00:01:57.855898" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 69, + "fields": { + "user": 10, + "location": 40, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2026-01-09T07:46:57.855902", + "updated_at": "2026-01-09T07:46:57.855902" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 70, + "fields": { + "user": 9, + "location": 40, + "emotional_value": 3, + "privacy_status": "public", + "created_at": "2025-12-25T10:36:57.855905", + "updated_at": "2025-12-25T10:36:57.855905" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 71, + "fields": { + "user": 10, + "location": 42, + "emotional_value": 4, + "privacy_status": "private", + "created_at": "2025-12-13T16:10:57.855909", + "updated_at": "2025-12-13T16:10:57.855909" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 72, + "fields": { + "user": 8, + "location": 42, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-05T05:26:57.855913", + "updated_at": "2025-12-05T05:26:57.855913" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 73, + "fields": { + "user": 1, + "location": 45, + "emotional_value": 4, + "privacy_status": "public", + "created_at": "2025-12-07T17:59:57.855917", + "updated_at": "2025-12-07T17:59:57.855917" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 74, + "fields": { + "user": 8, + "location": 46, + "emotional_value": 3, + "privacy_status": "public", + "created_at": "2025-11-29T01:03:57.855921", + "updated_at": "2025-11-29T01:03:57.855921" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 75, + "fields": { + "user": 9, + "location": 47, + "emotional_value": 4, + "privacy_status": "private", + "created_at": "2025-11-25T02:10:57.855926", + "updated_at": "2025-11-25T02:10:57.855926" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 76, + "fields": { + "user": 9, + "location": 50, + "emotional_value": 3, + "privacy_status": "public", + "created_at": "2025-12-22T18:34:57.855931", + "updated_at": "2025-12-22T18:34:57.855931" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 77, + "fields": { + "user": 10, + "location": 50, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-16T13:55:57.855934", + "updated_at": "2025-12-16T13:55:57.855934" + } + }, + { + "model": "emotions.emotionpoint", + "pk": 78, + "fields": { + "user": 8, + "location": 50, + "emotional_value": 2, + "privacy_status": "public", + "created_at": "2025-12-21T23:07:57.855938", + "updated_at": "2025-12-21T23:07:57.855938" + } + }, + { + "model": "emotions.comment", + "pk": 1, + "fields": { + "user": 8, + "location": 1, + "emotion_point": null, + "content": "Najpiękniejsze miejsce w Trójmieście!", + "privacy_status": "public", + "created_at": "2026-01-01T11:40:57.855954" + } + }, + { + "model": "emotions.comment", + "pk": 2, + "fields": { + "user": 8, + "location": 20, + "emotion_point": null, + "content": "Przepięknie tutaj, polecam w 100%!", + "privacy_status": "public", + "created_at": "2025-12-11T21:46:57.855959" + } + }, + { + "model": "emotions.comment", + "pk": 3, + "fields": { + "user": 8, + "location": 19, + "emotion_point": null, + "content": "Świetne miejsce, super klimat!", + "privacy_status": "private", + "created_at": "2025-12-26T07:01:57.855964" + } + }, + { + "model": "emotions.comment", + "pk": 4, + "fields": { + "user": 1, + "location": 14, + "emotion_point": null, + "content": "Spoko miejsce, dobra atmosfera.", + "privacy_status": "public", + "created_at": "2025-12-29T13:09:57.855967" + } + }, + { + "model": "emotions.comment", + "pk": 5, + "fields": { + "user": 10, + "location": 28, + "emotion_point": null, + "content": "Koszmar! Straszne miejsce.", + "privacy_status": "public", + "created_at": "2025-11-13T08:13:57.855972" + } + }, + { + "model": "emotions.comment", + "pk": 6, + "fields": { + "user": 8, + "location": 38, + "emotion_point": null, + "content": "Średnio utrzymane.", + "privacy_status": "private", + "created_at": "2025-11-22T21:03:57.855976" + } + }, + { + "model": "emotions.comment", + "pk": 7, + "fields": { + "user": 10, + "location": 39, + "emotion_point": null, + "content": "Czysto, zadbane, polecam.", + "privacy_status": "public", + "created_at": "2025-11-23T20:14:57.855979" + } + }, + { + "model": "emotions.comment", + "pk": 8, + "fields": { + "user": 9, + "location": 42, + "emotion_point": null, + "content": "Przeciętnie, nic ciekawego.", + "privacy_status": "private", + "created_at": "2025-12-01T04:25:57.855983" + } + }, + { + "model": "emotions.comment", + "pk": 9, + "fields": { + "user": 10, + "location": 21, + "emotion_point": null, + "content": "Bardzo mi się podobało!", + "privacy_status": "public", + "created_at": "2025-12-24T01:22:57.855986" + } + }, + { + "model": "emotions.comment", + "pk": 10, + "fields": { + "user": 1, + "location": 30, + "emotion_point": null, + "content": "Strata czasu i pieniędzy.", + "privacy_status": "public", + "created_at": "2025-12-16T13:33:57.855990" + } + }, + { + "model": "emotions.comment", + "pk": 11, + "fields": { + "user": 8, + "location": 29, + "emotion_point": null, + "content": "Tłoczno, hałaśliwie, nieznośnie.", + "privacy_status": "private", + "created_at": "2025-12-19T18:13:57.855993" + } + }, + { + "model": "emotions.comment", + "pk": 12, + "fields": { + "user": 10, + "location": 29, + "emotion_point": null, + "content": "Tragedia, bardzo źle.", + "privacy_status": "private", + "created_at": "2025-12-16T16:58:57.855997" + } + }, + { + "model": "emotions.comment", + "pk": 13, + "fields": { + "user": 1, + "location": 40, + "emotion_point": null, + "content": "Spoko miejsce, dobra atmosfera.", + "privacy_status": "private", + "created_at": "2026-01-10T00:23:57.856000" + } + }, + { + "model": "emotions.comment", + "pk": 14, + "fields": { + "user": 10, + "location": 40, + "emotion_point": null, + "content": "Takie sobie.", + "privacy_status": "public", + "created_at": "2025-12-10T04:25:57.856003" + } + }, + { + "model": "emotions.comment", + "pk": 15, + "fields": { + "user": 10, + "location": 47, + "emotion_point": null, + "content": "W porządku, nic specjalnego.", + "privacy_status": "private", + "created_at": "2025-12-19T13:59:57.856006" + } + }, + { + "model": "emotions.comment", + "pk": 16, + "fields": { + "user": 10, + "location": 47, + "emotion_point": null, + "content": "Przyjemne miejsce, spędziłem tu świetny czas.", + "privacy_status": "private", + "created_at": "2025-11-13T07:03:57.856010" + } + }, + { + "model": "emotions.comment", + "pk": 17, + "fields": { + "user": 1, + "location": 33, + "emotion_point": null, + "content": "Mogło być zdecydowanie lepiej.", + "privacy_status": "public", + "created_at": "2026-01-04T15:15:57.856014" + } + }, + { + "model": "emotions.comment", + "pk": 18, + "fields": { + "user": 8, + "location": 33, + "emotion_point": null, + "content": "Nie wrócę tu ponownie.", + "privacy_status": "public", + "created_at": "2025-12-24T23:22:57.856017" + } + }, + { + "model": "emotions.comment", + "pk": 19, + "fields": { + "user": 8, + "location": 31, + "emotion_point": null, + "content": "Niezbyt, są lepsze miejsca.", + "privacy_status": "private", + "created_at": "2025-11-22T23:25:57.856021" + } + }, + { + "model": "emotions.comment", + "pk": 20, + "fields": { + "user": 10, + "location": 31, + "emotion_point": null, + "content": "Rozczarowanie, spodziewałem się więcej.", + "privacy_status": "private", + "created_at": "2026-01-08T12:08:57.856024" + } + }, + { + "model": "emotions.comment", + "pk": 21, + "fields": { + "user": 1, + "location": 11, + "emotion_point": null, + "content": "Najpiękniejsze miejsce w Trójmieście!", + "privacy_status": "public", + "created_at": "2025-11-21T15:40:57.856027" + } + }, + { + "model": "emotions.comment", + "pk": 22, + "fields": { + "user": 1, + "location": 11, + "emotion_point": null, + "content": "Ładne miejsce, polecam na spacer.", + "privacy_status": "public", + "created_at": "2025-12-02T07:27:57.856030" + } + }, + { + "model": "emotions.comment", + "pk": 23, + "fields": { + "user": 8, + "location": 6, + "emotion_point": null, + "content": "Absolutnie przepiękne! Wrócę tu na pewno.", + "privacy_status": "public", + "created_at": "2025-12-11T13:26:57.856034" + } + }, + { + "model": "emotions.comment", + "pk": 24, + "fields": { + "user": 9, + "location": 6, + "emotion_point": null, + "content": "Polecam, naprawdę warto!", + "privacy_status": "public", + "created_at": "2025-11-22T06:23:57.856037" + } + }, + { + "model": "emotions.comment", + "pk": 25, + "fields": { + "user": 1, + "location": 48, + "emotion_point": null, + "content": "Bardzo fajnie, polecam!", + "privacy_status": "private", + "created_at": "2025-12-04T13:44:57.856040" + } + }, + { + "model": "emotions.comment", + "pk": 26, + "fields": { + "user": 10, + "location": 48, + "emotion_point": null, + "content": "Można przyjść, ale nie jest to must-see.", + "privacy_status": "public", + "created_at": "2025-12-03T12:49:57.856044" + } + }, + { + "model": "emotions.comment", + "pk": 27, + "fields": { + "user": 8, + "location": 48, + "emotion_point": null, + "content": "Przeciętnie, nic ciekawego.", + "privacy_status": "public", + "created_at": "2025-11-20T16:52:57.856047" + } + }, + { + "model": "emotions.comment", + "pk": 28, + "fields": { + "user": 1, + "location": 22, + "emotion_point": null, + "content": "Rewelacja! Najlepsze miejsce ever.", + "privacy_status": "private", + "created_at": "2025-11-29T00:38:57.856050" + } + }, + { + "model": "emotions.comment", + "pk": 29, + "fields": { + "user": 9, + "location": 22, + "emotion_point": null, + "content": "Spoko miejsce, dobra atmosfera.", + "privacy_status": "private", + "created_at": "2026-01-03T22:19:57.856054" + } + }, + { + "model": "emotions.comment", + "pk": 30, + "fields": { + "user": 10, + "location": 22, + "emotion_point": null, + "content": "Spoko miejsce, dobra atmosfera.", + "privacy_status": "public", + "created_at": "2025-11-24T19:01:57.856057" + } + }, + { + "model": "emotions.comment", + "pk": 31, + "fields": { + "user": 9, + "location": 3, + "emotion_point": null, + "content": "Świetne miejsce, super klimat!", + "privacy_status": "private", + "created_at": "2025-11-18T12:05:57.856060" + } + }, + { + "model": "emotions.comment", + "pk": 32, + "fields": { + "user": 9, + "location": 3, + "emotion_point": null, + "content": "Najpiękniejsze miejsce w Trójmieście!", + "privacy_status": "public", + "created_at": "2025-12-23T21:27:57.856063" + } + }, + { + "model": "emotions.comment", + "pk": 33, + "fields": { + "user": 10, + "location": 3, + "emotion_point": null, + "content": "Idealne na romantyczny spacer.", + "privacy_status": "public", + "created_at": "2025-12-10T01:31:57.856067" + } + }, + { + "model": "emotions.comment", + "pk": 34, + "fields": { + "user": 1, + "location": 37, + "emotion_point": 63, + "content": "To prawda, tak było.", + "privacy_status": "private", + "created_at": "2025-12-23T06:47:57.855880" + } + }, + { + "model": "emotions.comment", + "pk": 35, + "fields": { + "user": 8, + "location": 21, + "emotion_point": 28, + "content": "Ciekawe, ja miałem zupełnie inne wrażenia.", + "privacy_status": "private", + "created_at": "2026-01-16T15:38:57.855749" + } + }, + { + "model": "emotions.comment", + "pk": 36, + "fields": { + "user": 1, + "location": 30, + "emotion_point": 46, + "content": "Zgadzam się w 100%!", + "privacy_status": "private", + "created_at": "2025-11-16T21:42:57.855817" + } + }, + { + "model": "emotions.comment", + "pk": 37, + "fields": { + "user": 8, + "location": 24, + "emotion_point": 34, + "content": "Podzielam twoją opinię.", + "privacy_status": "private", + "created_at": "2026-01-04T02:14:57.855772" + } + }, + { + "model": "emotions.comment", + "pk": 38, + "fields": { + "user": 9, + "location": 29, + "emotion_point": 44, + "content": "Ja też tak uważam.", + "privacy_status": "private", + "created_at": "2025-12-10T15:09:57.855810" + } + }, + { + "model": "emotions.comment", + "pk": 39, + "fields": { + "user": 10, + "location": 25, + "emotion_point": 36, + "content": "Podzielam twoją opinię.", + "privacy_status": "private", + "created_at": "2025-11-21T05:45:57.855781" + } + }, + { + "model": "emotions.comment", + "pk": 40, + "fields": { + "user": 1, + "location": 45, + "emotion_point": 73, + "content": "Ciekawe, ja miałem zupełnie inne wrażenia.", + "privacy_status": "public", + "created_at": "2025-12-13T20:59:57.855917" + } + }, + { + "model": "emotions.comment", + "pk": 41, + "fields": { + "user": 8, + "location": 1, + "emotion_point": 2, + "content": "To prawda, tak było.", + "privacy_status": "public", + "created_at": "2025-12-04T17:40:57.855639" + } + }, + { + "model": "emotions.comment", + "pk": 42, + "fields": { + "user": 9, + "location": 39, + "emotion_point": 67, + "content": "Zgadzam się w 100%!", + "privacy_status": "public", + "created_at": "2025-12-21T08:25:57.855895" + } + }, + { + "model": "emotions.comment", + "pk": 43, + "fields": { + "user": 9, + "location": 19, + "emotion_point": 25, + "content": "Mam podobne odczucia.", + "privacy_status": "private", + "created_at": "2025-12-09T19:35:57.855738" + } + } +] \ No newline at end of file diff --git a/cityfeel/fixtures/comment_templates.py b/cityfeel/fixtures/comment_templates.py new file mode 100644 index 0000000..e666926 --- /dev/null +++ b/cityfeel/fixtures/comment_templates.py @@ -0,0 +1,100 @@ +""" +Szablony komentarzy w języku polskim dla fixtures CityFeel. +Komentarze są podzielone na kategorie odpowiadające emotional_value (1-5). +""" + +# Komentarze dla emotional_value = 5 (bardzo pozytywne) +COMMENTS_VERY_POSITIVE = [ + "Fantastyczne miejsce! Koniecznie trzeba odwiedzić!", + "Absolutnie przepiękne! Wrócę tu na pewno.", + "Wspaniałe doświadczenie, polecam każdemu!", + "Najpiękniejsze miejsce w Trójmieście!", + "Magiczne! Zachwycona atmosferą.", + "Idealne na romantyczny spacer.", + "Cudowny widok, niesamowita energia!", + "Świetne miejsce, super klimat!", + "Rewelacja! Najlepsze miejsce ever.", + "Przepięknie tutaj, polecam w 100%!", +] + +# Komentarze dla emotional_value = 4 (pozytywne) +COMMENTS_POSITIVE = [ + "Bardzo fajnie, polecam!", + "Miła atmosfera, warto odwiedzić.", + "Przyjemne miejsce, spędziłem tu świetny czas.", + "Ładne miejsce, polecam na spacer.", + "Bardzo mi się podobało!", + "Fajne miejsce, wrócę tu jeszcze.", + "Spoko miejsce, dobra atmosfera.", + "Czysto, zadbane, polecam.", + "Świetnie spędzony czas!", + "Polecam, naprawdę warto!", +] + +# Komentarze dla emotional_value = 3 (neutralne) +COMMENTS_NEUTRAL = [ + "W porządku, nic specjalnego.", + "Całkiem niezłe miejsce.", + "Standard, jak wszędzie.", + "Można przyjść, ale nie jest to must-see.", + "Przeciętnie, spodziewałem się więcej.", + "Okej, ale są lepsze miejsca.", + "Średnio, nic szczególnego.", + "W miarę ok, nic wybitnego.", + "Takie sobie.", + "Normalnie, bez szału.", +] + +# Komentarze dla emotional_value = 2 (negatywne) +COMMENTS_NEGATIVE = [ + "Nie polecam, mogło być lepiej.", + "Rozczarowanie, spodziewałem się więcej.", + "Nie wrócę tu ponownie.", + "Słabe miejsce, nie warto.", + "Przeciętnie, nic ciekawego.", + "Niezbyt, są lepsze miejsca.", + "Nie jestem zadowolony.", + "Słabo, nie polecam.", + "Średnio utrzymane.", + "Mogło być zdecydowanie lepiej.", +] + +# Komentarze dla emotional_value = 1 (bardzo negatywne) +COMMENTS_VERY_NEGATIVE = [ + "Okropne miejsce, nie polecam!", + "Fatalnie! Nigdy więcej!", + "Strata czasu i pieniędzy.", + "Tragedia, bardzo źle.", + "Koszmarne doświadczenie.", + "Brudno, zaniedbane, okropne.", + "Tłoczno, hałaśliwie, nieznośnie.", + "Bardzo słabo, nie polecam nikomu!", + "Koszmar! Straszne miejsce.", + "Najgorsze miejsce jakie odwiedziłem.", +] + +# Komentarze odpowiedzi (do EmotionPoint) - uniwersalne +COMMENTS_REPLY = [ + "Dokładnie tak myślę!", + "Zgadzam się w 100%!", + "Mam podobne odczucia.", + "To prawda, tak było.", + "Podzielam twoją opinię.", + "Całkowicie się zgadzam!", + "Ja też tak uważam.", + "Dokładnie! Tak jest!", + "Mam identyczne wrażenia.", + "Świetnie ujęte, zgadzam się!", + "Nie zgadzam się, moje doświadczenie było inne.", + "Ciekawe, ja miałem zupełnie inne wrażenia.", + "Różnie bywa, ja byłem bardziej zadowolony.", +] + +# Mapowanie emotional_value na szablony komentarzy +COMMENT_TEMPLATES_BY_VALUE = { + 1: COMMENTS_VERY_NEGATIVE, + 2: COMMENTS_NEGATIVE, + 3: COMMENTS_NEUTRAL, + 4: COMMENTS_POSITIVE, + 5: COMMENTS_VERY_POSITIVE, +} diff --git a/cityfeel/fixtures/generate_fixtures.py b/cityfeel/fixtures/generate_fixtures.py new file mode 100644 index 0000000..d571932 --- /dev/null +++ b/cityfeel/fixtures/generate_fixtures.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +""" +Skrypt generujący Django fixtures dla projektu CityFeel. + +Generuje: +- 50 lokalizacji w Trójmieście +- ~78 EmotionPoints (0-4 per lokalizacja) +- ~43 komentarze (do Location + EmotionPoint) + +Użycie: + python cityfeel/fixtures/generate_fixtures.py + # lub: + uv run cityfeel/fixtures/generate_fixtures.py +""" + +import json +import random +import os +import sys +from datetime import datetime, timedelta + +# Dodaj cityfeel/fixtures do PYTHONPATH +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from locations_data import LOCATIONS, CLUSTER_EMOTIONS +from comment_templates import COMMENT_TEMPLATES_BY_VALUE, COMMENTS_REPLY + +# Konfiguracja +USERS = [1, 8, 9, 10] # admin, starluna, byteforge, emberleaf +PRIVACY_WEIGHTS = [0.7, 0.3] # 70% public, 30% private +OUTPUT_FILE = "cityfeel/fixtures/cityfeel_data.json" + +# Seed dla powtarzalności (opcjonalnie) +random.seed(42) + + +def generate_location(pk, data): + """ + Generuje Location fixture z formatem EWKT dla PostGIS. + + Args: + pk: Primary key + data: Słownik z name, lon, lat + + Returns: + Dict z fixture Location + """ + return { + "model": "map.location", + "pk": pk, + "fields": { + "name": data["name"], + # Format EWKT: SRID=4326;POINT(longitude latitude) - WAŻNE: przestrzeń, nie przecinek! + "coordinates": f"SRID=4326;POINT({data['lon']} {data['lat']})" + } + } + + +def generate_emotion_points(locations): + """ + Generuje EmotionPoints z walidacją unique_together(user, location). + + Rozkład: + - 10 lokalizacji × 0 emocji = 0 + - 15 lokalizacji × 1 emocja = 15 + - 15 lokalizacji × 2 emocje = 30 + - 7 lokalizacji × 3 emocje = 21 + - 3 lokalizacje × 4 emocje = 12 + Total: 78 EmotionPoints + + Args: + locations: Lista słowników z danymi lokalizacji + + Returns: + Lista fixtures EmotionPoint + """ + emotion_points = [] + pk = 1 + + # Rozkład ilości emocji per lokalizacja + emotion_counts = [0] * 10 + [1] * 15 + [2] * 15 + [3] * 7 + [4] * 3 + random.shuffle(emotion_counts) + + for loc_idx, count in enumerate(emotion_counts): + location = locations[loc_idx] + location_pk = loc_idx + 1 + + # Wybierz unikalnych użytkowników dla tej lokalizacji + # (zapewnia constraint unique_together(user, location)) + users_for_location = random.sample(USERS, count) + + for user_id in users_for_location: + # Emotional value bazując na cluster + cluster = location.get("cluster", "neutral") + emotional_values = CLUSTER_EMOTIONS.get(cluster, [2, 3, 4]) + emotional_value = random.choice(emotional_values) + + # Privacy status (70% public, 30% private) + privacy_status = random.choices(["public", "private"], weights=PRIVACY_WEIGHTS)[0] + + # Timestamp - losowy w ostatnich 60 dniach + days_ago = random.randint(1, 60) + hours_ago = random.randint(0, 23) + minutes_ago = random.randint(0, 59) + created_at = ( + datetime.now() - timedelta(days=days_ago, hours=hours_ago, minutes=minutes_ago) + ).isoformat() + + emotion_points.append({ + "model": "emotions.emotionpoint", + "pk": pk, + "fields": { + "user": user_id, + "location": location_pk, + "emotional_value": emotional_value, + "privacy_status": privacy_status, + "created_at": created_at, + "updated_at": created_at + } + }) + pk += 1 + + return emotion_points + + +def generate_comments(locations, emotion_points): + """ + Generuje Comments (do Location i do EmotionPoint). + + - 40% lokalizacji (20 lokalizacji): 0-3 komentarze per lokalizacja + - 15% lokalizacji (~8): komentarz per EmotionPoint + + Args: + locations: Lista słowników z danymi lokalizacji + emotion_points: Lista fixtures EmotionPoint + + Returns: + Lista fixtures Comment + """ + comments = [] + pk = 1 + + # === KOMENTARZE DO LOKALIZACJI (40% = 20 lokalizacji) === + locations_with_comments = random.sample(range(50), 20) + + # Rozkład: 10×1, 7×2, 3×3 komentarze + comment_counts = [1] * 10 + [2] * 7 + [3] * 3 + + for loc_idx, count in zip(locations_with_comments, comment_counts): + location_pk = loc_idx + 1 + location = locations[loc_idx] + + for _ in range(count): + user_id = random.choice(USERS) + + # Treść komentarza bazując na cluster lokalizacji + cluster = location.get("cluster", "neutral") + if "positive" in cluster: + # Pozytywne komentarze (4-5) + emotional_value = random.choice([4, 5]) + elif "negative" in cluster: + # Negatywne komentarze (1-2) + emotional_value = random.choice([1, 2]) + else: + # Neutralne/mieszane (2-4) + emotional_value = random.choice([2, 3, 4]) + + content = random.choice(COMMENT_TEMPLATES_BY_VALUE[emotional_value]) + + privacy_status = random.choice(["public", "private"]) + + # Timestamp - losowy w ostatnich 60 dniach + days_ago = random.randint(1, 60) + hours_ago = random.randint(0, 23) + minutes_ago = random.randint(0, 59) + created_at = ( + datetime.now() - timedelta(days=days_ago, hours=hours_ago, minutes=minutes_ago) + ).isoformat() + + comments.append({ + "model": "emotions.comment", + "pk": pk, + "fields": { + "user": user_id, + "location": location_pk, # REQUIRED! + "emotion_point": None, # Komentarz do Location + "content": content, + "privacy_status": privacy_status, + "created_at": created_at + } + }) + pk += 1 + + # === KOMENTARZE DO EMOTIONPOINT (15% lokalizacji ~= 8 EmotionPoints) === + if len(emotion_points) > 0: + num_ep_comments = min(10, len(emotion_points)) + emotion_points_with_comments = random.sample(emotion_points, num_ep_comments) + + for ep in emotion_points_with_comments: + user_id = random.choice(USERS) + ep_pk = ep["pk"] + location_pk = ep["fields"]["location"] + + # Komentarz odpowiedzi + content = random.choice(COMMENTS_REPLY) + + privacy_status = random.choice(["public", "private"]) + + # Timestamp - później niż EmotionPoint + ep_created = datetime.fromisoformat(ep["fields"]["created_at"]) + days_after = random.randint(1, 10) + hours_after = random.randint(0, 23) + created_at = (ep_created + timedelta(days=days_after, hours=hours_after)).isoformat() + + comments.append({ + "model": "emotions.comment", + "pk": pk, + "fields": { + "user": user_id, + "location": location_pk, # REQUIRED! + "emotion_point": ep_pk, # FK do EmotionPoint + "content": content, + "privacy_status": privacy_status, + "created_at": created_at + } + }) + pk += 1 + + return comments + + +def validate_unique_together(emotion_points): + """ + Waliduje constraint unique_together(user, location). + + Args: + emotion_points: Lista fixtures EmotionPoint + + Raises: + ValueError: Jeśli constraint jest naruszony + """ + seen = set() + for ep in emotion_points: + user_id = ep["fields"]["user"] + location_id = ep["fields"]["location"] + key = (user_id, location_id) + + if key in seen: + raise ValueError(f"Duplicate (user, location): {key}") + seen.add(key) + + print("✓ Constraint unique_together validated") + + +def validate_foreign_keys(emotion_points, comments, location_count): + """ + Waliduje poprawność foreign keys. + + Args: + emotion_points: Lista fixtures EmotionPoint + comments: Lista fixtures Comment + location_count: Ilość lokalizacji + + Raises: + AssertionError: Jeśli FK są niepoprawne + """ + valid_users = [1, 8, 9, 10] + valid_locations = list(range(1, location_count + 1)) + valid_emotion_points = [ep["pk"] for ep in emotion_points] + + # Walidacja EmotionPoints + for ep in emotion_points: + assert ep["fields"]["user"] in valid_users, f"Invalid user FK: {ep['fields']['user']}" + assert ep["fields"]["location"] in valid_locations, f"Invalid location FK: {ep['fields']['location']}" + + # Walidacja Comments + for comment in comments: + assert comment["fields"]["user"] in valid_users, f"Invalid user FK: {comment['fields']['user']}" + assert comment["fields"]["location"] in valid_locations, f"Invalid location FK: {comment['fields']['location']}" + + ep_id = comment["fields"]["emotion_point"] + if ep_id is not None: + assert ep_id in valid_emotion_points, f"Invalid emotion_point FK: {ep_id}" + + print("✓ Foreign keys validated") + + +def main(): + """ + Główna funkcja generująca fixtures. + """ + print("=" * 60) + print("CityFeel Fixtures Generator") + print("=" * 60) + + fixtures = [] + + # 1. Generuj Locations + print("\n[1/5] Generating locations...") + location_fixtures = [generate_location(idx + 1, loc) for idx, loc in enumerate(LOCATIONS)] + fixtures.extend(location_fixtures) + print(f" ✓ Generated {len(location_fixtures)} locations") + + # 2. Generuj EmotionPoints + print("\n[2/5] Generating emotion points...") + emotion_fixtures = generate_emotion_points(LOCATIONS) + fixtures.extend(emotion_fixtures) + print(f" ✓ Generated {len(emotion_fixtures)} emotion points") + + # 3. Generuj Comments + print("\n[3/5] Generating comments...") + comment_fixtures = generate_comments(LOCATIONS, emotion_fixtures) + fixtures.extend(comment_fixtures) + print(f" ✓ Generated {len(comment_fixtures)} comments") + + # 4. Walidacja + print("\n[4/5] Validating data...") + validate_unique_together(emotion_fixtures) + validate_foreign_keys(emotion_fixtures, comment_fixtures, len(location_fixtures)) + + # 5. Zapisz do JSON + print("\n[5/5] Writing to JSON...") + output_path = OUTPUT_FILE + os.makedirs(os.path.dirname(output_path), exist_ok=True) + + with open(output_path, "w", encoding="utf-8") as f: + json.dump(fixtures, f, ensure_ascii=False, indent=2) + + print(f" ✓ Saved to: {output_path}") + + # Podsumowanie + print("\n" + "=" * 60) + print("SUMMARY") + print("=" * 60) + print(f"Total objects: {len(fixtures)}") + print(f" - Locations: {len(location_fixtures)}") + print(f" - EmotionPoints: {len(emotion_fixtures)}") + print(f" - Comments: {len(comment_fixtures)}") + print("\nNext steps:") + print(f" 1. Validate JSON: uv run python -m json.tool {output_path} > /dev/null && echo 'JSON valid'") + print(f" 2. Load to DB: uv run cityfeel/manage.py loaddata {output_path}") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/cityfeel/fixtures/locations_data.py b/cityfeel/fixtures/locations_data.py new file mode 100644 index 0000000..37a5fe1 --- /dev/null +++ b/cityfeel/fixtures/locations_data.py @@ -0,0 +1,95 @@ +""" +Dane 50 lokalizacji w Trójmieście dla fixtures CityFeel. +Każda lokalizacja ma: name, lon (longitude), lat (latitude), cluster. + +Cluster określa typ lokalizacji i wpływa na rozkład emotional_value: +- sopot_positive: Sopot turystyczny (emotional_value 4-5) +- gdansk_oldtown_positive: Gdańsk Stare Miasto (emotional_value 4-5) +- gdynia_bulwary_positive: Gdynia bulwary (emotional_value 4-5) +- gdansk_peripheral_negative: Gdańsk dzielnice peryferyjne (emotional_value 1-2) +- gdynia_residential_negative: Gdynia dzielnice mieszkalne (emotional_value 1-2) +- neutral: Lokalizacje mieszane (emotional_value 2-4) +""" + +LOCATIONS = [ + # Sopot - Strefa Turystyczna (10 lokalizacji) - POZYTYWNE + {"name": "Molo w Sopocie", "lon": 18.5695, "lat": 54.4438, "cluster": "sopot_positive"}, + {"name": "Krzywy Domek", "lon": 18.5672, "lat": 54.4420, "cluster": "sopot_positive"}, + {"name": "Plaża Sopocka", "lon": 18.5700, "lat": 54.4415, "cluster": "sopot_positive"}, + {"name": "Ulica Monte Cassino", "lon": 18.5665, "lat": 54.4425, "cluster": "sopot_positive"}, + {"name": "Łazienki Północne Sopot", "lon": 18.5680, "lat": 54.4445, "cluster": "sopot_positive"}, + {"name": "Kawiarnia na Żeromskiego", "lon": 18.5668, "lat": 54.4432, "cluster": "sopot_positive"}, + {"name": "Park Północny Sopot", "lon": 18.5675, "lat": 54.4450, "cluster": "sopot_positive"}, + {"name": "Opera Leśna", "lon": 18.5620, "lat": 54.4380, "cluster": "sopot_positive"}, + {"name": "Sopockie Muszle", "lon": 18.5688, "lat": 54.4412, "cluster": "sopot_positive"}, + {"name": "Restauracja Przystań Sopot", "lon": 18.5710, "lat": 54.4428, "cluster": "sopot_positive"}, + + # Gdańsk - Stare Miasto (8 lokalizacji) - POZYTYWNE + {"name": "Długi Targ", "lon": 18.6538, "lat": 54.3489, "cluster": "gdansk_oldtown_positive"}, + {"name": "Bazylika Mariacka", "lon": 18.6530, "lat": 54.3490, "cluster": "gdansk_oldtown_positive"}, + {"name": "Fontanna Neptuna", "lon": 18.6536, "lat": 54.3486, "cluster": "gdansk_oldtown_positive"}, + {"name": "Złota Brama", "lon": 18.6525, "lat": 54.3505, "cluster": "gdansk_oldtown_positive"}, + {"name": "Ulica Mariacka", "lon": 18.6545, "lat": 54.3485, "cluster": "gdansk_oldtown_positive"}, + {"name": "Gdańskie Koło Widokowe", "lon": 18.6555, "lat": 54.3492, "cluster": "gdansk_oldtown_positive"}, + {"name": "Muzeum Bursztynu", "lon": 18.6520, "lat": 54.3510, "cluster": "gdansk_oldtown_positive"}, + {"name": "Kawiarnia Drukarnia", "lon": 18.6528, "lat": 54.3495, "cluster": "gdansk_oldtown_positive"}, + + # Gdynia - Bulwary i Marina (7 lokalizacji) - POZYTYWNE + {"name": "Molo w Gdyni", "lon": 18.5515, "lat": 54.5195, "cluster": "gdynia_bulwary_positive"}, + {"name": "Bulwary Gdyńskie", "lon": 18.5505, "lat": 54.5180, "cluster": "gdynia_bulwary_positive"}, + {"name": "Akwarium Gdyńskie", "lon": 18.5520, "lat": 54.5188, "cluster": "gdynia_bulwary_positive"}, + {"name": "Plaża Miejska Gdynia", "lon": 18.5495, "lat": 54.5170, "cluster": "gdynia_bulwary_positive"}, + {"name": "Skwer Kościuszki", "lon": 18.5540, "lat": 54.5190, "cluster": "gdynia_bulwary_positive"}, + {"name": "Marina Gdynia", "lon": 18.5525, "lat": 54.5205, "cluster": "gdynia_bulwary_positive"}, + {"name": "Kamienica Żeromskiego", "lon": 18.5535, "lat": 54.5175, "cluster": "gdynia_bulwary_positive"}, + + # Gdańsk - Dzielnice Peryferyjne (8 lokalizacji) - NEGATYWNE + {"name": "Parking Zaspa", "lon": 18.6125, "lat": 54.3755, "cluster": "gdansk_peripheral_negative"}, + {"name": "Przystanek PKM Zaspa", "lon": 18.6145, "lat": 54.3765, "cluster": "gdansk_peripheral_negative"}, + {"name": "Sklep Biedronka Zaspa", "lon": 18.6130, "lat": 54.3750, "cluster": "gdansk_peripheral_negative"}, + {"name": "Osiedle Chełm", "lon": 18.5980, "lat": 54.3950, "cluster": "gdansk_peripheral_negative"}, + {"name": "Parking Przymorze", "lon": 18.6025, "lat": 54.4050, "cluster": "gdansk_peripheral_negative"}, + {"name": "Dworzec PKS Wrzeszcz", "lon": 18.6085, "lat": 54.3820, "cluster": "gdansk_peripheral_negative"}, + {"name": "Galeria Metropolia", "lon": 18.6010, "lat": 54.4020, "cluster": "gdansk_peripheral_negative"}, + {"name": "Rondo Sobótki", "lon": 18.6095, "lat": 54.3740, "cluster": "gdansk_peripheral_negative"}, + + # Gdynia - Dzielnice Mieszkalne (5 lokalizacji) - NEGATYWNE + {"name": "Dworzec PKP Gdynia Główna", "lon": 18.5385, "lat": 54.5210, "cluster": "gdynia_residential_negative"}, + {"name": "Parking Chwarzno", "lon": 18.5245, "lat": 54.5025, "cluster": "gdynia_residential_negative"}, + {"name": "Osiedle Witomino", "lon": 18.5185, "lat": 54.5425, "cluster": "gdynia_residential_negative"}, + {"name": "Centrum Handlowe Klif", "lon": 18.5325, "lat": 54.5295, "cluster": "gdynia_residential_negative"}, + {"name": "Sklep Żabka Chylonia", "lon": 18.5105, "lat": 54.4950, "cluster": "gdynia_residential_negative"}, + + # Gdańsk - Parki i Inne (7 lokalizacji) - MIESZANE + {"name": "Park Oliwski", "lon": 18.5710, "lat": 54.4075, "cluster": "neutral"}, + {"name": "Westerplatte", "lon": 18.6720, "lat": 54.4065, "cluster": "neutral"}, + {"name": "Ogród Botaniczny UG", "lon": 18.5795, "lat": 54.3955, "cluster": "neutral"}, + {"name": "Parking Oliwa", "lon": 18.5725, "lat": 54.4085, "cluster": "neutral"}, + {"name": "Plaża Stogi", "lon": 18.6820, "lat": 54.3615, "cluster": "neutral"}, + {"name": "PKS Gdańsk Oliwa", "lon": 18.5705, "lat": 54.4090, "cluster": "neutral"}, + {"name": "Jarmark Dominikański", "lon": 18.6585, "lat": 54.3515, "cluster": "neutral"}, + + # Sopot - Poza Centrum (5 lokalizacji) - MIESZANE + {"name": "Aquapark Sopot", "lon": 18.5455, "lat": 54.4295, "cluster": "neutral"}, + {"name": "Park Brodwino", "lon": 18.5555, "lat": 54.4525, "cluster": "neutral"}, + {"name": "SKM Sopot Kamienny Potok", "lon": 18.5525, "lat": 54.4365, "cluster": "neutral"}, + {"name": "Ergo Arena", "lon": 18.5815, "lat": 54.4145, "cluster": "neutral"}, + {"name": "Lidl Sopot Karlikowo", "lon": 18.5485, "lat": 54.4335, "cluster": "neutral"}, +] + +# Mapowanie clusterów na emotional_value ranges +CLUSTER_EMOTIONS = { + "sopot_positive": [4, 5], + "gdansk_oldtown_positive": [4, 5], + "gdynia_bulwary_positive": [4, 5], + "gdansk_peripheral_negative": [1, 2], + "gdynia_residential_negative": [1, 2], + "neutral": [2, 3, 4], +} + +# Weryfikacja ilości lokalizacji +assert len(LOCATIONS) == 50, f"Expected 50 locations, got {len(LOCATIONS)}" + +# Weryfikacja unikalności nazw +names = [loc["name"] for loc in LOCATIONS] +assert len(names) == len(set(names)), "Location names must be unique!" From fd927ee6cb9a2bf07465a0e998e4b3a42d2ec333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sosnowski?= Date: Sun, 11 Jan 2026 11:56:18 +0100 Subject: [PATCH 3/4] Aktualizacja OpenApi Schema --- cityfeel/api/spec/schema.yml | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/cityfeel/api/spec/schema.yml b/cityfeel/api/spec/schema.yml index 4cc56e4..25d4048 100644 --- a/cityfeel/api/spec/schema.yml +++ b/cityfeel/api/spec/schema.yml @@ -466,12 +466,6 @@ paths: name: name schema: type: string - - name: page - required: false - in: query - description: A page number within the paginated result set. - schema: - type: integer - in: query name: radius schema: @@ -485,7 +479,9 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PaginatedLocationListList' + type: array + items: + $ref: '#/components/schemas/LocationList' description: '' /api/locations/{id}/: get: @@ -891,29 +887,6 @@ components: type: array items: $ref: '#/components/schemas/EmotionPoint' - PaginatedLocationListList: - type: object - required: - - count - - results - properties: - count: - type: integer - example: 123 - next: - type: string - nullable: true - format: uri - example: http://api.example.org/accounts/?page=4 - previous: - type: string - nullable: true - format: uri - example: http://api.example.org/accounts/?page=2 - results: - type: array - items: - $ref: '#/components/schemas/LocationList' PatchedEmotionPoint: type: object properties: From 706f6bfb0118ece0e3e13a8625d7cf762998562d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sosnowski?= Date: Sun, 11 Jan 2026 12:07:35 +0100 Subject: [PATCH 4/4] Naprawa testow po usuniecia paginacji dla api locations --- cityfeel/api/tests.py | 58 +++++++------------ .../emotions/tests_privacy_integration.py | 12 ++-- cityfeel/tests_integration.py | 8 +-- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/cityfeel/api/tests.py b/cityfeel/api/tests.py index fc6156f..fefcfba 100644 --- a/cityfeel/api/tests.py +++ b/cityfeel/api/tests.py @@ -637,8 +637,8 @@ def test_list_locations_authenticated(self): response = self.client.get(self.url) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertIn('results', response.data) # Pagination - self.assertEqual(len(response.data['results']), 3) + self.assertIsInstance(response.data, list) # No pagination - direct array + self.assertEqual(len(response.data), 3) def test_list_locations_unauthenticated(self): """Test GET /api/locations/ - unauthorized returns 401/403.""" @@ -660,7 +660,7 @@ def test_location_response_structure(self): self.client.force_authenticate(user=self.user) response = self.client.get(self.url) - location_data = response.data['results'][0] + location_data = response.data[0] required_fields = ['id', 'name', 'coordinates', 'avg_emotional_value'] for field in required_fields: @@ -676,9 +676,9 @@ def test_avg_emotional_value_calculation(self): self.client.force_authenticate(user=self.user) response = self.client.get(self.url) - # Znajdź location1 w results + # Znajdź location1 w response location1_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location1.id), + (loc for loc in response.data if loc['id'] == self.location1.id), None ) @@ -693,7 +693,7 @@ def test_avg_emotional_value_includes_all_points(self): # location3 ma: prywatny=4, publiczny=3, avg=(4+3)/2=3.5 location3_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location3.id), + (loc for loc in response.data if loc['id'] == self.location3.id), None ) @@ -706,7 +706,7 @@ def test_avg_emotional_value_null_for_no_emotions(self): response = self.client.get(self.url) location2_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location2.id), + (loc for loc in response.data if loc['id'] == self.location2.id), None ) @@ -722,8 +722,8 @@ def test_filter_by_name_exact(self): self.assertEqual(response.status_code, status.HTTP_200_OK) # Powinny być 2 lokalizacje z "Gdańsk" w nazwie (location1) # Uwaga: plan wspominał o 2, ale w setup mamy tylko 1 - self.assertGreaterEqual(len(response.data['results']), 1) - for loc in response.data['results']: + self.assertGreaterEqual(len(response.data), 1) + for loc in response.data: self.assertIn('Gdańsk', loc['name']) def test_filter_by_name_case_insensitive(self): @@ -731,7 +731,7 @@ def test_filter_by_name_case_insensitive(self): self.client.force_authenticate(user=self.user) response = self.client.get(self.url, {'name': 'gdańsk'}) - self.assertGreaterEqual(len(response.data['results']), 1) + self.assertGreaterEqual(len(response.data), 1) def test_filter_by_radius(self): """Test ?lat=54.35&lon=18.64&radius=1000 (1km).""" @@ -746,8 +746,8 @@ def test_filter_by_radius(self): self.assertEqual(response.status_code, status.HTTP_200_OK) # Tylko location1 powinna być w tym promieniu - self.assertEqual(len(response.data['results']), 1) - self.assertEqual(response.data['results'][0]['id'], self.location1.id) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]['id'], self.location1.id) def test_filter_by_radius_large_area(self): """Test radius=25000 (25km) - wszystkie 3 lokalizacje.""" @@ -761,7 +761,7 @@ def test_filter_by_radius_large_area(self): 'radius': 25000 # 25km }) - self.assertEqual(len(response.data['results']), 3) + self.assertEqual(len(response.data), 3) def test_filter_by_radius_missing_params(self): """Test z brakującymi parametrami - powinno zwrócić wszystkie.""" @@ -773,7 +773,7 @@ def test_filter_by_radius_missing_params(self): 'lon': self.gdansk_lon, }) - self.assertEqual(len(response.data['results']), 3) + self.assertEqual(len(response.data), 3) def test_filter_by_radius_invalid_values(self): """Test z nieprawidłowymi wartościami - zwraca błąd lub pusty queryset.""" @@ -789,7 +789,7 @@ def test_filter_by_radius_invalid_values(self): # Filter może zwrócić 400 (błąd walidacji) lub 200 z pustym queryset # Oba są akceptowalne if response.status_code == status.HTTP_200_OK: - self.assertEqual(len(response.data['results']), 0) + self.assertEqual(len(response.data), 0) else: self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) @@ -803,7 +803,7 @@ def test_filter_by_bbox(self): }) self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(len(response.data['results']), 2) + self.assertEqual(len(response.data), 2) def test_filter_by_bbox_invalid_format(self): """Test bbox z nieprawidłowym formatem - pusty queryset.""" @@ -811,11 +811,11 @@ def test_filter_by_bbox_invalid_format(self): # Zbyt mało wartości response = self.client.get(self.url, {'bbox': '18.5,54.3,18.65'}) - self.assertEqual(len(response.data['results']), 0) + self.assertEqual(len(response.data), 0) # Nieprawidłowy format response = self.client.get(self.url, {'bbox': 'invalid,bbox,format,here'}) - self.assertEqual(len(response.data['results']), 0) + self.assertEqual(len(response.data), 0) # --- READ-ONLY TESTS --- def test_post_not_allowed(self): @@ -851,22 +851,8 @@ def test_delete_not_allowed(self): self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED) # --- PAGINATION TESTS --- - def test_pagination_default_page_size(self): - """Test paginacji (15 lokalizacji, page_size=10).""" - self.client.force_authenticate(user=self.user) - - # Utwórz 12 dodatkowych lokalizacji (12 + 3 istniejące = 15) - for i in range(12): - Location.objects.create( - name=f'Location {i}', - coordinates=Point(18.0 + i*0.01, 54.0 + i*0.01, srid=4326) - ) - - response = self.client.get(self.url) - - self.assertEqual(len(response.data['results']), 10) # PAGE_SIZE=10 - self.assertIn('next', response.data) - self.assertIsNotNone(response.data['next']) + # UWAGA: Paginacja dla LocationViewSet została wyłączona (pagination_class = None), + # więc testy paginacji zostały usunięte. API zwraca prostą tablicę bez paginacji. # --- EDGE CASES --- def test_location_with_mixed_privacy_emotion_points(self): @@ -878,7 +864,7 @@ def test_location_with_mixed_privacy_emotion_points(self): response = self.client.get(self.url) location3_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location3.id), + (loc for loc in response.data if loc['id'] == self.location3.id), None ) @@ -890,7 +876,7 @@ def test_ordering_by_avg_emotional_value(self): self.client.force_authenticate(user=self.user) response = self.client.get(self.url) - results = response.data['results'] + results = response.data # Sprawdź czy lokalizacje są posortowane po avg_emotional_value (malejąco) # Locations z wartościami powinny być przed null diff --git a/cityfeel/emotions/tests_privacy_integration.py b/cityfeel/emotions/tests_privacy_integration.py index bf4cb59..3714207 100644 --- a/cityfeel/emotions/tests_privacy_integration.py +++ b/cityfeel/emotions/tests_privacy_integration.py @@ -90,7 +90,7 @@ def test_both_types_visible_on_map_api(self): response = self.client.get('/api/locations/') - location_data = next((loc for loc in response.data['results'] if loc['id'] == self.location.id), None) + location_data = next((loc for loc in response.data if loc['id'] == self.location.id), None) self.assertEqual(float(location_data['avg_emotional_value']), 4.0) self.assertEqual(location_data['emotion_points_count'], 2) @@ -112,7 +112,7 @@ def test_both_types_affect_avg_emotional_value(self): ) response = self.client.get('/api/locations/') - loc = response.data['results'][0] + loc = response.data[0] self.assertEqual(float(loc['avg_emotional_value']), 3.0) def test_public_emotion_visible_in_api_emotion_points(self): @@ -176,7 +176,7 @@ def test_comments_only_on_public_emotion_points(self): ) response = self.client.get('/api/locations/') - loc = response.data['results'][0] + loc = response.data[0] # Oczekujemy 1, bo drugi jest prywatny self.assertEqual(loc['comments_count'], 1) @@ -213,7 +213,7 @@ def test_latest_comment_only_from_public_emotion_points(self): ) response = self.client.get('/api/locations/') - loc = response.data['results'][0] + loc = response.data[0] # Ponieważ 'Visible' został utworzony później, on jest najnowszy. # Gdyby 'Secret' był nowszy, API zwróciłoby go jako 'Anonim' (zgodnie z nową logiką) # Ten test weryfikuje głównie poprawne sortowanie i dostępność @@ -243,7 +243,7 @@ def test_comments_count_only_public_emotion_points(self): ) response = self.client.get('/api/locations/') - self.assertEqual(response.data['results'][0]['comments_count'], 2) + self.assertEqual(response.data[0]['comments_count'], 2) # ========================================================================= # 2. PHOTOS @@ -456,5 +456,5 @@ def test_photos_not_counted_in_emotion_stats(self): Photo.objects.create(user=self.user, location=self.location, image='p.jpg') response = self.client.get('/api/locations/') - loc = response.data['results'][0] + loc = response.data[0] self.assertEqual(loc['emotion_points_count'], 0) \ No newline at end of file diff --git a/cityfeel/tests_integration.py b/cityfeel/tests_integration.py index 1a6124c..70660d3 100644 --- a/cityfeel/tests_integration.py +++ b/cityfeel/tests_integration.py @@ -268,7 +268,7 @@ def test_add_emotions_changes_avg_emotional_value(self): # Sprawdź początkowe statystyki (brak ocen) response = client.get('/api/locations/') location_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location.id), + (loc for loc in response.data if loc['id'] == self.location.id), None ) self.assertIsNone(location_data['avg_emotional_value']) @@ -283,7 +283,7 @@ def test_add_emotions_changes_avg_emotional_value(self): response = client.get('/api/locations/') location_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location.id), + (loc for loc in response.data if loc['id'] == self.location.id), None ) self.assertEqual(float(location_data['avg_emotional_value']), 5.0) @@ -298,7 +298,7 @@ def test_add_emotions_changes_avg_emotional_value(self): response = client.get('/api/locations/') location_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location.id), + (loc for loc in response.data if loc['id'] == self.location.id), None ) # avg = (5 + 3) / 2 = 4.0 @@ -344,7 +344,7 @@ def test_add_comment_visible_in_latest_comment(self): # Sprawdź że widoczny w API locations latest_comment response = client.get('/api/locations/') location_data = next( - (loc for loc in response.data['results'] if loc['id'] == self.location.id), + (loc for loc in response.data if loc['id'] == self.location.id), None )