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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,862 changes: 1,439 additions & 1,423 deletions poetry.lock

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ classifiers = [
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Operating System :: POSIX :: Linux",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X",
Expand Down Expand Up @@ -49,10 +51,10 @@ exclude = [
]

[tool.poetry.dependencies]
python = "^3.9"
python = "^3.12"
fastapi = "0.104.*"
uvicorn = "0.24.*"
numpy = "1.24.3"
numpy = "1.26.*"
pandas = "1.5.3"
aiofiles = "*"
tqdm = "*"
Expand All @@ -70,10 +72,11 @@ httpx = "0.21.0"
starlette = "0.27.0"
firebase_admin = "*"
pyrebase4 = "4.7.1"
pydantic = { version = "1.10.13", extras = ["email", "dotenv"] }
pydantic = { version = "^2.0", extras = ["email", "dotenv"] }
pydantic-settings = "^2.0.0"
python-jose = { version = "*", extras = ["cryptography"] }
alembic = "1.9.2"
sqlmodel = "0.0.12"
sqlmodel = "^0.0.14"
pymysql = "1.1.0"
fastapi_pagination = "0.12.13"
plotly = "5.18.0"
Expand Down
6 changes: 3 additions & 3 deletions studio/app/Snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ if config.get('type') == "EDIT_ROI":
conda:
EditRoiUtils.conda(config)
script:
f"{DIRPATH.APP_DIR}/optinist/core/rules/edit_ROI.py"
DIRPATH.APP_DIR + "/optinist/core/rules/edit_ROI.py"
else:
rule all:
input: [join_filepath([DIRPATH.OUTPUT_DIR, x]) for x in config["last_output"]]
Expand All @@ -28,7 +28,7 @@ else:
params:
name = details
script:
f"{DIRPATH.APP_DIR}/common/core/rules/data.py"
DIRPATH.APP_DIR + "/common/core/rules/data.py"
else:
rule:
name:
Expand All @@ -42,4 +42,4 @@ else:
conda:
SmkUtils.conda(details)
script:
f"{DIRPATH.APP_DIR}/common/core/rules/func.py"
DIRPATH.APP_DIR + "/common/core/rules/func.py"
6 changes: 4 additions & 2 deletions studio/app/common/core/auth/auth_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pydantic import BaseSettings, Field
from pydantic import Field
from pydantic_settings import BaseSettings

from studio.app.dir_path import DIRPATH

Expand All @@ -9,11 +10,12 @@ class AuthConfig(BaseSettings):
)
SECRET_KEY: str = Field(default="123456", env="SECRET_KEY")
USE_FIREBASE_TOKEN: bool = Field(default=True, env="USE_FIREBASE_TOKEN")
ALGORITHM = "HS256"
ALGORITHM: str = "HS256"

class Config:
env_file = f"{DIRPATH.CONFIG_DIR}/.env"
env_file_encoding = "utf-8"
extra = "allow"


AUTH_CONFIG = AuthConfig()
24 changes: 21 additions & 3 deletions studio/app/common/core/experiment/experiment_builder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from studio.app.common.core.experiment.experiment import ExptConfig
from studio.app.optinist.schemas.nwb import NWBParams
from studio.app.common.core.snakemake.smk import SmkParam


class ExptConfigBuilder:
Expand Down Expand Up @@ -58,11 +60,27 @@
return self

def set_nwbfile(self, nwbfile) -> "ExptConfigBuilder":
self._nwbfile = nwbfile
# Convert dict to NWBParams if needed
if isinstance(nwbfile, dict):
try:
self._nwbfile = NWBParams(**nwbfile)
except Exception:
# If conversion fails, store as dict
self._nwbfile = nwbfile
else:
self._nwbfile = nwbfile
return self

def set_snakemake(self, snakemake) -> "ExptConfigBuilder":
self._snakemake = snakemake
# Convert dict to SmkParam if needed
if isinstance(snakemake, dict):
try:
self._snakemake = SmkParam(**snakemake)
except Exception:
# If conversion fails, store as dict
self._snakemake = snakemake
else:
self._snakemake = snakemake
return self

