fix: multiple fixes + event for market

This commit is contained in:
2026-10-07 21:38:30 +02:00
parent fdf84f9524
commit 6a5a224d3b
14 changed files with 51 additions and 24 deletions
@@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
+3 -1
View File
@@ -58,7 +58,9 @@ class ClockTimer(models.Model):
class Press(models.Model): class Press(models.Model):
event = models.ForeignKey(Event, on_delete=models.CASCADE, related_name="clock_presses") event = models.ForeignKey(
Event, on_delete=models.CASCADE, related_name="clock_presses"
)
player = models.ForeignKey( player = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="clock_presses" settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="clock_presses"
) )
+3 -1
View File
@@ -51,7 +51,9 @@ EVENT_PATH = os.environ.get("PLACE_EVENT_PATH", "loadtest")
USER_COUNT = int(os.environ.get("LOAD_TEST_USER_COUNT", "500")) USER_COUNT = int(os.environ.get("LOAD_TEST_USER_COUNT", "500"))
USER_PREFIX = os.environ.get("LOAD_TEST_USER_PREFIX", "loadtest") USER_PREFIX = os.environ.get("LOAD_TEST_USER_PREFIX", "loadtest")
PASSWORD = os.environ.get("LOAD_TEST_PASSWORD", "loadtest-only-password") PASSWORD = os.environ.get("LOAD_TEST_PASSWORD", "loadtest-only-password")
RELOAD_ON_FAILURE_PROBABILITY = float(os.environ.get("RELOAD_ON_FAILURE_PROBABILITY", "0.7")) RELOAD_ON_FAILURE_PROBABILITY = float(
os.environ.get("RELOAD_ON_FAILURE_PROBABILITY", "0.7")
)
# Mirrors place/models.py::PALETTE - kept as a plain constant here since # Mirrors place/models.py::PALETTE - kept as a plain constant here since
# loadtest runs standalone, without Django settings loaded. # loadtest runs standalone, without Django settings loaded.
+3 -3
View File
@@ -15,8 +15,8 @@ class MarketOptionInline(admin.TabularInline):
@admin.register(Market) @admin.register(Market)
class MarketAdmin(DjangoObjectActions, admin.ModelAdmin): class MarketAdmin(DjangoObjectActions, admin.ModelAdmin):
list_display = ["title", "status", "end_date", "created_by", "created_at"] list_display = ["title", "status", "event", "end_date", "created_by", "created_at"]
list_filter = ["status", "created_at"] list_filter = ["status", "created_at", "event"]
search_fields = ["uuid", "title"] search_fields = ["uuid", "title"]
readonly_fields = [ readonly_fields = [
"uuid", "uuid",
@@ -27,7 +27,7 @@ class MarketAdmin(DjangoObjectActions, admin.ModelAdmin):
] ]
inlines = [MarketOptionInline] inlines = [MarketOptionInline]
fieldsets = ( fieldsets = (
("Info", {"fields": ["uuid", "title", "description"]}), ("Info", {"fields": ["uuid", "title", "description", "event"]}),
("Configuration", {"fields": ["end_date", "multiplier"]}), ("Configuration", {"fields": ["end_date", "multiplier"]}),
("Status", {"fields": ["status", "winning_option"]}), ("Status", {"fields": ["status", "winning_option"]}),
("Metadata", {"fields": ["created_by", "created_at", "updated_at"]}), ("Metadata", {"fields": ["created_by", "created_at", "updated_at"]}),
+2 -1
View File
@@ -19,7 +19,8 @@ class PlaceSettingsAdmin(DjangoObjectActions, admin.ModelAdmin):
def resync_canvas(self, request, obj): def resync_canvas(self, request, obj):
count = rebuild_canvas_from_history(obj.event, obj.width, obj.height) count = rebuild_canvas_from_history(obj.event, obj.width, obj.height)
self.message_user( self.message_user(
request, f"Canvas resynced from {count} placement(s) at {obj.width}x{obj.height}." request,
f"Canvas resynced from {count} placement(s) at {obj.width}x{obj.height}.",
) )
+6 -2
View File
@@ -65,7 +65,9 @@ class PlaceConsumer(AsyncWebsocketConsumer):
await self.send(text_data=encode("r", "i")) await self.send(text_data=encode("r", "i"))
return return
result = await self._record_placement(user, x, y, color_index, place_settings.width) result = await self._record_placement(
user, x, y, color_index, place_settings.width
)
if result["rejected"]: if result["rejected"]:
await self.send(text_data=encode("r", "c", result["next"])) await self.send(text_data=encode("r", "c", result["next"]))
@@ -110,7 +112,9 @@ class PlaceConsumer(AsyncWebsocketConsumer):
return { return {
"rejected": False, "rejected": False,
"next": to_epoch_ms(placement.placed_at + timedelta(seconds=PLACE_COOLDOWN_SECONDS)), "next": to_epoch_ms(
placement.placed_at + timedelta(seconds=PLACE_COOLDOWN_SECONDS)
),
} }
@database_sync_to_async @database_sync_to_async
+5 -3
View File
@@ -35,9 +35,11 @@ def build_evolution_gif(event: Event) -> bytes:
place_settings = PlaceSettings.current(event) place_settings = PlaceSettings.current(event)
width, height = place_settings.width, place_settings.height width, height = place_settings.width, place_settings.height
placements = PixelPlacement.objects.filter(event=event).order_by( placements = (
"placed_at" PixelPlacement.objects.filter(event=event)
).values_list("x", "y", "color_index") .order_by("placed_at")
.values_list("x", "y", "color_index")
)
placements = list(placements) placements = list(placements)
grid = bytearray(width * height) grid = bytearray(width * height)
@@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
+3 -1
View File
@@ -17,7 +17,9 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "polylan_submitter.settings")
django_asgi_app = get_asgi_application() django_asgi_app = get_asgi_application()
from submissions.routing import websocket_urlpatterns as submissions_websocket_urlpatterns from submissions.routing import (
websocket_urlpatterns as submissions_websocket_urlpatterns,
)
from clock.routing import websocket_urlpatterns as clock_websocket_urlpatterns from clock.routing import websocket_urlpatterns as clock_websocket_urlpatterns
from place.routing import websocket_urlpatterns as place_websocket_urlpatterns from place.routing import websocket_urlpatterns as place_websocket_urlpatterns
@@ -58,7 +58,9 @@ DEBUG = os.environ.get("DJANGO_DEBUG", "false").lower() == "true"
ALLOWED_HOSTS = [ ALLOWED_HOSTS = [
host host
for host in (os.environ.get("DJANGO_ALLOWED_HOSTS") or "127.0.0.1,localhost").split(",") for host in (os.environ.get("DJANGO_ALLOWED_HOSTS") or "127.0.0.1,localhost").split(
","
)
if host if host
] ]
@@ -222,7 +224,9 @@ CAS_SERVER_URL = "https://polylan.ch/cas/"
# environment - the "Create load test users" admin action on CustomUser # environment - the "Create load test users" admin action on CustomUser
# (see accounts/admin.py) also hides itself without it, as a second layer # (see accounts/admin.py) also hides itself without it, as a second layer
# of the same guard rail. # of the same guard rail.
LOAD_TEST_LOGIN_ENABLED = (os.environ.get("LOAD_TEST_LOGIN_ENABLED", "false").lower() == "true") LOAD_TEST_LOGIN_ENABLED = (
os.environ.get("LOAD_TEST_LOGIN_ENABLED", "false").lower() == "true"
)
# Steam API Configuration # Steam API Configuration
STEAM_API_KEY = os.environ.get("STEAM_API_KEY") or None # Set via environment variable STEAM_API_KEY = os.environ.get("STEAM_API_KEY") or None # Set via environment variable
@@ -273,7 +277,9 @@ CHANNEL_LAYERS = {
# Redis for place's live pixel canvas (SETRANGE on the hot path - not a fit # Redis for place's live pixel canvas (SETRANGE on the hot path - not a fit
# for django-redis's cache API, so it gets its own raw redis-py client). # for django-redis's cache API, so it gets its own raw redis-py client).
PLACE_REDIS_URL = os.environ.get("PLACE_REDIS_URL") or f"redis://{REDIS_HOST}:{REDIS_PORT}/15" PLACE_REDIS_URL = (
os.environ.get("PLACE_REDIS_URL") or f"redis://{REDIS_HOST}:{REDIS_PORT}/15"
)
# Django Tasks Configuration (with RQ backend) # Django Tasks Configuration (with RQ backend)
TASKS = { TASKS = {
@@ -318,6 +324,7 @@ LOGOUT_REDIRECT_URL = "/"
DJANGO_VITE = { DJANGO_VITE = {
"default": { "default": {
"dev_mode": DEBUG, "dev_mode": DEBUG,
"dev_server_port": 5273,
} }
} }
STATIC_URL = "/static/" STATIC_URL = "/static/"
@@ -331,7 +338,9 @@ STATICFILES_DIRS = [
# that, so these must be set (via env) wherever the app runs, dev included. # that, so these must be set (via env) wherever the app runs, dev included.
AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "") AWS_ACCESS_KEY_ID = os.environ.get("AWS_ACCESS_KEY_ID", "")
AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "") AWS_SECRET_ACCESS_KEY = os.environ.get("AWS_SECRET_ACCESS_KEY", "")
AWS_S3_ENDPOINT_URL = os.environ.get("AWS_S3_ENDPOINT_URL") or "https://storage.polylan.ch" AWS_S3_ENDPOINT_URL = (
os.environ.get("AWS_S3_ENDPOINT_URL") or "https://storage.polylan.ch"
)
AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME") or "dev-submissions" AWS_STORAGE_BUCKET_NAME = os.environ.get("AWS_STORAGE_BUCKET_NAME") or "dev-submissions"
_s3_storage_options = { _s3_storage_options = {
@@ -356,9 +365,12 @@ STORAGES = {
} }
SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT") or ("development" if DEBUG else "production") SENTRY_ENVIRONMENT = os.environ.get("SENTRY_ENVIRONMENT") or (
"development" if DEBUG else "production"
)
SENTRY_DSN = ( SENTRY_DSN = (
os.environ.get("SENTRY_DSN") or "https://cc62a4ce3f3470890b43accf02cc6d8c@sentry2.polylan.ch/12" os.environ.get("SENTRY_DSN")
or "https://cc62a4ce3f3470890b43accf02cc6d8c@sentry2.polylan.ch/12"
) )
import sentry_sdk # noqa import sentry_sdk # noqa
+3 -1
View File
@@ -117,7 +117,9 @@ urlpatterns = [
# URL itself doesn't exist unless explicitly enabled, so it 404s rather than # URL itself doesn't exist unless explicitly enabled, so it 404s rather than
# 403s in every environment that hasn't opted in. # 403s in every environment that hasn't opted in.
if settings.LOAD_TEST_LOGIN_ENABLED: if settings.LOAD_TEST_LOGIN_ENABLED:
urlpatterns.append(path("testing/login/", TestLoginView.as_view(), name="testing.login")) urlpatterns.append(
path("testing/login/", TestLoginView.as_view(), name="testing.login")
)
# Serve media files in development # Serve media files in development
if settings.DEBUG: if settings.DEBUG:
+4 -1
View File
@@ -31,7 +31,10 @@ def list_my_scopes(request: HttpRequest, event_path: str, game_path: str):
) )
@router.get(f"{PREFIX}/{{game_path}}/available-scopes", response={200: list[ScopeOut], 403: dict}) @router.get(
f"{PREFIX}/{{game_path}}/available-scopes",
response={200: list[ScopeOut], 403: dict},
)
def list_available_scopes(request: HttpRequest, event_path: str, game_path: str): def list_available_scopes(request: HttpRequest, event_path: str, game_path: str):
"""Scopes the current user may submit to for this game - superusers see """Scopes the current user may submit to for this game - superusers see
every active scope, everyone else only the ones assigned to them. Used every active scope, everyone else only the ones assigned to them. Used
@@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("submissions", "0007_submission_scope"), ("submissions", "0007_submission_scope"),
] ]
+1 -1
View File
@@ -12,7 +12,7 @@ export default defineConfig({
// server, so dev-mode asset URLs (e.g. @font-face url()s injected // server, so dev-mode asset URLs (e.g. @font-face url()s injected
// via JS-imported CSS) must be absolute, not root-relative, or the // via JS-imported CSS) must be absolute, not root-relative, or the
// browser resolves them against Django's origin instead of Vite's. // browser resolves them against Django's origin instead of Vite's.
origin: 'http://localhost:5173', origin: 'http://localhost:5273',
}, },
plugins: [ plugins: [
vue(), vue(),