diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5d381cc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,162 @@
+# ---> Python
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..23c9d5b
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,30 @@
+repos:
+ - repo: https://github.com/psf/black
+ rev: 23.1.0
+ hooks:
+ - id: black
+ exclude: ^core/migrations
+ - repo: https://github.com/PyCQA/isort
+ rev: 5.11.5
+ hooks:
+ - id: isort
+ args: ["--profile", "black"]
+ - repo: https://github.com/PyCQA/flake8
+ rev: 6.0.0
+ hooks:
+ - id: flake8
+ args: [--max-line-length=88]
+ exclude: ^core/migrations
+ - repo: https://github.com/rtts/djhtml
+ rev: v2.0.0
+ hooks:
+ - id: djhtml
+ args: [-t 2]
+ - id: djcss
+ exclude : ^core/static/css # slow
+ - id: djjs
+ exclude: ^core/static/js # slow
+ - repo: https://github.com/sirwart/ripsecrets.git
+ rev: v0.1.5
+ hooks:
+ - id: ripsecrets
diff --git a/src/mixins/__init__.py b/src/mixins/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/mixins/__main__.py b/src/mixins/__main__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/mixins/restrictions.py b/src/mixins/restrictions.py
deleted file mode 100644
index 26666d2..0000000
--- a/src/mixins/restrictions.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
-from django.core.paginator import Paginator
-from django.db.models import QuerySet
-
-
-class RestrictedViewMixin:
- """
- This mixin overrides two helpers in order to pass the user object to the filters.
- get_queryset alters the objects returned for list views.
- get_form_kwargs passes the request object to the form class. Remaining permissions
- checks are in forms.py
- """
-
- allow_empty = True
- queryset = None
- model = None
- paginate_by = None
- paginate_orphans = 0
- context_object_name = None
- paginator_class = Paginator
- page_kwarg = "page"
- ordering = None
-
- def set_extra_args(self, user):
- """
- This function is overriden to filter the objects by the requesting user.
- """
- self.extra_permission_args = {}
-
- def get_queryset(self, **kwargs):
- """
- This function is overriden to filter the objects by the requesting user.
- """
- self.set_extra_args(self.request.user)
- if self.queryset is not None:
- queryset = self.queryset
- if isinstance(queryset, QuerySet):
- # queryset = queryset.all()
- queryset = queryset.filter(
- user=self.request.user, **self.extra_permission_args
- )
- elif self.model is not None:
- queryset = self.model._default_manager.filter(
- user=self.request.user, **self.extra_permission_args
- )
- else:
- raise ImproperlyConfigured(
- "%(cls)s is missing a QuerySet. Define "
- "%(cls)s.model, %(cls)s.queryset, or override "
- "%(cls)s.get_queryset()." % {"cls": self.__class__.__name__}
- )
- if hasattr(self, "get_ordering"):
- ordering = self.get_ordering()
- if ordering:
- if isinstance(ordering, str):
- ordering = (ordering,)
- queryset = queryset.order_by(*ordering)
-
- return queryset
-
- def get_form_kwargs(self):
- """Passes the request object to the form class.
- This is necessary to only display members that belong to a given user"""
-
- kwargs = super().get_form_kwargs()
- kwargs["request"] = self.request
- return kwargs
-
-
-class RestrictedFormMixin:
- """
- This mixin is used to restrict the queryset of a form to the current user.
- The request object is passed from the view.
- Fieldargs is used to pass additional arguments to the queryset filter.
- """
-
- fieldargs = {}
-
- # TODO: implement set_extra_args to check more permissions here
- # for completeness, however as views open forms, the permissions
- # are already checked there, so it may not be necessary.
- def __init__(self, *args, **kwargs):
- # self.fieldargs = {}
- self.request = kwargs.pop("request")
- super().__init__(*args, **kwargs)
- for field in self.fields:
- # Check it's not something like a CharField which has no queryset
- if not hasattr(self.fields[field], "queryset"):
- continue
-
- model = self.fields[field].queryset.model
- # Check if the model has a user field
- try:
- model._meta.get_field("user")
- # Add the user to the queryset filters
- self.fields[field].queryset = model.objects.filter(
- user=self.request.user, **self.fieldargs.get(field, {})
- )
- except FieldDoesNotExist:
- pass
diff --git a/src/mixins/static/modal.js b/src/mixins/static/modal.js
deleted file mode 100644
index 0535526..0000000
--- a/src/mixins/static/modal.js
+++ /dev/null
@@ -1,44 +0,0 @@
-// var modal = document.querySelector('.modal'); // assuming you have only 1
-var modal = document.getElementById("modal");
-var html = document.querySelector('html');
-
-var disableModal = function() {
- modal.classList.remove('is-active');
- html.classList.remove('is-clipped');
- var modal_refresh = document.getElementsByClassName("modal-refresh");
- for(var i = 0; i < modal_refresh.length; i++) {
- modal_refresh[i].remove();
- }
-}
-
-var elements = document.querySelectorAll('.modal-background');
-for(var i = 0; i < elements.length; i++) {
- elements[i].addEventListener('click', function(e) {
- // elements[i].preventDefault();
- disableModal();
- });
-}
-
-var elements = document.querySelectorAll('.modal-close');
-for(var i = 0; i < elements.length; i++) {
- elements[i].addEventListener('click', function(e) {
- // elements[i].preventDefault();
- disableModal();
- });
-}
-
-function activateButtons() {
- var elements = document.querySelectorAll('.modal-close-button');
- for(var i = 0; i < elements.length; i++) {
- elements[i].addEventListener('click', function(e) {
- // elements[i].preventDefault();
- disableModal();
- });
- }
-}
-activateButtons();
-// modal.querySelector('.modal-close-button').addEventListener('click', function(e) {
-// e.preventDefault();
-// modal.classList.remove('is-active');
-// html.classList.remove('is-clipped');
-// });
diff --git a/src/mixins/templates/mixins/partials/close-modal.html b/src/mixins/templates/mixins/partials/close-modal.html
deleted file mode 100644
index 6c0173c..0000000
--- a/src/mixins/templates/mixins/partials/close-modal.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/mixins/templates/mixins/partials/close-widget.html b/src/mixins/templates/mixins/partials/close-widget.html
deleted file mode 100644
index 5c6f6ea..0000000
--- a/src/mixins/templates/mixins/partials/close-widget.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/mixins/templates/mixins/partials/close-window.html b/src/mixins/templates/mixins/partials/close-window.html
deleted file mode 100644
index 894974c..0000000
--- a/src/mixins/templates/mixins/partials/close-window.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
\ No newline at end of file
diff --git a/src/mixins/templates/mixins/partials/generic-detail.html b/src/mixins/templates/mixins/partials/generic-detail.html
deleted file mode 100644
index 3eee173..0000000
--- a/src/mixins/templates/mixins/partials/generic-detail.html
+++ /dev/null
@@ -1,72 +0,0 @@
-{% load pretty %}
-{% include 'partials/notify.html' %}
-
-{% if live is not None %}
-
Live {{ context_object_name_singular }} info
-
-
-
attribute
-
value
-
-
- {% block live_tbody %}
- {% for key, item in live.items %}
- {% if key in pretty %}
-
-
{{ key }}
-
- {% if item is not None %}
-
{{ item|pretty }}
- {% endif %}
-
-
- {% else %}
-
-
{{ key }}
-
- {% if item is not None %}
- {{ item }}
- {% endif %}
-
-
- {% endif %}
- {% endfor %}
- {% endblock %}
-
-
-{% endif %}
-
-{% if object is not None %}
-
{{ title_singular }} info
-
-
-
attribute
-
value
-
-
- {% block tbody %}
- {% for key, item in object.items %}
- {% if key in pretty %}
-
-
{{ key }}
-
- {% if item is not None %}
-
{{ item|pretty }}
- {% endif %}
-
-
- {% else %}
-
-
{{ key }}
-
- {% if item is not None %}
- {{ item }}
- {% endif %}
-
-
- {% endif %}
- {% endfor %}
- {% endblock %}
-
-
-{% endif %}
\ No newline at end of file
diff --git a/src/mixins/templates/mixins/partials/notify.html b/src/mixins/templates/mixins/partials/notify.html
deleted file mode 100644
index fcf970b..0000000
--- a/src/mixins/templates/mixins/partials/notify.html
+++ /dev/null
@@ -1,7 +0,0 @@
-
- {% if message is not None %}
-
- {{ message }}
-
- {% endif %}
-
\ No newline at end of file
diff --git a/src/mixins/templates/mixins/window-content/object-form.html b/src/mixins/templates/mixins/window-content/object-form.html
deleted file mode 100644
index bdfb43a..0000000
--- a/src/mixins/templates/mixins/window-content/object-form.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% include 'partials/notify.html' %}
-{% if page_title is not None %}
-
{{ page_title }}
-{% endif %}
-{% if page_subtitle is not None %}
-