Skip to content

Commit

Permalink
✨ initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
MaferMazu committed Jul 29, 2024
0 parents commit 79e39a1
Show file tree
Hide file tree
Showing 14 changed files with 1,046 additions and 0 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Run tests

on:
pull_request:
branches: [master]

jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.12']
steps:
- uses: actions/checkout@v3
- 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
7 changes: 7 additions & 0 deletions .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 LICENSE.txt

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
recursive-include tutorpicasso/patches *
recursive-include tutorpicasso/templates *
28 changes: 28 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.DEFAULT_GOAL := help
.PHONY: docs
SRC_DIRS = ./tutorpicasso
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}'
25 changes: 25 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
picasso plugin for `Tutor <https://docs.tutor.edly.io>`__
#########################################################

picasso plugin for Tutor


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

.. code-block:: bash
pip install git+https://github.com/eduNEXT/tutor-contrib-picasso
Usage
*****

.. code-block:: bash
tutor plugins enable picasso
License
*******

This software is licensed under the terms of the AGPLv3.
67 changes: 67 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import io
import os

from setuptools import find_packages, setup

HERE = os.path.abspath(os.path.dirname(__file__))


def load_readme():
with io.open(os.path.join(HERE, "README.rst"), "rt", encoding="utf8") as f:
return f.read()


def load_about():
about = {}
with io.open(
os.path.join(HERE, "tutorpicasso", "__about__.py"),
"rt",
encoding="utf-8",
) as f:
exec(f.read(), about) # pylint: disable=exec-used
return about


ABOUT = load_about()


setup(
name="tutor-contrib-picasso",
version=ABOUT["__version__"],
url="https://github.com/eduNEXT/tutor-contrib-picasso",
project_urls={
"Code": "https://github.com/eduNEXT/tutor-contrib-picasso",
"Issue tracker": "https://github.com/eduNEXT/tutor-contrib-picasso/issues",
},
license="AGPLv3",
author="eduNEXT",
description="Picasso plugin for Tutor",
long_description=load_readme(),
long_description_content_type="text/x-rst",
packages=find_packages(exclude=["tests*"]),
include_package_data=True,
python_requires=">=3.8",
install_requires=["tutor>=18.0.0,<19.0.0"],
extras_require={
"dev": [
"tutor[dev]>=18.0.0,<19.0.0",
]
},
entry_points={
"tutor.plugin.v1": [
"picasso = tutorpicasso.plugin"
]
},
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.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
],
)
1 change: 1 addition & 0 deletions tutorpicasso/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "18.0.0"
Empty file added tutorpicasso/__init__.py
Empty file.
Empty file added tutorpicasso/patches/.gitignore
Empty file.
229 changes: 229 additions & 0 deletions tutorpicasso/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
from __future__ import annotations

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 'PICASSO_'.
("PICASSO_VERSION", __version__),
]
)

hooks.Filters.CONFIG_UNIQUE.add_items(
[
# Add settings that don't have a reasonable default for all users here.
# For instance: passwords, secret keys, etc.
# Each new setting is a pair: (setting_name, unique_generated_value).
# Prefix your setting names with 'PICASSO_'.
# For example:
### ("PICASSO_SECRET_KEY", "{{ 24|random_string }}"),
]
)

hooks.Filters.CONFIG_OVERRIDES.add_items(
[
# Danger zone!
# Add values to override settings from Tutor core or other plugins here.
# Each override is a pair: (setting_name, new_value). For example:
### ("PLATFORM_NAME", "My platform"),
]
)


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

# To add a custom initialization task, create a bash script template under:
# tutorpicasso/templates/picasso/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:
# tutorpicasso/templates/picasso/tasks/lms/init.sh
# And then add the line:
### ("lms", ("picasso", "tasks", "lms", "init.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("tutorpicasso")
/ 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))


########################################
# DOCKER IMAGE MANAGEMENT
########################################


