Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
cc1950c
added example module and fixed watchdog
bradley-erickson Jun 17, 2024
2a44200
missed small redis fix
bradley-erickson Jun 18, 2024
e9177c9
separate base LO and other stuff in makefile
bradley-erickson Jun 18, 2024
c83ce6d
quick update
bradley-erickson Jun 18, 2024
8bee6a7
added 3rd party items like font awesome
bradley-erickson Jun 18, 2024
3c8134d
added module settings
bradley-erickson Jun 19, 2024
0cbaa4c
added json to watchdog
bradley-erickson Jun 20, 2024
a300546
removed json that was a bad idea
bradley-erickson Jun 20, 2024
5029328
updates to docker compose
bradley-erickson Jun 20, 2024
7f3c86a
finding the right spot for install
bradley-erickson Jun 20, 2024
3e173a2
added install notes and some small docker updates
bradley-erickson Jun 21, 2024
65d79b4
added cookiecutter, removed prior example
bradley-erickson Jun 25, 2024
b5e2dac
more commands for things to change for workshop
bradley-erickson Jun 25, 2024
db32925
added fields.All after rebase and added random functionality to strea…
bradley-erickson Jun 26, 2024
ae9469f
updated docs
bradley-erickson Jun 26, 2024
36ded3c
more documentation
bradley-erickson Jun 26, 2024
5722a48
more documentation and some small notes on possible bugs
bradley-erickson Jun 28, 2024
665304f
Update workshop.md
pmitros Jun 28, 2024
cac8b51
added more documentation, still working through feedback
bradley-erickson Jun 30, 2024
d544348
small bug fixes for a smaller creds
bradley-erickson Jul 1, 2024
1805205
README merge
pmitros Jul 3, 2024
d76c499
Typo 'obbserver'
pmitros Jul 3, 2024
7a6b3bc
Fixes to docs
pmitros Jul 3, 2024
254fb9f
more documentation
bradley-erickson Jul 3, 2024
9833e85
Minor fixes
pmitros Jul 3, 2024
627d4ac
Minor fixes
pmitros Jul 3, 2024
c410eec
Minor fixes
pmitros Jul 3, 2024
8908801
Minor fixes
pmitros Jul 3, 2024
30ad4ff
WSL instructions
pmitros Jul 3, 2024
3b962ad
Minor updates
pmitros Jul 3, 2024
52ac1bc
Minor updates
pmitros Jul 3, 2024
9f4c1f1
Minor updates
pmitros Jul 3, 2024
4455cb9
Minor updates
pmitros Jul 3, 2024
37eb3a8
more doc update from meeting
bradley-erickson Jul 3, 2024
a6b3bd6
macos updates
bradley-erickson Jul 3, 2024
09b66c9
Broke virtualenv out into a file
pmitros Jul 4, 2024
b20ce82
Minor updates
pmitros Jul 4, 2024
35bf02c
Minor updates
pmitros Jul 4, 2024
8b6e5dd
Added memoization KVS
pmitros Jul 4, 2024
9feb7a1
Minor updates
pmitros Jul 4, 2024
4a39e00
Bug fixes
pmitros Jul 5, 2024
ad0e21f
Workshop keeps going
pmitros Jul 5, 2024
2ea3962
Workshop next commits
pmitros Jul 5, 2024
2645bef
Sync to laptop for travel
pmitros Jul 6, 2024
0fb62b8
Update workshop.md
pmitros Jul 6, 2024
736c40e
Update workshop.md
pmitros Jul 6, 2024
68e68b5
Update workshop.md
pmitros Jul 6, 2024
ba1efe7
Update workshop.md
pmitros Jul 6, 2024
5a6a792
lo_assess demo
pmitros Jul 10, 2024
568f7df
lo_assess
pmitros Jul 10, 2024
2060d76
Update workshop.md
pmitros Jul 11, 2024
a38d9a3
Cleanups prior to merging to master
pmitros Jul 14, 2024
c0af470
Bugfix (untested)
pmitros Jul 14, 2024
7aee9ca
Merge branch 'master' into berickson/workshop
pmitros Jul 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 12 additions & 20 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
FROM python:3.9-slim

