Skip to content
Draft
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions MAIA/dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""
MAIA Dashboard Package

This package contains the Django dashboard application for MAIA.
It includes all apps and core configuration for the web-based interface.
"""
Empty file added MAIA/dashboard/apps/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions MAIA/dashboard/apps/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
10 changes: 10 additions & 0 deletions MAIA/dashboard/apps/api/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from rest_framework import serializers


from apps.models import Book


class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__"
8 changes: 8 additions & 0 deletions MAIA/dashboard/apps/api/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.urls import re_path
from django.views.decorators.csrf import csrf_exempt

from apps.api.views import BookView

urlpatterns = [
re_path("books/((?P<pk>\d+)/)?", csrf_exempt(BookView.as_view())),
]
52 changes: 52 additions & 0 deletions MAIA/dashboard/apps/api/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from http import HTTPStatus
from django.http import Http404
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import get_object_or_404
from rest_framework.permissions import IsAuthenticatedOrReadOnly


from apps.api.serializers import BookSerializer
from apps.models import Book


class BookView(APIView):

permission_classes = (IsAuthenticatedOrReadOnly,)

def post(self, request):
serializer = BookSerializer(data=request.POST)
if not serializer.is_valid():
return Response(data={**serializer.errors, "success": False}, status=HTTPStatus.BAD_REQUEST)
serializer.save()
return Response(data={"message": "Record Created.", "success": True}, status=HTTPStatus.OK)

def get(self, request, pk=None):
if not pk:
return Response(
{"data": [BookSerializer(instance=obj).data for obj in Book.objects.all()], "success": True}, status=HTTPStatus.OK
)
try:
obj = get_object_or_404(Book, pk=pk)
except Http404:
return Response(data={"message": "object with given id not found.", "success": False}, status=HTTPStatus.NOT_FOUND)
return Response({"data": BookSerializer(instance=obj).data, "success": True}, status=HTTPStatus.OK)

def put(self, request, pk):
try:
obj = get_object_or_404(Book, pk=pk)
except Http404:
return Response(data={"message": "object with given id not found.", "success": False}, status=HTTPStatus.NOT_FOUND)
serializer = BookSerializer(instance=obj, data=request.POST, partial=True)
if not serializer.is_valid():
return Response(data={**serializer.errors, "success": False}, status=HTTPStatus.BAD_REQUEST)
serializer.save()
return Response(data={"message": "Record Updated.", "success": True}, status=HTTPStatus.OK)

def delete(self, request, pk):
try:
obj = get_object_or_404(Book, pk=pk)
except Http404:
return Response(data={"message": "object with given id not found.", "success": False}, status=HTTPStatus.NOT_FOUND)
obj.delete()
return Response(data={"message": "Record Deleted.", "success": True}, status=HTTPStatus.OK)
4 changes: 4 additions & 0 deletions MAIA/dashboard/apps/authentication/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""
7 changes: 7 additions & 0 deletions MAIA/dashboard/apps/authentication/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""


# Register your models here.
11 changes: 11 additions & 0 deletions MAIA/dashboard/apps/authentication/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""

from django.apps import AppConfig


class AuthConfig(AppConfig):
name = "apps.auth"
label = "apps_auth"
138 changes: 138 additions & 0 deletions MAIA/dashboard/apps/authentication/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""

from django import forms
from django.contrib.auth.forms import UserCreationForm
from apps.models import MAIAUser, MAIAProject
from .models import MAIAInfo
from django.conf import settings
from MAIA.keycloak_utils import get_groups_in_keycloak


class MAIAInfoForm(forms.Form):
email = forms.EmailField(widget=forms.EmailInput(attrs={"placeholder": "Your Email", "class": "form-control"}))

class Meta:
model = MAIAInfo
fields = "email"


class LoginForm(forms.Form):
username = forms.CharField(widget=forms.TextInput(attrs={"placeholder": "Username", "class": "form-control"}))
password = forms.CharField(widget=forms.PasswordInput(attrs={"placeholder": "Password", "class": "form-control"}))


class SignUpForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields["username"] = forms.CharField(
widget=forms.TextInput(attrs={"placeholder": "Your Username", "class": "form-control"})
)

maia_groups = get_groups_in_keycloak(settings=settings)
# Disable option for registering to pending projects
pending_projects = [] # get_pending_projects(settings=settings, maia_project_model=MAIAProject)

for pending_project in pending_projects:
maia_groups[pending_project] = pending_project + " (Pending)"

self.fields["namespace"] = forms.ChoiceField(
choices=[(maia_group, maia_group) for maia_group in maia_groups.values() if maia_group not in ["admin", "users"]],
required=False,
widget=forms.Select(
attrs={
"class": "form-select text-center fw-bold",
"style": "max-width: auto;",
}
),
)

self.fields["email"] = forms.EmailField(
widget=forms.EmailInput(attrs={"placeholder": "Your Email", "class": "form-control"})
)
self.fields["password1"] = forms.CharField(
initial="maiaPassword",
)
self.fields["password2"] = forms.CharField(
initial="maiaPassword",
)

class Meta:
model = MAIAUser
fields = ("username", "email", "namespace", "password1", "password2")


class RegisterProjectForm(forms.ModelForm):

namespace = forms.CharField(
widget=forms.TextInput(attrs={"placeholder": "A unique identifier for your project.", "class": "form-control"})
)

def clean_namespace(self):
namespace = self.cleaned_data.get("namespace")
if " " in namespace or any(char.isupper() for char in namespace) or "_" in namespace:
raise forms.ValidationError("Namespace must not contain spaces, capital letters, or underscores.")
return namespace

email = forms.EmailField(
widget=forms.EmailInput(
attrs={"placeholder": "Your Email. If you specify a Principal Investigator below, they will be registered as the Project Admin instead.", "class": "form-control"}
)
)

gpu = forms.ChoiceField(
choices=[(gpu["name"], gpu["name"]) for gpu in settings.GPU_SPECS],
widget=forms.Select(
attrs={
"class": "form-select text-center fw-bold",
"style": "max-width: auto;",
}
),
)

conda = forms.FileField(
required=False,
label="Upload here your Conda Environment/PIP Requirements file to automatically load it in your environment.",
)

date = forms.DateField(widget=forms.TextInput(attrs={"class": "form-control", "type": "date"}))

memory_limit = [(str(2**pow) + " Gi", str(2**pow) + " Gi") for pow in range(settings.MAX_MEMORY)]
cpu_limit = [(str(2**pow), str(2**pow)) for pow in range(settings.MAX_CPU)]

memory_limit = forms.ChoiceField(
label="memory_limit",
choices=memory_limit,
)

cpu_limit = forms.ChoiceField(
label="cpu_limit",
choices=cpu_limit,
)

description = forms.CharField(
required=False,
widget=forms.Textarea(
attrs={
"placeholder": "Brief description of your project",
"class": "form-control",
"rows": 3
}
)
)

supervisor = forms.EmailField(
required=False,
widget=forms.EmailInput(
attrs={
"placeholder": "Email of the Principal Investigator (optional for student projects)",
"class": "form-control"
}
)
)

class Meta:
model = MAIAProject
fields = ("id", "namespace", "gpu", "conda", "date", "email", "memory_limit", "cpu_limit", "description", "supervisor")
55 changes: 55 additions & 0 deletions MAIA/dashboard/apps/authentication/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 4.2.19 on 2025-02-13 15:37

import datetime
from django.conf import settings
import django.contrib.auth.models
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
("auth", "0012_alter_user_first_name_max_length"),
]

operations = [
migrations.CreateModel(
name="MAIAProject",
fields=[
("id", models.AutoField(auto_created=True, primary_key=True, serialize=False)),
("email", models.EmailField(max_length=150, null=True, verbose_name="email")),
("namespace", models.CharField(blank=True, max_length=150, unique=True, verbose_name="namespace")),
("gpu", models.CharField(blank=True, max_length=150, null=True, verbose_name="gpu")),
("date", models.DateField(default=datetime.date.today, verbose_name="date")),
("memory_limit", models.TextField(default="2G", null=True, verbose_name="memory_limit")),
("cpu_limit", models.TextField(default="2", null=True, verbose_name="memory_limit")),
("conda", models.TextField(default="N/A", null=True, verbose_name="conda")),
("cluster", models.TextField(default="N/A", null=True, verbose_name="cluster")),
("minimal_env", models.TextField(default="Minimal", null=True, verbose_name="minimal_env")),
],
),
migrations.CreateModel(
name="MAIAUser",
fields=[
(
"user_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to=settings.AUTH_USER_MODEL,
),
),
("namespace", models.CharField(blank=True, max_length=150, verbose_name="namespace")),
],
bases=("auth.user",),
managers=[
("objects", django.contrib.auth.models.UserManager()),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated migration for adding description and supervisor fields

from django.db import migrations, models


class Migration(migrations.Migration):

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

operations = [
migrations.AddField(
model_name='maiaproject',
name='description',
field=models.TextField(blank=True, null=True, verbose_name='description'),
),
migrations.AddField(
model_name='maiaproject',
name='supervisor',
field=models.EmailField(max_length=150, null=True, blank=True, verbose_name='supervisor'),
),
]
Empty file.
15 changes: 15 additions & 0 deletions MAIA/dashboard/apps/authentication/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- encoding: utf-8 -*-
"""
Copyright (c) 2019 - present AppSeed.us
"""

from django.db import models


class MAIAInfo(models.Model):
class Meta:
app_label = "maia_info"

email = models.CharField(
max_length=255,
)
Loading
Loading