Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process all courses' assignments; misc. other cleaning up (iss. #6, #7, #8, #10) #11

Merged
merged 56 commits into from
May 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d9c5d6e
#7 - upgrade Django; specify other module versions
lsloan Feb 9, 2023
e5448bf
#8 - keep container alive for debugging
lsloan Feb 10, 2023
a78268b
#10 - rename `CANVAS_API_URL` & remove U-M `canvas-test` default value
lsloan Feb 10, 2023
a9d5170
#10 - reformat code
lsloan Feb 10, 2023
f71f136
#6 - phasing out assignment ID config
lsloan Mar 24, 2023
6cb8c6e
#6 - debugging util: Canvas data to JSON
lsloan Mar 24, 2023
c4554fe
#6 - phasing out assignment ID config
lsloan Mar 24, 2023
2ba99f5
#6 - handle assessment missing assessor/submission
lsloan Mar 24, 2023
6573671
#6 - include test_student among users
lsloan Mar 24, 2023
b50558f
#6 - don't skip untyped submissions
lsloan Mar 24, 2023
51bdcf9
#6 - phasing out assignment ID config
lsloan Mar 24, 2023
94116bb
#6 - handle problems saving assessments
lsloan Mar 24, 2023
27ea688
#6 - remove skip for non-peer-reviews
lsloan Mar 27, 2023
178b0e4
#6 - new module for config-like code
lsloan Mar 28, 2023
088f350
#6 - new module for config-like code
lsloan Mar 28, 2023
bdb2b1b
#6 - additional logging
lsloan Mar 28, 2023
2ee46b1
#6 - use CSV list of course IDs
lsloan Mar 28, 2023
fe98342
#6 - iterate over all assignments in each course
lsloan Mar 28, 2023
87c644d
#6 - remove unneeded comments & code; reformatting
lsloan Mar 29, 2023
fc515f1
#6 - remove unneeded comments & code; reformatting
lsloan Mar 29, 2023
ea7e9f6
#6 - add DB model migrations
lsloan Mar 29, 2023
99fc0c9
#6 - update and reformat documentation
lsloan Mar 29, 2023
265de21
#6 - placeholder text, for giggles
lsloan Mar 29, 2023
e04b543
#6 - remove unneeded comments & code; reformatting
lsloan Mar 29, 2023
7c76570
#6 - restore command to copy source to container
lsloan Mar 31, 2023
25e4b44
Fix low-hanging fruit of typing and import errors
ssciolla Apr 6, 2023
84504bf
Fix db_table class imports
ssciolla Apr 6, 2023
2edf11d
#10 - move course ID CSV parsing to config
lsloan Apr 20, 2023
1831a3d
#10 - reformatting
lsloan Apr 20, 2023
2eee2c2
#10 - tip for a faster build
lsloan Apr 20, 2023
4dbf85a
#10 - logger no longer needed
lsloan Apr 20, 2023
5861955
#10 - clean up `canvasData` and other imports
lsloan Apr 20, 2023
8f5333a
Merge branch '6-etc-all-courses-assignments' into issue-6-type-import…
lsloan Apr 20, 2023
5a7b803
Merge pull request #1 from tl-its-umich-edu/issue-6-type-import-fixes
lsloan Apr 20, 2023
0df239a
#10 - resolve model table naming problem
lsloan Apr 20, 2023
cfd5a2e
#10 - typing needs to refer to `List`, not `list`
lsloan Apr 20, 2023
62af675
#10 - refactor and reduce logging level
lsloan Apr 21, 2023
b6c6c30
#10 - replace `Exception` with specific types
lsloan Apr 21, 2023
624be57
#10 - clean up imports
lsloan Apr 22, 2023
61b4f06
#10 - reformatted
lsloan Apr 22, 2023
231211c
#10 - support for Python style checking
lsloan Apr 22, 2023
ecaea7a
#10 - ignore another unreasonable style warning
lsloan Apr 22, 2023
e57dcd6
#10 - code style cleanup
lsloan Apr 22, 2023
e5d5f9f
#10 - reformatting
lsloan Apr 22, 2023
2a5913f
#8 - simplify logic of debugging utility functions
lsloan Apr 24, 2023
0b81702
#10 - improve config and Python style
lsloan Apr 24, 2023
6303004
#6 - skip non-number course IDs from config
lsloan Apr 24, 2023
4c3d14c
#6 - handle missing courses, no-rubric assignments
lsloan Apr 24, 2023
7b2895f
#10 - attempt to set up Python style check action
lsloan Apr 24, 2023
a132a38
`typing.Optional` → `| None`
lsloan May 3, 2023
2f0dc10
#10 - typing changes and related support
lsloan May 3, 2023
94597e0
#10 - log to stdout to allow redirection
lsloan May 3, 2023
64c0297
#10 - install mypy
lsloan May 4, 2023
9c1a794
#10 - clean-up: no-return, fix-continue, hasattr
lsloan May 4, 2023
5ecc8b3
#10 - clean-up: remove unnecessary exception catch
lsloan May 4, 2023
c814520
#10 - clean-up: remove exception handler & return
lsloan May 4, 2023
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
27 changes: 27 additions & 0 deletions .github/workflows/pycodestyle.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: peer-review-data

