Skip to content

Commit c4d9b44

Browse files
authored
Merge pull request #64 from atomscale-ai/enhancement/polling_trajectory_data
Add trajectory polling and move modules
2 parents a07e261 + 0967976 commit c4d9b44

24 files changed

+1179
-100
lines changed

README.md

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,70 @@
1-
# Atomscale Python SDK
1+
<p align="center">
2+
<img src="docs/_static/AtomscaleLogoFull.png" alt="Atomscale" width="300">
3+
</p>
24

3-
[![Testing](https://github.com/atomscale-ai/sdk/workflows/Testing/badge.svg)](https://github.com/atomscale-ai/sdk/actions?query=workflow:"Testing")
4-
[![GitHub tag](https://img.shields.io/github/tag/atomscale-ai/sdk?include_prereleases=&sort=semver&color=blue)](https://github.com/atomscale-ai/sdk/releases/)
5-
![Python](https://img.shields.io/badge/Python-3.10+-blue.svg?logo=python&logoColor=white)
6-
[![License](https://img.shields.io/badge/License-GPLv3-blue)](#license)
5+
<h1 align="center">Python SDK</h1>
76

8-
[![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](https://atomscale-ai.github.io/sdk/)
7+
<p align="center">
8+
<a href="https://github.com/atomscale-ai/sdk/actions/workflows/testing.yml?query=branch%3Amain"><img src="https://github.com/atomscale-ai/sdk/actions/workflows/testing.yml/badge.svg?branch=main" alt="Testing"></a>
9+
<a href="https://github.com/atomscale-ai/sdk/releases/"><img src="https://img.shields.io/github/tag/atomscale-ai/sdk?include_prereleases=&sort=semver&color=blue" alt="GitHub tag"></a>
10+
<img src="https://img.shields.io/badge/Python-3.10+-blue.svg?logo=python&logoColor=white" alt="Python">
11+
<a href="#license"><img src="https://img.shields.io/badge/License-MPL_2.0-blue" alt="License"></a>
12+
</p>
913

10-
## Install
14+
<p align="center">
15+
<a href="https://atomscale-ai.github.io/sdk/"><img src="https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge" alt="Documentation"></a>
16+
</p>
17+
18+
---
19+
20+
The official Python SDK for the [Atomscale](https://www.atomscale.ai) platform. Upload RHEED videos, stream live data, search the catalogue, and retrieve analysis results programmatically.
21+
22+
## Features
23+
24+
- **Unified Client** – Single interface for uploads, search, and downloads
25+
- **Live Streaming** – Push or pull RHEED frames in real-time
26+
- **Flexible Search** – Filter by keywords, data type, status, or time bounds
27+
- **Rich Results** – Access timeseries, diffraction graphs, and processed videos
28+
- **Polling Utilities** – Sync, async, and threaded options for monitoring updates
29+
30+
## Installation
1131

1232
```bash
1333
pip install atomscale
1434
```
1535

16-
> The package was renamed from `atomicds`. Importing `atomicds` (or installing
17-
> `atomicds`) still works via a thin shim but emits a `DeprecationWarning`.
36+
> **Note:** The package was renamed from `atomicds`. Importing `atomicds` still works but emits a `DeprecationWarning`.
37+
38+
## Quick Start
39+
40+
```python
41+
from atomscale import Client
42+
43+
# Create a client (reads AS_API_KEY from environment)
44+
client = Client()
45+
46+
# Upload files
47+
client.upload(files=["rheed_video.mp4"])
48+
49+
# Search the catalogue
50+
results = client.search(keywords=["GaN"], status="success")
51+
52+
# Fetch analysis results
53+
items = client.get(results["Data ID"].to_list())
54+
for item in items:
55+
print(item.timeseries_data.tail())
56+
```
57+
58+
## Documentation
59+
60+
Full documentation is available at **[atomscale-ai.github.io/sdk](https://atomscale-ai.github.io/sdk/)**.
61+
62+
- [Quickstart Guide](https://atomscale-ai.github.io/sdk/guides/quickstart.html)
63+
- [Upload Data](https://atomscale-ai.github.io/sdk/guides/upload-data.html)
64+
- [Search the Catalogue](https://atomscale-ai.github.io/sdk/guides/search-data.html)
65+
- [Stream RHEED Video](https://atomscale-ai.github.io/sdk/guides/stream-rheed.html)
66+
- [API Reference](https://atomscale-ai.github.io/sdk/modules.html)
67+
68+
## License
69+
70+
This project is licensed under the [Mozilla Public License 2.0](LICENSE).

docs/_static/AtomscaleLogoFull.png

38.2 KB
Loading

docs/_static/custom.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/* Override hardcoded logo dimensions */
2+
img[alt="Logo"] {
3+
height: 48px !important;
4+
width: auto !important;
5+
max-height: none !important;
6+
}
7+
8+
/* Target the header/nav area logo specifically */
9+
header img[alt="Logo"],
10+
nav img[alt="Logo"],
11+
a img[alt="Logo"] {
12+
height: 25px !important;
13+
width: auto !important;
14+
position: relative;
15+
top: -5px;
16+
}
17+
18+
/* Force badges to display inline horizontally */
19+
p > a.reference.external,
20+
p > img {
21+
display: inline !important;
22+
margin-right: 4px;
23+
}
24+
25+
p > a.reference.external > img {
26+
display: inline !important;
27+
vertical-align: middle;
28+
}
29+
30+
/* Reduce space between h1 and badges, add space below badges */
31+
section > h1 {
32+
margin-bottom: 0.25rem !important;
33+
}
34+
35+
section > p:first-of-type {
36+
margin-top: 0 !important;
37+
margin-bottom: 1.5rem !important;
38+
}

docs/conf.py

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -72,37 +72,21 @@
7272
# -- Options for HTML output -------------------------------------------------
7373
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
7474

75-
html_theme = "alabaster"
76-
# html_static_path = ['_static']
75+
html_theme = "sphinxawesome_theme"
76+
77+
html_static_path = ["_static"]
78+
html_css_files = ["custom.css"]
79+
html_logo = "_static/AtomscaleLogoFull.png"
80+
html_title = "Python SDK"
7781

78-
# Theme options are theme-specific and customize the look and feel of a theme
79-
# further. For a list of options available for each theme, see the
80-
# documentation.
81-
#
8282
html_theme_options = {
83-
"github_button": True,
84-
"github_type": "star&v=2",
85-
"github_user": "atomic-data-sciences",
86-
"github_repo": "api-client",
87-
"github_banner": True,
88-
"description": "Python SDK",
83+
"show_breadcrumbs": True,
84+
"show_prev_next": True,
85+
"logo_light": "_static/AtomscaleLogoFull.png",
86+
"logo_dark": "_static/AtomscaleLogoFull.png",
8987
}
9088

91-
# Custom sidebar templates, must be a dictionary that maps document names
92-
# to template names.
93-
#
94-
# The default sidebars (for documents that don't match any pattern) are
95-
# defined by theme itself. Builtin themes are using these templates by
96-
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
97-
# 'searchbox.html']``.
98-
#
99-
html_sidebars = {
100-
"**": [
101-
"about.html",
102-
"navigation.html",
103-
"searchbox.html",
104-
]
105-
}
89+
html_permalinks_icon = "<span>#</span>"
10690

10791
# -- Options for HTMLHelp output ---------------------------------------------
10892

docs/guides/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ single workflow so you can jump straight to what you need.
1313
inspect-results
1414
stream-rheed
1515
poll-timeseries
16+
poll-trajectory

docs/guides/inspect-results.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ Each item in ``analysed`` is a subclass of
2121
:class:`atomscale.results.RHEEDVideoResult` or
2222
:class:`atomscale.results.RHEEDImageResult`, depending on the source data.
2323

24+
.. note::
25+
26+
The :meth:`get` call fetches metadata and analysis artefacts for each ID.
27+
For large result sets, consider batching or filtering first.
28+
2429
Inspect time series data
2530
------------------------
2631

@@ -34,6 +39,21 @@ Inspect time series data
3439
The timeseries frame contains specular intensity, strain metrics, cluster IDs,
3540
and other summary features for every frame in the video.
3641

42+
.. list-table:: Common timeseries columns
43+
:header-rows: 1
44+
:widths: 30 70
45+
46+
* - Column
47+
- Description
48+
* - ``timestamp``
49+
- Frame timestamp in seconds
50+
* - ``specular_intensity``
51+
- Specular spot brightness
52+
* - ``strain``
53+
- Computed strain metric
54+
* - ``cluster_id``
55+
- Pattern cluster assignment
56+
3757
Work with extracted frames
3858
--------------------------
3959

@@ -47,6 +67,11 @@ Work with extracted frames
4767
``pattern_graph`` exposes the detected diffraction network as a NetworkX graph,
4868
while :meth:`get_pattern_dataframe` returns a tidy table describing each spot.
4969

70+
.. tip::
71+
72+
Use ``figure.savefig("snapshot.png")`` to export plots for reports or
73+
publications.
74+
5075
Download processed videos
5176
-------------------------
5277

@@ -58,3 +83,13 @@ Download processed videos
5883
)
5984
6085
The files are saved as MP4 (one per data ID) and mirror what you see in the UI.
86+
87+
.. caution::
88+
89+
Downloaded videos can be large. Ensure you have sufficient disk space and
90+
consider filtering to only the IDs you need.
91+
92+
.. seealso::
93+
94+
- :doc:`poll-timeseries` – Poll for live timeseries updates
95+
- :doc:`poll-trajectory` – Poll similarity trajectory data

docs/guides/poll-timeseries.rst

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ analysis. This guide summarises the four helper entry points in
1414
- **Async background task** with :func:`start_polling_task` – fire-and-forget
1515
inside an asyncio application.
1616

17+
.. tip::
18+
19+
For similarity trajectory data that tracks structure-property relationships,
20+
see :doc:`poll-trajectory` instead. That API auto-stops when the trajectory
21+
completes.
22+
1723
Shared setup
1824
------------
1925

@@ -41,9 +47,7 @@ Synchronous polling
4147

4248
Loop over :func:`iter_poll` to fetch fresh rows on a fixed cadence. The helper
4349
waits ``interval`` seconds between polls so a simple ``for`` loop is enough to
44-
keep the script going. Use ``distinct_by`` to avoid duplicates,
45-
``max_polls`` to stop automatically, and ``fire_immediately=False`` to skip the
46-
first immediate request if you only want timed polls.
50+
keep the script going.
4751

4852
.. code-block:: python
4953
@@ -61,13 +65,19 @@ first immediate request if you only want timed polls.
6165
print(f"Poll {idx}: latest timestamp -> {latest_timestamp(result)}")
6266
print(result.tail())
6367
68+
.. note::
69+
70+
The iterator uses **drift-corrected scheduling** to maintain accurate timing
71+
even when individual polls are slow. Use ``distinct_by`` to avoid processing
72+
duplicate data, and ``fire_immediately=False`` to skip the initial poll.
73+
6474
Background thread helper
6575
------------------------
6676

6777
Use :func:`start_polling_thread` when you want updates but cannot block the
6878
main thread (for example, inside a GUI or acquisition loop). The helper spawns
6979
a daemon thread, starts polling immediately, and forwards each update to your
70-
callback. Call ``stop_event.set()`` to shut it down cleanly.
80+
callback.
7181

7282
.. code-block:: python
7383
@@ -91,6 +101,11 @@ callback. Call ``stop_event.set()`` to shut it down cleanly.
91101
92102
# Call stop_event.set() to terminate early.
93103
104+
.. caution::
105+
106+
The callback runs in the polling thread, not the main thread. If you need to
107+
update UI elements, use thread-safe mechanisms (e.g., ``queue.Queue``).
108+
94109
Async utilities
95110
---------------
96111

@@ -101,6 +116,8 @@ Two helpers integrate with asyncio:
101116
* :func:`start_polling_task` creates a background task that awaits the poller
102117
in parallel and invokes an (optional) async handler for each result.
103118

119+
**Async iterator**
120+
104121
.. code-block:: python
105122
106123
import asyncio
@@ -121,6 +138,8 @@ Two helpers integrate with asyncio:
121138
122139
asyncio.run(stream_updates())
123140
141+
**Background task**
142+
124143
.. code-block:: python
125144
126145
async def handle_async(result):
@@ -141,3 +160,7 @@ Two helpers integrate with asyncio:
141160
142161
143162
asyncio.run(main())
163+
164+
.. tip::
165+
166+
Cancel the task with ``task.cancel()`` to stop polling early in async code.

0 commit comments

Comments
 (0)