Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 peeb

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ We recommend Docker Desktop for managing the images and containers. (Link: https

# How to run the platform:

### running on ramen

`sudo docker compose down -v`
`sudo docker compose build frontend`
`sudo docker compose up -d`
`sudo docker compose exec backend python manage.py migrate`

### building images

Inside the folder containing the repository, please run the following command:
Expand Down Expand Up @@ -49,12 +56,4 @@ Alternatively, if you have the docker desktop app, you can follow these steps:

### You're done! You can now access the platform at: localhost:3000

### MIT Liscense

Copyright (c) <2024>

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 changes: 31 additions & 12 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ 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
- ./media:/app/mysite/media
depends_on:
- db
Expand All @@ -17,28 +24,40 @@ 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:
- "3000:3000"
- "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
# das 'exact-worker'-Image baut, wenn der 'build'-Befehl ausgeführt wird.
worker:
build:
context: ./worker
image: exact-worker # Wir definieren den Namen des Images explizit
image: exact-worker # Wir definieren den Namen des Images explizit
platform: linux/amd64
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock

db:
image: postgres
image: postgres:16
restart: always
user: postgres
secrets:
- db-password
volumes:
Expand All @@ -47,11 +66,11 @@ services:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
ports:
- "5432:5432"
- "5433:5432"
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
test: [ "CMD", "pg_isready" ]
interval: 60s
timeout: 10s
retries: 5

devcontainer:
Expand All @@ -67,4 +86,4 @@ volumes:

secrets:
db-password:
file: db/password.txt
file: db/password.txt
36 changes: 36 additions & 0 deletions create_issues.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash

REPO="braindatalab/exact"
CSV_FILE="issues.csv"

# Skip header
tail -n +1 "$CSV_FILE" | while IFS=',' read -r raw_title raw_body raw_labels; do
# Remove surrounding quotes and clean up spacing
title=$(echo "$raw_title" | sed 's/^"//;s/"$//' | xargs)
body=$(echo "$raw_body" | sed 's/^"//;s/"$//' | xargs)
labels=$(echo "$raw_labels" | sed 's/^"//;s/"$//' | xargs)

# Write body to temp file
tmpfile=$(mktemp)
echo "$body" > "$tmpfile"

echo "🛠 Creating issue: $title"

# Build label flags
label_args=()
IFS=',' read -ra label_array <<< "$labels"
for label in "${label_array[@]}"; do
label_args+=(--label "$label")
done

# Create the issue
gh issue create \
--repo "$REPO" \
--title "$title" \
--body-file "$tmpfile" \
"${label_args[@]}"

rm "$tmpfile"

echo "✅ Created: $title"
done
4 changes: 4 additions & 0 deletions deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
sudo docker compose down
sudo docker compose up -d --build
sudo docker compose exec backend python manage.py migrate
17 changes: 17 additions & 0 deletions issues.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
"Define core user stories for the platform MVP","Create high-level user stories for registering, submitting a method, viewing leaderboard, and administering challenges.","planning,meta"
"Update user registration to require admin approval","Add a registration status field (e.g., pending, approved), create admin UI to approve users, and prevent unapproved users from accessing submission features.","frontend,backend,database"
"Design and store method name field for submissions","Add form field for method name in submission UI, store in DB, and expose via API.","frontend,backend,database"
"Display method names on leaderboard","Update leaderboard UI and API to show submitted method names alongside scores.","frontend,backend"
"Enable sorting leaderboard by method score","Allow users to click table headers to sort by score; add support for sorting server-side or client-side.","frontend,backend"
"Update leaderboard to show all metric results per submission","Show full list of metrics per method: EMD, IMA, etc. alongside method name and challenge.","frontend,backend"
"Enable leaderboard sorting by each metric, default to primary metric","Add column-based sorting to each metric; default sort by user-selected or predefined metric.","frontend,backend"
"Add metric selection (checkboxes) to challenge creation UI","Let admins select metrics (e.g. EMD, IMA) when creating a challenge; store this in DB.","frontend,backend"
"Create structure for XAI super-challenges","Group related scenarios (e.g. XAI-TRIS variants) under one meta-challenge with shared leaderboard and/or unified submission logic.","backend,database"
"Integrate all XAI-TRIS datasets as challenges","For each XAI-TRIS scenario, create challenge metadata, upload dataset, and add basic visualization/description.","backend,worker,database"
"Integrate MRI dataset into the platform","Add MRI semi-synthetic dataset as a challenge, with description and appropriate metrics.","backend,worker,database"
"Enable multi-worker processing system","Modify job dispatch system to handle concurrent/multiple workers processing submissions in parallel.","worker,backend"
"Implement full input/output validation and Docker sandboxing","Validate submission format, restrict Docker execution, check method outputs conform to required interface.","worker,security,backend"
"Add CI pipeline with GitHub Actions","Set up CI to lint, test backend/frontend, and verify Docker builds.","devops"
"Test deployment on PTB server","Ensure full system deploys on PTB infrastructure, including DB, backend, frontend, and workers.","devops,deployment"
"Platform stabilization and bug fixing sprint","Focus sprint on UI polish, error handling, and cross-component reliability.","frontend,backend,worker,qa"
"Populate known results from XAI-TRIS and MRI papers","Add historical experimental results to leaderboard for baseline comparisons.","backend,database"
1 change: 1 addition & 0 deletions platform/backend/api/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class ChallengeForm(forms.Form):
widget=forms.Textarea, # mehrzeiliges Textfeld
max_length=100 # optional: Längenlimit
)
creator = forms.CharField(required=False, max_length=150)
xai_method = forms.FileField()
dataset = forms.FileField()
mlmodel = forms.FileField()
Expand Down
48 changes: 48 additions & 0 deletions platform/backend/api/migrations/0002_auto_20260325_2147.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 3.2.23 on 2026-03-25 21:47

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='score',
name='emd_score',
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name='score',
name='emd_std',
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name='score',
name='error_message',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='score',
name='ima_score',
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name='score',
name='ima_std',
field=models.FloatField(blank=True, null=True),
),
migrations.AddField(
model_name='score',
name='status',
field=models.CharField(choices=[('pending', 'Pending'), ('running', 'Running'), ('completed', 'Completed'), ('failed', 'Failed')], default='completed', max_length=20),
),
migrations.AlterField(
model_name='score',
name='score',
field=models.FloatField(blank=True, null=True),
),
]
18 changes: 18 additions & 0 deletions platform/backend/api/migrations/0003_challenge_creator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2026-05-20 08:15

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0002_auto_20260325_2147'),
]

