From 119fdc2a514df41ffcc9b0ec9195df3bbe5d8021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Sun, 10 May 2026 00:57:51 +0200 Subject: [PATCH] basic noita submissions --- polylan_submitter/noita/__init__.py | 0 polylan_submitter/noita/admin.py | 1 + polylan_submitter/noita/api.py | 67 ++++++ polylan_submitter/noita/apps.py | 6 + .../noita/migrations/0001_initial.py | 61 +++++ ...002_rename_submission_logfilesubmission.py | 18 ++ .../noita/migrations/__init__.py | 0 polylan_submitter/noita/models.py | 44 ++++ polylan_submitter/noita/schemas.py | 16 ++ polylan_submitter/noita/tests.py | 1 + polylan_submitter/noita/views.py | 1 + polylan_submitter/polylan_submitter/api.py | 2 + .../polylan_submitter/settings.py | 1 + polylan_submitter/src/Noita.vue | 227 ++++++++++++++++-- 14 files changed, 430 insertions(+), 15 deletions(-) create mode 100644 polylan_submitter/noita/__init__.py create mode 100644 polylan_submitter/noita/admin.py create mode 100644 polylan_submitter/noita/api.py create mode 100644 polylan_submitter/noita/apps.py create mode 100644 polylan_submitter/noita/migrations/0001_initial.py create mode 100644 polylan_submitter/noita/migrations/0002_rename_submission_logfilesubmission.py create mode 100644 polylan_submitter/noita/migrations/__init__.py create mode 100644 polylan_submitter/noita/models.py create mode 100644 polylan_submitter/noita/schemas.py create mode 100644 polylan_submitter/noita/tests.py create mode 100644 polylan_submitter/noita/views.py diff --git a/polylan_submitter/noita/__init__.py b/polylan_submitter/noita/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/polylan_submitter/noita/admin.py b/polylan_submitter/noita/admin.py new file mode 100644 index 0000000..846f6b4 --- /dev/null +++ b/polylan_submitter/noita/admin.py @@ -0,0 +1 @@ +# Register your models here. diff --git a/polylan_submitter/noita/api.py b/polylan_submitter/noita/api.py new file mode 100644 index 0000000..3bce783 --- /dev/null +++ b/polylan_submitter/noita/api.py @@ -0,0 +1,67 @@ +from django.http import HttpRequest +from django.core.files.base import ContentFile +from ninja import Router, File +from ninja.files import UploadedFile + +from .models import LogfileSubmission +from .schemas import NoitaSubmissionOut + + +router = Router() + + +@router.post("submit", response=NoitaSubmissionOut) +def submit_log_file(request: HttpRequest, file: UploadedFile = File(...)): + """ + Submit a Noita run file (log file, screenshot, or video). + + Accepts: + - Text files (.txt) for polylan_mod_log.txt + - Images (.png, .jpg, .gif) + - Videos (.mp4, .webm) + + Max file size: 256 MB + """ + # Validate file type + allowed_types = [ + "text/plain", + "image/jpeg", + "image/jpg", + "image/png", + "image/gif", + "video/mp4", + "video/webm", + ] + + if file.content_type not in allowed_types: + return 400, { + "detail": f"Invalid file type: {file.content_type}. Allowed types: {', '.join(allowed_types)}" + } + + # Validate file size (256MB limit) + if file.size > 256 * 1024 * 1024: + return 400, {"detail": "File too large (max 256MB)"} + + try: + # Create submission + submission = LogfileSubmission.objects.create( + user=request.user if request.user.is_authenticated else None, + content_type=file.content_type, + file_size=file.size, + ) + + # Save the file + submission.file.save(file.name, ContentFile(file.read()), save=True) + + return { + "id": str(submission.id), + "user_id": submission.user_id, + "username": submission.user.username if submission.user else None, + "file_size": submission.file_size, + "content_type": submission.content_type, + "created_at": submission.created_at, + "processed": submission.processed, + } + + except Exception as e: + return 500, {"detail": f"Error creating submission: {str(e)}"} diff --git a/polylan_submitter/noita/apps.py b/polylan_submitter/noita/apps.py new file mode 100644 index 0000000..857e996 --- /dev/null +++ b/polylan_submitter/noita/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class NoitaConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "noita" diff --git a/polylan_submitter/noita/migrations/0001_initial.py b/polylan_submitter/noita/migrations/0001_initial.py new file mode 100644 index 0000000..b7222ba --- /dev/null +++ b/polylan_submitter/noita/migrations/0001_initial.py @@ -0,0 +1,61 @@ +# Generated by Django 5.2.7 on 2026-05-09 22:53 + +import django.db.models.deletion +import noita.models +import uuid +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="Submission", + fields=[ + ( + "id", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), + ( + "content_type", + models.CharField(help_text="MIME type of the file", max_length=100), + ), + ( + "file_size", + models.PositiveIntegerField(help_text="File size in bytes"), + ), + ( + "file", + models.FileField( + help_text="Uploaded file (image/gif)", + upload_to=noita.models.submission_file_upload_path, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("processed", models.BooleanField(default=False)), + ( + "user", + models.ForeignKey( + blank=True, + help_text="User who made the submission (null for anonymous)", + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="noita_submissions", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/polylan_submitter/noita/migrations/0002_rename_submission_logfilesubmission.py b/polylan_submitter/noita/migrations/0002_rename_submission_logfilesubmission.py new file mode 100644 index 0000000..bd3408d --- /dev/null +++ b/polylan_submitter/noita/migrations/0002_rename_submission_logfilesubmission.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-05-09 22:55 + +from django.conf import settings +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("noita", "0001_initial"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RenameModel( + old_name="Submission", + new_name="LogfileSubmission", + ), + ] diff --git a/polylan_submitter/noita/migrations/__init__.py b/polylan_submitter/noita/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/polylan_submitter/noita/models.py b/polylan_submitter/noita/models.py new file mode 100644 index 0000000..d320c9a --- /dev/null +++ b/polylan_submitter/noita/models.py @@ -0,0 +1,44 @@ +from django.contrib.auth import get_user_model +from django.db import models + +import uuid + +User = get_user_model() + + +def submission_file_upload_path(instance, filename): + """Generate upload path for submission files""" + # Create path: submissions/{submission_id}/{uuid}_{filename} + ext = filename.split(".")[-1] if "." in filename else "" + new_filename = f"{uuid.uuid4()}_{filename}" if ext else str(uuid.uuid4()) + return f"noita-submissions/{instance.id}/{new_filename}" + + +class LogfileSubmission(models.Model): + """Model representing a submission containing multiple puzzle responses""" + + # Identification + id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + + content_type = models.CharField(max_length=100, help_text="MIME type of the file") + file_size = models.PositiveIntegerField(help_text="File size in bytes") + file = models.FileField( + upload_to=submission_file_upload_path, + help_text="Uploaded file (image/gif)", + ) + + # User information (optional for anonymous submissions) + user = models.ForeignKey( + User, + on_delete=models.CASCADE, + null=True, + blank=True, + help_text="User who made the submission (null for anonymous)", + related_name="noita_submissions", + ) + + # Timestamps + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + processed = models.BooleanField(default=False) diff --git a/polylan_submitter/noita/schemas.py b/polylan_submitter/noita/schemas.py new file mode 100644 index 0000000..e456612 --- /dev/null +++ b/polylan_submitter/noita/schemas.py @@ -0,0 +1,16 @@ +from typing import Optional +from pydantic import BaseModel +from datetime import datetime + + +class NoitaSubmissionOut(BaseModel): + id: str + user_id: Optional[int] + username: Optional[str] + file_size: int + content_type: str + created_at: datetime + processed: bool + + class Config: + from_attributes = True diff --git a/polylan_submitter/noita/tests.py b/polylan_submitter/noita/tests.py new file mode 100644 index 0000000..a39b155 --- /dev/null +++ b/polylan_submitter/noita/tests.py @@ -0,0 +1 @@ +# Create your tests here. diff --git a/polylan_submitter/noita/views.py b/polylan_submitter/noita/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/polylan_submitter/noita/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/polylan_submitter/polylan_submitter/api.py b/polylan_submitter/polylan_submitter/api.py index de35bb0..b28b031 100644 --- a/polylan_submitter/polylan_submitter/api.py +++ b/polylan_submitter/polylan_submitter/api.py @@ -2,6 +2,7 @@ from ninja import NinjaAPI from submissions.api import router as submissions_router from submissions.schemas import UserInfoOut from animations.api import router as results_router +from noita.api import router as noita_router # Create the main API instance api = NinjaAPI( @@ -28,6 +29,7 @@ It provides features for user authentication, puzzle listing, submission uploads # Include the submissions router api.add_router("/submissions/", submissions_router, tags=["submissions"]) api.add_router("/results/", results_router, tags=["results"]) +api.add_router("/noita/", noita_router, tags=["noita"]) # Health check endpoint diff --git a/polylan_submitter/polylan_submitter/settings.py b/polylan_submitter/polylan_submitter/settings.py index 1cfd85d..3104330 100644 --- a/polylan_submitter/polylan_submitter/settings.py +++ b/polylan_submitter/polylan_submitter/settings.py @@ -42,6 +42,7 @@ INSTALLED_APPS = [ "accounts", "animations", "submissions", + "noita", ] MIDDLEWARE = [ diff --git a/polylan_submitter/src/Noita.vue b/polylan_submitter/src/Noita.vue index 60687b2..5f0176c 100644 --- a/polylan_submitter/src/Noita.vue +++ b/polylan_submitter/src/Noita.vue @@ -1,23 +1,220 @@