Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 31 additions & 0 deletions .github/workflows/ltistore-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Run ltistore tutor plugin tests

on:
pull_request:
paths:
# Trigger this workflow only if files change inside this plugin folder
- 'plugins/tutor-contrib-ltistore/**'

jobs:
test-ltistore:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.12']
defaults:
run:
# All steps will run from this directory
working-directory: plugins/tutor-contrib-ltistore
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Upgrade pip
run: python -m pip install --upgrade pip
- name: Install dependencies
run: |
pip install .[dev]
- name: Test lint, types, and format
run: make test
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Plugin Status (*Experimental*, *Production*, or *D
tutor-contrib-learner-dashboard-mfe Deprecated
tutor-contrib-test-legacy-js "Production" (Supported For Developers)
tutor-contrib-scout-apm Experimental
tutor-contrib-lti-store Experimental
=================================== ======================================================

Getting Started
Expand Down
7 changes: 7 additions & 0 deletions plugins/tutor-contrib-ltistore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.*.swp
!.gitignore
TODO
__pycache__
*.egg-info/
/build/
/dist/
662 changes: 662 additions & 0 deletions plugins/tutor-contrib-ltistore/LICENSE.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions plugins/tutor-contrib-ltistore/MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include tutor_ltistore/patches *
recursive-include tutor_ltistore/templates *
28 changes: 28 additions & 0 deletions plugins/tutor-contrib-ltistore/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.DEFAULT_GOAL := help
.PHONY: docs
SRC_DIRS = ./tutor_ltistore
BLACK_OPTS = --exclude templates ${SRC_DIRS}

# Warning: These checks are not necessarily run on every PR.
test: test-lint test-types test-format # Run some static checks.

test-format: ## Run code formatting tests
black --check --diff $(BLACK_OPTS)

test-lint: ## Run code linting tests
pylint --errors-only --enable=unused-import,unused-argument --ignore=templates --ignore=docs/_ext ${SRC_DIRS}

test-types: ## Run type checks.
mypy --exclude=templates --ignore-missing-imports --implicit-reexport --strict ${SRC_DIRS}

format: ## Format code automatically
black $(BLACK_OPTS)

isort: ## Sort imports. This target is not mandatory because the output may be incompatible with black formatting. Provided for convenience purposes.
isort --skip=templates ${SRC_DIRS}

ESCAPE = 
help: ## Print this help
@grep -E '^([a-zA-Z_-]+:.*?## .*|######* .+)$$' Makefile \
| sed 's/######* \(.*\)/@ $(ESCAPE)[1;31m\1$(ESCAPE)[0m/g' | tr '@' '\n' \
| awk 'BEGIN {FS = ":.*?## "}; {printf "\033[33m%-30s\033[0m %s\n", $$1, $$2}'
43 changes: 43 additions & 0 deletions plugins/tutor-contrib-ltistore/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
ltistore plugin for `Tutor <https://docs.tutor.edly.io>`__
##########################################################

A plugin to install and enable the openedx-ltistore for reausable lti configurations.


Installation
************

.. code-block:: bash

pip install git+https://github.com/openedx/openedx-tutor-plugins.git#subdirectory=plugins/tutor-contrib-ltistore

For development:

.. code-block:: bash

cd openedx-tutor-plugins/plugins/tutor-contrib-ltistore
pip install -e '.[dev]'

Usage
*****

.. code-block:: bash

tutor plugins enable ltistore
tutor images build openedx
tutor local do init --limit=ltistore
tutor local launch --skip-build --non-interactive

For development:

.. code-block:: bash

tutor plugins enable ltistore
tutor images build openedx-dev
tutor dev do init --limit=ltistore
tutor dev launch --skip-build --non-interactive

License
*******

This software is licensed under the terms of the AGPLv3.
60 changes: 60 additions & 0 deletions plugins/tutor-contrib-ltistore/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# https://packaging.python.org/en/latest/tutorials/packaging-projects/
# https://hatch.pypa.io/latest/config/build/

[project]
name = "tutor-contrib-ltistore"
description = "A plugin to install and enable the openedx-ltistore for reausable lti configurations."
authors = [
{ name = "Feanil Patel"},
{ email = "[email protected]" },
]
license = { text = "AGPL-3.0-only" }

readme = {file = "README.rst", content-type = "text/x-rst"}
requires-python = ">= 3.9"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Affero General Public License v3",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",

]
dependencies = [
"tutor>=19.0.0,<21.0.0",
]
optional-dependencies = { dev = ["tutor[dev]>=19.0.0,<21.0.0"] }

# These fields will be set by hatch_build.py
dynamic = ["version"]

[tool.hatch.version]
path = "tutor_ltistore/__about__.py"

# https://packaging.python.org/en/latest/specifications/well-known-project-urls/#well-known-labels
[project.urls]
Documentation = "https://github.com/openedx/openedx-tutor-plugins#readme"
Issues = "https://github.com/openedx/openedx-tutor-plugins/issues"
Source = "https://github.com/openedx/openedx-tutor-plugins"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["tutor_ltistore"]

