Skip to content

Commit c99a115

Browse files
Merge pull request #41 from biosimulations/simple-slurm-submission
Simple slurm submission
2 parents 90c853e + 474df2c commit c99a115

93 files changed

Lines changed: 2612 additions & 2895 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-test.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Main
2+
3+
on:
4+
push:
5+
6+
jobs:
7+
quality:
8+
runs-on: ubuntu-latest
9+
steps:
10+
- name: Check out
11+
uses: actions/checkout@v4
12+
13+
- uses: actions/cache@v4
14+
with:
15+
path: ~/.cache/pre-commit
16+
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
17+
18+
- name: Set up the environment
19+
uses: ./.github/actions/setup-python-env
20+
21+
- name: Run checks
22+
run: make check
23+
24+
tests-and-type-check:
25+
runs-on: ubuntu-latest
26+
strategy:
27+
matrix:
28+
python-version: ["3.12", "3.13"]
29+
fail-fast: false
30+
defaults:
31+
run:
32+
shell: bash
33+
steps:
34+
- name: Check out
35+
uses: actions/checkout@v4
36+
37+
- name: Set up the environment
38+
uses: ./.github/actions/setup-python-env
39+
with:
40+
python-version: ${{ matrix.python-version }}
41+
42+
- name: Run All tests
43+
run: uv run python -m pytest tests --cov --cov-config=pyproject.toml --cov-report=xml
44+
45+
- name: Check typing
46+
run: uv run mypy
47+
48+
- name: Upload coverage reports to Codecov with GitHub Action on Python 3.11
49+
uses: codecov/codecov-action@v4
50+
if: ${{ matrix.python-version == '3.11' }}
51+
env:
52+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ cython_debug/
177177
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
178178
# and can be added to the global gitignore or merged into this file. For a more nuclear
179179
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
180-
#.idea/
180+
.idea/
181181

182182
# Abstra
183183
# Abstra is an AI-powered process automation framework.

.idea/compose-api.iml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Dockerfile-api

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Use the official Python image from the Docker Hub
22
FROM python:3.13.2-slim-bookworm AS base
33

4+
ARG APP_GID=10000
5+
ARG APP_UID=16997
6+
47
RUN apt-get update && \
58
apt-get install -y --no-install-recommends \
69
openssh-client && \
@@ -11,18 +14,23 @@ ENV PYTHONUNBUFFERED=1
1114

1215
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
1316

17+
# create user/group
18+
RUN groupadd -g $APP_GID appgroup && \
19+
useradd -m -u $APP_UID -g $APP_GID appuser
20+
USER appuser
21+
1422
# Change the working directory to the `app` directory
1523
WORKDIR /app
1624

1725
# Copy the lockfile and `pyproject.toml` into the image
18-
COPY uv.lock /app/uv.lock
19-
COPY pyproject.toml /app/pyproject.toml
26+
COPY --chown=appuser:appgroup uv.lock /app/uv.lock
27+
COPY --chown=appuser:appgroup pyproject.toml /app/pyproject.toml
2028

2129
# Install dependencies
2230
RUN uv sync --frozen --no-install-project
2331

2432
# Copy the project into the image
25-
COPY . /app
33+
COPY --chown=appuser:appgroup . /app
2634

2735
# Sync the project
2836
RUN uv sync --frozen
@@ -37,4 +45,4 @@ ENV APP_DIR=/app/app
3745
ENV ASSETS_DIR=/app/assets
3846

3947
# Command to run the application
40-
CMD ["uvicorn", "compose_api.api.main:app", "--host", "0.0.0.0", "--port", "8000"]
48+
CMD ["/bin/bash", "-c", "source .venv/bin/activate && uvicorn compose_api.api.main:app --host 0.0.0.0 --port 8000"]

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ check: ## Run code quality tools.
1515
@echo "🚀 Checking for obsolete dependencies: Running deptry"
1616
@uv run deptry .
1717

18+
.PHONY: clients
19+
clients: ## Run code quality tools.
20+
@echo "🚀 Generating OpenAPI Spec"
21+
@python3 compose_api/api/openapi_spec.py
22+
@echo "🚀 Creating HTTPX Clients"
23+
@scripts/generate-api-client.sh
24+
1825
.PHONY: test
1926
test: ## Test the code with pytest
2027
@echo "🚀 Testing code: Running pytest"

assets/dev/config/.dev_env_TEMPLATE

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ SLURM_SUBMIT_USER=
2020
SLURM_SUBMIT_KEY_PATH=
2121
SLURM_PARTITION=
2222
SLURM_NODE_LIST=
23+
SLURM_BUILD_NODE=
2324
SLURM_QOS=
2425
SLURM_LOG_BASE_PATH=
2526
SLURM_SUBMIT_KNOWN_HOSTS=
2627

