Skip to content
Merged
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
4 changes: 4 additions & 0 deletions src/simmate/apps/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class PriceCatalogConfig(AppConfig):
class ProjectManagementConfig(AppConfig):
name = "simmate.apps.project_management"

def ready(self):
# signals a wallet creation for new users
import simmate.apps.project_management.signals


class QuantumEspressoConfig(AppConfig):
name = "simmate.apps.quantum_espresso"
Expand Down
6 changes: 3 additions & 3 deletions src/simmate/apps/project_management/components/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-

from .mixins import ProjectInput, ProjectManagementInput
from .project_form import ProjectForm

# from .mixins import ProjectInput, ProjectManagementInput
# from .tag_form import TagFormView
from .tag_form import TagForm
from .token_balance_form import TokenBalanceForm
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@

class ProjectInput:

class Meta:
javascript_exclude = ("project_options",)

project_id = None

@cached_property
def project_options(self):
projects = Project.objects.order_by("name").values_list("id", "name").all()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,45 +1,37 @@
# -*- coding: utf-8 -*-

from simmate.website.utilities import parse_multiselect

from ...models import Project, Tag
from .project import ProjectInput


class ProjectManagementInput(ProjectInput):

class Meta:
javascript_exclude = (*ProjectInput.Meta.javascript_exclude,)

search_inputs = [
"project_id",
# "tag_ids",
"hypothesis_id",
]

# -------------------------------------------------------------------------

# project_id = None --> inheritted
# project_options --> inheritted

project__display = None
_project_obj = None

def set_project_id(self, project_id):
self.project_id = project_id
self.project_obj = Project.objects.get(id=self.project_id)
def on_change_hook__project_id(self):

project_id = self.form_data["project_id"]
self.project_obj = Project.objects.get(id=project_id)

self.tag_options = self.get_tag_options(self.project_obj)
self.hypothesis_options = self.get_hypothesis_options(self.project_obj)

self.call("refresh_select2")
self.js_actions = [
{"refresh_select2": []},
]

# populate display value for bulk form
for p_id, p_name in self.project_options:
if p_id == project_id:
self.project__display = p_name
break

# -------------------------------------------------------------------------

tag_options: list[tuple] = [] # set dynamically using Project.tag_options

@staticmethod
def get_tag_options(project: Project) -> list[tuple]:

Expand All @@ -59,6 +51,22 @@ def get_tag_options(project: Project) -> list[tuple]:
(id, tag_name) for id, tag_name in list(project_tags) + list(generic_tags)
]

tags__display = None

def on_change_hook__tags__ids(self):
# populate display value for bulk form
tag_ids = self.form_data.get("tags__ids", [])
self.tags__display = ""
for tag_id in tag_ids:
for t_id, t_name in self.tag_options:
if t_id == tag_id:
self.tags__display += f"{t_name}; "
break

# -------------------------------------------------------------------------

hypothesis_options = None

@staticmethod
def get_hypothesis_options(project: Project) -> list[tuple]:

Expand All @@ -68,40 +76,14 @@ def get_hypothesis_options(project: Project) -> list[tuple]:
# reformat into tuple of (value, display)
return [(id, name) for id, name in tags]

# -------------------------------------------------------------------------

hypothesis_id = None
hypothesis__display = None
hypothesis_options = None # set using Project.hypothesis_options

def set_hypothesis_id(self, hypothesis_id):
self.hypothesis_id = hypothesis_id
if hypothesis_id is None:
return

def on_change_hook__hypothesis_id(self):
hypothesis_id = self.form_data["hypothesis_id"]
# populate display value for bulk form
for h_id, h_name in self.hypothesis_options:
if h_id == hypothesis_id:
self.hypothesis__display = h_name
break

# -------------------------------------------------------------------------

tag_ids = None
tags__display = None
tag_options = [] # set dynamically using Project.tag_options

def set_tag_ids(self, tag_ids):
self.tag_ids = parse_multiselect(tag_ids)
if tag_ids is None:
return

# populate display value for bulk form
self.tags__display = ""
for tag_id in self.tag_ids:
for t_id, t_name in self.tag_options:
if t_id == tag_id:
self.tags__display += f"{t_name}; "
break

# -------------------------------------------------------------------------
37 changes: 11 additions & 26 deletions src/simmate/apps/project_management/components/project_form.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-

from django.db import transaction

from simmate.website.htmx.components import DynamicTableForm, UserInput

from ..models import Project
Expand All @@ -11,9 +9,7 @@ class ProjectForm(DynamicTableForm, UserInput):

table = Project

template_names = dict(
default="project_management/project/form.html",
)
template_name = "project_management/project/form.html"

# -------------------------------------------------------------------------

