Skip to content

Commit 1c72d9e

Browse files
more descriptive table of contents plus initial communication protocol documentation how to
1 parent bad6fdb commit 1c72d9e

6 files changed

Lines changed: 253 additions & 4 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0+2025.09.25T00.00.42.365Z.a3fa17c9.master
1+
0.1.0+2025.09.25T16.23.50.822Z.bad6fdbb.master

autodocs/how-to.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
How-to
22
=============
33

4-
Practical instructions to solve specific problems or achieve goals.
4+
Practical instructions for achieving specific goals within Learning Observer. Use these guides when you know what outcome you need and want a proven recipe to follow:
5+
6+
- :doc:`Communication Protocol <docs/how-to/communication_protocol>` - How to query data from reducers or system endpoints for dashboards.
7+
- :doc:`Configure Learning Observer <docs/how-to/config>` - Set up credentials, environment variables, and other configuration details required for a smooth deployment.
8+
- :doc:`Build Dashboards <docs/how-to/dashboards>` - Walk through creating dashboards from reducer outputs, including layout choices and data wiring.
9+
- :doc:`Run with Docker <docs/how-to/docker>` - Learn how to containerize the stack, manage images, and operate the project using Docker Compose.
10+
- :doc:`Writing Observer Extension <docs/how-to/extension>` - Install, configure, and validate the Writing Observer browser extension for capturing events.
11+
- :doc:`Interactive Environments <docs/how-to/interactive_environments>` - Connect Learning Observer to Jupyter and other live coding setups for iterative development.
512

613
.. toctree::
14+
:hidden:
715
:maxdepth: 1
816
:titlesonly:
917

18+
docs/how-to/communication_protocol.md
1019
docs/how-to/config.md
1120
docs/how-to/dashboards.md
1221
docs/how-to/docker.md

autodocs/reference.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
Reference
22
=============
33

4-
Detailed, structured information about APIs, configurations, and technical details.
4+
Detailed, structured information about APIs, configurations, and technical details. Consult these resources when you need definitive answers about how the system behaves or how to integrate with it:
5+
6+
- :doc:`Code Quality Standards <docs/reference/code_quality>` - Understand our expectations for readability, style, and continuous improvement.
7+
- :doc:`Documentation Conventions <docs/reference/documentation>` - Learn how we structure docs, what tools we use, and how to contribute updates.
8+
- :doc:`Linting Rules <docs/reference/linting>` - Review the automated checks that keep the codebase healthy and how to run them locally.
9+
- :doc:`Testing Strategy <docs/reference/testing>` - Explore the testing layers we rely on and guidelines for writing reliable tests.
10+
- :doc:`Versioning and Releases <docs/reference/versioning>` - See how we tag releases, manage dependencies, and maintain backward compatibility.
11+
- :doc:`Module Reference <modules>` - Dive into the autogenerated API reference for Python modules within Learning Observer.
12+
- :doc:`API Reference <api>` - Inspect the internal functionality of the system.
513

614
.. toctree::
15+
:hidden:
716
:maxdepth: 1
817
:titlesonly:
918

autodocs/tutorials.rst

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
Tutorials
22
=============
33

4-
Step-by-step guides to help you learn by doing.
4+
Step-by-step guides that teach by doing. Follow these tutorials to get hands-on experience with core workflows:
5+
6+
- :doc:`Install Learning Observer <docs/tutorials/install>` - Set up the development environment, install dependencies, and verify your deployment.
7+
- :doc:`Create a Module with Cookiecutter <docs/tutorials/cookiecutter-module>` - Generate a new module scaffold, customize it, and understand the key files produced by the template.
58

69
.. toctree::
10+
:hidden:
711
:maxdepth: 1
812
:titlesonly:
913

docs/concepts/communication_protocol.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ instead of manually constructing requests. This keeps the protocol's
8787
flexibility while offering ergonomic entry points for UI
8888
components. (See: integration.py L49-L102)
8989