def set_data_usage(self, data_usage) -> "ExptConfigBuilder":
Expand All @@ -82,4 +100,4 @@
nwb=self._nwbfile,
snakemake=self._snakemake,
data_usage=self._data_usage,
)
)

Check warning on line 103 in studio/app/common/core/experiment/experiment_builder.py

View workflow job for this annotation

GitHub Actions / flake8

no newline at end of file
24 changes: 22 additions & 2 deletions studio/app/common/core/experiment/experiment_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@

self.build_function_from_nodeDict()

config = self.builder.build()
# Write EXPERIMENT_YML
self._write_raw(
self.workspace_id, self.unique_id, config=asdict(self.builder.build())
self.workspace_id, self.unique_id, config=self._convert_config_to_dict(
config
)
)

@classmethod
Expand All @@ -73,6 +76,19 @@
auto_file_lock=auto_file_lock,
)

@classmethod
def _convert_config_to_dict(self, config):
"""Convert ExptConfig to dictionary, handling Pydantic v2 models"""
config_dict = asdict(config)

Check warning on line 83 in studio/app/common/core/experiment/experiment_writer.py

View workflow job for this annotation

GitHub Actions / flake8

blank line contains whitespace
# Convert Pydantic v2 models to dictionaries
if hasattr(config.nwb, 'model_dump'):
config_dict['nwb'] = config.nwb.model_dump()
if hasattr(config.snakemake, 'model_dump'):
config_dict['snakemake'] = config.snakemake.model_dump()

Check warning on line 89 in studio/app/common/core/experiment/experiment_writer.py

View workflow job for this annotation

GitHub Actions / flake8

blank line contains whitespace
return config_dict

