dsa_helpers 3.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dsa_helpers-3.2.1/.gitignore +18 -0
- dsa_helpers-3.2.1/.readthedocs.yaml +16 -0
- dsa_helpers-3.2.1/LICENSE +19 -0
- dsa_helpers-3.2.1/PKG-INFO +69 -0
- dsa_helpers-3.2.1/README.md +21 -0
- dsa_helpers-3.2.1/docker/2.0.6-pytorch2.9.0-cuda13.0-cudnn9/Dockerfile +14 -0
- dsa_helpers-3.2.1/docker/README.md +2 -0
- dsa_helpers-3.2.1/docs/requirements.txt +16 -0
- dsa_helpers-3.2.1/docs/source/conf.py +42 -0
- dsa_helpers-3.2.1/docs/source/index.rst +18 -0
- dsa_helpers-3.2.1/dsa_helpers/__init__.py +9 -0
- dsa_helpers-3.2.1/dsa_helpers/cli_utils.py +183 -0
- dsa_helpers-3.2.1/dsa_helpers/dash/__init__.py +3 -0
- dsa_helpers-3.2.1/dsa_helpers/dash/dash_paperdragon_utils.py +76 -0
- dsa_helpers-3.2.1/dsa_helpers/dash/header.py +155 -0
- dsa_helpers-3.2.1/dsa_helpers/dash/header_callbacks.py +99 -0
- dsa_helpers-3.2.1/dsa_helpers/girder_utils.py +1262 -0
- dsa_helpers-3.2.1/dsa_helpers/gpd_utils.py +400 -0
- dsa_helpers-3.2.1/dsa_helpers/image_utils.py +179 -0
- dsa_helpers-3.2.1/dsa_helpers/imread.py +32 -0
- dsa_helpers-3.2.1/dsa_helpers/imwrite.py +37 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/__init__.py +2 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/callbacks.py +134 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/datasets/SegFormerSegmentationDataset.py +44 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/datasets/__init__.py +4 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/datasets/utils.py +53 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/evaluate.py +307 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/inference_results.py +67 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/metrics.py +123 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/object_detection/__init__.py +5 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/object_detection/get_pearce_roi_images.py +136 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/object_detection/tile_image.py +137 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/object_detection/utils.py +104 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/__init__.py +3 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/datasets.py +55 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/evaluate.py +126 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/inference.py +608 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/train.py +337 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/transforms.py +128 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/segformer_semantic_segmentation/utils.py +66 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/transforms/__init__.py +1 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/transforms/segformer_transforms.py +40 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/yolo/__init__.py +0 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/yolo/inference.py +661 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/yolo/tiling.py +344 -0
- dsa_helpers-3.2.1/dsa_helpers/ml/yolo/train.py +241 -0
- dsa_helpers-3.2.1/dsa_helpers/mongo_utils.py +266 -0
- dsa_helpers-3.2.1/dsa_helpers/tile_utils.py +60 -0
- dsa_helpers-3.2.1/dsa_helpers/tiling.py +760 -0
- dsa_helpers-3.2.1/dsa_helpers/utils.py +245 -0
- dsa_helpers-3.2.1/dsa_helpers.egg-info/PKG-INFO +69 -0
- dsa_helpers-3.2.1/dsa_helpers.egg-info/SOURCES.txt +55 -0
- dsa_helpers-3.2.1/dsa_helpers.egg-info/dependency_links.txt +1 -0
- dsa_helpers-3.2.1/dsa_helpers.egg-info/requires.txt +38 -0
- dsa_helpers-3.2.1/dsa_helpers.egg-info/top_level.txt +1 -0
- dsa_helpers-3.2.1/pyproject.toml +63 -0
- dsa_helpers-3.2.1/setup.cfg +4 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
|
|
3
|
+
build:
|
|
4
|
+
os: "ubuntu-24.04"
|
|
5
|
+
tools:
|
|
6
|
+
python: "3.12"
|
|
7
|
+
jobs:
|
|
8
|
+
pre_build:
|
|
9
|
+
- sphinx-apidoc -o docs/source dsa_helpers/
|
|
10
|
+
|
|
11
|
+
python:
|
|
12
|
+
install:
|
|
13
|
+
- requirements: docs/requirements.txt
|
|
14
|
+
|
|
15
|
+
sphinx:
|
|
16
|
+
configuration: docs/source/conf.py
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2018 The Python Packaging Authority
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dsa_helpers
|
|
3
|
+
Version: 3.2.1
|
|
4
|
+
Summary: Utility functions for working with the DSA girder client.
|
|
5
|
+
Author-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
6
|
+
Maintainer-email: "Juan C. Vizcarra" <jvizcar@emory.edu>
|
|
7
|
+
Project-URL: Repository, https://github.com/Gutman-Lab/dsa-python-utils
|
|
8
|
+
Keywords: dsa,girder,girder_client
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: matplotlib
|
|
15
|
+
Requires-Dist: girder-client
|
|
16
|
+
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: setuptools
|
|
18
|
+
Requires-Dist: pandas
|
|
19
|
+
Requires-Dist: pymongo
|
|
20
|
+
Requires-Dist: pillow
|
|
21
|
+
Requires-Dist: dash
|
|
22
|
+
Requires-Dist: dash-bootstrap-components
|
|
23
|
+
Requires-Dist: shapely
|
|
24
|
+
Requires-Dist: geopandas
|
|
25
|
+
Requires-Dist: rasterio
|
|
26
|
+
Requires-Dist: colorama
|
|
27
|
+
Requires-Dist: histomicstk
|
|
28
|
+
Requires-Dist: pykdtree
|
|
29
|
+
Provides-Extra: opencv
|
|
30
|
+
Requires-Dist: opencv-python; extra == "opencv"
|
|
31
|
+
Provides-Extra: opencv-headless
|
|
32
|
+
Requires-Dist: opencv-python-headless; extra == "opencv-headless"
|
|
33
|
+
Provides-Extra: ultralytics
|
|
34
|
+
Requires-Dist: ultralytics; extra == "ultralytics"
|
|
35
|
+
Provides-Extra: segformer
|
|
36
|
+
Requires-Dist: transformers[torch]; extra == "segformer"
|
|
37
|
+
Requires-Dist: datasets; extra == "segformer"
|
|
38
|
+
Requires-Dist: torchvision; extra == "segformer"
|
|
39
|
+
Requires-Dist: albumentations; extra == "segformer"
|
|
40
|
+
Provides-Extra: all
|
|
41
|
+
Requires-Dist: opencv-python-headless; extra == "all"
|
|
42
|
+
Requires-Dist: ultralytics; extra == "all"
|
|
43
|
+
Requires-Dist: transformers[torch]; extra == "all"
|
|
44
|
+
Requires-Dist: datasets; extra == "all"
|
|
45
|
+
Requires-Dist: torchvision; extra == "all"
|
|
46
|
+
Requires-Dist: albumentations; extra == "all"
|
|
47
|
+
Dynamic: license-file
|
|
48
|
+
|
|
49
|
+
# Digital Slide Archive (DSA) Helpers
|
|
50
|
+
Digital Slide Archive Python utility library.
|
|
51
|
+
|
|
52
|
+
This library is available for installation through [Python Package Index (PyPI)](https://pypi.org/).
|
|
53
|
+
|
|
54
|
+
This library was tested using Python version 3.11.8 and uses the dependencies described in requirements.txt.
|
|
55
|
+
|
|
56
|
+
This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
|
|
57
|
+
|
|
58
|
+
## Installation instructions
|
|
59
|
+
DSA Helpers depends on large image and large image eager iterator and OpenCV, which you must install separately. Also, you must install the eager iterator from wheel and it should be installed at the end. Python 3.12 or below is supported, Python 3.14 is not currently supported!
|
|
60
|
+
|
|
61
|
+
1. Install large with all tile sources:
|
|
62
|
+
```
|
|
63
|
+
$ pip install large-image[all] --find-links https://girder.github.io/large_image_wheels
|
|
64
|
+
```
|
|
65
|
+
2. Install OpenCV, either the headless version (better for running in Docker) or full version
|
|
66
|
+
3. Install dsa-helpers from pip
|
|
67
|
+
4. Pip install the large image with eager iterator wheel, use the --force flag and you can safely igonore the warnings that you get about some large image library compatibilities
|
|
68
|
+
|
|
69
|
+
A readthedocs for this library can be found [here](https://david-andrew-gutman-dsa-helpers.readthedocs-hosted.com/en/latest/index.html).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Digital Slide Archive (DSA) Helpers
|
|
2
|
+
Digital Slide Archive Python utility library.
|
|
3
|
+
|
|
4
|
+
This library is available for installation through [Python Package Index (PyPI)](https://pypi.org/).
|
|
5
|
+
|
|
6
|
+
This library was tested using Python version 3.11.8 and uses the dependencies described in requirements.txt.
|
|
7
|
+
|
|
8
|
+
This Python PyPI package is found [here](https://pypi.org/project/dsa-helpers/).
|
|
9
|
+
|
|
10
|
+
## Installation instructions
|
|
11
|
+
DSA Helpers depends on large image and large image eager iterator and OpenCV, which you must install separately. Also, you must install the eager iterator from wheel and it should be installed at the end. Python 3.12 or below is supported, Python 3.14 is not currently supported!
|
|
12
|
+
|
|
13
|
+
1. Install large with all tile sources:
|
|
14
|
+
```
|
|
15
|
+
$ pip install large-image[all] --find-links https://girder.github.io/large_image_wheels
|
|
16
|
+
```
|
|
17
|
+
2. Install OpenCV, either the headless version (better for running in Docker) or full version
|
|
18
|
+
3. Install dsa-helpers from pip
|
|
19
|
+
4. Pip install the large image with eager iterator wheel, use the --force flag and you can safely igonore the warnings that you get about some large image library compatibilities
|
|
20
|
+
|
|
21
|
+
A readthedocs for this library can be found [here](https://david-andrew-gutman-dsa-helpers.readthedocs-hosted.com/en/latest/index.html).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM pytorch/pytorch:2.9.0-cuda13.0-cudnn9-runtime
|
|
2
|
+
|
|
3
|
+
ENV PYTHONUNBUFFERED=TRUE
|
|
4
|
+
ARG DEBIAN_FRONTEND=noninteractive
|
|
5
|
+
|
|
6
|
+
# Install large image and its tile sources.
|
|
7
|
+
RUN pip install large-image[all] --find-links https://girder.github.io/large_image_wheels
|
|
8
|
+
|
|
9
|
+
# Install dsa-helpers.
|
|
10
|
+
RUN pip install --no-cache-dir dsa-helpers==2.0.6
|
|
11
|
+
|
|
12
|
+
# Create a cache directory.
|
|
13
|
+
RUN mkdir /.cache
|
|
14
|
+
RUN chmod 777 /.cache
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# For the full list of built-in configuration values, see the documentation:
|
|
4
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
|
5
|
+
|
|
6
|
+
# -- Path setup --------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
|
9
|
+
# add these directories to sys.path here. If the directory is relative to the
|
|
10
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
11
|
+
#
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
sys.path.insert(0, os.path.abspath("../.."))
|
|
16
|
+
|
|
17
|
+
# -- Project information -----------------------------------------------------
|
|
18
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
|
19
|
+
|
|
20
|
+
project = "DSA Helpers"
|
|
21
|
+
copyright = "2024, Juan Carlos Vizcarra"
|
|
22
|
+
author = "Juan Carlos Vizcarra"
|
|
23
|
+
release = "00.00.01"
|
|
24
|
+
|
|
25
|
+
# -- General configuration ---------------------------------------------------
|
|
26
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
|
27
|
+
|
|
28
|
+
extensions = [
|
|
29
|
+
"sphinx.ext.autodoc",
|
|
30
|
+
"sphinx.ext.viewcode",
|
|
31
|
+
"sphinx.ext.napoleon",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
templates_path = ["_templates"]
|
|
35
|
+
exclude_patterns = []
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# -- Options for HTML output -------------------------------------------------
|
|
39
|
+
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
|
40
|
+
|
|
41
|
+
html_theme = "sphinx_rtd_theme"
|
|
42
|
+
html_static_path = ["_static"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.. DSA Helpers documentation master file, created by
|
|
2
|
+
sphinx-quickstart on Fri Dec 6 12:07:31 2024.
|
|
3
|
+
You can adapt this file completely to your liking, but it should at least
|
|
4
|
+
contain the root `toctree` directive.
|
|
5
|
+
|
|
6
|
+
DSA Helpers documentation
|
|
7
|
+
=========================
|
|
8
|
+
|
|
9
|
+
Add your content using ``reStructuredText`` syntax. See the
|
|
10
|
+
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
|
|
11
|
+
documentation for details.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
.. toctree::
|
|
15
|
+
:maxdepth: 2
|
|
16
|
+
:caption: Contents:
|
|
17
|
+
|
|
18
|
+
modules
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from collections import deque
|
|
2
|
+
import psutil, threading, time
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SystemMonitor:
|
|
7
|
+
def __init__(self, interval=0.5):
|
|
8
|
+
self.interval = interval
|
|
9
|
+
self.cpu_percentages = deque(maxlen=100)
|
|
10
|
+
self.memory_usage = deque(maxlen=100)
|
|
11
|
+
self.io_wait = deque(maxlen=100)
|
|
12
|
+
self.cpu_per_core = deque(maxlen=100) # Track per-core usage
|
|
13
|
+
self.memory_bandwidth = deque(maxlen=100)
|
|
14
|
+
self.io_stats = deque(maxlen=100)
|
|
15
|
+
self.stop_flag = False
|
|
16
|
+
self.process = psutil.Process()
|
|
17
|
+
|
|
18
|
+
def start(self):
|
|
19
|
+
self.stop_flag = False
|
|
20
|
+
self.monitor_thread = threading.Thread(target=self._monitor)
|
|
21
|
+
self.monitor_thread.start()
|
|
22
|
+
|
|
23
|
+
def stop(self):
|
|
24
|
+
self.stop_flag = True
|
|
25
|
+
self.monitor_thread.join()
|
|
26
|
+
|
|
27
|
+
# Calculate statistics with safety checks
|
|
28
|
+
cpu_stats = {
|
|
29
|
+
"mean": (
|
|
30
|
+
np.mean(self.cpu_percentages)
|
|
31
|
+
if len(self.cpu_percentages) > 0
|
|
32
|
+
else 0
|
|
33
|
+
),
|
|
34
|
+
"max": (
|
|
35
|
+
np.max(self.cpu_percentages)
|
|
36
|
+
if len(self.cpu_percentages) > 0
|
|
37
|
+
else 0
|
|
38
|
+
),
|
|
39
|
+
"min": (
|
|
40
|
+
np.min(self.cpu_percentages)
|
|
41
|
+
if len(self.cpu_percentages) > 0
|
|
42
|
+
else 0
|
|
43
|
+
),
|
|
44
|
+
"per_core_mean": (
|
|
45
|
+
np.mean(self.cpu_per_core, axis=0)
|
|
46
|
+
if len(self.cpu_per_core) > 0
|
|
47
|
+
else np.zeros(psutil.cpu_count())
|
|
48
|
+
),
|
|
49
|
+
"per_core_max": (
|
|
50
|
+
np.max(self.cpu_per_core, axis=0)
|
|
51
|
+
if len(self.cpu_per_core) > 0
|
|
52
|
+
else np.zeros(psutil.cpu_count())
|
|
53
|
+
),
|
|
54
|
+
"active_cores_mean": (
|
|
55
|
+
np.mean(
|
|
56
|
+
[
|
|
57
|
+
sum(1 for x in cores if x > 10)
|
|
58
|
+
for cores in self.cpu_per_core
|
|
59
|
+
]
|
|
60
|
+
)
|
|
61
|
+
if len(self.cpu_per_core) > 0
|
|
62
|
+
else 0
|
|
63
|
+
),
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
mem_stats = {
|
|
67
|
+
"mean": (
|
|
68
|
+
np.mean(self.memory_usage) if len(self.memory_usage) > 0 else 0
|
|
69
|
+
),
|
|
70
|
+
"max": (
|
|
71
|
+
np.max(self.memory_usage) if len(self.memory_usage) > 0 else 0
|
|
72
|
+
),
|
|
73
|
+
"min": (
|
|
74
|
+
np.min(self.memory_usage) if len(self.memory_usage) > 0 else 0
|
|
75
|
+
),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
io_stats = {
|
|
79
|
+
"wait_mean": np.mean(self.io_wait) if len(self.io_wait) > 0 else 0,
|
|
80
|
+
"wait_max": np.max(self.io_wait) if len(self.io_wait) > 0 else 0,
|
|
81
|
+
"throughput_read": (
|
|
82
|
+
np.mean(
|
|
83
|
+
[
|
|
84
|
+
sum(disk["read_bytes"] for disk in x["disk"].values())
|
|
85
|
+
/ x["time_delta"]
|
|
86
|
+
for x in self.io_stats
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
/ 1024
|
|
90
|
+
/ 1024
|
|
91
|
+
if len(self.io_stats) > 0
|
|
92
|
+
else 0
|
|
93
|
+
),
|
|
94
|
+
"throughput_write": (
|
|
95
|
+
np.mean(
|
|
96
|
+
[
|
|
97
|
+
sum(disk["write_bytes"] for disk in x["disk"].values())
|
|
98
|
+
/ x["time_delta"]
|
|
99
|
+
for x in self.io_stats
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
/ 1024
|
|
103
|
+
/ 1024
|
|
104
|
+
if len(self.io_stats) > 0
|
|
105
|
+
else 0
|
|
106
|
+
),
|
|
107
|
+
"network_read": (
|
|
108
|
+
np.mean(
|
|
109
|
+
[
|
|
110
|
+
x["network"]["bytes_recv"] / x["time_delta"]
|
|
111
|
+
for x in self.io_stats
|
|
112
|
+
]
|
|
113
|
+
)
|
|
114
|
+
/ 1024
|
|
115
|
+
/ 1024
|
|
116
|
+
if len(self.io_stats) > 0
|
|
117
|
+
else 0
|
|
118
|
+
),
|
|
119
|
+
"network_write": (
|
|
120
|
+
np.mean(
|
|
121
|
+
[
|
|
122
|
+
x["network"]["bytes_sent"] / x["time_delta"]
|
|
123
|
+
for x in self.io_stats
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
/ 1024
|
|
127
|
+
/ 1024
|
|
128
|
+
if len(self.io_stats) > 0
|
|
129
|
+
else 0
|
|
130
|
+
),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {"cpu": cpu_stats, "memory": mem_stats, "io": io_stats}
|
|
134
|
+
|
|
135
|
+
def _monitor(self):
|
|
136
|
+
last_io = psutil.disk_io_counters(perdisk=True) # Monitor per-disk
|
|
137
|
+
last_net = psutil.net_io_counters() # Add network monitoring
|
|
138
|
+
last_time = time.time()
|
|
139
|
+
|
|
140
|
+
while not self.stop_flag:
|
|
141
|
+
# CPU usage (overall and per-core)
|
|
142
|
+
per_core = psutil.cpu_percent(percpu=True)
|
|
143
|
+
self.cpu_percentages.append(np.mean(per_core))
|
|
144
|
+
self.cpu_per_core.append(per_core)
|
|
145
|
+
|
|
146
|
+
# Memory usage
|
|
147
|
+
mem = self.process.memory_info()
|
|
148
|
+
self.memory_usage.append(mem.rss / 1024 / 1024) # Convert to MB
|
|
149
|
+
|
|
150
|
+
# IO wait and throughput
|
|
151
|
+
cpu_times = psutil.cpu_times_percent()
|
|
152
|
+
self.io_wait.append(cpu_times.iowait)
|
|
153
|
+
|
|
154
|
+
# Enhanced IO monitoring
|
|
155
|
+
current_io = psutil.disk_io_counters(perdisk=True)
|
|
156
|
+
current_net = psutil.net_io_counters()
|
|
157
|
+
current_time = time.time()
|
|
158
|
+
|
|
159
|
+
# Calculate both disk and network throughput
|
|
160
|
+
delta_time = current_time - last_time
|
|
161
|
+
io_delta = {
|
|
162
|
+
"disk": {
|
|
163
|
+
disk: {
|
|
164
|
+
"read_bytes": current_io[disk].read_bytes
|
|
165
|
+
- last_io[disk].read_bytes,
|
|
166
|
+
"write_bytes": current_io[disk].write_bytes
|
|
167
|
+
- last_io[disk].write_bytes,
|
|
168
|
+
}
|
|
169
|
+
for disk in current_io
|
|
170
|
+
},
|
|
171
|
+
"network": {
|
|
172
|
+
"bytes_recv": current_net.bytes_recv - last_net.bytes_recv,
|
|
173
|
+
"bytes_sent": current_net.bytes_sent - last_net.bytes_sent,
|
|
174
|
+
},
|
|
175
|
+
"time_delta": delta_time,
|
|
176
|
+
}
|
|
177
|
+
self.io_stats.append(io_delta)
|
|
178
|
+
|
|
179
|
+
last_io = current_io
|
|
180
|
+
last_net = current_net
|
|
181
|
+
last_time = current_time
|
|
182
|
+
|
|
183
|
+
time.sleep(self.interval)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
def format_ann_docs_for_paperjs(
|
|
2
|
+
geojson_ann_docs: list[dict], return_as_input_to_paper: bool = False
|
|
3
|
+
) -> list[dict]:
|
|
4
|
+
"""Format DSA annotations in the geojson format into a format that paperdragon
|
|
5
|
+
can read and show as polygons.
|
|
6
|
+
|
|
7
|
+
Args:
|
|
8
|
+
geojson_ann_docs (list[dict]): A list of geojson annotation documents.
|
|
9
|
+
return_as_input_to_paper (bool): Whether to return the formatted documents as
|
|
10
|
+
input to paper. Defaults to False.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
list[dict]: A list of formatted annotation documents for paperjs. Note that
|
|
14
|
+
it will return an empty list if the documents could not be formatted.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
# NOTE: hard-coded to only do the first doc.
|
|
18
|
+
formatted_docs = []
|
|
19
|
+
|
|
20
|
+
# Loop through each annotation document.
|
|
21
|
+
for ann_doc in geojson_ann_docs:
|
|
22
|
+
# Grab the features of this document, contains the polygon info.
|
|
23
|
+
features_to_include = []
|
|
24
|
+
|
|
25
|
+
for feature in ann_doc["features"]:
|
|
26
|
+
geometry = feature["geometry"]
|
|
27
|
+
if geometry["type"] == "Polygon":
|
|
28
|
+
properties = feature["properties"]
|
|
29
|
+
properties["rescale"] = {"strokeWidth": properties["lineWidth"]}
|
|
30
|
+
properties["strokeColor"] = properties["lineColor"]
|
|
31
|
+
properties["source"] = "dsa"
|
|
32
|
+
|
|
33
|
+
# Paperjs only supports multipolygons.
|
|
34
|
+
geometry["type"] = "MultiPolygon"
|
|
35
|
+
|
|
36
|
+
coordinates = geometry["coordinates"]
|
|
37
|
+
|
|
38
|
+
adjusted_coordinates = [
|
|
39
|
+
[[[int(coord) for coord in point[:2]] for point in coordinates[0]]]
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
geometry["coordinates"] = adjusted_coordinates
|
|
43
|
+
|
|
44
|
+
features_to_include.append(feature)
|
|
45
|
+
|
|
46
|
+
if len(features_to_include):
|
|
47
|
+
formatted_docs.append(features_to_include)
|
|
48
|
+
|
|
49
|
+
if return_as_input_to_paper:
|
|
50
|
+
return get_input_to_paper_dict(formatted_docs)
|
|
51
|
+
|
|
52
|
+
return formatted_docs
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_input_to_paper_dict(formatted_docs: list[dict] | None = None) -> dict:
|
|
56
|
+
"""Get the input to paper dictionary.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
formatted_docs (list[dict]): A list of formatted annotation documents for paperjs.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
dict: The input to paper dictionary.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
input_to_paper = [{"type": "clearItems"}]
|
|
66
|
+
|
|
67
|
+
if formatted_docs is not None and len(formatted_docs):
|
|
68
|
+
for doc in formatted_docs:
|
|
69
|
+
input_to_paper.append(
|
|
70
|
+
{
|
|
71
|
+
"type": "drawItems",
|
|
72
|
+
"itemList": doc,
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return {"actions": input_to_paper}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
from dash import html, dcc
|
|
2
|
+
import dash_bootstrap_components as dbc
|
|
3
|
+
from .header_callbacks import get_callbacks
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def get_header(
|
|
7
|
+
dsa_api_url: str,
|
|
8
|
+
title: str = "App",
|
|
9
|
+
store_id: str = "user-store",
|
|
10
|
+
config: dict | None = None,
|
|
11
|
+
) -> html.Div:
|
|
12
|
+
"""Get the Dash html component that contains the header of the application
|
|
13
|
+
with login capabilities.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
dsa_api_url (str): The URL of the DSA API.
|
|
17
|
+
title (str, optional): The title of the application. Defaults to "App".
|
|
18
|
+
config (dict, optional): The configuration of the component.
|
|
19
|
+
Defaults to None which uses default settings.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
html.Div: The header component.
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
if config is None:
|
|
26
|
+
config = {}
|
|
27
|
+
|
|
28
|
+
get_callbacks(dsa_api_url, store_id)
|
|
29
|
+
|
|
30
|
+
login_modal = dbc.Modal(
|
|
31
|
+
[
|
|
32
|
+
dbc.ModalHeader("Log in"),
|
|
33
|
+
dbc.ModalBody(
|
|
34
|
+
[
|
|
35
|
+
html.Div(
|
|
36
|
+
"Login or email",
|
|
37
|
+
style={"margin": 5, "fontWeight": "bold"},
|
|
38
|
+
),
|
|
39
|
+
dbc.Input(
|
|
40
|
+
id=f"{store_id}=login",
|
|
41
|
+
type="text",
|
|
42
|
+
placeholder="Enter login",
|
|
43
|
+
style={"margin": 5},
|
|
44
|
+
),
|
|
45
|
+
html.Div(
|
|
46
|
+
"Password",
|
|
47
|
+
style={
|
|
48
|
+
"margin": 5,
|
|
49
|
+
"marginTop": 15,
|
|
50
|
+
"fontWeight": "bold",
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
dbc.Input(
|
|
54
|
+
id=f"{store_id}=password",
|
|
55
|
+
type="password",
|
|
56
|
+
placeholder="Enter password",
|
|
57
|
+
style={"margin": 5},
|
|
58
|
+
),
|
|
59
|
+
html.Div(
|
|
60
|
+
"Login failed.",
|
|
61
|
+
hidden=True,
|
|
62
|
+
id=f"{store_id}=login-failed",
|
|
63
|
+
style={
|
|
64
|
+
"color": "red",
|
|
65
|
+
"fontWeight": "bold",
|
|
66
|
+
"margin": 10,
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
],
|
|
70
|
+
),
|
|
71
|
+
dbc.ModalFooter(
|
|
72
|
+
dbc.Row(
|
|
73
|
+
[
|
|
74
|
+
dbc.Col(
|
|
75
|
+
dbc.Button(
|
|
76
|
+
"Close",
|
|
77
|
+
id=f"{store_id}=close-login-modal",
|
|
78
|
+
className="me-1",
|
|
79
|
+
color="light",
|
|
80
|
+
)
|
|
81
|
+
),
|
|
82
|
+
dbc.Col(
|
|
83
|
+
dbc.Button(
|
|
84
|
+
"Login",
|
|
85
|
+
id=f"{store_id}=log-in-btn",
|
|
86
|
+
className="me-1",
|
|
87
|
+
color="primary",
|
|
88
|
+
)
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
),
|
|
93
|
+
],
|
|
94
|
+
is_open=False,
|
|
95
|
+
id=f"{store_id}=login-modal",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
logout_modal = dbc.Modal(
|
|
99
|
+
[
|
|
100
|
+
dbc.ModalFooter(
|
|
101
|
+
[
|
|
102
|
+
dbc.Button(
|
|
103
|
+
"Log out",
|
|
104
|
+
id=f"{store_id}=logout-btn",
|
|
105
|
+
color="danger",
|
|
106
|
+
className="me-1",
|
|
107
|
+
),
|
|
108
|
+
]
|
|
109
|
+
)
|
|
110
|
+
],
|
|
111
|
+
is_open=False,
|
|
112
|
+
id=f"{store_id}=logout-modal",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return html.Div(
|
|
116
|
+
[
|
|
117
|
+
dbc.Row(
|
|
118
|
+
[
|
|
119
|
+
dbc.Col(
|
|
120
|
+
html.H1(
|
|
121
|
+
title,
|
|
122
|
+
style={
|
|
123
|
+
"fontWeight": "bold",
|
|
124
|
+
"color": config.get("titleColor", "#d9d9d6"),
|
|
125
|
+
"marginLeft": 5,
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
width="auto",
|
|
129
|
+
),
|
|
130
|
+
dbc.Col(
|
|
131
|
+
dbc.Button(
|
|
132
|
+
"Log in",
|
|
133
|
+
id=f"{store_id}=login-btn",
|
|
134
|
+
color=config.get("backgroundColor", "#012169"),
|
|
135
|
+
style={
|
|
136
|
+
"color": config.get("fontColor", "#f2a900"),
|
|
137
|
+
"fontSize": "1.2rem",
|
|
138
|
+
},
|
|
139
|
+
className="me-1",
|
|
140
|
+
),
|
|
141
|
+
width="auto",
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
justify="between",
|
|
145
|
+
align="center",
|
|
146
|
+
),
|
|
147
|
+
login_modal,
|
|
148
|
+
logout_modal,
|
|
149
|
+
dcc.Store(id=store_id, storage_type="local", data={}),
|
|
150
|
+
],
|
|
151
|
+
style={
|
|
152
|
+
"backgroundColor": config.get("backgroundColor", "#012169"),
|
|
153
|
+
"padding": 5,
|
|
154
|
+
},
|
|
155
|
+
)
|