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
8 changes: 8 additions & 0 deletions .issueflows/01-current-issues/issue187_original.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Issue #187 — name of groups

Source: https://github.com/cellpy/cellpy-simple-gui/issues/187

It does not seem to be any options for naming the groups. Have not tested if
cellpy-simple-gui can read the group label from batch journal files. But maybe
it does not. We need option for naming groups (and it should be stored when
saving).
44 changes: 44 additions & 0 deletions .issueflows/01-current-issues/issue187_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Plan — #187 name of groups

## Findings

- `collect._batch` already hands cellpy a `group_labels` map, but it is always
the default `"group <n>"`. cellpy (≥2.1.5) uses that label as the legend name
of group-averaged traces and writes it into the `group_label` column of the
collected summary frame.
- Ungrouped summary / `per_cycle` cycles traces carry `legendgroup="<group id>"`
(and no `legendgrouptitle`), so a group name can be shown there as a Plotly
legend-group title without touching cellpy.
- cellpy batch journals carry a `group_label` column (`journal_from_frame` →
`write_journal` → `from_journal` round-trips it); the app currently reads
only `group`.
- Project manifests (`project.json`) have no group-level section.

## Approach

Group names live on the `Library` (they are group-level, not cell-level):

1. **Core** — `Library._group_labels: dict[int, str]`, `set_group_label()`,
`group_label()`, `group_labels()` (in-use groups only), `groups()` (id,
custom label, effective name, cell count, colour). `all()` syncs a
`CellRecord.group_label` snapshot so collectors need no extra plumbing;
`CellRecord.group_name()` resolves "custom or `group <n>`". `clear()`
forgets the names. `CellMeta.group_label` exposes the effective name.
2. **Plots** — `_batch` passes the effective names to `from_cells`; new
`collect.group_titles(records)` + `figure_json(group_titles=…)` sets
`legendgrouptitle` on ungrouped traces whose legendgroup is a named group
(summary + cycles pane).
3. **API** — `_state()` gains `groups`; `POST /api/groups/{group}` with
`{"label": …}` (empty clears) returns the state.
4. **Persistence** — `ProjectManifest.groups: [{id, label}]` (optional, so old
manifests still open); saved from `library.group_labels()`, restored on
open. Journals: `cellpy_adapter.load_journal()` returns cells + group labels
read from the `group_label` column (skipping the `group <n>` defaults);
`load_journal_cells()` stays as a thin wrapper; the journal job applies them.
5. **UI** — a *Groups* strip in the Manage cells modal (swatch, id, name input
with `group <n>` placeholder, cell count); renaming goes through the
deferred-replot path (#184). Sidebar group inputs show the name as a
tooltip. All state writes go through one `_applyState()` helper.
6. **Tests** — core (batch labels in grouped legend, legend-group titles),
projects (round trip + legacy manifest), journal (`group_label` read), API
(endpoint + state), plus template/component drift tests already in place.
47 changes: 47 additions & 0 deletions .issueflows/01-current-issues/issue187_status.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Status — #187 name of groups

- [x] Done

## What was done

- `Library` keeps custom group names (`set_group_label`, `group_label`,
`group_labels`, `groups`); `all()` stamps `CellRecord.group_label` so the
collectors and `CellMeta.group_label` see the name without extra plumbing.
`clear()` forgets the names.
- `collect._batch` hands cellpy the effective name (`custom` or `group <n>`),
so group-averaged traces are named after it. New `collect.group_titles()` +
`figure_json(group_titles=…)` caption per-cell traces of a named group with
a Plotly `legendgrouptitle` (summary + cycles pane).
- API: `/api/state` carries `groups`; `POST /api/groups/{group}` with
`{"label": …}` names a group (blank → default) and returns the state.
- Persistence: `ProjectManifest.groups` (`[{id, label}]`, optional, so older
manifests still open) is saved from the in-use named groups and restored on
open. `cellpy_adapter.load_journal()` returns `(cells, group_labels)` read
from the journal's `group_label` column (cellpy's `group <n>` placeholders
dropped); `load_journal_cells()` stays as a wrapper; the journal job applies
the names.
- UI: a *Groups* strip at the top of the Manage cells modal (swatch, number,
editable name with `group <n>` placeholder, cell count). Renames follow the
deferred-replot rule from #184. Group inputs in the sidebar and table show
the group name as a tooltip. State writes go through `_applyState()`.
- Docs: README feature list / project layout mention group names;
`llms-full.txt` regenerated.