on: [ push, pull_request ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ 3.11 ]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements_dev.txt ]; then pip install -r requirements_dev.txt; fi
python -m pip install .
lsloan marked this conversation as resolved.
Show resolved Hide resolved
- name: Python Style Checker
uses: andymckay/[email protected]
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ WORKDIR /app
COPY requirements.txt .
RUN pip install -r /app/requirements.txt

# With `docker compose`, dev. directory already mounted at /app
#COPY . .
# With `docker compose`, dev. directory already mounted at /app.
# In dev., the `COPY` line may be commented out to build faster.
COPY . .

CMD ["./start.sh"]
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# peer-review-data

Extract LMS peer review data for analysis by external applications.

## Running
Expand All @@ -7,27 +8,47 @@ Extract LMS peer review data for analysis by external applications.

Use Docker compose to run in a local development environment.

1. Copy `config/.env.sample` to `.env` in the root directory of the project. Edit the file to replace the values contained therein with valid ones for the test environment. Refer to comments in the file as guides to the appropriate values.
1. Copy `config/.env.sample` to `.env` in the root directory of the project.
Edit the file to replace the values contained therein with valid ones for
the test environment. Refer to comments in the file as guides to the
appropriate values.

```sh
cp config/.env.sample .env
vim .env
```

1. Use Docker's `compose` tool to build the application, run it, and produce an output file.
2. Use Docker's `compose` tool to run the application after building it. The
application will query the Canvas course(s) specified in `.env` and save
data from all of the peer-reviewed assignments to the database.

```sh
docker compose up --build
```

1. Examine the data in the output file, `assessments.json`.
3. Examine the data in the database, referring to the model diagram below. To
connect to the database, make a MySQL or MariaDB connection with the
following parameter values, most of which come from `docker-compose.yaml`:
* Host: `localhost` (because Docker is running on the local computer)
* Port: `5555` (via `.services.db.ports`)
* Database: `peer-review-data`
* Username: `peer-review-data`
* Password: `peer-review-data_pw`

## Resources

### Database Model Diagram

![](dataModel.png)

### Canvas API functions

1. `/api/v1/courses/<course_id>` — canvasapi.canvas.Canvas.get_course()
2. `/api/v1/courses/<course_id>/assignments/<assignment_id>` — canvasapi.course.Course.get_assignment()
3. `/api/v1/courses/<course_id>/rubrics/<rubric_id>` — canvasapi.course.Course.get_rubric()
4. `/api/v1/courses/<course_id>/assignments/<assignment_id>/submissions` — canvasapi.assignment.Assignment.get_submissions()
5. `/api/v1/courses/<course_id>/search_users` — canvasapi.course.Course.get_users()
2. `/api/v1/courses/<course_id>/assignments/<assignment_id>` —
canvasapi.course.Course.get_assignment()
3. `/api/v1/courses/<course_id>/rubrics/<rubric_id>` —
canvasapi.course.Course.get_rubric()
4. `/api/v1/courses/<course_id>/assignments/<assignment_id>/submissions` —
canvasapi.assignment.Assignment.get_submissions()
5. `/api/v1/courses/<course_id>/search_users` —
canvasapi.course.Course.get_users()
31 changes: 2 additions & 29 deletions canvasData.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# -*- coding: utf-8 -*-
import os
import sys
from logging import Logger, getLogger
from typing import List

from canvasapi import Canvas
Expand All @@ -11,33 +8,9 @@
from canvasapi.submission import Submission
from canvasapi.user import User

# from canvasapi.canvas_object import CanvasObject
import config

LOGGER: Logger = getLogger(__name__)

CANVAS_API_URL: str = os.getenv('CANVAS_API_URL',
'https://canvas-test.it.umich.edu/')
CANVAS_API_TOKEN: str = os.getenv('CANVAS_API_TOKEN')
COURSE_ID: str = os.getenv('COURSE_ID')
ASSIGNMENT_ID: str = os.getenv('ASSIGNMENT_ID')

