Skip to content

Commit 6ce38d1

Browse files
authored
Merge pull request #35 from dandi/readme-badges
Add data freshness badges to the README
2 parents 4a2028a + 5c22980 commit 6ce38d1

3 files changed

Lines changed: 52 additions & 2 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
<h1 align="center">DANDI Brain Atlas Explorer</h1>
66

7+
<p align="center">
8+
<a href="https://github.com/dandi/dandi-atlas/actions/workflows/update-data.yml"><img src="https://github.com/dandi/dandi-atlas/actions/workflows/update-data.yml/badge.svg" alt="Nightly data update"></a>
9+
<a href="data/last_updated.json"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Fdandi%2Fdandi-atlas%2Fmain%2Fdata%2Flast_updated.json&query=%24.date&label=data%20updated&color=blue" alt="Data last updated"></a>
10+
</p>
11+
712
Interactive 3D viewer for finding data in the [DANDI Archive](https://dandiarchive.org) by brain region. Regions come from standard reference atlases and are shaded by how much data covers them, and selecting one lists the dandisets, subjects, and sessions that recorded there. Where a session provides electrode coordinates, the individual contacts are drawn in the same 3D space.
813

914
Live at **[atlas.dandiarchive.org](https://atlas.dandiarchive.org)**. There is a [blog post](https://about.dandiarchive.org/blog/2026/02/24/introducing-dandi-atlas-explorer-explore-the-dandi-archive-in-3d/) introducing the project, and a [demo video](https://www.youtube.com/watch?v=D8514CLVXYo) walking through what the viewer can do.

data/last_updated.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@
3333
"mode": "incremental",
3434
"asset_count": 794
3535
}
36-
}
37-
}
36+
},
37+
"date": "2026-07-30"
38+
}

scripts/build_atlases_index.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
from __future__ import annotations
2626

2727
import json
28+
from datetime import datetime
2829
from pathlib import Path
2930

3031
REPO_ROOT = Path(__file__).resolve().parent.parent
3132
ATLASES_DIR = REPO_ROOT / "data" / "atlases"
3233
INDEX_PATH = REPO_ROOT / "data" / "atlases_index.json"
34+
LAST_UPDATED_PATH = REPO_ROOT / "data" / "last_updated.json"
3335

3436
# Display metadata not derivable from the per-atlas JSON files. Order in
3537
# this list defines the order of cards on the landing page.
@@ -60,6 +62,46 @@ def summarize_atlas(atlas_dir: Path) -> dict:
6062
}
6163

6264

65+
def stamp_last_updated_date() -> None:
66+
"""Add a plain YYYY-MM-DD `date` field to last_updated.json.
67+
68+
The three per-atlas update scripts each stamp the top-level `timestamp`,
69+
in two different formats ("...Z" from update_data.py, isoformat with
70+
microseconds from the macaque and rat scripts), and whichever runs last
71+
wins. Neither is usable in a README badge, because shields.io prints the
72+
raw string it reads. This derives one short value from the newest per-atlas
73+
run, so the badge has something stable to point at and does not depend on
74+
which script happened to finish last.
75+
"""
76+
if not LAST_UPDATED_PATH.exists():
77+
return
78+
try:
79+
record = json.loads(LAST_UPDATED_PATH.read_text())
80+
except (OSError, json.JSONDecodeError):
81+
print(" warning: last_updated.json unreadable, leaving date unset")
82+
return
83+
84+
stamps = [v.get("timestamp") for v in (record.get("per_atlas") or {}).values()]
85+
stamps.append(record.get("timestamp"))
86+
87+
newest = None
88+
for raw in filter(None, stamps):
89+
try:
90+
parsed = datetime.fromisoformat(raw.replace("Z", "+00:00"))
91+
except (AttributeError, ValueError):
92+
continue
93+
if newest is None or parsed > newest:
94+
newest = parsed
95+
96+
if newest is None:
97+
print(" warning: no parseable timestamps, leaving date unset")
98+
return
99+
100+
record["date"] = newest.date().isoformat()
101+
LAST_UPDATED_PATH.write_text(json.dumps(record, indent=2) + "\n")
102+
print(f" last_updated.json date = {record['date']}")
103+
104+
63105
def main() -> None:
64106
atlases = []
65107
for entry in ATLAS_DISPLAY:
@@ -83,6 +125,8 @@ def main() -> None:
83125
INDEX_PATH.write_text(json.dumps({"atlases": atlases}, indent=2) + "\n")
84126
print(f"\nWrote {INDEX_PATH.relative_to(REPO_ROOT)} ({len(atlases)} atlases)")
85127

128+
stamp_last_updated_date()
129+
86130

87131
if __name__ == "__main__":
88132
main()

0 commit comments

Comments
 (0)