## Tests

- `tests/test_core.py`: library group-name semantics; names reach the
grouped legend; legend-group titles on ungrouped summary and cycles plots.
- `tests/test_projects.py`: save → open round trip; manifest without `groups`.
- `tests/test_journal.py`: `group_label` column read (and absent); journal job
applies the names end to end via the API.
- `tests/test_api.py`: `/api/groups/{group}` + state shape.
- `tests/test_gui_playwright.py` (e2e): naming and un-naming a group in the
modal, legend caption after close.

Full suite: `uv run pytest -q -p no:warnings` green (3 skips, example data).

## Remaining

Nothing for this issue. Appending a journal whose group numbers collide with
groups already in the library overwrites those groups' names — the general
replace-vs-append question is #174.
23 changes: 23 additions & 0 deletions .issueflows/04-designs-and-guides/manage-cells-modal.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,29 @@
fetches with `AbortController` (the server would keep computing the figure
anyway; sequence numbers give the same visible result with less machinery).

## Group names (issue #187)

- Names are **library-level** (`Library._group_labels`, keyed by group number),
not a per-cell field: cells move between groups, the name stays with the
number. `all()` stamps a `CellRecord.group_label` snapshot so `collect._batch`
and `CellMeta` see it without threading a map through every collector.
- Only names the user (or a journal / manifest) chose are stored; blank or the
default spelling `group <n>` removes the entry. Names of groups with no cells
are kept in memory (cells may move back) but not reported by `groups()` /
`group_labels()`, so they are not saved.
- Plots: cellpy already names group-averaged traces after `group_labels`;
per-cell traces of a named group get a Plotly `legendgrouptitle` from
`collect.group_titles(records)` (only for named groups — a bare number adds
nothing the swatch does not).
- Persistence: `project.json` → `groups: [{id, label}]` (optional). Journals →
the `group_label` column of `journal.pages` via `cellpy_adapter.load_journal`.
- UI: the *Groups* strip above the table (`.mgr-groups`, one `.mgr-group` chip
per group in use); `renameGroup()` → `POST /api/groups/{id}` → `_applyState`
→ `_replotAfterEdit()` (same deferred redraw as other modal edits).
- Alternatives considered: a `group_label` column on each cell row (drifts as
soon as two cells disagree) and a separate "Groups" modal (one more dialog
for a one-field edit).

## UI location

- Markup: `web/templates/index.html` (modal after `.layout`)
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@
- **Cycle summary** — capacity, CE, cumulated CE, end voltages, internal resistance, C-rate, capacity loss. Gravimetric, areal, or absolute. Group mean ± std. Shared or independent y-scales.
- **Cell explorer** — voltage–capacity, dQ/dV, dV/dQ. Compare cells, overlaid or side by side.
- **Cycles collector** — per cycle, per cell, or film plot.
- **Cell list** — rename, group, select, filter, sort, remove.
- **Projects** — save and reopen the loaded cells, grouping, labels, and selection. Unsaved edits show as `name*`.
- **Cell list** — rename, group, name the groups, select, filter, sort, remove.
- **Projects** — save and reopen the loaded cells, grouping, group names, labels, and selection. Unsaved edits show as `name*`.
- **Export** — CSV, Excel, Parquet, JSON; figures as PNG, SVG, PDF.
- **Light and dark themes.**
- **Background loading** with progress.
Expand Down Expand Up @@ -267,7 +267,7 @@ A project is a portable folder — move it, zip it, share it:

```
<project>/
├── project.json # manifest: name, timestamps, versions, per-cell grouping/labels/selection
├── project.json # manifest: name, timestamps, versions, per-cell grouping/labels/selection, group names
├── cellpy.toml # optional: cellpy settings pinned to this project
└── data/
├── c1.cellpy # every loaded cell saved as a self-contained cellpy file
Expand Down
Loading
Loading