Skip to content
Merged
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 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ poetry run inv export # dump db tables to csv/json/html/md/sql
- **pre-commit excludes** `alembic/` and `notebooks/`
- Database is **read-only by default** via URI parameter. Alembic needs write access.
- Adding a property: update model → alembic revision → upgrade → update PropertyMetadata → `inv render-data-docs`
- Tests use `-n auto` (pytest-xdist). Set `PYTEST_ADDOPTS=""` to override.
- Tests use `-n auto` (pytest-xdist). Set `PYTEST_ADDOPTS=""` to override. **Use pytest-style tests only** — plain functions with `assert`, no `unittest.TestCase` or class-based tests.
- CI matrix: 3 OS × 5 Python versions (3.10–3.14). Runs ruff (pre-commit) then pytest.
- Documentation uses **sphinx-immaterial** theme (fork of sphinx-material). Workaround: `object_description_options` disables `generate_synopses` to avoid a sphinx-immaterial KeyError. If upgrading sphinx-immaterial or Sphinx, try removing that workaround first. Notebooks in `docs/source/notebooks/` are rendered by nbsphinx. Dev notebooks in root `notebooks/` are ignored by pre-commit.
- Package published to PyPI on tags via trusted publishing (no password needed).
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,26 @@ poetry run inv render-data-docs

Inspect the ``data.rst`` file before committing to check that everything looks correct.

### Writing Tests

Tests must be **pytest-style** — plain functions with `assert`, no `unittest.TestCase` or class-based tests. Use `@pytest.mark.parametrize` for data-driven tests.

```python
def test_example():
result = some_function()
assert result == expected

@pytest.mark.parametrize("input,expected", [(1, 2), (3, 4)])
def test_parametrized(input, expected):
assert some_function(input) == expected
```

Run the full suite before committing:

```bash
poetry run pytest
```

## Styleguides
### Commit Messages
<!-- TODO
Expand Down
2 changes: 0 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@
"sphinxcontrib.bibtex",
]

nbsphinx_allow_errors = True

# sphinxcontrib.bibtex settings
bibtex_bibfiles = ["references.bib"]

Expand Down
16 changes: 12 additions & 4 deletions mendeleev/vis/plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@


def create_tile(
element: pd.Series, color: str, x_offset: float = 0.45, y_offset: float = 0.45
element: pd.Series,
color: str,
default_color: str = "#ffffff",
x_offset: float = 0.45,
y_offset: float = 0.45,
) -> Shape:
"""
Create tile shape
"""
fill = element[color] if pd.notna(element[color]) else default_color
return Shape(
type="rect",
x0=element["x"] - x_offset,
y0=element["y"] - y_offset,
x1=element["x"] + x_offset,
y1=element["y"] + y_offset,
line=dict(color=element[color]),
fillcolor=element[color],
line=dict(color=fill),
fillcolor=fill,
opacity=0.8,
)

Expand Down Expand Up @@ -92,7 +97,10 @@ def periodic_table_plotly(
colorby = "attribute_color"

# tiles
tiles = [create_tile(row, color=colorby) for _, row in elements.iterrows()]
tiles = [
create_tile(row, color=colorby, default_color=missing)
for _, row in elements.iterrows()
]
fig.layout["shapes"] += tuple(tiles)

# symbols
Expand Down
44 changes: 44 additions & 0 deletions tests/test_vis.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pandas as pd
import pytest
from mendeleev.fetch import fetch_table
from plotly.graph_objects import Figure as PlotlyFigure
from plotly.graph_objs.layout import Shape
from bokeh.plotting import figure as BokehFigure
from mendeleev.vis import create_vis_dataframe, add_tile_coordinates
from mendeleev.vis import (
Expand All @@ -9,6 +11,7 @@
heatmap,
periodic_table,
)
from mendeleev.vis.plotly import create_tile


def test_add_tile_coordinates():
Expand Down Expand Up @@ -50,3 +53,44 @@ def test_periodic_table(attribute, colorby, wide_layout, backend):
assert isinstance(fig, PlotlyFigure)
elif backend == "bokeh":
assert isinstance(fig, BokehFigure)


def _make_element(color_value):
return pd.Series({"x": 5, "y": 3, "color": color_value})


def test_create_tile_valid_hex_color():
element = _make_element("#ff0000")
tile = create_tile(element, color="color")
assert isinstance(tile, Shape)
assert tile.fillcolor == "#ff0000"
assert tile.line["color"] == "#ff0000"


def test_create_tile_nan_color_uses_default():
element = _make_element(float("nan"))
tile = create_tile(element, color="color")
assert tile.fillcolor == "#ffffff"
assert tile.line["color"] == "#ffffff"


def test_create_tile_nan_color_uses_custom_default():
element = _make_element(float("nan"))
tile = create_tile(element, color="color", default_color="#cccccc")
assert tile.fillcolor == "#cccccc"
assert tile.line["color"] == "#cccccc"


def test_create_tile_none_color_uses_default():
element = _make_element(None)
tile = create_tile(element, color="color")
assert tile.fillcolor == "#ffffff"


def test_create_tile_shape_bounds():
element = _make_element("#000000")
tile = create_tile(element, color="color", x_offset=0.5, y_offset=0.5)
assert tile.x0 == 4.5
assert tile.y0 == 2.5
assert tile.x1 == 5.5
assert tile.y1 == 3.5
Loading