def overwrite(self, update_params: dict) -> None:
expt_filepath = ExptConfigReader.get_config_yaml_path(
self.workspace_id, self.unique_id
Expand All @@ -85,7 +101,10 @@
config = ExptConfigReader.read(self.workspace_id, self.unique_id)

# Merge overwrite params
config_merged = differential_deep_merge(asdict(config), update_params)
config_merged = differential_deep_merge(
self._convert_config_to_dict(config),
update_params
)

# Overwrite experiment config
__class__._write_raw(
Expand Down Expand Up @@ -382,3 +401,4 @@
except Exception as e:
logger.error(f"Failed to update experiment.yml: {e}")
return False

Check warning on line 404 in studio/app/common/core/experiment/experiment_writer.py

View workflow job for this annotation

GitHub Actions / flake8

no newline at end of file

Check warning on line 404 in studio/app/common/core/experiment/experiment_writer.py

View workflow job for this annotation

GitHub Actions / flake8

blank line contains whitespace
4 changes: 3 additions & 1 deletion studio/app/common/core/mode.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pydantic import BaseSettings, Field
from pydantic import Field
from pydantic_settings import BaseSettings

from studio.app.dir_path import DIRPATH

Expand All @@ -20,6 +21,7 @@ def reset_mode(self, is_standalone: bool):
class Config:
env_file = f"{DIRPATH.CONFIG_DIR}/.env"
env_file_encoding = "utf-8"
extra = "allow"


MODE = Mode()
7 changes: 3 additions & 4 deletions studio/app/common/core/snakemake/smk.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Dict, List, Union

from pydantic import BaseModel
Expand Down Expand Up @@ -29,11 +29,10 @@ class ForceRun(BaseModel):
name: str


@dataclass
class SmkParam:
class SmkParam(BaseModel):
use_conda: bool
cores: int
forceall: bool
forcetargets: bool
lock: bool
forcerun: List[ForceRun] = field(default_factory=list)
forcerun: List[ForceRun] = []
13 changes: 12 additions & 1 deletion studio/app/common/core/snakemake/smk_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,18 @@
config_copy = copy.deepcopy(config)
nwb_template = config_copy.get("nwb_template", {})

template_str = json.dumps(nwb_template, sort_keys=True) if nwb_template else ""
# Handle Pydantic models by converting to dict
if hasattr(nwb_template, 'model_dump'):
# Pydantic v2
nwb_template_dict = nwb_template.model_dump()
elif hasattr(nwb_template, 'dict'):
# Pydantic v1 fallback
nwb_template_dict = nwb_template.dict()
else:
# Regular dict or other type
nwb_template_dict = nwb_template

template_str = json.dumps(nwb_template_dict, sort_keys=True) if nwb_template_dict else ""

Check failure on line 171 in studio/app/common/core/snakemake/smk_utils.py

View workflow job for this annotation

GitHub Actions / flake8

line too long (97 > 88 characters)

# Check each rule and convert matching nwbfiles to references
for rule_name, rule in config_copy.get("rules", {}).items():
Expand Down
12 changes: 12 additions & 0 deletions studio/app/common/core/utils/config_handler.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os

from pydantic import BaseModel
import yaml
from filelock import FileLock

Expand Down Expand Up @@ -49,6 +50,17 @@

config_path = join_filepath([dirname, filename])

def convert_to_serializable(obj):
if isinstance(obj, BaseModel):
return obj.model_dump()
elif isinstance(obj, dict):
return {k: convert_to_serializable(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [convert_to_serializable(item) for item in obj]
return obj

Check warning on line 61 in studio/app/common/core/utils/config_handler.py

View workflow job for this annotation

GitHub Actions / flake8

blank line contains whitespace
config = convert_to_serializable(config)

if auto_file_lock:
# Exclusive control for parallel updates from multiple processes.
lock_path = FileLockUtils.get_lockfile_path(config_path)
Expand Down
24 changes: 12 additions & 12 deletions studio/app/common/core/workflow/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class NodeItem(BaseModel):
class OutputPath:
path: str
type: str
max_index: int = None
max_index: Optional[int] = None
data_shape: Optional[list] = field(default_factory=list)


Expand Down Expand Up @@ -186,9 +186,9 @@ class NodeData:
param: dict
path: Union[str, List]
type: str
fileType: str = None
hdf5Path: str = None
matPath: str = None
fileType: Optional[str] = None
hdf5Path: Optional[str] = None
matPath: Optional[str] = None
dataFilterParam: Union[DataFilterParam, dict, None] = field(
default_factory=lambda: DataFilterParam(dim1=[], roi=[])
)
Expand All @@ -199,17 +199,17 @@ class NodeData:

@dataclass
class NodePosition:
x: int
y: int
x: float
y: float


@dataclass
class Style:
border: str = None
height: int = None
padding: int = None
width: int = None
borderRadius: int = None
border: Optional[str] = None
height: Optional[int] = None
padding: Optional[int] = None
width: Optional[int] = None
borderRadius: Optional[int] = None


@dataclass
Expand All @@ -234,7 +234,7 @@ class Edge:


class RunItem(BaseModel):
name: str = None
name: Optional[str] = None
nodeDict: Dict[str, Node] = {}
edgeDict: Dict[str, Edge] = {}
snakemakeParam: dict = {}
Expand Down
24 changes: 22 additions & 2 deletions studio/app/common/core/workflow/workflow_params.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
from studio.app.common.core.logger import AppLogger
from studio.app.common.core.utils.config_handler import ConfigReader
from studio.app.common.core.utils.filepath_finder import find_param_filepath
from studio.app.optinist.schemas.nwb import NWBParams
from studio.app.common.core.snakemake.smk import SmkParam

logger = AppLogger.get_logger()


def get_typecheck_params(message_params, name):
default_params = ConfigReader.read(find_param_filepath(name))
if message_params != {} and message_params is not None:
return check_types(nest2dict(message_params), default_params)
return default_params
params = check_types(nest2dict(message_params), default_params)
else:
params = default_params

if name == "nwb":
try:
validated_params = NWBParams(**params)
return validated_params
except Exception as e:
logger.error(f"Error converting params to NWBParams: {e}")
return params
elif name == "snakemake":
try:
validated_params = SmkParam(**params)
return validated_params
except Exception as e:
logger.error(f"Error converting params to SmkParam: {e}")
return params

Check warning on line 31 in studio/app/common/core/workflow/workflow_params.py

View workflow job for this annotation

GitHub Actions / flake8

blank line contains whitespace
return params


def check_types(params, default_params):
Expand Down
Loading
Loading