diff --git a/.gitignore b/.gitignore index 4bb0ce1..2887fed 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # Python __pycache__/ +**/__pycache__/** *.py[cod] +*pyc *.pyo *.pyd *.so diff --git a/api/admin.py b/api/admin.py index 77550fc..45e6514 100644 --- a/api/admin.py +++ b/api/admin.py @@ -1,9 +1,8 @@ from django.contrib import admin -from .models import User, Prediction, UserPrediction, Satellite, TelemetryPacket +from .models import User, Prediction, Satellite, TelemetryPacket admin.site.register(User) admin.site.register(Prediction) -admin.site.register(UserPrediction) admin.site.register(Satellite) admin.site.register(TelemetryPacket) \ No newline at end of file diff --git a/api/migrations/0002_preditctiontemplate_savedpoint_savedrateprofile_and_more.py b/api/migrations/0002_preditctiontemplate_savedpoint_savedrateprofile_and_more.py new file mode 100644 index 0000000..0696c23 --- /dev/null +++ b/api/migrations/0002_preditctiontemplate_savedpoint_savedrateprofile_and_more.py @@ -0,0 +1,136 @@ +# Generated by Django 4.2.23 on 2025-06-25 11:17 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='PreditctionTemplate', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('template_data', models.JSONField(blank=True, default=dict)), + ('is_default', models.BooleanField(default=False)), + ('user', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'name')}, + }, + ), + migrations.CreateModel( + name='SavedPoint', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('lat', models.FloatField(default=0.0)), + ('lon', models.FloatField(default=0.0)), + ('alt', models.FloatField(default=0.0)), + ('is_default', models.BooleanField(default=False)), + ('user', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'name')}, + }, + ), + migrations.CreateModel( + name='SavedRateProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('rate_profile_data', models.JSONField(blank=True, default=dict)), + ('is_default', models.BooleanField(default=False)), + ('user', models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'unique_together': {('user', 'name')}, + }, + ), + migrations.RemoveField( + model_name='prediction', + name='deleted_at', + ), + migrations.AddField( + model_name='prediction', + name='request', + field=models.JSONField(default=dict), + ), + migrations.AddField( + model_name='prediction', + name='user', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, related_name='predictions', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='satellite', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='satellite', + name='image', + field=models.ImageField(blank=True, null=True, upload_to='satellite_images/'), + ), + migrations.AddField( + model_name='satellite', + name='metadata', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AddField( + model_name='satellite', + name='user', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='telemetrypacket', + name='raw_data', + field=models.JSONField(blank=True, default=dict), + ), + migrations.AddField( + model_name='telemetrypacket', + name='user', + field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='prediction', + name='result', + field=models.JSONField(default=dict), + ), + migrations.AlterField( + model_name='telemetrypacket', + name='alt', + field=models.FloatField(default=0.0), + ), + migrations.AlterField( + model_name='telemetrypacket', + name='lat', + field=models.FloatField(default=0.0), + ), + migrations.AlterField( + model_name='telemetrypacket', + name='lon', + field=models.FloatField(default=0.0), + ), + migrations.AlterField( + model_name='telemetrypacket', + name='payload', + field=models.JSONField(blank=True, default=dict), + ), + migrations.DeleteModel( + name='UserPrediction', + ), + ] diff --git a/api/migrations/__pycache__/0001_initial.cpython-311.pyc b/api/migrations/__pycache__/0001_initial.cpython-311.pyc deleted file mode 100644 index 5cce3a3..0000000 Binary files a/api/migrations/__pycache__/0001_initial.cpython-311.pyc and /dev/null differ diff --git a/api/migrations/__pycache__/__init__.cpython-311.pyc b/api/migrations/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 8fc4c03..0000000 Binary files a/api/migrations/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/api/models.py b/api/models.py index bb29c77..5d7b7c3 100644 --- a/api/models.py +++ b/api/models.py @@ -1,36 +1,87 @@ import uuid from django.db import models from django.contrib.auth import get_user_model -from django.contrib.auth.models import AbstractUser +from django.contrib.auth.models import AbstractUser, AnonymousUser + class User(AbstractUser): satellites = models.ManyToManyField("Satellite", related_name="users") class Prediction(models.Model): - satellite = models.ForeignKey('Satellite', on_delete=models.CASCADE, related_name='predictions', null=True) + user = models.ForeignKey(get_user_model( + ), on_delete=models.CASCADE, default=0, related_name='predictions') + satellite = models.ForeignKey( + 'Satellite', on_delete=models.CASCADE, related_name='predictions', null=True) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) - result = models.JSONField() - deleted_at = models.DateTimeField(null=True, blank=True) + request = models.JSONField(default=dict) + result = models.JSONField(default=dict) -class UserPrediction(models.Model): - user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) - prediction = models.ForeignKey("Prediction", on_delete=models.CASCADE) - created_at = models.DateTimeField() - class Satellite(models.Model): + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, default=0) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=100) + metadata = models.JSONField(blank=True, default=dict) + image = models.ImageField( + upload_to='satellite_images/', blank=True, null=True) + created_at = models.DateTimeField(auto_now_add=True) + class TelemetryPacket(models.Model): + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, default=0) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - satellite = models.ForeignKey(Satellite, on_delete=models.CASCADE, related_name="telemetry") + satellite = models.ForeignKey( + Satellite, on_delete=models.CASCADE, related_name="telemetry") timestamp = models.BigIntegerField() # unix time - lat = models.FloatField() - lon = models.FloatField() - alt = models.FloatField() - payload = models.JSONField(null=True, blank=True) - created_at = models.DateTimeField(auto_now_add=True) \ No newline at end of file + lat = models.FloatField(default=0.0) + lon = models.FloatField(default=0.0) + alt = models.FloatField(default=0.0) + payload = models.JSONField(blank=True, default=dict) + raw_data = models.JSONField(blank=True, default=dict) + created_at = models.DateTimeField(auto_now_add=True) + + +class PreditctionTemplate(models.Model): + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, default=0) + name = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + template_data = models.JSONField(blank=True, default=dict) + is_default = models.BooleanField(default=False) + + class Meta: + unique_together = ('user', 'name') + + +class SavedPoint(models.Model): + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, default=0) + name = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + lat = models.FloatField(default=0.0) + lon = models.FloatField(default=0.0) + alt = models.FloatField(default=0.0) + is_default = models.BooleanField(default=False) + + class Meta: + unique_together = ('user', 'name') + + +class SavedRateProfile(models.Model): + user = models.ForeignKey( + get_user_model(), on_delete=models.CASCADE, default=0) + name = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + rate_profile_data = models.JSONField(blank=True, default=dict) + is_default = models.BooleanField(default=False) + + class Meta: + unique_together = ('user', 'name') \ No newline at end of file diff --git a/api/views.py b/api/views.py index 8c1469c..8c46851 100644 --- a/api/views.py +++ b/api/views.py @@ -2,7 +2,7 @@ from rest_framework import status, generics, permissions from rest_framework.response import Response from rest_framework.views import APIView from django.utils import timezone -from .models import Prediction, User, UserPrediction +from .models import Prediction, User from .serializers import PredictionSerializer, PredictionRequestSerializer, PredictionListSerializer, PredictionDetailSerializer from rest_framework.permissions import IsAuthenticated import requests @@ -57,8 +57,7 @@ class PredictionCreateView(APIView): print("Tawhiri error:", str(e), e.response.text if e.response else "no response") return Response({"error": f"Tawhiri error: {str(e)}"}, status=status.HTTP_502_BAD_GATEWAY) - prediction = Prediction.objects.create(result=prediction_result) - UserPrediction.objects.create(user=request.user, prediction=prediction, created_at=timezone.now()) + prediction = Prediction.objects.create(result=prediction_result, user=request.user, request=validated_data) return Response({ "id": prediction.id, "created_at": prediction.created_at, @@ -80,7 +79,7 @@ class PredictionListView(APIView): return Response({'detail': 'Access to this satellite is forbidden.'}, status=403) filters = { - 'id__in': UserPrediction.objects.filter(user=user).values_list('prediction_id', flat=True), + 'id__in': Prediction.objects.filter(user=user).values_list('prediction_id', flat=True), 'deleted_at__isnull': True } print(filters) @@ -103,8 +102,7 @@ class PredictionHistoryListView(generics.ListAPIView): def get_queryset(self): return Prediction.objects.filter( - id__in=UserPrediction.objects.filter(user=self.request.user).values_list('prediction_id', flat=True), - deleted_at__isnull=True + user=self.request.user, ) @@ -115,8 +113,7 @@ class PredictionHistoryDetailView(generics.RetrieveAPIView): def get_queryset(self): return Prediction.objects.filter( - id__in=UserPrediction.objects.filter(user=self.request.user).values_list('prediction_id', flat=True), - deleted_at__isnull=True + user=self.request.user, ) @@ -126,15 +123,9 @@ class PredictionHistoryDeleteView(generics.DestroyAPIView): def get_queryset(self): return Prediction.objects.filter( - id__in=UserPrediction.objects.filter(user=self.request.user).values_list('prediction_id', flat=True), - deleted_at__isnull=True + user=self.request.user, ) - def perform_destroy(self, instance): - instance.deleted_at = timezone.now() - instance.save() - - class TelemetryListCreateView(generics.ListCreateAPIView): serializer_class = TelemetryPacketSerializer diff --git a/requirements.txt b/requirements.txt index ad39d85..72cda28 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ psycopg2-binary drf-spectacular requests django-cors-headers +Pillow