operations = [
migrations.AddField(
model_name='challenge',
name='creator',
field=models.CharField(blank=True, max_length=150, null=True),
),
]
18 changes: 18 additions & 0 deletions platform/backend/api/migrations/0004_score_plot_base64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.23 on 2026-05-20 08:58

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('api', '0003_challenge_creator'),
]

operations = [
migrations.AddField(
model_name='score',
name='plot_base64',
field=models.TextField(blank=True, null=True),
),
]
4 changes: 3 additions & 1 deletion platform/backend/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Score(models.Model):
ima_score = models.FloatField(null=True, blank=True)
ima_std = models.FloatField(null=True, blank=True)

plot_base64 = models.TextField(null=True, blank=True)

method_name = models.CharField(max_length=100, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)

Expand Down Expand Up @@ -76,11 +78,11 @@ def __str__(self):
score_display = " | ".join(scores_str) if scores_str else "No scores"
return f"User {self.username} - Challenge {self.challenge_id} - {score_display}"

# Challenge model (bleibt unverändert)
class Challenge(models.Model):
challenge_id = models.CharField(max_length=100, unique=True, editable=False)
title = models.CharField(max_length=100)
description = models.TextField()
creator = models.CharField(max_length=150, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)

# Uploads
Expand Down
4 changes: 4 additions & 0 deletions platform/backend/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ class ScoreSerializer(serializers.ModelSerializer):
ima_score = serializers.FloatField(required=False, allow_null=True)
ima_std = serializers.FloatField(required=False, allow_null=True)

plot_base64 = serializers.CharField(required=False, allow_null=True)

# Legacy score field - now optional
score = serializers.FloatField(required=False, allow_null=True)

Expand All @@ -27,6 +29,7 @@ class Meta:
'emd_std',
'ima_score',
'ima_std',
'plot_base64',
'primary_score',
'method_name',
'created_at',
Expand All @@ -48,6 +51,7 @@ class Meta:
'challenge_id',
'title',
'description',
'creator',
'created_at',
'dataset',
'mlmodel',
Expand Down
Loading