envErrors = []

if CANVAS_API_TOKEN is None:
envErrors.append('CANVAS_API_TOKEN')

if COURSE_ID is None:
envErrors.append('COURSE_ID')

if ASSIGNMENT_ID is None:
envErrors.append('ASSIGNMENT_ID')

if len(envErrors) > 0:
LOGGER.critical('The following environment variable(s) are not set: '
f'{", ".join(envErrors)}')
sys.exit()

canvas = Canvas(CANVAS_API_URL, CANVAS_API_TOKEN)
canvas = Canvas(config.CANVAS_BASE_URL, config.CANVAS_API_TOKEN)


class CanvasUser(User):
Expand Down
40 changes: 40 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
import os
from logging import Logger, getLogger

import sys

LOGGER: Logger = getLogger(__name__)

CANVAS_BASE_URL: str | None = os.getenv('CANVAS_BASE_URL')
CANVAS_API_TOKEN: str | None = os.getenv('CANVAS_API_TOKEN')
COURSE_IDS_CSV: str | None = os.getenv('COURSE_IDS_CSV')
COURSE_IDS: list[str] | None = [
c.strip() for c in COURSE_IDS_CSV.split(',') if c.isdigit()
] if COURSE_IDS_CSV else None


def checkConfig():
envErrors = []

if CANVAS_BASE_URL is None:
envErrors.append('CANVAS_BASE_URL')

if CANVAS_API_TOKEN is None:
envErrors.append('CANVAS_API_TOKEN')

if COURSE_IDS_CSV is None:
envErrors.append('COURSE_IDS_CSV')

if len(envErrors) > 0:
LOGGER.critical('The following environment variable(s) are not set: '
f'{", ".join(envErrors)}')
sys.exit()

if COURSE_IDS is None:
LOGGER.critical('COURSE_IDS could not be set. '
'(Problem parsing COURSE_IDS_CSV?)')
sys.exit()


checkConfig()
22 changes: 12 additions & 10 deletions config/.env.sample
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
# DOCKER_COMPOSE: any - Setting a value indicates running via `docker compose`
DOCKER_COMPOSE=TRUE

# DOCKER_KEEP_ALIVE_TIME: any - Keep container running after the app exits.
# Useful for debugging. Time value may use the following optional suffixes…
# "s": seconds (the default), "m": minutes, "h": hours, or "d": days.
DOCKER_KEEP_ALIVE_TIME=''
lsloan marked this conversation as resolved.
Show resolved Hide resolved

# LOG_LEVEL: any - A string or number indicating the log level. If not set,
# the default value is "INFO". Note: String values are upper-cased before use.
# NB: The "DEBUG" level turns on Django framework debugging, which is very,
# very verbose.
# TODO: Implement another log level setting for the application only.
LOG_LEVEL=INFO

# CANVAS_API_URL: url - URL of the Canvas instance's API
# This is the BASE URL of the Canvas instance. The "canvasapi" module this
# app uses will add the necessary path elements.
# TODO: Rename to `CANVAS_BASE_URL`.
CANVAS_API_URL=https://canvas.example.edu/
# CANVAS_BASE_URL: URL - URL of the Canvas instance's API
# This is the BASE URL of the Canvas instance. The "canvasapi" module used
# by this app will add the necessary path elements.
CANVAS_BASE_URL=https://canvas.example.edu/

# CANVAS_API_TOKEN: str - Token for Canvas API access
CANVAS_API_TOKEN=CANVAS_API_TOKEN_VALUE_HERE

# COURSE_ID: int - ID number of the Canvas course containing the assignment
COURSE_ID=1234567890

# ASSIGNMENT_ID: int - ID number of the Canvas assignment
ASSIGNMENT_ID=1234567890
# COURSE_IDS_CSV: array[str] - ID numbers of Canvas courses to look for
# assignments. Use simple CSV format.
COURSE_IDS_CSV=123,456,7890

# DB_HOST: str - Hostname or docker compose label of DB server
DB_HOST=peer-review-data_db
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ services:
- ./.data/mysql:/var/lib/mysql:delegated
- ./mysql:/docker-entrypoint-initdb.d:ro
container_name: peer-review-data_db

main:
build:
context: .
Expand Down
4 changes: 4 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[mypy]
python_version = 3.11
warn_return_any = True
warn_unused_configs = True
Loading