# Images to be built by `tutor images build`.
# Each item is a quadruple in the form:
# ("<tutor_image_name>", ("path", "to", "build", "dir"), "<docker_image_tag>", "<build_args>")
hooks.Filters.IMAGES_BUILD.add_items(
[
# To build `myimage` with `tutor images build myimage`,
# you would add a Dockerfile to templates/picasso/build/myimage,
# and then write:
### (
### "myimage",
### ("plugins", "picasso", "build", "myimage"),
### "docker.io/myimage:{{ PICASSO_VERSION }}",
### (),
### ),
]
)


# Images to be pulled as part of `tutor images pull`.
# Each item is a pair in the form:
# ("<tutor_image_name>", "<docker_image_tag>")
hooks.Filters.IMAGES_PULL.add_items(
[
# To pull `myimage` with `tutor images pull myimage`, you would write:
### (
### "myimage",
### "docker.io/myimage:{{ PICASSO_VERSION }}",
### ),
]
)


# Images to be pushed as part of `tutor images push`.
# Each item is a pair in the form:
# ("<tutor_image_name>", "<docker_image_tag>")
hooks.Filters.IMAGES_PUSH.add_items(
[
# To push `myimage` with `tutor images push myimage`, you would write:
### (
### "myimage",
### "docker.io/myimage:{{ PICASSO_VERSION }}",
### ),
]
)


########################################
# TEMPLATE RENDERING
# (It is safe & recommended to leave
# this section as-is :)
########################################

hooks.Filters.ENV_TEMPLATE_ROOTS.add_items(
# Root paths for template files, relative to the project root.
[
str(importlib_resources.files("tutorpicasso") / "templates"),
]
)

hooks.Filters.ENV_TEMPLATE_TARGETS.add_items(
# For each pair (source_path, destination_path):
# templates at ``source_path`` (relative to your ENV_TEMPLATE_ROOTS) will be
# rendered to ``source_path/destination_path`` (relative to your Tutor environment).
# For example, ``tutorpicasso/templates/picasso/build``
# will be rendered to ``$(tutor config printroot)/env/plugins/picasso/build``.
[
("picasso/build", "plugins"),
("picasso/apps", "plugins"),
],
)


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

# For each file in tutorpicasso/patches,
# apply a patch based on the file's name and contents.
for path in glob(str(importlib_resources.files("tutorpicasso") / "patches" / "*")):
with open(path, encoding="utf-8") as patch_file:
hooks.Filters.ENV_PATCHES.add_item((os.path.basename(path), patch_file.read()))


########################################
# CUSTOM JOBS (a.k.a. "do-commands")
########################################

# A job is a set of tasks, each of which run inside a certain container.
# Jobs are invoked using the `do` command, for example: `tutor local do importdemocourse`.
# A few jobs are built in to Tutor, such as `init` and `createuser`.
# You can also add your own custom jobs:


# To add a custom job, define a Click command that returns a list of tasks,
# where each task is a pair in the form ("<service>", "<shell_command>").
# For example:
### @click.command()
### @click.option("-n", "--name", default="plugin developer")
### def say_hi(name: str) -> list[tuple[str, str]]:
### """
### An example job that just prints 'hello' from within both LMS and CMS.
### """
### return [
### ("lms", f"echo 'Hello from LMS, {name}!'"),
### ("cms", f"echo 'Hello from CMS, {name}!'"),
### ]


# Then, add the command function to CLI_DO_COMMANDS:
## hooks.Filters.CLI_DO_COMMANDS.add_item(say_hi)

# Now, you can run your job like this:
# $ tutor local do say-hi --name="eduNEXT"


#######################################
# CUSTOM CLI COMMANDS
#######################################

# Your plugin can also add custom commands directly to the Tutor CLI.
# These commands are run directly on the user's host computer
# (unlike jobs, which are run in containers).

# To define a command group for your plugin, you would define a Click
# group and then add it to CLI_COMMANDS:


### @click.group()
### def picasso() -> None:
### pass


### hooks.Filters.CLI_COMMANDS.add_item(picasso)


# Then, you would add subcommands directly to the Click group, for example:


### @picasso.command()
### def example_command() -> None:
### """
### This is helptext for an example command.
### """
### print("You've run an example command.")


# This would allow you to run:
# $ tutor picasso example-command
Empty file.
Empty file.
Empty file.

0 comments on commit 79e39a1

Please sign in to comment.