From f82c7ba323d6a4fad201aa987756f52dbf44d0b7 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Apr 2026 09:37:55 -0400 Subject: [PATCH 1/5] added more documentation around pmss rulesets and updated some other docs --- VERSION | 2 +- devops/README.md | 63 +++++++++++++++++++++++++++++++- docs/concepts/system_settings.md | 8 ++++ docs/how-to/offline_replay.md | 24 +++++++++--- modules/portfolio_diff/README.md | 4 +- 5 files changed, 91 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index f17f5621..c927f21c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.04.16T12.07.43.534Z.b0eade5c.berickson.20260406.portfolio.fixes +0.1.0+2026.04.23T13.37.55.571Z.6b46a00f.berickson.20260423.documentation diff --git a/devops/README.md b/devops/README.md index 352ae056..4e5cfb7b 100644 --- a/devops/README.md +++ b/devops/README.md @@ -10,4 +10,65 @@ We would like to be cross-platform, and evenually support both Debian-based distros and RPM-based distros. We're not there yet either. We'd also like to support multiple cloud providers. We're not there yet either. However, we probably won't accept PRs which move us -away from this goal. \ No newline at end of file +away from this goal. + +## Create an AWS account and an EC2 instance. +* Select Ubuntu, nano AMI. The cost should be 0.5 cents per hour. +* In security groups, add HTTP, HTTPS rules. Open up only to your computer's IP address (the client). +* Launch instance. +* Suppose your instance public DNS is {ec2_ip}, e.g., ec2-18-223-122-172.us-east-2.compute.amazonaws.com. +* Create aSSH key pair, save PEM file say under ~/.ssh, chmod to u+r. + +## Set up the EC2 instance. +* SSH into the machine: `bash ssh -i {pem_file} ubuntu@{ec2_ip}`. +* (Optional) Create a user account: sudo useradd {user} +* Download the server code (same repository as the extension): +```bash +cd +git clone https://github.com/ETS-Next-Gen/writing_analysis.git writing_analysis +``` +* Install Ansible. +```bash +sudo apt-get update +sudo apt-get upgrade +sudo apt-get install ansible +```` +* Configure Ansible. +sudo pico /etc/ansible/hosts +Add +``` +[localhost] +127.0.0.1 +``` +* `cd ~/writing_analysis/configuration`. +* Run `sudo ansible-playbook local.yaml`. This may take a while on an EC2 nanon machine. +If all goes well, you should see an output with no errors, like this: +``` +bash +... +PLAY RECAP ****************************************************************************************** +127.0.0.1 : ok=5 changed=4 unreachable=0 failed=0 +``` +* Navigate to http://{ec2_ip}; you should see the message "Welcome to nginx!" if it's working. + +## Obtain a free domain name +* Go to noip.com. +* Sign up + +## Obtain a free SSL Certificate Using Certbot +* Run the following commands: +```bash +sudo apt-get install software-properties-common +Sudo add-apt-repository universe +sudo add-apt-repository ppa:certbot/certbot +sudo apt-get update +sudo apt-get install certbot python-certbot-nginx +``` +``` +bash +sudo certbot --nginx +``` +-- Put in your {mydomain}.hopto.org address. +-- Choose 1 - no redirect. + +## Stand up a backend server on the EC2 instance. diff --git a/docs/concepts/system_settings.md b/docs/concepts/system_settings.md index f1749dc4..af819193 100644 --- a/docs/concepts/system_settings.md +++ b/docs/concepts/system_settings.md @@ -34,6 +34,14 @@ logic expands directories into sorted file lists, then loads each file as a ends in `.pmss`. Any other file suffix is skipped with a warning so you can keep README files or notes alongside the rulesets without breaking startup. +This argument is intended for normal startup entry points too (for example, a +`Makefile` target or deployment startup scripts), not just ad-hoc local runs. +For example: + +```bash +python -m learning_observer.main --pmss-rulesets creds.yaml schools.pmss +``` + ## The role of `creds.yaml` Most installations load configuration from `creds.yaml`. When the process diff --git a/docs/how-to/offline_replay.md b/docs/how-to/offline_replay.md index 12c97328..aac87d96 100644 --- a/docs/how-to/offline_replay.md +++ b/docs/how-to/offline_replay.md @@ -21,15 +21,17 @@ Use the helper functions in `learning_observer/offline.py` to initialize the env ```bash python - <<'PY' import asyncio +import learning_observer.settings from learning_observer import offline -# Prepare the in-memory KVS and reducers for offline replay -offline.init('creds.yaml') - -# TODO: Offline replay currently initializes PMSS with default rulesets only. -# If you need custom `.pmss` overlays or alternate YAML files, update the -# offline initializer to accept ruleset paths. +# If you need PMSS overlays, initialize PMSS rulesets before offline.init(). +# This is equivalent to passing: +# --pmss-rulesets creds.yaml schools.pmss +# in normal server startup. +learning_observer.settings.init_pmss_settings(['creds.yaml', 'schools.pmss']) +# Then load the YAML settings used for offline replay. +offline.init('creds.yaml') # Replace this path with the study log you want to replay log_path = "/path/to/your/session.study.log" @@ -42,6 +44,16 @@ asyncio.run(main()) PY ``` +`offline.init()` currently calls: + +```python +learning_observer.settings.init_pmss_settings() +learning_observer.settings.load_settings(settings) +``` + +so when you need custom rulesets, call `init_pmss_settings([...])` first (as +shown above) and then initialize offline replay. + - `process_file` only accepts study logs ending in `.study.log` or `.study.log.gz` and will reject other log types. - If you do not provide a `userid`, a random safe username is generated to avoid inadvertently reusing PII from the log. diff --git a/modules/portfolio_diff/README.md b/modules/portfolio_diff/README.md index 0e74aea4..3f1fc230 100644 --- a/modules/portfolio_diff/README.md +++ b/modules/portfolio_diff/README.md @@ -27,11 +27,11 @@ After build completion, the static export is written to `out/`. If you need an artifact for an assets repository, archive the exported files from `out/`: ```bash -VERSION_TAG=$(date +%Y%m%d-%H%M%S) +VERSION_TAG=$(date +%Y%m%d) tar -C out -czf "portfolio_diff_${VERSION_TAG}.tar.gz" . ``` -That creates a tarball in `modules/portfolio_diff` (for example `portfolio_diff-20260710-153010.tar.gz`) containing the static site root. +That creates a tarball in `modules/portfolio_diff` (for example `portfolio_diff-20260710.tar.gz`) containing the static site root. You should include copy this to the [`lo_assets` repository](github.com/ArgLab/lo_assets) and update the `portfolio_diff-current.tar.gz` link to point to the new version. From c3ea41331cb6e6b92aa3edb7002fe2a6fce18037 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Apr 2026 09:51:16 -0400 Subject: [PATCH 2/5] updated offline docs to also include settings --- VERSION | 2 +- docs/how-to/offline_replay.md | 28 ++++++++----------- learning_observer/VERSION | 2 +- .../learning_observer/offline.py | 15 ++++++++-- 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/VERSION b/VERSION index c927f21c..c2ff7c32 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.04.23T13.37.55.571Z.6b46a00f.berickson.20260423.documentation +0.1.0+2026.04.23T13.51.16.444Z.f82c7ba3.berickson.20260423.documentation diff --git a/docs/how-to/offline_replay.md b/docs/how-to/offline_replay.md index aac87d96..50a50e27 100644 --- a/docs/how-to/offline_replay.md +++ b/docs/how-to/offline_replay.md @@ -24,14 +24,11 @@ import asyncio import learning_observer.settings from learning_observer import offline -# If you need PMSS overlays, initialize PMSS rulesets before offline.init(). -# This is equivalent to passing: -# --pmss-rulesets creds.yaml schools.pmss -# in normal server startup. -learning_observer.settings.init_pmss_settings(['creds.yaml', 'schools.pmss']) - -# Then load the YAML settings used for offline replay. -offline.init('creds.yaml') +# Provide YAML settings and optional PMSS overlays directly. +offline.init( + 'creds.yaml', + pmss_rulesets=['creds.yaml', 'schools.pmss'] +) # Replace this path with the study log you want to replay log_path = "/path/to/your/session.study.log" @@ -44,15 +41,14 @@ asyncio.run(main()) PY ``` -`offline.init()` currently calls: - -```python -learning_observer.settings.init_pmss_settings() -learning_observer.settings.load_settings(settings) -``` +`offline.init()` accepts `pmss_rulesets` and forwards them to +`learning_observer.settings.init_pmss_settings(...)` before loading settings. +This mirrors startup behavior where the web server accepts +`--pmss-rulesets creds.yaml schools.pmss`. -so when you need custom rulesets, call `init_pmss_settings([...])` first (as -shown above) and then initialize offline replay. +The offline module still does not expose a first-class CLI parser like +`learning_observer.main`; there is a TODO in `offline.py` to add an +`argparse`-based entrypoint for settings and ruleset arguments. - `process_file` only accepts study logs ending in `.study.log` or `.study.log.gz` and will reject other log types. - If you do not provide a `userid`, a random safe username is generated to avoid inadvertently reusing PII from the log. diff --git a/learning_observer/VERSION b/learning_observer/VERSION index ae344fc5..c2ff7c32 100644 --- a/learning_observer/VERSION +++ b/learning_observer/VERSION @@ -1 +1 @@ -0.1.0+2026.04.06T14.01.45.433Z.6e6d9aca.berickson.20260406.portfolio.fixes +0.1.0+2026.04.23T13.51.16.444Z.f82c7ba3.berickson.20260423.documentation diff --git a/learning_observer/learning_observer/offline.py b/learning_observer/learning_observer/offline.py index d968688d..74538b63 100644 --- a/learning_observer/learning_observer/offline.py +++ b/learning_observer/learning_observer/offline.py @@ -41,10 +41,17 @@ } -def init(settings=INTERACTIVE_SETTINGS): +def init(settings=INTERACTIVE_SETTINGS, pmss_rulesets=None): ''' Initialize the Learning Observer library. + Args: + settings (str|dict): Path to a YAML settings file or an in-memory + settings dictionary. + pmss_rulesets (list[str]|None): Optional PMSS ruleset files/directories + to initialize before loading settings. If omitted, defaults to the + standard PMSS initialization behavior. + Returns: None @@ -55,8 +62,10 @@ def init(settings=INTERACTIVE_SETTINGS): # anything before we've loaded the settings. This might not be necessary, # depending on the (still-changing) startup order learning_observer.log_event.DEBUG_LOG_LEVEL = learning_observer.log_event.LogLevel.NONE - # TODO: Allow offline callers to pass PMSS ruleset files or directories. - learning_observer.settings.init_pmss_settings() + learning_observer.settings.init_pmss_settings(pmss_rulesets) + # TODO: Add an argparse-powered CLI entrypoint (similar to main.py) so + # offline workflows can accept settings and PMSS rulesets directly from + # command-line arguments without wrapper scripts. learning_observer.settings.load_settings(settings) learning_observer.prestartup.startup_checks_and_init() learning_observer.kvs.kvs_startup_check() # Set up the KVS From ebf3778333416044e4c494725a0407ab22f80647 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Apr 2026 10:00:02 -0400 Subject: [PATCH 3/5] added lo assets documentation --- VERSION | 2 +- autodocs/reference.rst | 2 ++ docs/reference/lo_assets.md | 63 +++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 docs/reference/lo_assets.md diff --git a/VERSION b/VERSION index c2ff7c32..ffc4549b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.04.23T13.51.16.444Z.f82c7ba3.berickson.20260423.documentation +0.1.0+2026.04.23T14.00.02.181Z.c3ea4133.berickson.20260423.documentation diff --git a/autodocs/reference.rst b/autodocs/reference.rst index ba358f17..3c09777e 100644 --- a/autodocs/reference.rst +++ b/autodocs/reference.rst @@ -9,6 +9,7 @@ Detailed, structured information about APIs, configurations, and technical detai - :doc:`Linting Rules ` - Review the automated checks that keep the codebase healthy and how to run them locally. - :doc:`Testing Strategy ` - Explore the testing layers we rely on and guidelines for writing reliable tests. - :doc:`Versioning and Releases ` - See how we tag releases, manage dependencies, and maintain backward compatibility. +- :doc:`lo_assets Built Packages ` - Understand how prebuilt package artifacts are published and consumed. - :doc:`Module Reference ` - Dive into the autogenerated API reference for Python modules within Learning Observer. - :doc:`API Reference ` - Inspect the internal functionality of the system. @@ -23,5 +24,6 @@ Detailed, structured information about APIs, configurations, and technical detai docs/reference/linting.md docs/reference/testing.md docs/reference/versioning.md + docs/reference/lo_assets.md modules api diff --git a/docs/reference/lo_assets.md b/docs/reference/lo_assets.md new file mode 100644 index 00000000..fd41c840 --- /dev/null +++ b/docs/reference/lo_assets.md @@ -0,0 +1,63 @@ +# lo_assets Built Package Reference + +This document explains how Learning Observer consumes built packages from [`ArgLab/lo_assets`](https://github.com/ArgLab/lo_assets), why this exists, and current operational caveats. + +## Why `lo_assets` exists + +Some frontend-related packages require an `npm` build step before they are usable. That build tooling is not always available on deployment servers, so we publish prebuilt artifacts to `lo_assets` and fetch them during setup/startup. + +In `lo_assets`, each package typically has its own directory containing: + +- versioned archives (for example, `package-name-v1.2.3.tar.gz`) +- a `package-name-current.tar.gz` pointer file (a plain-text file containing the current archive filename) + +## Where this repository is referenced in Learning Observer + +### 1) Python package install path (`Makefile`) + +The top-level `Makefile` installs `lo_dash_react_components` by: + +1. downloading `lo_dash_react_components-current.tar.gz` from `lo_assets` +2. reading the returned filename +3. installing that resolved tarball with `pip` + +This is implemented in the `install` target and is currently specialized to LODRC. + +### 2) Runtime frontend asset fetch (`remote_assets.py`) + +`learning_observer.remote_assets.fetch_module_assets()` supports a generic pointer-file workflow for prebuilt frontend bundles (for example exported Next.js apps): + +1. read a `*-current.tar.gz` pointer file from `lo_assets` +2. resolve it to a concrete archive name +3. download and extract the archive into a target module directory + +This logic is used for modules that ship prebuilt frontend assets outside of the main repo. + +## Package-specific notes + +### LODRC (`lo_dash_react_components`) + +- This is a Python package, so downloading and reinstalling is an acceptable update path. +- Current behavior is handled by the top-level `Makefile` install process. + +### Portfolio Diff (`wo_portfolio_diff`) + +- This is a Next.js-derived frontend bundle copied into `modules/wo_portfolio_diff/wo_portfolio_diff`. +- Current fetch logic can pull the bundle when assets are missing. +- Current behavior does **not** automatically refresh when a newer remote bundle exists and local assets are already present. + +## Operational caveats + +- Whenever a new package version is published in `lo_assets`, the `*-current.tar.gz` pointer file should also be updated to point to that new archive. +- Most consuming code paths intentionally resolve the `current` pointer rather than pinning a hard-coded version. +- In some cases, users may need to remove local assets and re-run setup/startup steps to force retrieval of the latest bundle. + +## Future improvement (TODO) + +A generic Learning Observer startup check should: + +1. track the resolved remote archive version in a local state file +2. compare local version vs. current `*-current.tar.gz` pointer target +3. auto-refresh assets when the remote current version changes + +This would eliminate manual remove-and-refetch workflows and make behavior consistent across modules. From c8e7590821c8bebb640b054521a4f09a262a24a2 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Apr 2026 10:13:09 -0400 Subject: [PATCH 4/5] added module inventory list --- README.md | 1 + VERSION | 2 +- autodocs/reference.rst | 2 ++ docs/reference/module_inventory.md | 58 ++++++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 docs/reference/module_inventory.md diff --git a/README.md b/README.md index b555f227..30e6f51a 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ that the core approach and APIs are correct. We have a short guide to [installing the system](docs/tutorials/install.md). Getting the base system working is pretty easy. To create a new module for the system to use, check out our [cookiecutter module guide](docs/tutorials/cookiecutter-module.md). +For a current package/module overview, see the [module inventory](docs/reference/module_inventory.md). ### System requirements diff --git a/VERSION b/VERSION index ffc4549b..7a5dcb41 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.04.23T14.00.02.181Z.c3ea4133.berickson.20260423.documentation +0.1.0+2026.04.23T14.13.09.239Z.ebf37783.berickson.20260423.documentation diff --git a/autodocs/reference.rst b/autodocs/reference.rst index 3c09777e..7be14d6d 100644 --- a/autodocs/reference.rst +++ b/autodocs/reference.rst @@ -10,6 +10,7 @@ Detailed, structured information about APIs, configurations, and technical detai - :doc:`Testing Strategy ` - Explore the testing layers we rely on and guidelines for writing reliable tests. - :doc:`Versioning and Releases ` - See how we tag releases, manage dependencies, and maintain backward compatibility. - :doc:`lo_assets Built Packages ` - Understand how prebuilt package artifacts are published and consumed. +- :doc:`Module Inventory ` - Get a quick at-a-glance list of current monorepo modules and their roles. - :doc:`Module Reference ` - Dive into the autogenerated API reference for Python modules within Learning Observer. - :doc:`API Reference ` - Inspect the internal functionality of the system. @@ -25,5 +26,6 @@ Detailed, structured information about APIs, configurations, and technical detai docs/reference/testing.md docs/reference/versioning.md docs/reference/lo_assets.md + docs/reference/module_inventory.md modules api diff --git a/docs/reference/module_inventory.md b/docs/reference/module_inventory.md new file mode 100644 index 00000000..c52dde53 --- /dev/null +++ b/docs/reference/module_inventory.md @@ -0,0 +1,58 @@ +# Module Inventory + +This page is a quick-reference inventory of modules/packages in this monorepo: +what they are and what they do. + +## Current modules at a glance + +### Common modules + +| Module | What it does | +| --- | --- | +| `lo_dash_react_components` | Shared custom Dash UI components used across dashboards. | +| `lo_gpt` | Shared interfaces/utilities for accessing LLMs. | +| `lo_template_module` | Cookiecutter template used to scaffold new Learning Observer modules. | + +### Additional module + +| Module | What it does | +| --- | --- | +| `ccss` | Common Core State Standards-related package/resources. | + +### Writing-focused modules + +| Module | What it does | +| --- | --- | +| `portfolio_diff` | Next.js code for the portfolio dashboard UI. | +| `wo_classroom_text_highlighter` | Classroom text highlight dashboard module. | +| `wo_bulk_essay_analysis` | Classroom dashboard for bulk essay analysis (LLM-assisted workflows). | +| `wo_portfolio_diff` | Learning Observer entrypoint module for the built/deployed `portfolio_diff` dashboard. | +| `writing_observer` | Core writing-process analytics module and integrations. | + +### Unused / superseded modules + +| Module | Current state | +| --- | --- | +| `language_tool` | Standalone module is not actively used; relevant functionality now lives in `writing_observer`. | +| `websocket_debug` | Standalone module is not actively used; websocket debugging is mostly handled by scripts in `scripts/`. | + +### Prototype modules + +| Module | What it does | Current status | +| --- | --- | --- | +| `lo_action_summary` | Dashboard listing student events/actions. | Prototype. | +| `lo_lti_grade_demo` | Demo for LTI grade submission and reading flow. | Prototype/demo. | +| `lo_event` | Event-focused module (includes Next.js dashboard component work). | Moved to its own repository. | +| `toy-assess` | Initial LO Blocks implementation. | Being phased out (still used in some studies). | +| `lo_toy_sba` | Save/load state module used primarily with `toy-assess` + `lo_event`. | Prototype support module. | + +### Legacy prototype dashboards (not fully migrated) + +When the communication protocol changed to an async-generator pattern, some +prototype dashboards were not migrated. + +| Module | What it does | Note | +| --- | --- | --- | +| `wo_common_student_errors` | Aggregate classroom LanguageTool dashboard. | Legacy prototype; may be folded into newer dashboard patterns. | +| `wo_document_list` | Toy dashboard showing documents across students. | Legacy prototype. | +| `wo_highlight_dashboard` | Initial highlight dashboard implementation. | Superseded by newer highlight work. | From 93fdf5119fac73c1d93f8f4cd6c50844cc2f4dd2 Mon Sep 17 00:00:00 2001 From: Bradley Erickson Date: Thu, 23 Apr 2026 10:17:45 -0400 Subject: [PATCH 5/5] added comments to dockerfile --- Dockerfile | 9 +++++++++ VERSION | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 61cdb7d6..9c7096dc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,15 @@ FROM python:3.10 RUN git config --global --add safe.directory /app WORKDIR /app +# Future work: +# - This image currently uses an older Python base image version. +# - Update the Python version to align with current LO/WO support targets. +# - Some configurations may also require extra roster files mounted/provided +# at runtime (for example: admins.yaml or teachers.yaml). +# - Consider a layered setup: +# 1) maintain a shared "base LO" Docker image +# 2) extend it with a WO-specific image (or other module-set images) + # TODO start redis in here # see about docker loopback RUN apt-get update && \ diff --git a/VERSION b/VERSION index 7a5dcb41..9550e54b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.1.0+2026.04.23T14.13.09.239Z.ebf37783.berickson.20260423.documentation +0.1.0+2026.04.23T14.17.45.604Z.c8e75908.berickson.20260423.documentation