Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • AlekSIS/official/AlekSIS-App-Resint
  • sunweaver/AlekSIS-App-Resint
2 results
Show changes
Showing
with 573 additions and 3982 deletions
......@@ -23,7 +23,6 @@ urlpatterns = [
path("upload/", PosterUploadView.as_view(), name="poster_upload"),
path("<int:pk>/edit/", PosterEditView.as_view(), name="poster_edit"),
path("<int:pk>/delete/", PosterDeleteView.as_view(), name="poster_delete"),
path("<str:slug>.pdf", PosterCurrentView.as_view(), name="poster_show_current"),
path("groups/", PosterGroupListView.as_view(), name="poster_group_list"),
path("groups/create/", PosterGroupCreateView.as_view(), name="create_poster_group"),
path("groups/<int:pk>/edit/", PosterGroupEditView.as_view(), name="edit_poster_group"),
......@@ -34,14 +33,24 @@ urlpatterns = [
LiveDocumentCreateView.as_view(),
name="create_live_document",
),
path("live/<int:pk>/edit/", LiveDocumentEditView.as_view(), name="edit_live_document",),
path(
"live/<int:pk>/edit/",
LiveDocumentEditView.as_view(),
name="edit_live_document",
),
path(
"live_documents/<int:pk>/delete/",
LiveDocumentDeleteView.as_view(),
name="delete_live_document",
),
]
api_urlpatterns = [
path("<str:slug>.pdf", PosterCurrentView.as_view(), name="poster_show_current"),
path(
"live_documents/<str:slug>.pdf", LiveDocumentShowView.as_view(), name="show_live_document",
"live_documents/<str:slug>.pdf",
LiveDocumentShowView.as_view(),
name="show_live_document",
),
path(
"api/live_documents/<str:slug>.pdf",
......
from typing import Any, Dict, List, Type
from typing import Any
from django.contrib.contenttypes.models import ContentType
from django.db.models import QuerySet
......@@ -92,14 +92,14 @@ class PosterListView(PermissionRequiredMixin, ListView):
posters = get_objects_for_user(self.request.user, "resint.view_poster", qs)
return qs.filter(group__in=allowed_groups) | posters
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context["poster_groups"] = PosterGroup.objects.all().order_by("name")
return context
class RequestMixin:
def get_form_kwargs(self) -> Dict[str, Any]:
def get_form_kwargs(self) -> dict[str, Any]:
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
......@@ -160,7 +160,7 @@ class LiveDocumentListView(PermissionRequiredMixin, SingleTableView):
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context["widget_types"] = [
context["document_types"] = [
(ContentType.objects.get_for_model(m, False), m) for m in LiveDocument.__subclasses__()
]
return context
......@@ -201,7 +201,7 @@ class LiveDocumentCreateView(PermissionRequiredMixin, AdvancedCreateView):
class LiveDocumentEditView(PermissionRequiredMixin, AdvancedEditView):
"""Edit view for live documents."""
def get_form_class(self) -> Type[BaseModelForm]:
def get_form_class(self) -> type[BaseModelForm]:
return modelform_factory(self.object.__class__, fields=self.fields)
model = LiveDocument
......@@ -247,6 +247,6 @@ class LiveDocumentShowAPIView(
):
"""Show the current version of the live document in API."""
def get_scopes(self, *args, **kwargs) -> List[str]:
def get_scopes(self, *args, **kwargs) -> list[str]:
"""Return the scope needed to access the PDF file."""
return [self.get_object().scope]
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
docs/_static/create_live_document.png

151 KiB

docs/_static/create_poster_group.png

161 KiB

docs/_static/manage_posters_list.png

176 KiB

docs/_static/upload_poster.png

135 KiB

Providing time-based documents with Resint
==========================================
.. toctree::
:glob:
*
Uploadable posters for time-based documents
===========================================
Posters are documents that can be manually supplied in a
time-based manner. Example use cases are cantine menus,
that are provided weekly by an external supplier, who
will only get privileges to upload this poster as a PDF
file.
Poster files can be uploaded for a defined time period,
and AlekSIS will then deliver the currently valid version
under a stable menu item and URL.
Defining poster groups
----------------------
Uploadable posters are categorised into poster groups, with
each poster group representing one time-based document that
AlekSIS will provide. Each poster group has:
* A name
* A URL slug making up the final, stable URL
* A schedule defining the cycle for providing the document
* A default PDF file for periods for which no document is uploaded
Permissions to upload documents are managed per poster group
as well.
Creating poster groups
~~~~~~~~~~~~~~~~~~~~~~
Poster groups are created from the menu under the `Documents → Poster groups`
menu item. After clicking the `Create new poster group` button or the `Edit`
action for any poster group, the following form allows editing the details
of the poster group.
.. image:: ../_static/create_poster_group.png
:width: 600
:alt: Create or edit a poster group
In addition to the properties explained above, the form provides two options
to configure if and how the document will be presented to users. It is possible
to include or exclude the document from the main menu, and to allow access
by anonymous users. Documents that are hidden from the menu can be used if
they should only be accessible through links from external sites, or
loaded by a digital signage system.
The stable URL for the poster group can be copied from the link
in the `Filename` column of the `Poster groups` list.
Configuring live documents
==========================
Live documents are another type of time-based documents.
In contrast to posters, they are not uplaoded manually,
but automatically updated by some trigger.
Types of live documents are provided by other AlekSIS apps.
All live document types provided by apps can then be configured
from the `Documents` menu.
An example for a live document is an automatically generated
substitution plan, which can be linked to from a website,
or displayed on digital signage.
.. image:: ../_static/create_live_document.png
:width: 600
:alt: Create live document
All fields except the slug are provided by the app.
The slug again makes up the stable URL the live document
will be provided under, which can be copied from the live document
list.
The app providing a live document type will take care of
updating the document. A live substitution plan might thus be
re-generated whenever timetable or substitution data changes.
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
import os
import sys
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import django
sys.path.insert(0, os.path.abspath(".."))
os.environ["DJANGO_SETTINGS_MODULE"] = "aleksis.core.settings"
os.environ["LOCAL_SETTINGS_FILE"] = os.path.abspath(os.path.join("..", "local.cfg"))
django.setup()
# -- Project information -----------------------------------------------------
project = "AlekSIS-App-Resint"
copyright = "2018-2022 The AlekSIS team"
author = "The AlekSIS Team"
# The short X.Y version
version = "4.0"
# The full version, including alpha/beta/rc tags
release = "4.0.0.dev0"
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinxcontrib_django",
"sphinx_autodoc_typehints",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = ".rst"
# The master toctree document.
master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "sphinx_material"
html_favicon = "../aleksis/core/static/img/aleksis-icon.png"
html_logo = "../aleksis/core/static/img/aleksis-banner.svg"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
"repo_url": f"https://edugit.org/AlekSIS/official/{project}",
"repo_name": "EduGit",
"repo_type": "gitlab",
"theme_color": "#0d5eaf",
"color_primary": "#0d5eaf",
"color_accent": "#0d5eaf",
"globaltoc_depth": 2,
"globaltoc_collapse": False,
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
html_sidebars = {
"**": ["logo-text.html", "globaltoc.html", "localtoc.html", "searchbox.html"]
}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = f"{project}doc"
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, f"{project}.tex", f"{project} Documentation", author, "manual"),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "aleksis", f"{project} Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
project,
f"{project} Documentation",
author,
project,
"One line description of project.",
"Miscellaneous",
),
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ["search.html"]
# -- Extension configuration -------------------------------------------------
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"https://docs.python.org/": None,
"https://docs.djangoproject.com/en/stable": "https://docs.djangoproject.com/en/stable/_objects",
}
Providing auto-generated documents from apps
============================================
.. toctree::
:glob:
*
Providing live document types from other apps
=============================================
AlekSIS apps can provide live document types that
are then managed by Resint. Live document types are
Django models sub-classing the ``LiveDocument`` model,
and providing fields and some methods that define how
the live document is generated.
The following stripped-down example shows how to
provide a live document type.
.. code-block:: python
from aleksis.apps.resint.models import LiveDocument
class AutomaticPlan(LiveDocument):
# Template name to render
template = "my_app/pdf_template.html"
# A field to be rendered in the edit form
number_of_days = models.PositiveIntegerField(
default=1,
)
def get_context_data(self) -> Dict[str, Any]:
"""Get context data to pass to the PDF template."""
context = {}
# Do something ehre to construct the context data
return context
This basic example generates a PDF by defining an HTML template
and overriding the ``get_context_data`` method. This method has
to return a context dictionary, which will then be passed to
the template.
If you need more control over how the PDF is generated, you
can instead override the ``update`` method:
.. code-block:: python
class AutomaticPlan(LiveDocument):
def update(self, triggered_manually: bool = True):
"""Re-generate PDF for this live document."""
# Do whatever is necessary to get file contents
self.current_file.save(self.filename, content)
You need to ensure that ``update()`` is called whenever you
need to provide a new version of your document. One possibility
is to listen to some relevant DJango signal, then call ``update()``
if necessary.
.. AlekSIS documentation master file, created by
sphinx-quickstart on Thu Aug 15 10:49:03 2019.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to AlekSIS-App-Resint's documentation!
==============================================
.. toctree::
:maxdepth: 2
:caption: Contents:
user/00_index
admin/00_index
dev/00_index
ref/00_index
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
Time-based documents with Resint
================================
.. toctree::
:glob:
*
Publishing posters
==================
Posters are documents that can be manually supplied in a
time-based manner.
Poster files can be uploaded for a defined time period,
and AlekSIS will then deliver the currently valid version
under a stable menu item and URL.
Posters are organised in psoter groups, which can be
created by administrators. Users can then upload PDF files
which shall be published for each time period.
Users who have the permission to upload posters for at
least one poster group can manager their psoters from the
main menu under the `Documents → Manage posters` menu item.
.. image:: ../_static/manage_posters_list.png
:width: 600
:alt: Manage posters
Using the `Upload poster` button or the `Edit` action for
an already uploaded poster will open the dialog for manaing
a document for one time period.
.. image:: ../_static/upload_poster.png
:width: 600
:alt: Upload poster
After uploading, AlekSIS will deliver the uploaded file
during the week selected in the form.
This diff is collapsed.
[tool.poetry]
name = "AlekSIS-App-Resint"
version = "2.0b0"
version = "4.0.0.dev2"
packages = [
{ include = "aleksis" }
]
readme = "README.rst"
include = ["CHANGELOG.rst", "LICENCE.rst", "aleksis/**/*.mo"]
include = [
{ path = "aleksis/**/*.mo", format = ["sdist", "wheel"] },
{ path = "*.rst", format = "sdist" },
{ path = "docs/*", format = "sdist" },
{ path = "docs/**/*", format = "sdist" },
{ path = "conftest.py", format = "sdist" },
{ path = "tox.ini", format = "sdist" }
]
description = "AlekSIS (School Information System) — App Resint (Public poster)"
authors = [
......@@ -13,39 +20,73 @@ authors = [
"Frank Poetzsch-Heffter <p-h@katharineum.de>",
"Jonathan Weth <dev@jonathanweth.de>"
]
maintainers = [
"Jonathan Weth <dev@jonathanweth.de>",
"Dominik George <dominik.george@teckids.org>"
]
maintainers = ["Jonathan Weth <jonathan.weth@teckids.org>", "Dominik George <dominik.george@teckids.org>"]
license = "EUPL-1.2-or-later"
homepage = "https://aleksis.org/"
homepage = "https://aleksis.org"
repository = "https://edugit.org/AlekSIS/official/AlekSIS-App-Resint"
classifiers = [
"Development Status :: 4 - Beta",
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Intended Audience :: Education",
"Topic :: Education"
]
[[tool.poetry.source]]
name = "PyPI"
priority = "primary"
[[tool.poetry.source]]
name = "gitlab"
url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
secondary = true
priority = "supplemental"
[tool.poetry.dependencies]
python = "^3.9"
AlekSIS-Core = "^2.1"
[tool.poetry.dev-dependencies]
aleksis-builddeps = "*"
python = "^3.10"
AlekSIS-Core = "^4.0.0.dev11"
[tool.poetry.plugins."aleksis.app"]
resint = "aleksis.apps.resint.apps:ResintConfig"
[tool.black]
[tool.poetry.group.dev.dependencies]
django-stubs = "^4.2"
safety = "^2.3.5"
ruff = "^0.8.2"
[tool.poetry.group.test.dependencies]
pytest = "^8.3"
pytest-django = "^4.9"
pytest-django-testing-postgresql = "^0.2"
pytest-cov = "^6.0.0"
pytest-sugar = "^1.0.0"
selenium = "^4.27.0"
freezegun = "^1.5.0"
[tool.poetry.group.docs]
optional = true
[tool.poetry.group.docs.dependencies]
sphinx = "^7.0"
sphinxcontrib-django = "^2.3.0"
sphinxcontrib-svg2pdfconverter = "^1.1.1"
sphinx-autodoc-typehints = "^1.7"
sphinx_material = "^0.0.35"
[tool.ruff]
exclude = ["migrations"]
line-length = 100
exclude = "/migrations/"
[tool.ruff.lint]
select = ["E", "F", "UP", "B", "SIM", "I", "DJ", "A", "S"]
ignore = ["UP034", "UP015", "B028"]
[tool.ruff.lint.extend-per-file-ignores]
"**/*/tests/**/*.py" = ["S101", "ARG", "FBT", "PLR2004", "S311", "S105"]
[tool.ruff.lint.isort]
known-first-party = ["aleksis"]
section-order = ["future", "standard-library", "django", "third-party", "first-party", "local-folder"]
[tool.ruff.lint.isort.sections]
django = ["django"]
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
[tox]
skipsdist = True
skip_missing_interpreters = true
envlist = py37,py38,py39
envlist = py310,py311,py312
[testenv]
whitelist_externals = poetry
sudo
allowlist_externals =
poetry
yarnpkg
skip_install = true
envdir = {toxworkdir}/globalenv
commands_pre =
poetry install
poetry run aleksis-admin yarn install
poetry install --all-extras
poetry run aleksis-admin vite build
poetry run aleksis-admin collectstatic --no-input
commands =
poetry run pytest --cov=. {posargs} aleksis/
......@@ -23,12 +23,18 @@ setenv =
TEST_HOST = {env:TEST_HOST:172.17.0.1}
[testenv:lint]
commands_pre =
poetry install
yarnpkg --cwd=.dev-js
commands =
poetry run black --check --diff aleksis/
poetry run isort -c --diff --stdout aleksis/
poetry run flake8 {posargs} aleksis/
poetry run ruff check {posargs} aleksis/
yarnpkg --cwd=.dev-js run prettier --ignore-path={toxinidir}/.prettierignore {posargs} --check ..
poetry run aleksis-admin graphql_schema --schema aleksis.core.schema.schema --out .dev-js/schema.json
yarnpkg --cwd=.dev-js run eslint ../aleksis/**/*/frontend/**/*.{js,vue,graphql} --config={toxinidir}/.dev-js/.eslintrc.js
[testenv:security]
commands_pre =
poetry install --all-extras
commands =
poetry show --no-dev
poetry run safety check --full-report
......@@ -37,46 +43,29 @@ commands =
commands_pre =
poetry install
poetry run sh -c "cd aleksis; aleksis-admin compilemessages"
poetry run aleksis-admin yarn install
poetry run aleksis-admin compile_scss
commands = poetry build
[testenv:docs]
commands_pre =
poetry install --with docs
commands = poetry run make -C docs/ html {posargs}
[testenv:reformat]
commands_pre =
poetry install --only=dev
yarnpkg --cwd=.dev-js
commands =
poetry run isort aleksis/
poetry run black aleksis/
poetry run ruff format aleksis/
yarnpkg --cwd=.dev-js run prettier --ignore-path={toxinidir}/.prettierignore --write ..
[testenv:makemessages]
commands_pre =
poetry install
commands =
poetry run aleksis-admin makemessages --no-wrap -e html,txt,py,email -i static -l ar -l de_DE -l fr -l nb_NO -l tr_TR -l la
poetry run aleksis-admin makemessages --no-wrap -d djangojs -i **/node_modules -l ar -l de_DE -l fr -l nb_NO -l tr_TR -l la
[flake8]
max_line_length = 100
exclude = migrations,tests
ignore = BLK100,E203,E231,W503,D100,D101,D102,D103,D104,D105,D106,D107,RST215,RST214,F821,F841,S106,T100,T101,DJ05
[isort]
profile = black
line_length = 100
default_section = THIRDPARTY
known_first_party = aleksis
known_django = django
skip = migrations
sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
[mypy]
plugins = mypy_django_plugin.main
python_version = 3.8
platform = linux
show_column_numbers = True
follow_imports = skip
ignore_missing_imports = True
cache_dir = /dev/null
[mypy.plugins.django-stubs]
django_settings_module = aleksis.core.settings
poetry run aleksis-admin makemessages --no-wrap -e html,txt,py,email -i static -l ar -l de_DE -l fr -l nb_NO -l tr_TR -l la -l uk -l ru
poetry run aleksis-admin makemessages --no-wrap -d djangojs -i **/node_modules -l ar -l de_DE -l fr -l nb_NO -l tr_TR -l la -l uk -l ru
[pytest]
DJANGO_SETTINGS_MODULE = aleksis.core.settings
......