= FunctionComponent
& WidgetAttributes;
19 |
--------------------------------------------------------------------------------
/static/img/dataframe_head_sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/static/img/dataframe_head_sample.png
--------------------------------------------------------------------------------
/static/img/spotlight.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/static/img/spotlight_features.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/static/img/spotlight_features.gif
--------------------------------------------------------------------------------
/static/img/spotlight_video.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/static/img/spotlight_video.gif
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/__init__.py
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | import pytest
4 |
5 |
6 | @pytest.fixture
7 | def project_root() -> Path:
8 | return Path(__file__).parent.parent
9 |
--------------------------------------------------------------------------------
/tests/integration/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/__init__.py
--------------------------------------------------------------------------------
/tests/integration/backend/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/backend/__init__.py
--------------------------------------------------------------------------------
/tests/integration/backend/test_app_csv_df.py:
--------------------------------------------------------------------------------
1 | """
2 | Tests for the renumics spotlight app and in mem data frame serving
3 | """
4 |
5 | import json
6 | from typing import Any, List
7 |
8 | import requests
9 |
10 | from renumics import spotlight
11 |
12 |
13 | def _column_by_name(columns: List, col_name: str) -> Any:
14 | return [col for col in columns if col["name"] == col_name][0]
15 |
16 |
17 | def test_read_table(viewer_csv_df: spotlight.Viewer) -> None:
18 | """test full table can be read an returns data"""
19 |
20 | app_url = f"http://{viewer_csv_df.host}:{viewer_csv_df.port}"
21 |
22 | response = requests.get(app_url + "/api/table/", timeout=5)
23 | assert response.status_code == 200
24 | assert len(response.text) > 1000
25 | json_data = json.loads((response.text))
26 | assert _column_by_name(json_data["columns"], "bool")["dtype"]["name"] == "bool"
27 | assert _column_by_name(json_data["columns"], "float")["dtype"]["name"] == "float"
28 | assert _column_by_name(json_data["columns"], "audio")["dtype"]["name"] == "Audio"
29 | assert (
30 | _column_by_name(json_data["columns"], "embedding")["dtype"]["name"]
31 | == "Embedding"
32 | )
33 | assert _column_by_name(json_data["columns"], "video")["dtype"]["name"] == "Video"
34 |
--------------------------------------------------------------------------------
/tests/integration/dataset/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/dataset/__init__.py
--------------------------------------------------------------------------------
/tests/integration/formats/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/formats/__init__.py
--------------------------------------------------------------------------------
/tests/integration/formats/test_formats.py:
--------------------------------------------------------------------------------
1 | """
2 | Test various supported data formats
3 | """
4 |
5 | import sys
6 | from pathlib import Path
7 |
8 | import pytest
9 |
10 | from renumics import spotlight
11 |
12 |
13 | @pytest.mark.parametrize(
14 | "extension",
15 | [
16 | "csv",
17 | "feather",
18 | "parquet",
19 | pytest.param(
20 | "orc",
21 | marks=pytest.mark.skipif(
22 | sys.platform.startswith("win"),
23 | reason="Flaky `pyarrow.orc` on Windows.",
24 | ),
25 | ),
26 | ],
27 | )
28 | def test_successful_load(extension: str, project_root: Path) -> None:
29 | """
30 | Check if the file loads without error when calling spotlight.show
31 | """
32 | source = project_root / "data" / extension / f"multimodal-random-small.{extension}"
33 | viewer = spotlight.show(source, wait=False, no_browser=True)
34 | viewer.close()
35 |
--------------------------------------------------------------------------------
/tests/integration/h5/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/h5/__init__.py
--------------------------------------------------------------------------------
/tests/integration/h5/conftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Pytest Fixtures for h5 tests
3 | """
4 |
5 | import os.path
6 | import tempfile
7 | from typing import Iterator
8 |
9 | import pytest
10 |
11 | from renumics import spotlight
12 |
13 | from .data import COLUMNS
14 |
15 |
16 | @pytest.fixture
17 | def dataset_path() -> Iterator[str]:
18 | """
19 | H5 Dataset for tests
20 | """
21 |
22 | with tempfile.TemporaryDirectory() as temp_dir:
23 | temp_dataset = os.path.join(temp_dir, "dataset.h5")
24 | with spotlight.Dataset(temp_dataset, "w") as dataset:
25 | for col, (dtype, values) in COLUMNS.items():
26 | dataset.append_column(
27 | col,
28 | dtype=dtype,
29 | values=values,
30 | optional=dtype not in (int, bool),
31 | )
32 | yield temp_dataset
33 |
--------------------------------------------------------------------------------
/tests/integration/h5/data.py:
--------------------------------------------------------------------------------
1 | """
2 | Data for h5 tests
3 | """
4 |
5 | import datetime
6 | from typing import Dict, Tuple
7 |
8 | import numpy as np
9 |
10 | from renumics import spotlight
11 |
12 | COLUMNS: Dict[str, Tuple[str, list]] = {
13 | "bool": ("bool", [True, False]),
14 | "int": ("int", [0, 1]),
15 | "float": ("float", [0.0, np.nan]),
16 | "str": ("str", ["foobar", ""]),
17 | "datetime": ("datetime", [datetime.datetime.min, np.datetime64("NaT")]),
18 | "categorical": ("Category", ["foo", "bar"]),
19 | "array": ("array", [[[0]], [1, 2, 3]]),
20 | "window": ("Window", [[0, 1], [-np.inf, np.nan]]),
21 | "embedding": ("Embedding", [[1, 2, 3], [4, np.nan, 5]]),
22 | "sequence": ("Sequence1D", [[[1, 2, 3], [2, 3, 4]], [[2, 3], [5, 5]]]),
23 | "optional_sequence": ("Sequence1D", [[[1, 2, 3], [2, 3, 4]], None]),
24 | "image": ("Image", [spotlight.Image.empty(), None]),
25 | "audio": ("Audio", [spotlight.Audio.empty(), None]),
26 | "video": ("Video", [spotlight.Video.empty(), None]),
27 | "mesh": ("Mesh", [spotlight.Mesh.empty(), None]),
28 | }
29 |
--------------------------------------------------------------------------------
/tests/integration/huggingface/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/huggingface/__init__.py
--------------------------------------------------------------------------------
/tests/integration/huggingface/conftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Pytest Fixtures for Hugging Face tests
3 | """
4 |
5 | import datasets
6 | import pytest
7 |
8 | from .dataset import create_hf_dataset
9 |
10 |
11 | @pytest.fixture
12 | def dataset() -> datasets.Dataset:
13 | """
14 | H5 Dataset for tests
15 | """
16 |
17 | return create_hf_dataset()
18 |
--------------------------------------------------------------------------------
/tests/integration/huggingface/test_hf.py:
--------------------------------------------------------------------------------
1 | """
2 | Integration Test on API level for h5 data sources
3 | """
4 |
5 | import datasets
6 | import httpx
7 | import pytest
8 |
9 | from renumics import spotlight
10 |
11 | from .dataset import DATA
12 |
13 |
14 | def test_get_table_returns_http_ok(dataset: datasets.Dataset) -> None:
15 | """
16 | Ensure /api/table/ returns a valid response
17 | """
18 | viewer = spotlight.show(dataset, no_browser=True, wait=False)
19 | response = httpx.Client(base_url=viewer.url).get("/api/table/")
20 | viewer.close()
21 | assert response.status_code == 200
22 |
23 |
24 | @pytest.mark.parametrize("col", DATA.keys())
25 | def test_get_cell_returns_http_ok(dataset: str, col: str) -> None:
26 | """
27 | Serve h5 dataset and get cell data for dtype
28 | """
29 | viewer = spotlight.show(dataset, no_browser=True, wait=False)
30 | gen_id = (
31 | httpx.Client(base_url=viewer.url).get("/api/table/").json()["generation_id"]
32 | )
33 | response = httpx.Client(base_url=viewer.url).get(
34 | f"/api/table/{col}/0?generation_id={gen_id}"
35 | )
36 | viewer.close()
37 | assert response.status_code == 200
38 |
--------------------------------------------------------------------------------
/tests/integration/layout/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/layout/__init__.py
--------------------------------------------------------------------------------
/tests/integration/media/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/integration/media/__init__.py
--------------------------------------------------------------------------------
/tests/integration/media/data.py:
--------------------------------------------------------------------------------
1 | BASE_URL = "https://spotlightpublic.blob.core.windows.net/internal-test-data/"
2 |
--------------------------------------------------------------------------------
/tests/integration/media/test_audio.py:
--------------------------------------------------------------------------------
1 | """
2 | Test `renumics.spotlight.media.Audio` class.
3 | """
4 |
5 | from urllib.parse import urljoin
6 |
7 | import pytest
8 |
9 | from renumics.spotlight.media import Audio
10 |
11 | from .data import BASE_URL
12 |
13 |
14 | @pytest.mark.parametrize(
15 | "filename",
16 | [
17 | "gs-16b-2c-44100hz.aac",
18 | "gs-16b-2c-44100hz.ac3",
19 | "gs-16b-2c-44100hz.aiff",
20 | "gs-16b-2c-44100hz.flac",
21 | "gs-16b-2c-44100hz.mp3",
22 | "gs-16b-2c-44100hz.ogg",
23 | "gs-16b-2c-44100hz.ogx",
24 | "gs-16b-2c-44100hz.ts",
25 | "gs-16b-2c-44100hz.wav",
26 | "gs-16b-2c-44100hz.wma",
27 | ],
28 | )
29 | def test_audio_from_url(filename: str) -> None:
30 | """
31 | Test reading audio from a URL.
32 | """
33 | url = urljoin(BASE_URL, filename)
34 | _ = Audio.from_file(url)
35 |
--------------------------------------------------------------------------------
/tests/integration/media/test_image.py:
--------------------------------------------------------------------------------
1 | """
2 | Test `renumics.spotlight.media.Image` class.
3 | """
4 |
5 | from urllib.parse import urljoin
6 |
7 | import pytest
8 |
9 | from renumics.spotlight.media import Image
10 |
11 | from .data import BASE_URL
12 |
13 |
14 | @pytest.mark.parametrize(
15 | "filename",
16 | [
17 | "nature-256p.ico",
18 | "nature-360p.bmp",
19 | "nature-360p.gif",
20 | "nature-360p.jpg",
21 | "nature-360p.png",
22 | "nature-360p.tif",
23 | "nature-360p.webp",
24 | "nature-720p.jpg",
25 | "nature-1080p.jpg",
26 | "sea-360p.gif",
27 | "sea-360p.apng",
28 | ],
29 | )
30 | def test_image_from_url(filename: str) -> None:
31 | """
32 | Test reading image from a URL.
33 | """
34 | url = urljoin(BASE_URL, filename)
35 | _ = Image.from_file(url)
36 |
--------------------------------------------------------------------------------
/tests/integration/media/test_mesh.py:
--------------------------------------------------------------------------------
1 | """
2 | Test `renumics.spotlight.media.Mesh` class.
3 | """
4 |
5 | from urllib.parse import urljoin
6 |
7 | import pytest
8 |
9 | from renumics.spotlight.media import Mesh
10 |
11 | from .data import BASE_URL
12 |
13 |
14 | @pytest.mark.parametrize(
15 | "filename",
16 | [
17 | "tree.ascii.stl",
18 | "tree.glb",
19 | "tree.gltf",
20 | "tree.obj",
21 | "tree.off",
22 | "tree.ply",
23 | "tree.stl",
24 | ],
25 | )
26 | def test_mesh_from_url(filename: str) -> None:
27 | """
28 | Test reading mesh from a URL.
29 | """
30 | url = urljoin(BASE_URL, filename)
31 | _ = Mesh.from_file(url)
32 |
--------------------------------------------------------------------------------
/tests/integration/media/test_video.py:
--------------------------------------------------------------------------------
1 | """
2 | Test `renumics.spotlight.media.Mesh` class.
3 | """
4 |
5 | from urllib.parse import urljoin
6 |
7 | import pytest
8 |
9 | from renumics.spotlight.media import Video
10 |
11 | from .data import BASE_URL
12 |
13 |
14 | @pytest.mark.parametrize(
15 | "filename",
16 | [
17 | "sea-360p.avi",
18 | "sea-360p.mkv",
19 | "sea-360p.mov",
20 | "sea-360p.mp4",
21 | "sea-360p.mpg",
22 | "sea-360p.ogg",
23 | "sea-360p.webm",
24 | "sea-360p.wmv",
25 | "sea-360p-10s.mp4",
26 | ],
27 | )
28 | def test_video_from_url(filename: str) -> None:
29 | """
30 | Test reading video from an URL.
31 | """
32 | url = urljoin(BASE_URL, filename)
33 | _ = Video.from_file(url)
34 |
--------------------------------------------------------------------------------
/tests/integration/pandas/test_pandas.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 |
3 | from renumics import spotlight
4 |
5 |
6 | def test_read_df() -> None:
7 | df = pd.DataFrame({"foo": range(4)})
8 | viewer = spotlight.show(df, wait=False, no_browser=True)
9 | df_after_close = viewer.df
10 | viewer.close()
11 | assert df_after_close is not None
12 | assert "foo" in df_after_close
13 |
14 |
15 | def test_read_df_after_close() -> None:
16 | df = pd.DataFrame({"foo": range(4)})
17 | viewer = spotlight.show(df, wait=False, no_browser=True)
18 | viewer.close()
19 | df_after_close = viewer.df
20 | assert df_after_close is not None
21 | assert "foo" in df_after_close
22 |
--------------------------------------------------------------------------------
/tests/ui/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/ui/__init__.py
--------------------------------------------------------------------------------
/tests/ui/basic_actions.py:
--------------------------------------------------------------------------------
1 | """basic user itections to be used during tests"""
2 |
3 | from selenium.webdriver import ActionChains
4 | from selenium.webdriver.common.by import By
5 | from selenium.webdriver.remote.webdriver import WebDriver
6 |
7 | from .helpers import wait_for_tagged_element
8 |
9 |
10 | def select_points_similaritymap(
11 | driver: WebDriver, selection_width: float = 0.5
12 | ) -> None:
13 | """select point in similarity map"""
14 | wait_for_tagged_element(driver, "similaritymap")
15 | element = driver.find_element(
16 | by=By.CSS_SELECTOR, value="[data-test-tag='similaritymap']"
17 | )
18 |
19 | actions = ActionChains(driver)
20 | height, width = element.size["height"], element.size["width"]
21 | actions.move_to_element(element).move_by_offset(
22 | 0.45 * width, 0.45 * height
23 | ).click_and_hold().move_by_offset(
24 | -selection_width * width, -0.99 * height
25 | ).release().perform()
26 |
--------------------------------------------------------------------------------
/tests/unit/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/unit/__init__.py
--------------------------------------------------------------------------------
/tests/unit/dtypes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/unit/dtypes/__init__.py
--------------------------------------------------------------------------------
/tests/unit/io/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/unit/io/__init__.py
--------------------------------------------------------------------------------
/tests/unit/media/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Renumics/spotlight/2fafe54ba16060d0294784284ad12f91565bf9eb/tests/unit/media/__init__.py
--------------------------------------------------------------------------------
/tests/unit/media/data.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 | DATA_FOLDER = Path("data")
4 | AUDIO_FOLDER = DATA_FOLDER / "audio"
5 | IMAGES_FOLDER = DATA_FOLDER / "images"
6 | MESHES_FOLDER = DATA_FOLDER / "meshes"
7 | VIDEOS_FOLDER = DATA_FOLDER / "videos"
8 |
9 | SEED = 42
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
6 | "allowJs": true,
7 | "skipLibCheck": true,
8 | "esModuleInterop": true,
9 | "allowSyntheticDefaultImports": true,
10 | "strict": true,
11 | "forceConsistentCasingInFileNames": true,
12 | "module": "ESNext",
13 | "moduleResolution": "Node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx",
18 | "noFallthroughCasesInSwitch": true
19 | },
20 | "include": ["src", "types"],
21 | "references": [{ "path": "./tsconfig.node.json" }]
22 | }
23 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/types/chroma-js.d.ts:
--------------------------------------------------------------------------------
1 | // chroma-js.d.ts
2 | import chroma from 'chroma-js';
3 |
4 | declare module 'chroma-js' {
5 | interface Scale