From 8f61eb0834faa96c8aa32c23820840330aa2939d Mon Sep 17 00:00:00 2001 From: ThePetrovich Date: Wed, 25 Jun 2025 19:19:59 +0800 Subject: [PATCH 1/2] Add models for user data --- .gitignore | 2 + api/admin.py | 3 +- ...te_savedpoint_savedrateprofile_and_more.py | 136 ++++++++++++++++++ api/models.py | 81 +++++++++-- api/views.py | 21 +-- requirements.txt | 1 + 6 files changed, 212 insertions(+), 32 deletions(-) create mode 100644 api/migrations/0002_preditctiontemplate_savedpoint_savedrateprofile_and_more.py 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/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 From efcfe06198ec3e66a6bedd1e1a9d16f165961332 Mon Sep 17 00:00:00 2001 From: ThePetrovich Date: Wed, 25 Jun 2025 19:20:14 +0800 Subject: [PATCH 2/2] Remove cache files --- .../__pycache__/0001_initial.cpython-311.pyc | Bin 3207 -> 0 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 140 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 api/migrations/__pycache__/0001_initial.cpython-311.pyc delete mode 100644 api/migrations/__pycache__/__init__.cpython-311.pyc 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 5cce3a30a688b188a2ca2b3f9c98edc2fbbb80b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3207 zcmbVO%}*Og6yIHc;9U&ZgkT&9o3AEuK7cAfm87jo3{C_2Qh_LSBCQr?aMoF`o!xa3 zQdQ#6Lyx(Yzks$1ha7U?kVB3=j-@%EIYsIzw~fLnr@mQx!9Xeyv)<=7^WOa4$IQIh zKik?A0ouUS$3iP`|ME`IA`Rx#ePI3&kbp!}D2aJd#k9@ z92PB=5jLIM^No4_Ust2^>3Nv@hK0O{guH~rd`J>-xES~@;ApWWn7AT>eR%7wAm(Gf zt{jXY3GvxVu{9XM@nT!Rqc9Rtcr~&D7GJ?zZwnHN`g7WY5hSj*Y_|kD+>%mk){%GV zD85cdfU^S3tDqwQM(a)tuoPdBKnvb_TaXHo)wbl$`+*K2!|$nNal436~TP2l8Tf zz@gM?&-SdtdZiDF*1TtT;}*huWaP*}@{D?YZ&N`;yK>X#%X|J;esm9c{~mJHmv8QV zSi>*u?tPuLV?NSBR`EDGv3hcQm4Xu$+nMh|B%em7pmu%5egcxwyLHHwKQz$CCs7=1~!qy8 zjVYDQOuT5necX4@#()CpVCZ({2#aZcU(=YP zX>iX}O$>Zo)1FjylWRh+#jp$)7Rlx{pLIFU^h$-VB*aj`7Q)v2qRlTg)-g6a^P@I9 zJ@J!v>(2PZO%~tOFtEc~v;G3#;}?{8*KKGZr9oPHKQ;Fo21@Ta%CGE1#1d6RTLF;|HCIz}RLzmZ^(MjE z3{kJu)N53|MiSZeWSaDzUmm5s=WD$eY41gn7-&{L^$yf0Yw9FbCrM(;YmIibdl6Yy zJ_I}X5kmyf{YS&bosy0bD>q&_09FOeW>w9m0rXv${1txp`tAp{^(UNdNHi0_`8J^^em2QIhy(J=slqGRslg zld1Kbr#4r~XBzaTvk}}UgoM!;Y1}9!t9O#TLP4dT{2o6q0 zX07#uJ1}3i7V0HMZj7Kgzxve6MtXB_p#Et*5FB#*JB~IQK-%M+^DX3n`yBhWUgEyD zC!xzfgq>f3cp*^~;e3k6iEym>w=SF|U;3>#sjUmGOa9|UN5!Zo>?SW2nmF~53pTzW H_1*gq(|atX diff --git a/api/migrations/__pycache__/__init__.cpython-311.pyc b/api/migrations/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 8fc4c0334a9df1783bb7839082f216fc02c6f27e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140 zcmZ3^%ge<81bW^d(?RrO5CH>>P{wCAAY(d13PUi1CZpd-kF z>gQ&r7bTWt=I0gb$H!;pWtPOp>lIY~;;_lhPbtkwwJTx;st1`^%nu|!Ff%eTeqewR IMa)1k0Dz<&aR2}S