🌅
This commit is contained in:
commit
15518f0c59
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
.*.swp
|
||||
!.gitignore
|
||||
TODO
|
||||
__pycache__
|
||||
*.egg-info/
|
||||
/build/
|
||||
/dist/
|
||||
40
.gitlab-ci.yml
Normal file
40
.gitlab-ci.yml
Normal file
@ -0,0 +1,40 @@
|
||||
build:image:
|
||||
script:
|
||||
- apk add --no-cache docker
|
||||
- python setup.py install
|
||||
- tutor plugins enable notes
|
||||
- tutor config save
|
||||
- tutor images build notes
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
tags:
|
||||
- private
|
||||
stage: build
|
||||
|
||||
deploy:image:
|
||||
script:
|
||||
- apk add --no-cache docker
|
||||
- docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
|
||||
- python setup.py install
|
||||
- tutor plugins enable notes
|
||||
- tutor config save
|
||||
- tutor images push notes
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
tags:
|
||||
- private
|
||||
stage: deploy
|
||||
|
||||
deploy:pypi:
|
||||
script:
|
||||
- pip3 install -U setuptools twine
|
||||
- python3 setup.py sdist
|
||||
- twine upload --skip-existing dist/tutor-notes*.tar.gz
|
||||
only:
|
||||
refs:
|
||||
- master
|
||||
tags:
|
||||
- private
|
||||
stage: deploy
|
||||
2
MANIFEST.in
Normal file
2
MANIFEST.in
Normal file
@ -0,0 +1,2 @@
|
||||
recursive-include tutornotes/patches *
|
||||
recursive-include tutornotes/templates *
|
||||
37
README.rst
Normal file
37
README.rst
Normal file
@ -0,0 +1,37 @@
|
||||
Students notes plugin for `Tutor <https://docs.tutor.overhang.io>`_
|
||||
===================================================================
|
||||
|
||||
This is a plugin for `Tutor <https://docs.tutor.overhang.io>`_ to easily add the `Open edX note-taking app <https://github.com/edx/edx-notes-api>`_ to an Open edX platform. This app allows students to annotate portions of the courseware (see `the official documentation <https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/open-release-ironwood.master/exercises_tools/notes.html?highlight=notes>`_).
|
||||
|
||||
.. image:: https://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/open-release-ironwood.master/_images/SFD_SN_bodyexample.png
|
||||
:alt: Notes in action
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The plugin is currently bundled with the `binary releases of Tutor <https://github.com/overhangio/tutor/releases>`_. If you have installed Tutor from source, you will have to install this plugin from source, too::
|
||||
|
||||
pip install tutor-notes
|
||||
|
||||
Then, to enable this plugin, run::
|
||||
|
||||
tutor plugins enable notes
|
||||
|
||||
|
||||
You should beware that the ``notes.<LMS_HOST>`` domain name should exist and point to your server. For instance, if your LMS is hosted at http://myopenedx.com, the notes service should be found at http://notes.myopenedx.com.
|
||||
|
||||
If you would like to host the notes service at a different domain name, you can set the ``NOTES_HOST`` configuration variable (see below). In particular, in development you should set this configuration variable to ``notes.localhost`` in order to be able to access the notes service from the LMS. Otherwise you will get a "Sorry, we could not search the store for annotations" error.
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
- ``NOTES_MYSQL_PASSWORD`` (default: ``"{{ 8|random_string }}"``)
|
||||
- ``NOTES_SECRET_KEY`` (default: ``"{{ 24|random_string }}"``)
|
||||
- ``NOTES_OAUTH2_SECRET`` (default: ``"{{ 24|random_string }}"``)
|
||||
- ``NOTES_DOCKER_IMAGE`` (default: ``"overhangio/openedx-notes:{{ TUTOR_VERSION }}"``)
|
||||
- ``NOTES_HOST`` (default: ``"notes.{{ LMS_HOST }}"``)
|
||||
- ``NOTES_MYSQL_DATABASE`` (default: ``"notes"``)
|
||||
- ``NOTES_MYSQL_USERNAME`` (default: ``"notes"``)
|
||||
|
||||
These values can be modified with ``tutor config save --set PARAM_NAME=VALUE`` commands.
|
||||
46
setup.py
Normal file
46
setup.py
Normal file
@ -0,0 +1,46 @@
|
||||
import io
|
||||
import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
with io.open(os.path.join(here, "README.rst"), "rt", encoding="utf8") as f:
|
||||
readme = f.read()
|
||||
|
||||
about = {}
|
||||
with io.open(
|
||||
os.path.join(here, "tutornotes", "__about__.py"), "rt", encoding="utf-8"
|
||||
) as f:
|
||||
exec(f.read(), about)
|
||||
|
||||
setup(
|
||||
name="tutor-notes",
|
||||
version=about["__version__"],
|
||||
url="https://docs.tutor.overhang.io/",
|
||||
project_urls={
|
||||
"Documentation": "https://docs.tutor.overhang.io/",
|
||||
"Code": "https://github.com/overhangio/tutor/tree/master/plugins/notes",
|
||||
"Issue tracker": "https://github.com/overhangio/tutor/issues",
|
||||
"Community": "https://discuss.overhang.io",
|
||||
},
|
||||
license="AGPLv3",
|
||||
author="Overhang.io",
|
||||
author_email="contact@overhang.io",
|
||||
description="A Tutor plugin for student notes",
|
||||
long_description=readme,
|
||||
packages=find_packages(exclude=["tests*"]),
|
||||
include_package_data=True,
|
||||
python_requires=">=3.5",
|
||||
install_requires=["tutor-openedx"],
|
||||
entry_points={"tutor.plugin.v0": ["notes = tutornotes.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.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
],
|
||||
)
|
||||
1
tutornotes/__about__.py
Normal file
1
tutornotes/__about__.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = "0.1.1"
|
||||
0
tutornotes/__init__.py
Normal file
0
tutornotes/__init__.py
Normal file
1
tutornotes/patches/common-env-features
Normal file
1
tutornotes/patches/common-env-features
Normal file
@ -0,0 +1 @@
|
||||
"ENABLE_EDXNOTES": true
|
||||
1
tutornotes/patches/https-create
Normal file
1
tutornotes/patches/https-create
Normal file
@ -0,0 +1 @@
|
||||
certbot certonly --standalone -n --agree-tos -m admin@{{ LMS_HOST }} -d {{ NOTES_HOST }}
|
||||
32
tutornotes/patches/k8s-deployments
Normal file
32
tutornotes/patches/k8s-deployments
Normal file
@ -0,0 +1,32 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: notes
|
||||
labels:
|
||||
app.kubernetes.io/name: notes
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: notes
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: notes
|
||||
spec:
|
||||
containers:
|
||||
- name: notes
|
||||
image: {{ DOCKER_REGISTRY }}{{ NOTES_DOCKER_IMAGE }}
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
env:
|
||||
- name: DJANGO_SETTINGS_MODULE
|
||||
value: notesserver.settings.tutor
|
||||
volumeMounts:
|
||||
- mountPath: /openedx/edx-notes-api/notesserver/settings/tutor.py
|
||||
name: settings
|
||||
subPath: tutor.py
|
||||
volumes:
|
||||
- name: settings
|
||||
configMap:
|
||||
name: notes-settings
|
||||
18
tutornotes/patches/k8s-ingress-certificates
Normal file
18
tutornotes/patches/k8s-ingress-certificates
Normal file
@ -0,0 +1,18 @@
|
||||
---
|
||||
apiVersion: certmanager.k8s.io/v1alpha1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: {{ NOTES_HOST|replace(".", "-") }}
|
||||
spec:
|
||||
secretName: {{ NOTES_HOST }}-tls
|
||||
issuerRef:
|
||||
name: letsencrypt
|
||||
commonName: {{ NOTES_HOST }}
|
||||
dnsNames:
|
||||
- {{ NOTES_HOST }}
|
||||
acme:
|
||||
config:
|
||||
- http01:
|
||||
ingress: web
|
||||
domains:
|
||||
- {{ NOTES_HOST }}
|
||||
6
tutornotes/patches/k8s-ingress-rules
Normal file
6
tutornotes/patches/k8s-ingress-rules
Normal file
@ -0,0 +1,6 @@
|
||||
- host: {{ NOTES_HOST }}
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: nginx
|
||||
servicePort: {% if ACTIVATE_HTTPS %}443{% else %}80{% endif %}
|
||||
1
tutornotes/patches/k8s-ingress-tls-hosts
Normal file
1
tutornotes/patches/k8s-ingress-tls-hosts
Normal file
@ -0,0 +1 @@
|
||||
- {{ NOTES_HOST }}
|
||||
12
tutornotes/patches/k8s-services
Normal file
12
tutornotes/patches/k8s-services
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: notes
|
||||
spec:
|
||||
type: NodePort
|
||||
ports:
|
||||
- port: 8000
|
||||
protocol: TCP
|
||||
selector:
|
||||
app.kubernetes.io/name: notes
|
||||
3
tutornotes/patches/kustomization-configmapgenerator
Normal file
3
tutornotes/patches/kustomization-configmapgenerator
Normal file
@ -0,0 +1,3 @@
|
||||
- name: notes-settings
|
||||
files:
|
||||
- plugins/notes/apps/settings/tutor.py
|
||||
2
tutornotes/patches/lms-env
Normal file
2
tutornotes/patches/lms-env
Normal file
@ -0,0 +1,2 @@
|
||||
"EDXNOTES_PUBLIC_API": "{{ "https" if ACTIVATE_HTTPS else "http" }}://{{ NOTES_HOST }}/api/v1",
|
||||
"EDXNOTES_INTERNAL_API": "http://notes:8000/api/v1"
|
||||
11
tutornotes/patches/local-docker-compose-services
Normal file
11
tutornotes/patches/local-docker-compose-services
Normal file
@ -0,0 +1,11 @@
|
||||
############# Notes: backend store for edX Student Notes
|
||||
notes:
|
||||
image: {{ DOCKER_REGISTRY }}{{ NOTES_DOCKER_IMAGE }}
|
||||
environment:
|
||||
DJANGO_SETTINGS_MODULE: notesserver.settings.tutor
|
||||
volumes:
|
||||
- ../plugins/notes/apps/settings/tutor.py:/openedx/edx-notes-api/notesserver/settings/tutor.py
|
||||
- ../../data/notes:/openedx/data
|
||||
restart: unless-stopped
|
||||
{% if ACTIVATE_MYSQL %}depends_on:
|
||||
- mysql{% endif %}
|
||||
37
tutornotes/patches/nginx-extra
Normal file
37
tutornotes/patches/nginx-extra
Normal file
@ -0,0 +1,37 @@
|
||||
### Student notes service
|
||||
upstream notes-backend {
|
||||
server notes:8000 fail_timeout=0;
|
||||
}
|
||||
|
||||
{% if ACTIVATE_HTTPS %}
|
||||
server {
|
||||
server_name {{ NOTES_HOST }};
|
||||
listen 80;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
server {
|
||||
{% if ACTIVATE_HTTPS %}listen 443 {{ "" if WEB_PROXY else "ssl" }};{% else %}listen 80;{% endif %}
|
||||
server_name notes.localhost {{ NOTES_HOST }};
|
||||
|
||||
{% if ACTIVATE_HTTPS and not WEB_PROXY %}
|
||||
ssl_certificate /etc/letsencrypt/live/{{ NOTES_HOST }}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{ NOTES_HOST }}/privkey.pem;
|
||||
{% endif %}
|
||||
|
||||
# Disables server version feedback on pages and in headers
|
||||
server_tokens off;
|
||||
|
||||
location / {
|
||||
{% if not WEB_PROXY %}
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
{% endif %}
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
|
||||
proxy_pass http://notes-backend;
|
||||
}
|
||||
}
|
||||
27
tutornotes/patches/proxy-apache
Normal file
27
tutornotes/patches/proxy-apache
Normal file
@ -0,0 +1,27 @@
|
||||
{% if ACTIVATE_HTTPS %}
|
||||
<VirtualHost *:80>
|
||||
ServerName {{ NOTES_HOST }}
|
||||
Redirect / https://notes.{{ LMS_HOST }}/
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
ServerName {{ NOTES_HOST }}
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/letsencrypt/live/{{ NOTES_HOST }}/fullchain.pem
|
||||
SSLCertificateKeyFile /etc/letsencrypt/live/{{ NOTES_HOST }}/privkey.pem
|
||||
|
||||
ProxyPreserveHost On
|
||||
ProxyRequests On
|
||||
ProxyPass / http://localhost:{{ NGINX_HTTP_PORT }}/
|
||||
ProxyPassReverse / http://localhost:{{ NGINX_HTTP_PORT }}/
|
||||
</VirtualHost>
|
||||
{% else %}
|
||||
<VirtualHost *:80>
|
||||
ServerName {{ NOTES_HOST }}
|
||||
|
||||
ProxyPreserveHost On
|
||||
ProxyRequests On
|
||||
ProxyPass / http://localhost:{{ NGINX_HTTP_PORT }}/
|
||||
ProxyPassReverse / http://localhost:{{ NGINX_HTTP_PORT }}/
|
||||
</VirtualHost>
|
||||
{% endif %}
|
||||
33
tutornotes/patches/proxy-nginx
Normal file
33
tutornotes/patches/proxy-nginx
Normal file
@ -0,0 +1,33 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name {{ NOTES_HOST }};
|
||||
|
||||
server_tokens off;
|
||||
location / {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://localhost:{{ NGINX_HTTP_PORT }};
|
||||
}
|
||||
}
|
||||
{% if ACTIVATE_HTTPS %}
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name {{ NOTES_HOST }};
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/{{ NOTES_HOST }}/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/{{ NOTES_HOST }}/privkey.pem;
|
||||
|
||||
server_tokens off;
|
||||
location / {
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://localhost:{{ NGINX_HTTPS_PORT }};
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
41
tutornotes/plugin.py
Normal file
41
tutornotes/plugin.py
Normal file
@ -0,0 +1,41 @@
|
||||
from glob import glob
|
||||
import os
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from .__about__ import __version__
|
||||
|
||||
|
||||
config = {
|
||||
"add": {
|
||||
"MYSQL_PASSWORD": "{{ 8|random_string }}",
|
||||
"SECRET_KEY": "{{ 24|random_string }}",
|
||||
"OAUTH2_SECRET": "{{ 24|random_string }}",
|
||||
},
|
||||
"defaults": {
|
||||
"VERSION": __version__,
|
||||
"DOCKER_IMAGE": "overhangio/openedx-notes:{{ NOTES_VERSION }}",
|
||||
"HOST": "notes.{{ LMS_HOST }}",
|
||||
"MYSQL_DATABASE": "notes",
|
||||
"MYSQL_USERNAME": "notes",
|
||||
},
|
||||
}
|
||||
|
||||
templates = pkg_resources.resource_filename("tutornotes", "templates")
|
||||
hooks = {
|
||||
"init": ["mysql-client", "lms", "notes"],
|
||||
"build-image": {"notes": "{{ NOTES_DOCKER_IMAGE }}"},
|
||||
"remote-image": {"notes": "{{ NOTES_DOCKER_IMAGE }}"},
|
||||
}
|
||||
|
||||
|
||||
def patches():
|
||||
all_patches = {}
|
||||
for path in glob(
|
||||
os.path.join(pkg_resources.resource_filename("tutornotes", "patches"), "*")
|
||||
):
|
||||
with open(path) as patch_file:
|
||||
name = os.path.basename(path)
|
||||
content = patch_file.read()
|
||||
all_patches[name] = content
|
||||
return all_patches
|
||||
33
tutornotes/templates/notes/apps/settings/tutor.py
Normal file
33
tutornotes/templates/notes/apps/settings/tutor.py
Normal file
@ -0,0 +1,33 @@
|
||||
from .common import *
|
||||
|
||||
SECRET_KEY = "{{ NOTES_SECRET_KEY }}"
|
||||
ALLOWED_HOSTS = [
|
||||
"localhost",
|
||||
"notes",
|
||||
"notes.localhost",
|
||||
"{{ NOTES_HOST }}",
|
||||
]
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.mysql",
|
||||
"HOST": "{{ MYSQL_HOST }}",
|
||||
"PORT": {{MYSQL_PORT}},
|
||||
"NAME": "{{ NOTES_MYSQL_DATABASE }}",
|
||||
"USER": "{{ NOTES_MYSQL_USERNAME }}",
|
||||
"PASSWORD": "{{ NOTES_MYSQL_PASSWORD }}",
|
||||
}
|
||||
}
|
||||
|
||||
CLIENT_ID = "notes"
|
||||
CLIENT_SECRET = "{{ NOTES_OAUTH2_SECRET }}"
|
||||
|
||||
HAYSTACK_CONNECTIONS = {
|
||||
"default": {
|
||||
"ENGINE": "notesserver.highlight.ElasticsearchSearchEngine",
|
||||
"URL": "http://{{ ELASTICSEARCH_HOST }}:{{ ELASTICSEARCH_PORT }}/",
|
||||
"INDEX_NAME": "notes",
|
||||
}
|
||||
}
|
||||
|
||||
LOGGING["handlers"]["local"] = LOGGING["handlers"]["console"].copy()
|
||||
15
tutornotes/templates/notes/build/notes/Dockerfile
Normal file
15
tutornotes/templates/notes/build/notes/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM ubuntu:18.04
|
||||
MAINTAINER Overhang.io <contact@overhang.io>
|
||||
|
||||
RUN apt update && \
|
||||
apt upgrade -y && \
|
||||
apt install -y language-pack-en git python-pip libmysqlclient-dev
|
||||
|
||||
RUN mkdir /openedx
|
||||
RUN git clone https://github.com/edx/edx-notes-api --branch open-release/ironwood.2 --depth 1 /openedx/edx-notes-api
|
||||
WORKDIR /openedx/edx-notes-api
|
||||
|
||||
RUN pip install -r requirements/base.txt
|
||||
|
||||
EXPOSE 8000
|
||||
CMD gunicorn --workers=2 --name notes --bind=0.0.0.0:8000 --max-requests=1000 notesserver.wsgi:application
|
||||
18
tutornotes/templates/notes/hooks/lms/init
Normal file
18
tutornotes/templates/notes/hooks/lms/init
Normal file
@ -0,0 +1,18 @@
|
||||
export DJANGO_SETTINGS_MODULE=lms.envs.tutor.production
|
||||
|
||||
# Modify users created an incorrect email and that might clash with the newly created users
|
||||
./manage.py lms shell -c \
|
||||
"from django.contrib.auth import get_user_model;\
|
||||
get_user_model().objects.filter(username='notes').exclude(email='notes@openedx').update(email='notes@openedx')"
|
||||
|
||||
./manage.py lms manage_user notes notes@openedx --staff --superuser
|
||||
./manage.py lms create_oauth2_client \
|
||||
"http://notes:8000" \
|
||||
"http://notes:8000/complete/edx-oidc/" \
|
||||
confidential \
|
||||
--client_name edx-notes \
|
||||
--client_id notes \
|
||||
--client_secret {{ NOTES_OAUTH2_SECRET }} \
|
||||
--trusted \
|
||||
--logout_uri "http://notes:8000/logout/" \
|
||||
--username notes
|
||||
2
tutornotes/templates/notes/hooks/mysql-client/init
Normal file
2
tutornotes/templates/notes/hooks/mysql-client/init
Normal file
@ -0,0 +1,2 @@
|
||||
mysql -u root --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'CREATE DATABASE IF NOT EXISTS {{ NOTES_MYSQL_DATABASE }};'
|
||||
mysql -u root --password="{{ MYSQL_ROOT_PASSWORD }}" --host "{{ MYSQL_HOST }}" --port {{ MYSQL_PORT }} -e 'GRANT ALL ON {{ NOTES_MYSQL_DATABASE }}.* TO "{{ NOTES_MYSQL_USERNAME }}"@"%" IDENTIFIED BY "{{ NOTES_MYSQL_PASSWORD }}";'
|
||||
1
tutornotes/templates/notes/hooks/notes/init
Normal file
1
tutornotes/templates/notes/hooks/notes/init
Normal file
@ -0,0 +1 @@
|
||||
./manage.py migrate --settings=notesserver.settings.tutor
|
||||
Loading…
x
Reference in New Issue
Block a user