[tool.hatch.build.targets.sdist]
# Disable strict naming, otherwise twine is not able to detect name/version
strict-naming = false
include = [ "/tutor_ltistore"]
exclude = ["tests*"]

[project.entry-points."tutor.plugin.v1"]
ltistore = "tutor_ltistore.plugin"
1 change: 1 addition & 0 deletions plugins/tutor-contrib-ltistore/tutor_ltistore/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "19.0.0"
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# Add the lti store filter to the filter list, creating the configuration if it doesn't
# exist but updating it if it does exist.

_filter_name = 'org.openedx.xblock.lti_consumer.configuration.listed.v1'

# Get or create the filter and pipeline list for the filter we care about
_filter_to_update = OPEN_EDX_FILTERS_CONFIG.get(_filter_name, {})
_pipeline_list = _filter_to_update.get('pipeline', [])

# Update the list to add our new filter.
_pipeline_list.append('lti_store.pipelines.GetLtiConfigurations')

# Replace the existing setting with the updated version from the bottom up.
# We do this because we might have created the object via the gets above. In
# which case, the parent object would not have a reference to it.
_filter_to_update['pipeline'] = _pipeline_list
# Just override this, we don't care if it's there or not.
_filter_to_update['fail_silently'] = False
OPEN_EDX_FILTERS_CONFIG[_filter_name] = _filter_to_update

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
RUN --mount=type=cache,target=/openedx/.cache/pip,sharing=shared \
pip install git+https://github.com/feanil/openedx-ltistore.git@feanil/lti1p3_fixes
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

# Add the lti store filter to the filter list, creating the configuration if it doesn't
# exist but updating it if it does exist.

_filter_name = 'org.openedx.xblock.lti_consumer.configuration.listed.v1'

# Get or create the filter and pipeline list for the filter we care about
_filter_to_update = OPEN_EDX_FILTERS_CONFIG.get(_filter_name, {})
_pipeline_list = _filter_to_update.get('pipeline', [])

# Update the list to add our new filter.
_pipeline_list.append('lti_store.pipelines.GetLtiConfigurations')

# Replace the existing setting with the updated version from the bottom up.
# We do this because we might have created the object via the gets above. In
# which case, the parent object would not have a reference to it.
_filter_to_update['pipeline'] = _pipeline_list
# Just override this, we don't care if it's there or not.
_filter_to_update['fail_silently'] = False
OPEN_EDX_FILTERS_CONFIG[_filter_name] = _filter_to_update

62 changes: 62 additions & 0 deletions plugins/tutor-contrib-ltistore/tutor_ltistore/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import os
from glob import glob

import click
import importlib_resources
from tutor import hooks

from .__about__ import __version__

########################################
# CONFIGURATION
########################################

hooks.Filters.CONFIG_DEFAULTS.add_items(
[
# Add your new settings that have default values here.
# Each new setting is a pair: (setting_name, default_value).
# Prefix your setting names with 'LTISTORE_'.
("LTISTORE_VERSION", __version__),
]
)

########################################
# INITIALIZATION TASKS
########################################

# To add a custom initialization task, create a bash script template under:
# tutor_ltistore/templates/ltistore/tasks/
# and then add it to the MY_INIT_TASKS list. Each task is in the format:
# ("<service>", ("<path>", "<to>", "<script>", "<template>"))
MY_INIT_TASKS: list[tuple[str, tuple[str, ...]]] = [
# For example, to add LMS initialization steps, you could add the script template at:
# tutor_ltistore/templates/ltistore/tasks/lms/init.sh
# And then add the line:
("lms", ("ltistore", "tasks", "lms", "enable_lti_store.sh")),
]


# For each task added to MY_INIT_TASKS, we load the task template
# and add it to the CLI_DO_INIT_TASKS filter, which tells Tutor to
# run it as part of the `init` job.
for service, template_path in MY_INIT_TASKS:
full_path: str = str(
importlib_resources.files("tutor_ltistore")
/ os.path.join("templates", *template_path)
)
with open(full_path, encoding="utf-8") as init_task_file:
init_task: str = init_task_file.read()
hooks.Filters.CLI_DO_INIT_TASKS.add_item((service, init_task))


########################################
# PATCH LOADING
# (It is safe & recommended to leave
# this section as-is :)
########################################

# For each file in tutor_ltistore/patches,
# apply a patch based on the file's name and contents.
for path in glob(str(importlib_resources.files("tutor_ltistore") / "patches" / "*")):
with open(path, encoding="utf-8") as patch_file:
hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read()))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Set the lti_consumer.enable_external_config_filter waffle flag
./manage.py lms waffle_flag --create --everyone lti_consumer.enable_external_config_filter

# Run migrations for lti_store
./manage.py lms migrate lti_store