unfinished logging

This commit is contained in:
afanasyev.aa 2025-04-06 04:59:28 +09:00
parent 94501bf986
commit 43ccf7fb34
10 changed files with 117 additions and 50 deletions

Binary file not shown.

View file

@ -1,9 +1,12 @@
# Generated by Django 4.2.20 on 2025-04-05 15:24 # Generated by Django 5.1.7 on 2025-04-05 16:49
import django.contrib.auth.models
import django.contrib.auth.validators
import django.db.models.deletion
import django.utils.timezone
import uuid
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -11,20 +14,10 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), ('auth', '0012_alter_user_first_name_max_length'),
] ]
operations = [ operations = [
migrations.CreateModel(
name='Prediction',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('result', models.JSONField()),
('deleted_at', models.DateTimeField(blank=True, null=True)),
],
),
migrations.CreateModel( migrations.CreateModel(
name='Satellite', name='Satellite',
fields=[ fields=[
@ -33,18 +26,41 @@ class Migration(migrations.Migration):
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='User', name='Prediction',
fields=[ fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('result', models.JSONField()),
('deleted_at', models.DateTimeField(blank=True, null=True)),
('satellite', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='predictions', to='api.satellite')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
name='UserPrediction', name='User',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField()), ('password', models.CharField(max_length=128, verbose_name='password')),
('prediction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.prediction')), ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
('satellites', models.ManyToManyField(related_name='users', to='api.satellite')),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
'abstract': False,
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@ -60,4 +76,13 @@ class Migration(migrations.Migration):
('satellite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='telemetry', to='api.satellite')), ('satellite', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='telemetry', to='api.satellite')),
], ],
), ),
migrations.CreateModel(
name='UserPrediction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField()),
('prediction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='api.prediction')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
] ]

View file

@ -1,10 +1,14 @@
import uuid import uuid
from django.db import models from django.db import models
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
class User(models.Model): from django.contrib.auth.models import AbstractUser
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class User(AbstractUser):
satellites = models.ManyToManyField("Satellite", related_name="users")
class Prediction(models.Model): class Prediction(models.Model):
satellite = models.ForeignKey('Satellite', on_delete=models.CASCADE, related_name='predictions', null=True)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@ -12,7 +16,6 @@ class Prediction(models.Model):
deleted_at = models.DateTimeField(null=True, blank=True) deleted_at = models.DateTimeField(null=True, blank=True)
class UserPrediction(models.Model): class UserPrediction(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
prediction = models.ForeignKey("Prediction", on_delete=models.CASCADE) prediction = models.ForeignKey("Prediction", on_delete=models.CASCADE)

View file

@ -12,7 +12,7 @@ from rest_framework.authtoken.views import obtain_auth_token
from .views import TelemetryListCreateView from .views import TelemetryListCreateView
urlpatterns = [ urlpatterns = [
path('predictions', PredictionCreateView.as_view(), name='create_prediction'), path('predictions', PredictionCreateView.as_view(), name='create_prediction'),
path('predictions', PredictionListView.as_view(), name='get_predictions'), path('predictions/list/', PredictionListView.as_view(), name='get_predictions'),
path('token', obtain_auth_token, name = 'get_token'), path('token', obtain_auth_token, name = 'get_token'),
path("history", PredictionHistoryListView.as_view(), name='view_history_list'), path("history", PredictionHistoryListView.as_view(), name='view_history_list'),
path("history/<uuid:pk>/", PredictionHistoryDetailView.as_view(), name='view_history_detail'), path("history/<uuid:pk>/", PredictionHistoryDetailView.as_view(), name='view_history_detail'),

View file

@ -20,6 +20,9 @@ from rest_framework.authentication import SessionAuthentication, BasicAuthentica
from django.contrib.auth import authenticate, login, logout from django.contrib.auth import authenticate, login, logout
import json import json
from django.middleware.csrf import get_token from django.middleware.csrf import get_token
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from drf_spectacular.utils import extend_schema
from django.utils.dateparse import parse_datetime
User = get_user_model() User = get_user_model()
@ -50,7 +53,6 @@ class PredictionCreateView(APIView):
try: try:
prediction_result = TawhiriClient.get_prediction(validated_data) prediction_result = TawhiriClient.get_prediction(validated_data)
print(prediction_result)
except requests.RequestException as e: except requests.RequestException as e:
print("Tawhiri error:", str(e), e.response.text if e.response else "no response") 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) return Response({"error": f"Tawhiri error: {str(e)}"}, status=status.HTTP_502_BAD_GATEWAY)
@ -65,20 +67,36 @@ class PredictionCreateView(APIView):
class PredictionListView(APIView): class PredictionListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request): def get(self, request):
user_id = request.query_params.get('user_id') user = request.user
satellite_id = request.query_params.get('satellite_id')
created_from = request.query_params.get('created_from') created_from = request.query_params.get('created_from')
created_till = request.query_params.get('created_till') created_till = request.query_params.get('created_till')
predictions = Prediction.objects.filter( # Проверка доступа к спутнику
id__in=UserPrediction.objects.filter(user_id=user_id).values_list('prediction_id'), if satellite_id and not user.satellites.filter(id=satellite_id).exists():
created_at__gte=created_from, return Response({'detail': 'Access to this satellite is forbidden.'}, status=403)
created_at__lte=created_till,
deleted_at__isnull=True filters = {
) 'id__in': UserPrediction.objects.filter(user=user).values_list('prediction_id', flat=True),
'deleted_at__isnull': True
}
print(filters)
print(Prediction.objects.filter(**filters))
if created_from:
filters['created_at__gte'] = parse_datetime(created_from)
if created_till:
filters['created_at__lte'] = parse_datetime(created_till)
if satellite_id:
filters['satellite_id'] = satellite_id
predictions = Prediction.objects.filter(**filters)
return Response(PredictionSerializer(predictions, many=True).data) return Response(PredictionSerializer(predictions, many=True).data)
class PredictionHistoryListView(generics.ListAPIView): class PredictionHistoryListView(generics.ListAPIView):
permission_classes = [permissions.IsAuthenticated] permission_classes = [permissions.IsAuthenticated]
serializer_class = PredictionListSerializer serializer_class = PredictionListSerializer
@ -172,12 +190,20 @@ class WhoAmIView(APIView):
return JsonResponse({'username': request.user.username}) return JsonResponse({'username': request.user.username})
@extend_schema(methods=["GET"], description="Get CSRF token")
@api_view(["GET"])
@permission_classes([AllowAny])
def get_csrf(request): def get_csrf(request):
response = JsonResponse({'detail': 'CSRF cookie set'}) response = JsonResponse({'detail': 'CSRF cookie set'})
response['X-CSRFToken'] = get_token(request) response['X-CSRFToken'] = get_token(request)
return response return response
@extend_schema(methods=["POST"], description="Login user")
@csrf_exempt
@api_view(["POST"])
@authentication_classes([])
@permission_classes([AllowAny])
def login_view(request): def login_view(request):
data = json.loads(request.body) data = json.loads(request.body)
username = data.get('username') username = data.get('username')
@ -187,7 +213,6 @@ def login_view(request):
return JsonResponse({'detail': 'Please provide username and password.'}, status=400) return JsonResponse({'detail': 'Please provide username and password.'}, status=400)
user = authenticate(username=username, password=password) user = authenticate(username=username, password=password)
if user is None: if user is None:
return JsonResponse({'detail': 'Invalid credentials.'}, status=400) return JsonResponse({'detail': 'Invalid credentials.'}, status=400)
@ -195,6 +220,9 @@ def login_view(request):
return JsonResponse({'detail': 'Successfully logged in.'}) return JsonResponse({'detail': 'Successfully logged in.'})
@extend_schema(methods=["POST"], description="Logout user")
@api_view(["POST"])
@permission_classes([AllowAny])
def logout_view(request): def logout_view(request):
if not request.user.is_authenticated: if not request.user.is_authenticated:
return JsonResponse({'detail': 'You\'re not logged in.'}, status=400) return JsonResponse({'detail': 'You\'re not logged in.'}, status=400)

View file

@ -89,16 +89,24 @@ WSGI_APPLICATION = 'testapi.wsgi.application'
# Database # Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases # https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = { if DEBUG:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
else:
DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get("DB_NAME", "drfapi"), 'NAME': 'drfapi', # Your database name
'USER': os.environ.get("DB_USER", "postgres"), 'USER': 'postgres', # Your PostgreSQL username
'PASSWORD': os.environ.get("DB_PASSWORD", "1235"), 'PASSWORD': '1235', # Your PostgreSQL password
'HOST': os.environ.get("DB_HOST", "localhost"), 'HOST': 'localhost', # Or your DB server's IP
'PORT': os.environ.get("DB_PORT", "5432"), 'PORT': '5432', # Default PostgreSQL port
}
} }
}
# Password validation # Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
@ -140,27 +148,30 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
AUTH_USER_MODEL = 'api.User'
REST_FRAMEWORK = { REST_FRAMEWORK = {
# ВАШИ НАСТРОЙКИ
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100, 'PAGE_SIZE': 100,
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication', 'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
], ],
'DEFAULT_PERMISSION_CLASSES': [ 'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated', 'rest_framework.permissions.IsAuthenticated',
#'rest_framework.permissions.AllowAny', # 'rest_framework.permissions.AllowAny',
], ],
'DEFAULT_RENDERER_CLASSES': [ 'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.JSONRenderer',
], ],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
],
} }
CSRF_COOKIE_SAMESITE = 'Lax' CSRF_COOKIE_SAMESITE = 'Lax'
SESSION_COOKIE_SAMESITE = 'Lax' SESSION_COOKIE_SAMESITE = 'Lax'
CSRF_COOKIE_HTTPONLY = True CSRF_COOKIE_HTTPONLY = True