90+
## WebSocket Endpoint
91+
92+
Dashboards and other clients interact with the communication protocol
93+
through a dedicated WebSocket endpoint exposed at
94+
`/wsapi/communication_protocol`. The aiohttp application wires that path
95+
to `websocket_dashboard_handler`, making the protocol available to
96+
browser sessions and backend consumers alike. (See: learning_observer/routes.py L195-L213)
97+
98+
When a client connects, the handler waits for a JSON payload describing
99+
one or more queries. Each entry typically includes the flattened
100+
`execution_dag`, a list of `target_exports` to stream, and optional
101+
`kwargs` that provide runtime parameters. Whenever the client submits a
102+
new payload, the server builds the requested DAG generators, executes
103+
them, and schedules reruns based on the provided settings. Responses are
104+
batched into arrays of `{op, path, value}` records so the client can
105+
efficiently apply partial updates to its local state. (See: learning_observer/dashboard.py L331-L411)
106+
90107
## Tooling and Debugging
91108

92109
Two exploratory tools live alongside the protocol implementation:
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# How to Build and Run Communication Protocol Queries
2+
3+
This guide explains the end-to-end workflow for turning a reporting idea into a runnable query on the Learning Observer communication protocol. Follow the steps in order—both humans and language models can use this as a checklist when creating or automating queries.
4+
5+
## 1. Frame the Data Task
6+
7+
1. Write a one-sentence description of the insight or dataset you need (e.g., *“Return the latest writing sample for each student in a course”*).
8+
2. Identify the reducers, helper functions, or key-value store documents that expose the data. Concept docs provide summaries of the executor lifecycle, node types, and utilities for transforming reducer outputs. 【F:docs/concepts/communication_protocol.md†L1-L100】
9+
3. Check whether an existing helper (e.g., `generate_base_dag_for_student_reducer`) already provides most of the DAG structure. Reusing helpers keeps queries consistent and concise. 【F:docs/concepts/communication_protocol.md†L62-L87】
10+
11+
## 2. Confirm Your Goal and Required Data
12+
13+
1. Identify the data source(s):
14+
15+
* **Reducers** - Aggregated documents stored in the key-value store.
16+
* **Helper functions** - Python callables published with `publish_function`.
17+
* **Roster/metadata** - Collections that need to be joined with reducer data.
18+
2. Decide which fields must appear in the output. Note whether you need the entire document or only specific fields.
19+
3. List all runtime values (course ID, time range, student list, etc.). These become `parameter` nodes later.
20+
21+
Document these choices (in comments or metadata) so you can refer to them in later steps.
22+
23+
## 3. Declare Parameters and Defaults
24+
25+
Each runtime input must be expressed as a `parameter` node. Parameters can be required or optional and may include default values:
26+
27+
```python
28+
course_id = query.parameter("course_id", required=True)
29+
student_id = query.parameter("student_id", required=False, default=None)
30+
```
31+
32+
For each parameter, document:
33+
34+
* **Name** - Identifier passed to the DAG node.
35+
* **Type** - String, UUID, ISO date, etc.
36+
* **Required** - Boolean flag.
37+
* **Default** - Optional fallback value.
38+
39+
> Tip: Emit parameter declarations first so later steps can reuse variables like `course_id["variable"]` consistently.
40+
41+
For fixed values (e.g., reducer names or field lists), define constants once near where they are used.
42+
43+
## 4. Plan the Data Flow (DAG Skeleton)
44+
45+
Translate the goal into a linear sequence of operations. A typical reducer query involves:
46+
47+
1. Fetching roster metadata or other context.
48+
2. Producing keys for each entity (`keys` nodes).
49+
3. Retrieving reducer documents with `select`.
50+
4. Joining reducer outputs with metadata.
51+
5. (Optional) Post-processing with `map` or `call`.
52+
53+
Example outline:
54+
55+
```
56+
roster = call("get_course_roster", course_id)
57+
reducer_keys = keys(reducer_name, roster.students)
58+
reducer_docs = select(reducer_keys, fields=[...])
59+
enriched = join(reducer_docs, roster, left_on="student.id", right_on="id")
60+
export enriched
61+
```
62+
63+
Verify that every step depends only on earlier outputs, and adjust until the flow is acyclic.
64+
65+
## 5. Construct Nodes with Query Helpers
66+
67+
Use `query.py` helpers to implement the skeleton:
68+
69+
```python
70+
from learning_observer.communication_protocol import query
71+
72+
roster = query.call("get_course_roster", args={"course_id": course_id})
73+
reducer_keys = query.keys(
74+
reducer="reading_fluency",
75+
entities=query.variable(roster, "students"),
76+
)
77+
reducer_docs = query.select(
78+
keys=reducer_keys,
79+
fields=query.SelectFields.SUMMARY,
80+
)
81+
enriched = query.join(
82+
left=reducer_docs,
83+
right=query.variable(roster),
84+
left_on="student_id",
85+
right_on="id",
86+
)
87+
```
88+
89+
Guidelines:
90+
91+
* Use `query.variable(node, path=None)` for downstream access to prior outputs.
92+
* Encapsulate repeated or complex logic in functions for reuse and testing.
93+
* Use explicit names and keyword arguments—avoid positional arguments for clarity.
94+
95+
## 6. Define Exports and Integrations
96+
97+
Choose which nodes should be externally accessible:
98+
99+
```python
100+
exports = {
101+
"reading_fluency": query.export("reading_fluency", enriched)
102+
}
103+
```
104+
105+
If integrating with the async helper layer, pass `exports` to `learning_observer.communication_protocol.integration.bind_exports`.
106+
107+
Document required parameters and defaults with the export definitions.
108+
109+
## 7. Flatten, Validate, and Serialise
110+
111+
1. Convert the nested DAG into executor-ready form:
112+
113+
```python
114+
from learning_observer.communication_protocol import util
115+
dag = util.flatten(exports)
116+
```
117+
118+
2. Confirm all node IDs are unique and reference earlier nodes. Inspect the flattened DAG if generated automatically.
119+
120+
3. Serialise to JSON (e.g., `json.dumps(dag)`) when sending over the wire.
121+
122+
4. Add automated tests—at minimum a smoke test against a fixture store.
123+
124+
## 8. Expose the DAG to Clients
125+
126+
To make the DAG discoverable over the websocket interface:
127+
128+
* Define `EXECUTION_DAG` in the module file and register it with the loader.
129+
* On server start, the DAG will be advertised under the module’s namespace.
130+
131+
Production deployments should prefer predefined DAGs for security. Open-query mode is optional and must be explicitly enabled.
132+
133+
## 9. Execute the Query
134+
135+
Submit the flattened DAG to the communication protocol endpoint with runtime parameters:
136+
137+
```json
138+
{
139+
"parameters": {
140+
"course_id": "course-123",
141+
"start_date": "2023-09-01"
142+
},
143+
"exports": ["reading_fluency"],
144+
"dag": { ... flattened nodes ... }
145+
}
146+
```
147+
148+
On success, the response includes export payloads keyed by export name. Inspect `DAGExecutionException` for error details.
149+
150+
When using integration bindings, call the generated async function with the same parameters.
151+
152+
## 10. Construct Websocket Requests
153+
154+
Clients interact with `/wsapi/communication_protocol` via JSON messages. Each message contains:
155+
156+
* `execution_dag` - Name of a predefined DAG or a full DAG object.
157+
* `target_exports` - List of exports to run.
158+
* `kwargs` - Runtime parameters.
159+
160+
Example:
161+
162+
```json
163+
{
164+
"docs_request": {
165+
"execution_dag": "writing_observer",
166+
"target_exports": ["docs_with_roster"],
167+
"kwargs": { "course_id": "COURSE-123" }
168+
}
169+
}
170+
```
171+
172+
The server streams back updates in messages shaped like:
173+
174+
```json
175+
[
176+
{
177+
"op": "update",
178+
"path": "students.student-1",
179+
"value": { "text": "...", "provenance": { ... } }
180+
}
181+
]
182+
```
183+
184+
If `rerun_dag_delay` is set, the server automatically re-executes the DAG and pushes updates.
185+
186+
## 11. Iterate and Maintain
187+
188+
* Profile slow queries; large joins may need new helpers or precomputed reducers.
189+
* Keep DAGs version-controlled. Update dependent queries when reducers or helpers change.
190+
* Review security before exposing exports to untrusted clients.
191+
192+
## 12. Test End-to-End
193+
194+
* **Unit-test** reducers and helpers independently.
195+
* **Reference** `learning_observer/learning_observer/communication_protocol/test_cases.py` for DAG tests.
196+
* **Exercise websocket flows** manually or with automated integration tests.
197+
198+
## 13. Document Parameters and Outputs
199+
200+
Update module documentation with:
201+
202+
* Export descriptions, parameter types, and return structures.
203+
* Sample request payloads.
204+
* Notes on authentication or runtime context.
205+
206+
Good documentation ensures developers and tooling can invoke queries reliably.
207+
208+
### Summary
209+
210+
Following this workflow ensures queries are consistent, testable, and safe to expose across dashboards, notebooks, and automation tools.

0 commit comments

Comments
 (0)