2728
HPC_IMAGE_BASE_PATH=
2829
HPC_SIM_BASE_PATH=
2930
HPC_SIM_CONFIG_FILE=
31+
SLURM_SBATCH_BASE_PATH=
32+
33+
# The home compose_api dir for crbmapi
34+
INTERNAL_MOUNT_DIR=/mnt/crbmapi
35+
NAMESPACE=test
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
from http import HTTPStatus
2+
from typing import Any, Optional, Union, cast
3+
4+
import httpx
5+
6+
from ...client import AuthenticatedClient, Client
7+
from ...types import Response, UNSET
8+
from ... import errors
9+
10+
from ...models.body_analyze_simulation_omex import BodyAnalyzeSimulationOmex
11+
from ...models.http_validation_error import HTTPValidationError
12+
from typing import cast
13+
14+
15+
def _get_kwargs(
16+
*,
17+
body: BodyAnalyzeSimulationOmex,
18+
) -> dict[str, Any]:
19+
headers: dict[str, Any] = {}
20+
21+
_kwargs: dict[str, Any] = {
22+
"method": "post",
23+
"url": "/core/simulation/analyze",
24+
}
25+
26+
_kwargs["files"] = body.to_multipart()
27+
28+
_kwargs["headers"] = headers
29+
return _kwargs
30+
31+
32+
def _parse_response(
33+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
34+
) -> Optional[Union[HTTPValidationError, str]]:
35+
if response.status_code == 200:
36+
response_200 = response.text
37+
return response_200
38+
if response.status_code == 422:
39+
response_422 = HTTPValidationError.from_dict(response.json())
40+
41+
return response_422
42+
if client.raise_on_unexpected_status:
43+
raise errors.UnexpectedStatus(response.status_code, response.content)
44+
else:
45+
return None
46+
47+
48+
def _build_response(
49+
*, client: Union[AuthenticatedClient, Client], response: httpx.Response
50+
) -> Response[Union[HTTPValidationError, str]]:
51+
return Response(
52+
status_code=HTTPStatus(response.status_code),
53+
content=response.content,
54+
headers=response.headers,
55+
parsed=_parse_response(client=client, response=response),
56+
)
57+
58+
59+
def sync_detailed(
60+
*,
61+
client: Union[AuthenticatedClient, Client],
62+
body: BodyAnalyzeSimulationOmex,
63+
) -> Response[Union[HTTPValidationError, str]]:
64+
"""Analyze a process bi-graph,
65+
and determine the singularity definition file which would build an environment it can run in.
66+
67+
Resulting container definition file
68+
69+
Args:
70+
body (BodyAnalyzeSimulationOmex):
71+
72+
Raises:
73+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
74+
httpx.TimeoutException: If the request takes longer than Client.timeout.
75+
76+
Returns:
77+
Response[Union[HTTPValidationError, str]]
78+
"""
79+
80+
kwargs = _get_kwargs(
81+
body=body,
82+
)
83+
84+
response = client.get_httpx_client().request(
85+
**kwargs,
86+
)
87+
88+
return _build_response(client=client, response=response)
89+
90+
91+
def sync(
92+
*,
93+
client: Union[AuthenticatedClient, Client],
94+
body: BodyAnalyzeSimulationOmex,
95+
) -> Optional[Union[HTTPValidationError, str]]:
96+
"""Analyze a process bi-graph,
97+
and determine the singularity definition file which would build an environment it can run in.
98+
99+
Resulting container definition file
100+
101+
Args:
102+
body (BodyAnalyzeSimulationOmex):
103+
104+
Raises:
105+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
106+
httpx.TimeoutException: If the request takes longer than Client.timeout.
107+
108+
Returns:
109+
Union[HTTPValidationError, str]
110+
"""
111+
112+
return sync_detailed(
113+
client=client,
114+
body=body,
115+
).parsed
116+
117+
118+
async def asyncio_detailed(
119+
*,
120+
client: Union[AuthenticatedClient, Client],
121+
body: BodyAnalyzeSimulationOmex,
122+
) -> Response[Union[HTTPValidationError, str]]:
123+
"""Analyze a process bi-graph,
124+
and determine the singularity definition file which would build an environment it can run in.
125+
126+
Resulting container definition file
127+
128+
Args:
129+
body (BodyAnalyzeSimulationOmex):
130+
131+
Raises:
132+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
133+
httpx.TimeoutException: If the request takes longer than Client.timeout.
134+
135+
Returns:
136+
Response[Union[HTTPValidationError, str]]
137+
"""
138+
139+
kwargs = _get_kwargs(
140+
body=body,
141+
)
142+
143+
response = await client.get_async_httpx_client().request(**kwargs)
144+
145+
return _build_response(client=client, response=response)
146+
147+
148+
async def asyncio(
149+
*,
150+
client: Union[AuthenticatedClient, Client],
151+
body: BodyAnalyzeSimulationOmex,
152+
) -> Optional[Union[HTTPValidationError, str]]:
153+
"""Analyze a process bi-graph,
154+
and determine the singularity definition file which would build an environment it can run in.
155+
156+
Resulting container definition file
157+
158+
Args:
159+
body (BodyAnalyzeSimulationOmex):
160+
161+
Raises:
162+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
163+
httpx.TimeoutException: If the request takes longer than Client.timeout.
164+
165+
Returns:
166+
Union[HTTPValidationError, str]
167+
"""
168+
169+
return (
170+
await asyncio_detailed(
171+
client=client,
172+
body=body,
173+
)
174+
).parsed

0 commit comments

Comments
 (0)