Initialize FishIQ development foundation

This commit is contained in:
reaper
2026-08-28 10:56:14 -05:00
commit 3eb459a307
81 changed files with 1642 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
+6
View File
@@ -0,0 +1,6 @@
from django.apps import AppConfig
class FishingConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "fishing"
@@ -0,0 +1,14 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]
operations = [
migrations.CreateModel(name="Species", fields=[("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("common_name", models.CharField(max_length=120, unique=True)), ("scientific_name", models.CharField(blank=True, max_length=160))]),
migrations.CreateModel(name="WaterBody", fields=[("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("name", models.CharField(max_length=180)), ("region", models.CharField(max_length=120)), ("latitude", models.DecimalField(decimal_places=6, max_digits=9)), ("longitude", models.DecimalField(decimal_places=6, max_digits=9)), ("species", models.ManyToManyField(blank=True, related_name="water_bodies", to="fishing.species"))], options={"constraints": [models.UniqueConstraint(fields=("name", "region"), name="unique_water_body_region")]}),
migrations.CreateModel(name="Trip", fields=[("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("starts_at", models.DateTimeField()), ("ends_at", models.DateTimeField()), ("status", models.CharField(choices=[("planned", "Planned"), ("active", "Active"), ("complete", "Complete")], default="planned", max_length=12)), ("notes", models.TextField(blank=True)), ("created_at", models.DateTimeField(auto_now_add=True)), ("owner", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="trips", to=settings.AUTH_USER_MODEL)), ("target_species", models.ManyToManyField(related_name="trips", to="fishing.species")), ("water_body", models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name="trips", to="fishing.waterbody"))]),
migrations.CreateModel(name="Recommendation", fields=[("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), ("title", models.CharField(max_length=180)), ("rationale", models.TextField()), ("tackle_setup", models.JSONField(default=dict)), ("confidence", models.DecimalField(decimal_places=3, max_digits=4)), ("generated_at", models.DateTimeField(auto_now_add=True)), ("trip", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="recommendations", to="fishing.trip"))]),
]
+1
View File
@@ -0,0 +1 @@
+40
View File
@@ -0,0 +1,40 @@
from django.conf import settings
from django.db import models
class Species(models.Model):
common_name = models.CharField(max_length=120, unique=True)
scientific_name = models.CharField(max_length=160, blank=True)
def __str__(self): return self.common_name
class WaterBody(models.Model):
name = models.CharField(max_length=180)
region = models.CharField(max_length=120)
latitude = models.DecimalField(max_digits=9, decimal_places=6)
longitude = models.DecimalField(max_digits=9, decimal_places=6)
species = models.ManyToManyField(Species, related_name="water_bodies", blank=True)
class Meta:
constraints = [models.UniqueConstraint(fields=["name", "region"], name="unique_water_body_region")]
def __str__(self): return f"{self.name}, {self.region}"
class Trip(models.Model):
STATUS_CHOICES = [("planned", "Planned"), ("active", "Active"), ("complete", "Complete")]
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="trips")
water_body = models.ForeignKey(WaterBody, on_delete=models.PROTECT, related_name="trips")
target_species = models.ManyToManyField(Species, related_name="trips")
starts_at = models.DateTimeField()
ends_at = models.DateTimeField()
status = models.CharField(max_length=12, choices=STATUS_CHOICES, default="planned")
notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Recommendation(models.Model):
trip = models.ForeignKey(Trip, on_delete=models.CASCADE, related_name="recommendations")
title = models.CharField(max_length=180)
rationale = models.TextField()
tackle_setup = models.JSONField(default=dict)
confidence = models.DecimalField(max_digits=4, decimal_places=3)
generated_at = models.DateTimeField(auto_now_add=True)
+16
View File
@@ -0,0 +1,16 @@
from rest_framework import serializers
from .models import Recommendation, Species, Trip, WaterBody
class SpeciesSerializer(serializers.ModelSerializer):
class Meta: model = Species; fields = "__all__"
class WaterBodySerializer(serializers.ModelSerializer):
species = SpeciesSerializer(many=True, read_only=True)
class Meta: model = WaterBody; fields = "__all__"
class TripSerializer(serializers.ModelSerializer):
class Meta: model = Trip; fields = "__all__"; read_only_fields = ("owner",)
class RecommendationSerializer(serializers.ModelSerializer):
class Meta: model = Recommendation; fields = "__all__"
+11
View File
@@ -0,0 +1,11 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import RecommendationViewSet, SpeciesViewSet, TripViewSet, WaterBodyViewSet, health
router = DefaultRouter()
router.register("species", SpeciesViewSet)
router.register("water-bodies", WaterBodyViewSet)
router.register("trips", TripViewSet, basename="trip")
router.register("recommendations", RecommendationViewSet, basename="recommendation")
urlpatterns = [path("health/", health), path("", include(router.urls))]
+30
View File
@@ -0,0 +1,30 @@
from django.http import JsonResponse
from rest_framework import permissions, viewsets
from .models import Recommendation, Species, Trip, WaterBody
from .serializers import RecommendationSerializer, SpeciesSerializer, TripSerializer, WaterBodySerializer
def health(_request):
return JsonResponse({"status": "ok", "service": "fishiq-api"})
class ReadOnlyCatalogViewSet(viewsets.ReadOnlyModelViewSet):
permission_classes = [permissions.AllowAny]
class SpeciesViewSet(ReadOnlyCatalogViewSet):
queryset = Species.objects.all().order_by("common_name")
serializer_class = SpeciesSerializer
class WaterBodyViewSet(ReadOnlyCatalogViewSet):
queryset = WaterBody.objects.prefetch_related("species").all().order_by("name")
serializer_class = WaterBodySerializer
class TripViewSet(viewsets.ModelViewSet):
serializer_class = TripSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self): return Trip.objects.filter(owner=self.request.user).select_related("water_body")
def perform_create(self, serializer): serializer.save(owner=self.request.user)
class RecommendationViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = RecommendationSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self): return Recommendation.objects.filter(trip__owner=self.request.user)