Skip to content

Commit

Permalink
Merge branch 'main' into public/main
Browse files Browse the repository at this point in the history
  • Loading branch information
JoOkuma committed Jul 3, 2024
2 parents a413f39 + 182819f commit 4dd8151
Show file tree
Hide file tree
Showing 33 changed files with 1,456 additions and 208 deletions.
2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
include README.md
include LICENSE
include ultrack/widgets/ultrackwidget/resources/*.json
recursive-include ultrack/widgets/ultrackwidget/resources *.json
319 changes: 217 additions & 102 deletions examples/api/api_example.ipynb

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/api/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
dependencies:
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- pip
Expand Down
2 changes: 1 addition & 1 deletion examples/flow_field_3d/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ channels:
dependencies:
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- napari
Expand Down
2 changes: 1 addition & 1 deletion examples/micro_sam/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ channels:
dependencies:
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- pip
Expand Down
2 changes: 1 addition & 1 deletion examples/multi_color_ensemble/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies:
- cellpose
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- pip
Expand Down
2 changes: 1 addition & 1 deletion examples/neuromast_plantseg/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ channels:
dependencies:
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- pyqt
Expand Down
2 changes: 1 addition & 1 deletion examples/stardist_2d/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dependencies:
- conda-forge::cudatoolkit=11.2
- conda-forge::cudnn=8.4.1
- conda-forge::tensorflow-gpu=2.11.0
- conda-forge::cupy!=13.0.*
- conda-forge::cupy!=13.*
- coin-or-cbc
- gurobi
- jupyter
Expand Down
2 changes: 1 addition & 1 deletion examples/zebrahub/environment_gpu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ channels:
dependencies:
- coin-or-cbc
- cucim
- cupy!=13.0.*
- cupy!=13.*
- gurobi
- jupyter
- pip
Expand Down
5 changes: 4 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ install_requires =
httpx >= 0.26.0
websockets >= 12.0
qtawesome >= 1.3.1
pydot >= 2.0.0

[options.extras_require]
testing =
Expand Down Expand Up @@ -71,4 +72,6 @@ napari.manifest =
ultrack = ultrack:napari.yaml

[options.package_data]
ultrack = napari.yaml
ultrack =
napari.yaml
widgets/ultrackwidget/resources/*.json
2 changes: 2 additions & 0 deletions ultrack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@

from ultrack.config.config import MainConfig, load_config
from ultrack.core.export.ctc import to_ctc
from ultrack.core.export.exporter import export_tracks_by_extension
from ultrack.core.export.trackmate import to_trackmate
from ultrack.core.export.tracks_layer import to_tracks_layer
from ultrack.core.export.zarr import tracks_to_zarr
from ultrack.core.interactive import add_new_node
from ultrack.core.linking.processing import link
from ultrack.core.main import track
from ultrack.core.segmentation.processing import segment
Expand Down
3 changes: 1 addition & 2 deletions ultrack/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
from ultrack.api.database import Experiment, ExperimentStatus
from ultrack.api.main import start_server
from ultrack.api.database import Experiment
from ultrack.api.database import ExperimentStatus
4 changes: 1 addition & 3 deletions ultrack/api/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import sqlalchemy as sqla
from pydantic import BaseModel, Json, validator
from sqlalchemy import JSON, Column, DateTime, Enum, Integer, String, Text
from sqlalchemy import JSON, Column, Enum, Integer, String, Text
from sqlalchemy.orm import declarative_base, sessionmaker

from ultrack import MainConfig
Expand Down Expand Up @@ -262,8 +262,6 @@ def update_experiment(experiment: Experiment) -> None:
session.close()




def get_experiment(id: int) -> Experiment:
"""Get an experiment from the database.
Expand Down
11 changes: 7 additions & 4 deletions ultrack/api/main.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import os
from multiprocessing import Process
from pathlib import Path
from threading import Thread
from typing import Union

import uvicorn

from ultrack import MainConfig
from ultrack.api import app
from ultrack.config import DataConfig


def _in_notebook():
try:
from IPython import get_ipython
if 'IPKernelApp' not in get_ipython().config: # pragma: no cover

if "IPKernelApp" not in get_ipython().config: # pragma: no cover
return False
except ImportError:
return False
except AttributeError:
return False
return True


def start_server(
api_results_path: Union[Path, str, None] = None,
ultrack_data_config: Union[DataConfig, None] = None,
ultrack_data_config: Union[MainConfig, None] = None,
host: str = "0.0.0.0",
port: int = 8000,
):
Expand All @@ -41,8 +42,10 @@ def start_server(
os.environ["ULTRACK_DATA_CONFIG"] = ultrack_data_config.json()

if _in_notebook():

def start_in_notebook():
uvicorn.run(app.app, host=host, port=port)

Process(target=start_in_notebook).start()
else:
uvicorn.run(app.app, host=host, port=port)
Expand Down
47 changes: 22 additions & 25 deletions ultrack/cli/_test/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
from ultrack.utils.data import make_config_content


def _run_server(instance_config_path: str):
_run_command(["server", "--port", "54123", "-cfg", instance_config_path])

def _run_command(command_and_args: List[str]) -> None:
try:
main(command_and_args)
Expand Down Expand Up @@ -161,6 +164,25 @@ def test_clear_database(self, instance_config_path: str, mode: str) -> None:
]
)

def test_server(self, instance_config_path: str) -> None:
# Start server in a background thread
process = Process(target=_run_server, args=(instance_config_path,))
process.start()

# Wait for server to start
import time

time.sleep(10)

response = requests.get("http://127.0.0.1:54123")
print(response.content)

assert process.is_alive()
assert response.status_code == 200

process.terminate()
process.join()


def test_create_config(tmp_path: Path) -> None:
_run_command(["create_config", str(tmp_path / "config.toml")])
Expand All @@ -178,28 +200,3 @@ def test_labels_to_contours(zarr_dataset_paths: List[str], tmp_path: Path) -> No

def test_check_gurobi() -> None:
_run_command(["check_gurobi"])


def _run_server():
_run_command(["server", "--port", "54123"])


def test_server() -> None:

# Start server in a background thread
process = Process(target=_run_server)
process.start()

# Wait for server to start
import time

time.sleep(10)

response = requests.get("http://127.0.0.1:54123")
print(response.content)

assert process.is_alive()
assert response.status_code == 200

process.terminate()
process.join()
19 changes: 11 additions & 8 deletions ultrack/cli/server.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import os
from typing import Optional

import click
import uvicorn
import toml

from ultrack.api.app import app
from ultrack import MainConfig
from ultrack.api import start_server
from ultrack.cli.utils import config_option
from ultrack.config import DataConfig


@click.command("server")
Expand All @@ -22,9 +25,9 @@
default=None,
show_default=True,
)
def server_cli(host: str, port: int, api_results_path: str) -> None:
@config_option()
def server_cli(
host: str, port: int, api_results_path: Optional[str], config: MainConfig
) -> None:
"""Start the websockets ultrack API."""
if api_results_path is not None:
os.environ["API_RESULTS_PATH"] = str(api_results_path)

uvicorn.run(app, host=host, port=port)
start_server(api_results_path, ultrack_data_config=config, host=host, port=port)
57 changes: 57 additions & 0 deletions ultrack/core/_test/test_interactive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from typing import Tuple

import numpy as np
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import Session

from ultrack import MainConfig, add_new_node
from ultrack.core.database import LinkDB, NodeDB, OverlapDB


def _get_table_sizes(session: Session) -> Tuple[int, int, int]:
return (
session.query(NodeDB).count(),
session.query(LinkDB).count(),
session.query(OverlapDB).count(),
)


@pytest.mark.parametrize(
"config_content",
[
{
"data.database": "sqlite",
"segmentation.n_workers": 4,
"linking.n_workers": 4,
"linking.max_distance": 500, # too big and ignored
},
],
indirect=True,
)
def test_clear_solution(
linked_database_mock_data: MainConfig,
) -> None:

mask = np.ones((7, 12, 12), dtype=bool)
bbox = np.array([15, 24, 24, 22, 36, 36], dtype=int)

engine = create_engine(linked_database_mock_data.data_config.database_path)
with Session(engine) as session:
n_nodes, n_links, n_overlaps = _get_table_sizes(session)

add_new_node(
linked_database_mock_data,
0,
mask,
bbox,
)

new_n_nodes, new_n_links, new_n_overlaps = _get_table_sizes(session)

assert new_n_nodes == n_nodes + 1
assert new_n_overlaps > n_overlaps
# could smaller than max neighbors because of radius
assert (
new_n_links == n_links + linked_database_mock_data.linking_config.max_neighbors
)
42 changes: 42 additions & 0 deletions ultrack/core/export/_test/test_exporter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from pathlib import Path

from ultrack import MainConfig, export_tracks_by_extension


def test_exporter(tracked_database_mock_data: MainConfig, tmp_path: Path) -> None:
file_ext_list = [".xml", ".csv", ".zarr", ".dot", ".json"]
last_modified_time = {}
for file_ext in file_ext_list:
tmp_file = tmp_path / f"tracks{file_ext}"
export_tracks_by_extension(tracked_database_mock_data, tmp_file)

# assert file exists
assert (tmp_path / f"tracks{file_ext}").exists()
# assert file size is not zero
assert (tmp_path / f"tracks{file_ext}").stat().st_size > 0

# store last modified time
last_modified_time[str(tmp_file)] = tmp_file.stat().st_mtime

# loop again testing overwrite=False
for file_ext in file_ext_list:
tmp_file = tmp_path / f"tracks{file_ext}"
try:
export_tracks_by_extension(
tracked_database_mock_data, tmp_file, overwrite=False
)
assert False, "FileExistsError should be raised"
except FileExistsError:
pass

# loop again testing overwrite=True
for file_ext in file_ext_list:
tmp_file = tmp_path / f"tracks{file_ext}"
export_tracks_by_extension(tracked_database_mock_data, tmp_file, overwrite=True)

# assert file exists
assert (tmp_path / f"tracks{file_ext}").exists()
# assert file size is not zero
assert (tmp_path / f"tracks{file_ext}").stat().st_size > 0

assert last_modified_time[str(tmp_file)] != tmp_file.stat().st_mtime
Loading

0 comments on commit 4dd8151

Please sign in to comment.