WORKDIR /usr/src/app

RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates git gcc g++ python3-dev && \
curl -sL https://deb.nodesource.com/setup_16.x | bash - && \
apt-get install -y nodejs=16.* && \
apt-get remove --purge -y curl && \
apt-get -y autoremove && \
FROM python:3.10
RUN git config --global --add safe.directory /app
WORKDIR /app

# TODO start redis in here
# see about docker loopback
RUN apt-get update && \
apt-get install -y python3-dev && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

COPY . .
rm -rf /var/lib/apt/lists/*

RUN pip install -U pip setuptools
RUN pip install --no-cache-dir learning_observer/[wo,awe]
# HACK we need to run the install a second time to properly install lo_dash_react_components
RUN pip install learning_observer/
COPY . /app

# TODO we may want this to be a generic image that we can do a variety of things with
# For example, we may want to just run tests or deploy via this dockerfile.
# We should support both
CMD ["pytest", "modules/wo_highlight_dashboard"]
RUN make install
CMD ["make", "run"]
29 changes: 25 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ PACKAGES ?= wo,awe
run:
# If you haven't done so yet, run: make install
# we need to make sure we are on the virtual env when we do this
cd learning_observer && python learning_observer
cd learning_observer && python learning_observer --watchdog=restart

venv:
pip install --no-cache-dir -r requirements.txt
# This is unnecessary since LO installs requirements on install.
# pip install --no-cache-dir -r requirements.txt

# install commands
install: venv
# The following only works with specified packages
# we need to install learning_observer in dev mode to
# more easily pass in specific files we need, such as creds
pip install --no-cache-dir -e learning_observer/[${PACKAGES}]
# TODO resolve the lodrc-current symlink and fetch that url instead
pip install --no-cache-dir -e learning_observer/

# Installing Learning Oberser (LO) Dash React Components
# TODO properly fetch the current version of lodrc.
# We have a symbolic link between `lodrc-current` and the most
# recent version. We would like to directly fetch `lodrc-current`,
# however, the fetch only returns the name of the file it's
# linked to. We do an additional fetch for the linked file.
@LODRC_CURRENT=$$(curl -s https://raw.githubusercontent.com/ETS-Next-Gen/lo_assets/main/lo_dash_react_components/lo_dash_react_components-current.tar.gz); \
pip install https://raw.githubusercontent.com/ETS-Next-Gen/lo_assets/main/lo_dash_react_components/$${LODRC_CURRENT}

Expand All @@ -23,6 +30,20 @@ install-dev: venv
pip install --no-cache-dir -e learning_observer/[${PACKAGES}]
. ${HOME}/.nvm/nvm.sh && nvm use && pip install -v -e modules/lo_dash_react_components/

install-packages: venv
pip install -e learning_observer/[${PACKAGES}]

# Just a little bit of dependency hell...
# The AWE Components are built using a specific version of
# `spacy`. This requires an out-of-date `typing-extensions`
# package. There are few other dependecies that require a
# newer version. As far as I can tell, upgrading this package
# does not effect the functionality we receive from the AWE
# components.
# TODO remove this extra step after AWE Component's `spacy`
# is no longer version locked.
pip install -U typing-extensions

# testing commands
test:
# this is where we run doctests
Expand Down
64 changes: 17 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,33 +54,9 @@ that the core approach and APIs are correct.

## Getting Started

As an early prototype, getting started isn't seamless. Run:

```~bash
make install
```

And follow the instructions. You'll probably run into bugs. Work around the bugs. Then fix up the makefile and make a PR to address those bugs :)

Once that's done, run:

```bash
make
```

Again, fix up the makefile, and make a PR.

You can also go into the devops directory, which has scripts in
progress for spinning up a cloud instance and managing flocks of
_Learning Observer_ instances.

### Installing Writing Observer

To setup writing_observer on top of the learning observer platform you must go into modules/writing_observer and run:

```bash
python setup.py develop
```
We have a short guide to [getting started](docs/workshop.md). Getting
the base system working is pretty easy. The guide is pretty
comprehensive (including how to develop your first module).

### System requirements

Expand All @@ -92,34 +68,28 @@ pilots. These instances have 512MB of RAM, and minimal CPU. It's
important that this configuration remains usable.

For deployment and more sophisticated uses (e.g. NLP) in larger
numbers of classrooms, we expect to need **heavy** metal. As we're
playing with algorithms, deep learning is turning out to work
surprisingly well, and at the same time, requires surprisingly large
amounts of computing power. A GPGPU with plenty of RAM is helpful if
you want to work with more sophisticated algorithms, and is likely to
be a requirement for many types of uses.
numbers of classrooms, we need **heavy** metal. As we're playing with
algorithms, deep learning is turning out to work surprisingly well,
and at the same time, requires surprisingly large amounts of computing
power. A GPGPU with plenty of RAM is helpful if you want to work with
more sophisticated algorithms, and is likely to be a requirement for
many types of uses.

All _Learning Observer_ development has been on Linux-based platforms
(including Ubuntu and RHEL). There are folks outside of the core team
who have tried to run it on Mac or on WSL, with mixed success.
who have tried to run it on Mac or on WSL, with some success.

Running on RHEL requires the following services:
Running on RHEL typically uses the following services:

* REDIS
* nginx.
* redis
* nginx

#### bcrypt

A note on bcrypt. The code uses bcrypt for some internal password management.
We are not including it directly in the install because it acts oddly across
platforms so you may need to install some version manually.

#### Additional Notes

At present the system also uses static content that is served from a repo.
This allows us to actually select different sources for the static data.
This can also point to the current copy if necessary and can be configured
as part of the creds.yaml file to generate the repo or add it on startup.
A note on bcrypt. The code uses bcrypt for internal password
management. There is a mess of incompatible versions. Be careful if
installing any way other than the official install to get the right
one.

## Contributing or learning more

Expand Down
16 changes: 16 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3.8'
services:
app:
build: .
volumes:
- ./:/app
image: learning-observer-image:latest
stdin_open: true # Keep standard input open
tty: true # Allocate a pseudo-TTY
restart: always
ports:
- 8888:8888
depends_on:
- redis
redis:
image: redis:latest
182 changes: 182 additions & 0 deletions docs/dashboards.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Dashboards

We can create custom dashboards for the system.

## Dash

Dash is a package for writing and serving web applications directly in Python. In Dash, there are 2 primary items, 1) page components such as headers, divs, spans, etc. and 2) callbacks.

### Getting started

Page components can be set up similar to other `html` layouts, like so

```python
from dash import html

layout = html.Div([
html.H1(children='This is a header'),
html.Div(id='A'),
html.Div(id='B'),
html.Input(id='input')
])
# html version
# <div>
# <h1>This is a header</h1>
# <div id="A"/>
# <input id="input"/>
# </div>
```

Adding callbacks can introduce interactivity to the dashboard. Dash listens for the value of any `Input` item to change, then runs code and updates the value of the `Output` components. The updated `Output` components could be the `Input` trigger for other callbacks.

```python
from dash import callback, Output, Input

@callback(
Output('A', 'children'),
Input('input', 'value')
)
def update_output_children(value):
'''This callback will trigger whenever the contents of `input`'s `value`
property changes. It will update the `children` property of `A`.
'''
return f'The callback value is: {value}'
```

Callbacks are handled on the server, since we are running Python code. This creates an increase in the network and server resources. Instead, we can use `clientside_callbacks` to run Javascript code on the client's browser.

```python
from dash import clientside_callback, ClientsideFunction, Output, Input

# note this is no longer a decorator, Dash handles adding this code
# to the pages it serves
clientside_callback(
ClientsideFunction(namespace='my_module', function_name='updateOutputChildren')
Output('B', 'children'),
Input('input', 'value')
)
```

```javascript
// `my_module/assets/scripts.js`

// make sure `dash_clientside` is defined first
if (!window.dash_clientside) {
window.dash_clientside = {};
}

// create a dictionary of functions
window.dash_clientside.my_module = {
updateOutputChildren: function(value) {
return `The callback value is: ${value}`
}
}
```

### Dash in the Learning Observer

The `lo_dash_react_components` offers a variety of components (written in React, ported to Python). This includes a handy websocket component for connecting directly to the communication protocol. We can build components based on the information we receive from the communcation protocol. The protocol may eventually offer partial updates in the future, we so any time we get a new message, we should update a stored object. This stored object should be used to build the components.

```python
from dash import html, dcc, callback, clientside_callback, ClientsideFunction, Output, Input
import lo_dash_react_components as lodrc

layout = html.Div([
lodrc.LOConnectionStatusAIO(aio_id=_websocket),
dcc.Store(id=_websocket_storage),
html.H2('Output from reducers'),
html.Div(id=_output)
])

clientside_callback(
ClientsideFunction(namespace=_namespace, function_name='sendToLOConnection'),
Output(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'send'),
Input(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'state'), # used for initial setup
Input('_pages_location', 'hash')
)

clientside_callback(
ClientsideFunction(namespace=_namespace, function_name='receiveWSMessage'),
Output(_websocket_storage, 'data'),
Input(lodrc.LOConnectionStatusAIO.ids.websocket(_websocket), 'message'),
prevent_initial_call=True
)

@callback(
Output(_output, 'children'),
Input(_websocket_storage, 'data'),
)
def populate_output(data):
if not data:
return 'No students'
output = [html.Div([
lodrc.LONameTag(
profile=s['profile'], className='d-inline-block student-name-tag',
includeName=True, id=f'{s["user_id"]}-name-tag'
),
html.Span(f' - {s["count"]} events')
]) for s in data]
return output
```

And here are the relevant Javascript functions:

```javascript
window.dash_clientside.learning_observer_template = {
sendToLOConnection: async function (wsReadyState, urlHash) {
if (wsReadyState === undefined) {
return window.dash_clientside.no_update
}
if (wsReadyState.readyState === 1) {
// decode url parameters from hash
if (urlHash.length === 0) { return window.dash_clientside.no_update }
const decodedParams = decode_string_dict(urlHash.slice(1))
if (!decodedParams.course_id) { return window.dash_clientside.no_update }
// send our request to LO
const outgoingMessage = {
learning_observer_template_query: {
execution_dag: 'learning_observer_template',
target_exports: ['student_event_counter_export'],
kwargs: decodedParams
}
};
return JSON.stringify(outgoingMessage);
}
return window.dash_clientside.no_update;
},

receiveWSMessage: async function (incomingMessage) {
// parse incoming message
const messageData = JSON.parse(incomingMessage.data).learning_observer_template_query.student_event_counter_join_roster || [];
if (messageData.error !== undefined) {
console.error('Error received from server', messageData.error);
return [];
}
return messageData;
}
}
```

To add a dashboard to a module, add the following to the module's `module.py` file

```python
# module.py
# ...other definitions
DASH_PAGES = [
{
'MODULE': module.path.dash_dashboard,
'LAYOUT': module.path.dash_dashboard.layout,
'ASSETS': 'assets', # define where to find addtional js, css files are
'TITLE': 'My dashboard title',
'DESCRIPTION': 'My dashboard description.',
'SUBPATH': 'my-dashboard-subpath',
# additional js, css files we want to included
'CSS': [
thirdparty_url("css/fontawesome_all.css")
],
'SCRIPTS': [
static_url("liblo.js")
]
}
]
```
Loading
Loading