Expand All @@ -24,8 +20,8 @@ class ProjectForm(DynamicTableForm, UserInput):
"description",
"discipline",
"status",
"leader_ids",
"member_ids",
"leaders__ids",
"members__ids",
]

def check_form_for_create(self):
Expand All @@ -40,32 +36,21 @@ def check_form_for_create(self):
f"A project with the name '{project_name}' already exists."
)

def save_to_db_for_create(self):
# We are saving multiple objects, so we want to make sure ALL work.
# If not, we roll back everything
with transaction.atomic():
# Save the request to the database
self.table_entry.save()
# add leaders and members
self.table_entry.leaders.set(self.form_data["leader_ids"])
self.table_entry.members.set(self.form_data["member_ids"])

# -------------------------------------------------------------------------

# UPDATE

def mount_for_update(self):
super().mount_for_update()
self.form_data["leader_ids"] = list(
self.table_entry.leaders.values_list("id", flat=True).all()
)
self.form_data["member_ids"] = list(
self.table_entry.members.values_list("id", flat=True).all()
)
mount_for_update_columns = [
"name",
"description",
"discipline",
"status",
"leaders__ids",
"members__ids",
]

def check_form_for_update(self):
self.check_required_inputs()

# if the sure the Project name was changed, make sure it is a new one
project_name = self.form_data.get("name", None)
if project_name:
Expand Down
61 changes: 34 additions & 27 deletions src/simmate/apps/project_management/components/tag_form.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,59 @@
# -*- coding: utf-8 -*-


from simmate.website.core_components.components import DynamicFormComponent
from simmate.website.htmx.components import DynamicTableForm

from ..models import Project, Tag


class TagFormView(DynamicFormComponent):
class TagForm(DynamicTableForm):

template_name = "projects/tag/form.html"
table = Tag

template_name = "project_management/tag/form.html"

# -------------------------------------------------------------------------

def mount_extra(self):
self.project_options = Project.project_options

def update_tag_type(self):
if self.form_data.get("tag_type") == "project-specific":
self.js_actions = [
{"refresh_select2": []},
]

# -------------------------------------------------------------------------

# CREATE

required_inputs = [
"tag_type",
"name",
"description",
]
search_inputs = [
"name",
"tag_type",
"project_id",
]

class Meta:
javascript_exclude = (
"tag_type_options",
"project_options",
*DynamicFormComponent.Meta.javascript_exclude,
)
# -------------------------------------------------------------------------

def mount_extra(self):
self.project_options = Project.project_options
# UPDATE

# -------------------------------------------------------------------------

tag_type = None
tag_type_options = [(o, o) for o in Tag.tag_type_options]
# CREATE MANY
# disabled

project_id = None
project_options = [] # set by mount()
# -------------------------------------------------------------------------

name = None
# UPDATE MANY
# disabled

description = None
# -------------------------------------------------------------------------

# SEARCH

def set_tag_type(self, tag_type):
self.tag_type = tag_type
if self.tag_type == "project-specific":
self.call("refresh_select2")
search_inputs = [
"name",
"tag_type",
"project_id",
]

# -------------------------------------------------------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-

from django.http import HttpResponseRedirect

from simmate.website.htmx.components import HtmxComponent


class TokenBalanceForm(HtmxComponent):

template_name = "project_management/wallet/token_balance_form.html"

def submit(self):

wallet = self.request.user.wallet

wallet.adjust_tokens(
token_amount=float(self.form_data["token_amount"]),
sending_user=self.request.user,
)

# just refresh the current page to get the updated balance
return HttpResponseRedirect(self.initial_context.request.path_info)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.7 on 2025-12-14 02:52
# Generated by Django 4.2.7 on 2026-01-01 23:09

import django.db.models.deletion
from django.conf import settings
Expand Down
35 changes: 35 additions & 0 deletions src/simmate/apps/project_management/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,38 @@ def html_extra_entry_context(self) -> dict:
}

# -------------------------------------------------------------------------

# This section is DEPRECIATED in favor of form mix-ins

@property
def tag_options(self) -> list[tuple]:

from .tag import Tag

# query for all tags directly linked
project_tags = self.tags.order_by("name").values_list("id", "name").all()

# add in generic tags after specific ones
generic_tags = (
Tag.objects.filter(tag_type="all-projects")
.order_by("name")
.values_list("id", "name")
.all()
)

# reformat into tuple of (value, display)
return [
(id, tag_name) for id, tag_name in list(project_tags) + list(generic_tags)
]

@classmethod
@property
def project_options(cls) -> list[tuple]:

# query for all tags
projects = cls.objects.order_by("name").values_list("id", "name").all()

# reformat into tuple of (value, display)
return [(id, name) for id, name in projects]

# -------------------------------------------------------------------------
Loading
Loading