Skip to content

Commit eed7bbf

Browse files
committed
fixes for publication
1 parent a92996e commit eed7bbf

7 files changed

Lines changed: 420 additions & 34 deletions

File tree

‎.github/workflows/publish.yml‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Publish to PyPI
2+
3+
# Publishes teal-bio whenever a GitHub Release is published.
4+
# Uses PyPI Trusted Publishing (OIDC): there is no API token to create or leak.
5+
# One-time setup on PyPI: Publishing -> Add a new pending publisher
6+
# owner: IronSheep16 repo: Teal workflow: publish.yml environment: pypi
7+
8+
on:
9+
release:
10+
types: [published]
11+
12+
jobs:
13+
build:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: actions/setup-python@v5
19+
with:
20+
python-version: "3.11"
21+
22+
- name: Build sdist and wheel
23+
run: |
24+
python -m pip install --upgrade build
25+
python -m build
26+
27+
- name: Check the metadata renders on PyPI
28+
run: |
29+
python -m pip install --upgrade twine
30+
python -m twine check dist/*
31+
32+
- uses: actions/upload-artifact@v4
33+
with:
34+
name: dist
35+
path: dist/
36+
37+
publish:
38+
needs: build
39+
runs-on: ubuntu-latest
40+
environment: pypi
41+
permissions:
42+
id-token: write # required for trusted publishing
43+
steps:
44+
- uses: actions/download-artifact@v4
45+
with:
46+
name: dist
47+
path: dist/
48+
49+
- uses: pypa/gh-action-pypi-publish@release/v1

‎.github/workflows/tests.yml‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: ["3.9", "3.11", "3.12"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
23+
- name: Install teal and its test deps
24+
run: |
25+
python -m pip install --upgrade pip
26+
python -m pip install -e .
27+
python -m pip install pytest
28+
29+
- name: Run the test suite
30+
run: pytest tests/ -v
31+
32+
- name: Check the examples still run end to end
33+
run: |
34+
python examples/minimal_pipeline.py
35+
python examples/multi_file_pipeline.py

‎CITATION.cff‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
cff-version: 1.2.0
2+
title: "Teal: Timelapse Express Analysis Library"
3+
message: "If you use Teal in your research, please cite it as below."
4+
type: software
5+
authors:
6+
- given-names: Benjamin
7+
family-names: Lapointe
8+
email: benjamin.lapointe@umontreal.ca
9+
affiliation: Université de Montréal
10+
repository-code: "https://github.com/IronSheep16/Teal"
11+
abstract: >-
12+
Teal analyses cell lineage data from timelapse microscopy. It builds a single
13+
spatiotemporal graph in which every cell is a node carrying its attributes, its
14+
timepoint and the mesh labels it came from, connected by lineage edges (parent to
15+
child) and spatial edges (neighbouring cells). Samples exported as several
16+
overlapping meshes are merged into one graph, including cells imaged in more than
17+
one mesh and cells whose parent lies in another mesh. Built to round-trip with
18+
MorphoGraphX, though any connected cell mesh works.
19+
keywords:
20+
- cell lineage
21+
- lineage tracing
22+
- timelapse microscopy
23+
- morphogenesis
24+
- MorphoGraphX
25+
- bioimage analysis
26+
license: GPL-2.0
27+
version: 0.1.0
28+
# once a release is archived on Zenodo, add the DOI it mints:
29+
# doi: 10.5281/zenodo.XXXXXXX

‎README.md‎

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
A Python library for analyzing cell lineage data from timelapse microscopy, with a focus on plant tissue development.
66
Meant to be used in combination with MorphoGraphX (Pierre Barbier de Reuille et al., 2015), but having an otherwise connected cell mesh is sufficient for usage.
77

8-
![TEAL Overview](teal/Teal.png)
8+
![TEAL Overview](https://raw.githubusercontent.com/IronSheep16/Teal/main/teal/Teal.png)
99

1010
---
1111

@@ -23,28 +23,46 @@ Meant to be used in combination with MorphoGraphX (Pierre Barbier de Reuille et
2323
## Installation
2424

2525
```bash
26-
# Clone the repository
27-
git clone https://github.com/IronSheep16/Teal.git
28-
cd Teal
26+
pip install teal-bio
27+
```
2928

30-
# Install dependencies
31-
pip install -r requirements.txt
29+
The distribution is called `teal-bio` (the name `teal` was already taken on PyPI), but you
30+
still import it as `teal`:
31+
32+
```python
33+
import teal
34+
```
3235

33-
# Install in development mode
36+
To work on the library itself:
37+
38+
```bash
39+
git clone https://github.com/IronSheep16/Teal.git
40+
cd Teal
3441
pip install -e .
3542
```
3643

3744
### Requirements
38-
- Python 3.8+
39-
- numpy
40-
- pandas
41-
- matplotlib
42-
- networkX
45+
Python 3.9+. `numpy`, `pandas`, `networkx` and `matplotlib` are installed automatically.
4346

4447
---
4548

4649
## Quick Start
4750

51+
### 1. Export the cell graph from MorphoGraphX
52+
53+
Teal reads the cell connectivity of a segmented mesh as a `.ply`. In MGX, run this once per
54+
timepoint on the segmented mesh you want to analyse:
55+
56+
```python
57+
Process.Mesh__Export__Cell_Graph_Ply_File_Save(filename, 'No', 'Active Mesh')
58+
```
59+
60+
Name the files so the timepoint is readable from the filename (`02_T0_cellGraph.ply`,
61+
`02_T1_cellGraph.ply`, …) — that is what `files_by_t` keys them by. Attributes (growth, size,
62+
zones, …) and parent labels are the CSVs MGX already exports.
63+
64+
### 2. Build and analyse in Teal
65+
4866
```python
4967
import teal as tl
5068
from teal import files_by_t
@@ -67,6 +85,12 @@ tmlps.filter_attr('GROWTH', 'avg_all') # a cell has many children -> average t
6785
tmlps.lineages('GROWTH')
6886
```
6987

88+
Results go back to MGX as heatmap CSVs:
89+
90+
```python
91+
tmlps.heat('GROWTH_heat', 'GROWTH') # one CSV per timepoint, per mesh
92+
```
93+
7094
---
7195

7296
## Core Concepts

‎pyproject.toml‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,41 @@ requires = ["setuptools>=61"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
6-
name = "Teal"
6+
# 'teal' is already taken on PyPI, so the distribution ships as teal-bio.
7+
# the import name is unaffected: users still write `import teal`.
8+
name = "teal-bio"
79
version = "0.1.0"
8-
description = "Conversion of cell (nodes) timelapse to a spruce graph, including some analysis tools."
9-
authors = [{ name = "Benjamin Lapointe" }]
10+
description = "Cell lineage analysis for timelapse microscopy: one spatiotemporal graph of lineage and spatial neighbours, built from MorphoGraphX meshes."
11+
authors = [{ name = "Benjamin Lapointe", email = "benjamin.lapointe@umontreal.ca" }]
1012
readme = "README.md"
1113
requires-python = ">=3.9"
12-
dependencies = ["numpy", "pandas", "networkx"]
14+
license = { file = "LICENSE" }
15+
keywords = [
16+
"cell-lineage",
17+
"lineage-tracing",
18+
"timelapse",
19+
"microscopy",
20+
"morphographx",
21+
"morphogenesis",
22+
"bioimage-analysis",
23+
]
24+
classifiers = [
25+
"Programming Language :: Python :: 3",
26+
"Intended Audience :: Science/Research",
27+
"Topic :: Scientific/Engineering :: Bio-Informatics",
28+
"Topic :: Scientific/Engineering :: Image Processing",
29+
"Operating System :: OS Independent",
30+
]
31+
dependencies = [
32+
"numpy",
33+
"pandas",
34+
"networkx",
35+
"matplotlib", # graph_viz only; imported lazily so it never slows down `import teal`
36+
]
37+
38+
[project.urls]
39+
Homepage = "https://github.com/IronSheep16/Teal"
40+
Repository = "https://github.com/IronSheep16/Teal"
1341

1442
[tool.setuptools]
1543
packages = ["teal"]

‎teal/core.py‎

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -201,25 +201,27 @@ def graph_viz(self, root=None, edge_type=None, attr_name=None, path=None):
201201
if d.get("edge_type") == edge_type
202202
]
203203
graph = graph.edge_subgraph(edges_to_keep).copy()
204-
205-
A = nx.nx_agraph.to_agraph(graph)
206-
207-
for n in graph.nodes():
208-
node_attr = graph.nodes[n].get(attr_name, n) if attr_name else n
209-
A.get_node(n).attr.update(
210-
label=str(node_attr),
211-
rank=str(graph.nodes[n]["layer"])
212-
)
213-
214-
A.graph_attr.update(
215-
rankdir="LR",
216-
ranksep="2.0",
217-
nodesep="0.2"
204+
205+
import matplotlib.pyplot as plt # only needed to draw, so keep it out of the import
206+
207+
# one column per timepoint: 'layer' already ranks every cell, so it lays itself out
208+
pos = nx.multipartite_layout(graph, subset_key="layer")
209+
labels = {
210+
n: str(graph.nodes[n].get(attr_name, n) if attr_name else n)
211+
for n in graph.nodes()
212+
}
213+
214+
fig, ax = plt.subplots(figsize=(12, 8))
215+
nx.draw_networkx(
216+
graph, pos, ax=ax, labels=labels,
217+
node_size=300, node_color="lightsteelblue",
218+
edge_color="grey", arrowsize=8, font_size=6,
218219
)
219-
220-
A.layout(prog="dot")
221-
A.draw(path=path or "graph.png", format="png", prog="dot", args="-Gdpi=200")
222-
220+
ax.set_axis_off()
221+
fig.tight_layout()
222+
fig.savefig(path or "graph.png", dpi=200)
223+
plt.close(fig)
224+
223225
return graph
224226

225227
# checking up on common graph errors

0 commit comments

Comments
 (0)