+
{error}
)}
diff --git a/platform/frontend/app/utils/storage.ts b/platform/frontend/app/utils/storage.ts
index a7ad66b..f3ef29d 100644
--- a/platform/frontend/app/utils/storage.ts
+++ b/platform/frontend/app/utils/storage.ts
@@ -12,33 +12,42 @@ interface CookieOptions {
const defaultOptions: CookieOptions = {
expires: 7, // 7 days
path: '/',
- secure: process.env.NODE_ENV === 'production',
+ secure: typeof window !== 'undefined' ? window.location.protocol === 'https:' : false,
sameSite: 'lax',
};
// Storage class to handle both cookies and localStorage
export class Storage {
private static hasConsent(): boolean {
- const consent = Cookies.get('cookieConsent');
- console.log('Checking cookie consent:', consent);
- return consent === 'accepted';
+ try {
+ const consent = Cookies.get('cookieConsent');
+ console.log('Checking cookie consent:', consent);
+ return consent === 'accepted';
+ } catch (e) {
+ console.error('Error checking cookie consent:', e);
+ return false;
+ }
}
// Cookie methods
static setCookie(key: string, value: any, options: CookieOptions = {}) {
- // Always allow setting the cookieConsent cookie
- if (key === 'cookieConsent') {
- console.log('Setting cookieConsent cookie:', value);
- const mergedOptions = { ...defaultOptions, ...options };
- Cookies.set(key, typeof value === 'string' ? value : JSON.stringify(value), mergedOptions);
- return true;
- }
-
- if (this.hasConsent()) {
- console.log('Setting cookie with consent:', key, value);
- const mergedOptions = { ...defaultOptions, ...options };
- Cookies.set(key, typeof value === 'string' ? value : JSON.stringify(value), mergedOptions);
- return true;
+ try {
+ // Always allow setting the cookieConsent cookie
+ if (key === 'cookieConsent') {
+ console.log('Setting cookieConsent cookie:', value);
+ const mergedOptions = { ...defaultOptions, ...options };
+ Cookies.set(key, typeof value === 'string' ? value : JSON.stringify(value), mergedOptions);
+ return true;
+ }
+
+ if (this.hasConsent()) {
+ console.log('Setting cookie with consent:', key, value);
+ const mergedOptions = { ...defaultOptions, ...options };
+ Cookies.set(key, typeof value === 'string' ? value : JSON.stringify(value), mergedOptions);
+ return true;
+ }
+ } catch (e) {
+ console.error('Error setting cookie:', e);
}
console.log('Blocked setting cookie (no consent):', key);
@@ -46,21 +55,25 @@ export class Storage {
}
static getCookie(key: string) {
- // Always allow reading the cookieConsent cookie
- const value = Cookies.get(key);
-
- if (key === 'cookieConsent') {
- console.log('Reading cookieConsent cookie:', value);
- return value;
- }
-
- if (this.hasConsent()) {
- console.log('Reading cookie with consent:', key, value);
- try {
- return value ? JSON.parse(value) : null;
- } catch {
+ try {
+ // Always allow reading the cookieConsent cookie
+ const value = Cookies.get(key);
+
+ if (key === 'cookieConsent') {
+ console.log('Reading cookieConsent cookie:', value);
return value;
}
+
+ if (this.hasConsent()) {
+ console.log('Reading cookie with consent:', key, value);
+ try {
+ return value ? JSON.parse(value) : null;
+ } catch {
+ return value;
+ }
+ }
+ } catch (e) {
+ console.error('Error getting cookie:', e);
}
console.log('Blocked reading cookie (no consent):', key);
@@ -68,9 +81,13 @@ export class Storage {
}
static removeCookie(key: string) {
- // Always allow removing any cookie
- console.log('Removing cookie:', key);
- Cookies.remove(key, { path: '/' });
+ try {
+ // Always allow removing any cookie
+ console.log('Removing cookie:', key);
+ Cookies.remove(key, { path: '/' });
+ } catch (e) {
+ console.error('Error removing cookie:', e);
+ }
}
// LocalStorage methods with error handling
diff --git a/worker/emd.py b/worker/emd.py
index 253429b..a2bd80d 100644
--- a/worker/emd.py
+++ b/worker/emd.py
@@ -38,6 +38,15 @@ def continuous_emd(gt_mask, attribution, n_dim=64):
print(f"[EMD] Fehler in continuous_emd: {e}", flush=True)
return 0.0
+def importance_mass_accuracy(gt_mask, attribution):
+ gt_mask_flat = gt_mask.flatten()
+ attribution_flat = np.abs(attribution.flatten())
+ total_importance = np.sum(attribution_flat)
+ if total_importance == 0:
+ return 0.0
+ importance_in_gt = np.sum(attribution_flat[gt_mask_flat == 1])
+ return importance_in_gt / total_importance
+
try:
print("[EMD] Lese 'xai_method' aus Umgebungsvariable...", flush=True)
xai_method = os.getenv('xai_method')
@@ -95,7 +104,7 @@ def continuous_emd(gt_mask, attribution, n_dim=64):
combined_mask = combined_mask.reshape((8,8))
plt.style.use('seaborn-v0_8-colorblind')
- fig, axes = plt.subplots(3, batch_size, figsize=(2 * batch_size, 6))
+ fig, axes = plt.subplots(3, batch_size, figsize=(3 * batch_size, 9))
if batch_size == 1:
axes = np.expand_dims(axes, 1)
@@ -108,7 +117,9 @@ def continuous_emd(gt_mask, attribution, n_dim=64):
data_img = x_test_batch[i].detach().numpy().reshape(edge_length, edge_length)
axes[0, i].imshow(data_img, cmap='RdBu_r', vmin=-1, vmax=1)
axes[0, i].axis('off')
- axes[0, i].set_title(f"Sample #{i}", fontsize=10)
+ sample_emd = emd_scores[i]
+ sample_ima = importance_mass_accuracy(d.masks_test[i], explanations[i].detach().numpy())
+ axes[0, i].set_title(f"Sample #{i}\nEMD: {sample_emd:.2f} | IMA: {sample_ima:.2f}", fontsize=12, fontweight='bold', pad=10)
if i == 0:
axes[0, i].text(-0.2, 0.5, 'Data', va='center', ha='right', rotation=90, transform=axes[0, i].transAxes, fontsize=12)
From f7a601ef6ba20953f81cc8e58020f651f634f9f3 Mon Sep 17 00:00:00 2001
From: Benny Clark
Date: Wed, 20 May 2026 17:20:23 +0200
Subject: [PATCH 07/10] added deployment script for easier remote deployment
---
deploy.sh | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 deploy.sh
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..9230e0f
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+sudo docker compose down -v
+sudo docker compose build frontend
+sudo docker compose up -d
+sudo docker compose exec backend python manage.py migrate
\ No newline at end of file
From c301f535020041be5a61b2342897e097d84af266 Mon Sep 17 00:00:00 2001
From: Benny Clark
Date: Wed, 20 May 2026 17:46:44 +0200
Subject: [PATCH 08/10] added frontend and backend healthchecks for auto
restart on errors, changed to npm start over run dev
---
compose.yaml | 25 ++++++++++++++++++++-----
1 file changed, 20 insertions(+), 5 deletions(-)
diff --git a/compose.yaml b/compose.yaml
index 7c691bd..6d109d5 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -6,6 +6,12 @@ services:
ports:
- "8000:8000"
user: root
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD-SHELL", "curl -f http://localhost:8000/ || kill 1" ]
+ interval: 60s
+ timeout: 20s
+ retries: 5
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./platform/backend:/app
@@ -18,15 +24,24 @@ services:
frontend:
build:
context: ./platform/frontend
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD-SHELL", "wget -qO- http://localhost:3000/ > /dev/null || kill 1" ]
+ interval: 60s
+ timeout: 20s
+ retries: 3
volumes:
- ./platform/frontend:/app
- /app/node_modules
ports:
- "8080:3000"
working_dir: /app
- command: npm run dev
- environment:
- - CHOKIDAR_USEPOLLING=true
+ command:
+ - sh
+ - -c
+ - |
+ npm run build
+ npm start
# Dieser Dienst ist jetzt AKTIVIERT.
# Er läuft nicht permanent, aber Docker Compose weiß jetzt, wie man
@@ -54,8 +69,8 @@ services:
- "5433:5432"
healthcheck:
test: [ "CMD", "pg_isready" ]
- interval: 10s
- timeout: 5s
+ interval: 60s
+ timeout: 10s
retries: 5
devcontainer:
From 6e64821b5b951145b5bbd5acac193fddb5b1051c Mon Sep 17 00:00:00 2001
From: Benny Clark
Date: Wed, 20 May 2026 17:48:52 +0200
Subject: [PATCH 09/10] updated deploy script
---
deploy.sh | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/deploy.sh b/deploy.sh
index 9230e0f..1e4e911 100644
--- a/deploy.sh
+++ b/deploy.sh
@@ -1,5 +1,4 @@
#!/bin/bash
-sudo docker compose down -v
-sudo docker compose build frontend
-sudo docker compose up -d
+sudo docker compose down
+sudo docker compose up -d --build
sudo docker compose exec backend python manage.py migrate
\ No newline at end of file
From f68a9de9058f396a7b620225894713fb11de1d34 Mon Sep 17 00:00:00 2001
From: Benny Clark
Date: Wed, 20 May 2026 18:16:09 +0200
Subject: [PATCH 10/10] fixed invisible button bug caused by tailwind and css
conflict
---
deploy.sh | 0
.../frontend/app/competitions/create/page.tsx | 2 +-
.../app/components/SubmissionUpload.tsx | 18 +++++++++++-------
platform/frontend/app/page.tsx | 8 ++++----
platform/frontend/tailwind.config.ts | 3 +++
5 files changed, 19 insertions(+), 12 deletions(-)
mode change 100644 => 100755 deploy.sh
diff --git a/deploy.sh b/deploy.sh
old mode 100644
new mode 100755
diff --git a/platform/frontend/app/competitions/create/page.tsx b/platform/frontend/app/competitions/create/page.tsx
index 79b2ede..c18d524 100644
--- a/platform/frontend/app/competitions/create/page.tsx
+++ b/platform/frontend/app/competitions/create/page.tsx
@@ -261,7 +261,7 @@ const CreateChallenge = () => {
))}