2 Commits

15 changed files with 227 additions and 180 deletions

View File

@@ -1,11 +1,7 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %} {% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
{% block additional_js %}
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
{% endblock %}
{% block additional_css %} {% block additional_css %}
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
<link rel="stylesheet" href="{{ static("club/members.scss") }}"> <link rel="stylesheet" href="{{ static("club/members.scss") }}">
{% endblock %} {% endblock %}

View File

@@ -39,16 +39,12 @@ class Command(BaseCommand):
return None return None
return xapian.version_string() return xapian.version_string()
def _desired_version(self) -> tuple[str, str, str]: def _desired_version(self) -> str:
with open( with open(
Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb" Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb"
) as f: ) as f:
pyproject = tomli.load(f) pyproject = tomli.load(f)
return ( return pyproject["tool"]["xapian"]["version"]
pyproject["tool"]["xapian"]["version"],
pyproject["tool"]["xapian"]["core-sha256"],
pyproject["tool"]["xapian"]["bindings-sha256"],
)
def handle(self, *args, force: bool, **options): def handle(self, *args, force: bool, **options):
if not os.environ.get("VIRTUAL_ENV", None): if not os.environ.get("VIRTUAL_ENV", None):
@@ -57,7 +53,7 @@ class Command(BaseCommand):
) )
return return
desired, core_checksum, bindings_checksum = self._desired_version() desired = self._desired_version()
if desired == self._current_version(): if desired == self._current_version():
if not force: if not force:
self.stdout.write( self.stdout.write(
@@ -69,12 +65,7 @@ class Command(BaseCommand):
f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}" f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}"
) )
subprocess.run( subprocess.run(
[ [str(Path(__file__).parent / "install_xapian.sh"), desired],
str(Path(__file__).parent / "install_xapian.sh"),
desired,
core_checksum,
bindings_checksum,
],
env=dict(os.environ), env=dict(os.environ),
check=True, check=True,
) )

View File

@@ -1,11 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd # Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd
# first argument of the script is Xapian version (e.g. 1.2.19) # first argument of the script is Xapian version (e.g. 1.2.19)
# second argument of the script is core sha256
# second argument of the script is binding sha256
VERSION="$1" VERSION="$1"
CORE_SHA256="$2"
BINDINGS_SHA256="$3"
# Cleanup env vars for auto discovery mechanism # Cleanup env vars for auto discovery mechanism
unset CPATH unset CPATH
@@ -25,15 +21,9 @@ BINDINGS=xapian-bindings-$VERSION
# download # download
echo "Downloading source..." echo "Downloading source..."
curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz" || exit 1 curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz"
echo "${CORE_SHA256} ${CORE}.tar.xz" | sha256sum -c - || exit 1
curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz" curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz"
echo "${BINDINGS_SHA256} ${BINDINGS}.tar.xz" | sha256sum -c - || exit 1
# extract # extract
echo "Extracting source..." echo "Extracting source..."
tar xf "${CORE}.tar.xz" tar xf "${CORE}.tar.xz"

View File

@@ -1,18 +1,106 @@
import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts"; import { inheritHtmlElement, registerComponent } from "#core:utils/web-components.ts";
/**
* Create an abstract class for ElementOnce types Web Components
*
* Those class aren't really abstract because that would be complicated with the
* multiple inheritance involved
* Instead, we just raise an unimplemented error
**/
function elementOnce<K extends keyof HTMLElementTagNameMap>(tagName: K) {
return class ElementOnce extends inheritHtmlElement(tagName) {
getElementQuerySelector(): string {
throw new Error("Unimplemented");
}
clearNode() {
while (this.firstChild) {
this.removeChild(this.lastChild);
}
}
refresh() {
this.clearNode();
if (document.querySelectorAll(this.getElementQuerySelector()).length === 0) {
this.appendChild(this.node);
}
}
connectedCallback() {
super.connectedCallback(false);
this.refresh();
}
disconnectedCallback() {
// The MutationObserver can't see web components being removed
// It also can't see if something is removed inside after the component gets deleted
// We need to manually clear the containing node to trigger the observer
this.clearNode();
}
};
}
// Set of ElementOnce type components to refresh with the observer
const registeredComponents: Set<string> = new Set();
/**
* Helper to register ElementOnce types Web Components
* It's a wrapper around registerComponent that registers that component on
* a MutationObserver that activates a refresh on them when elements are removed
**/
function registerElementOnce(name: string, options?: ElementDefinitionOptions) {
registeredComponents.add(name);
return registerComponent(name, options);
}
const startObserver = (observer: MutationObserver) => {
observer.observe(document, {
// We want to also listen for elements contained in the header (eg: link)
subtree: true,
childList: true,
});
};
// Refresh *-once components when changes happens
const observer = new MutationObserver((mutations: MutationRecord[]) => {
observer.disconnect();
for (const mutation of mutations) {
for (const node of mutation.removedNodes) {
if (node.nodeType !== node.ELEMENT_NODE) {
continue;
}
const refreshElement = (componentName: string, tagName: string) => {
for (const element of document.getElementsByTagName(componentName)) {
// We can't guess if an element is compatible before we get one
// We exit the function completely if it's not compatible
if (
(element as any).inheritedTagName.toUpperCase() !== tagName.toUpperCase()
) {
return;
}
(element as any).refresh();
}
};
for (const registered of registeredComponents) {
refreshElement(registered, (node as HTMLElement).tagName);
}
}
}
startObserver(observer);
});
startObserver(observer);
/** /**
* Web component used to import css files only once * Web component used to import css files only once
* If called multiple times or the file was already imported, it does nothing * If called multiple times or the file was already imported, it does nothing
**/ **/
@registerComponent("link-once") @registerElementOnce("link-once")
export class LinkOnce extends inheritHtmlElement("link") { export class LinkOnce extends elementOnce("link") {
connectedCallback() { getElementQuerySelector(): string {
super.connectedCallback(false);
// We get href from node.attributes instead of node.href to avoid getting the domain part // We get href from node.attributes instead of node.href to avoid getting the domain part
const href = this.node.attributes.getNamedItem("href").nodeValue; return `link[href='${this.node.attributes.getNamedItem("href").nodeValue}']`;
if (document.querySelectorAll(`link[href='${href}']`).length === 0) {
this.appendChild(this.node);
}
} }
} }
@@ -20,14 +108,10 @@ export class LinkOnce extends inheritHtmlElement("link") {
* Web component used to import javascript files only once * Web component used to import javascript files only once
* If called multiple times or the file was already imported, it does nothing * If called multiple times or the file was already imported, it does nothing
**/ **/
@registerComponent("script-once") @registerElementOnce("script-once")
export class ScriptOnce extends inheritHtmlElement("script") { export class ScriptOnce extends inheritHtmlElement("script") {
connectedCallback() { getElementQuerySelector(): string {
super.connectedCallback(false); // We get href from node.attributes instead of node.src to avoid getting the domain part
// We get src from node.attributes instead of node.src to avoid getting the domain part return `script[src='${this.node.attributes.getNamedItem("src").nodeValue}']`;
const src = this.node.attributes.getNamedItem("src").nodeValue;
if (document.querySelectorAll(`script[src='${src}']`).length === 0) {
this.appendChild(this.node);
}
} }
} }

View File

@@ -36,6 +36,7 @@ export function registerComponent(name: string, options?: ElementDefinitionOptio
**/ **/
export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) { export function inheritHtmlElement<K extends keyof HTMLElementTagNameMap>(tagName: K) {
return class Inherited extends HTMLElement { return class Inherited extends HTMLElement {
readonly inheritedTagName = tagName;
protected node: HTMLElementTagNameMap[K]; protected node: HTMLElementTagNameMap[K];
connectedCallback(autoAddNode?: boolean) { connectedCallback(autoAddNode?: boolean) {

View File

@@ -48,6 +48,6 @@
>{% trans %}Delete{% endtrans %}</button></p> >{% trans %}Delete{% endtrans %}</button></p>
</div> </div>
{% endfor %} {% endfor %}
{{ paginate_htmx(request, page_obj, paginator) }} {{ paginate_htmx(page_obj, paginator) }}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -118,21 +118,20 @@
</nav> </nav>
{% endmacro %} {% endmacro %}
{% macro paginate_jinja(request, current_page, paginator) %} {% macro paginate_jinja(current_page, paginator) %}
{# Add pagination buttons for pages without Alpine. {# Add pagination buttons for pages without Alpine.
This must be coupled with a view that handles pagination This must be coupled with a view that handles pagination
with the Django Paginator object. with the Django Paginator object.
Parameters: Parameters:
request (django.http.request.HttpRequest): the current django request
current_page (django.core.paginator.Page): the current page object current_page (django.core.paginator.Page): the current page object
paginator (django.core.paginator.Paginator): the paginator object paginator (django.core.paginator.Paginator): the paginator object
#} #}
{{ paginate_server_side(request, current_page, paginator, False) }} {{ paginate_server_side(current_page, paginator, False) }}
{% endmacro %} {% endmacro %}
{% macro paginate_htmx(request, current_page, paginator) %} {% macro paginate_htmx(current_page, paginator) %}
{# Add pagination buttons for pages without Alpine but supporting fragments. {# Add pagination buttons for pages without Alpine but supporting fragments.
This must be coupled with a view that handles pagination This must be coupled with a view that handles pagination
@@ -141,25 +140,24 @@
The replaced fragment will be #content so make sure you are calling this macro inside your content block. The replaced fragment will be #content so make sure you are calling this macro inside your content block.
Parameters: Parameters:
request (django.http.request.HttpRequest): the current django request
current_page (django.core.paginator.Page): the current page object current_page (django.core.paginator.Page): the current page object
paginator (django.core.paginator.Paginator): the paginator object paginator (django.core.paginator.Paginator): the paginator object
#} #}
{{ paginate_server_side(request, current_page, paginator, True) }} {{ paginate_server_side(current_page, paginator, True) }}
{% endmacro %} {% endmacro %}
{% macro paginate_server_side(request, current_page, paginator, use_htmx) %} {% macro paginate_server_side(current_page, paginator, use_htmx) %}
<nav class="pagination"> <nav class="pagination">
{% if current_page.has_previous() %} {% if current_page.has_previous() %}
<a <a
{% if use_htmx -%} {% if use_htmx -%}
hx-get="?{{ querystring(request, page=current_page.previous_page_number()) }}" hx-get="?{{ querystring(page=current_page.previous_page_number()) }}"
hx-swap="innerHTML" hx-swap="innerHTML"
hx-target="#content" hx-target="#content"
hx-push-url="true" hx-push-url="true"
hx-trigger="click, keyup[key=='ArrowLeft'] from:body" hx-trigger="click, keyup[key=='ArrowLeft'] from:body"
{%- else -%} {%- else -%}
href="?{{ querystring(request, page=current_page.previous_page_number()) }}" href="?{{ querystring(page=current_page.previous_page_number()) }}"
{%- endif -%} {%- endif -%}
> >
<button> <button>
@@ -177,12 +175,12 @@
{% else %} {% else %}
<a <a
{% if use_htmx -%} {% if use_htmx -%}
hx-get="?{{ querystring(request, page=i) }}" hx-get="?{{ querystring(page=i) }}"
hx-swap="innerHTML" hx-swap="innerHTML"
hx-target="#content" hx-target="#content"
hx-push-url="true" hx-push-url="true"
{%- else -%} {%- else -%}
href="?{{ querystring(request, page=i) }}" href="?{{ querystring(page=i) }}"
{%- endif -%} {%- endif -%}
> >
<button>{{ i }}</button> <button>{{ i }}</button>
@@ -192,13 +190,13 @@
{% if current_page.has_next() %} {% if current_page.has_next() %}
<a <a
{% if use_htmx -%} {% if use_htmx -%}
hx-get="?{{querystring(request, page=current_page.next_page_number())}}" hx-get="?{{querystring(page=current_page.next_page_number())}}"
hx-swap="innerHTML" hx-swap="innerHTML"
hx-target="#content" hx-target="#content"
hx-push-url="true" hx-push-url="true"
hx-trigger="click, keyup[key=='ArrowRight'] from:body" hx-trigger="click, keyup[key=='ArrowRight'] from:body"
{%- else -%} {%- else -%}
href="?{{querystring(request, page=current_page.next_page_number())}}" href="?{{querystring(page=current_page.next_page_number())}}"
{%- endif -%} {%- endif -%}
><button> ><button>
<i class="fa fa-caret-right"></i> <i class="fa fa-caret-right"></i>
@@ -249,8 +247,15 @@
{% endmacro %} {% endmacro %}
{% macro querystring(request) %} {% macro querystring() %}
{%- set qs = request.GET.copy() -%} {%- for key, values in request.GET.lists() -%}
{%- do qs.update(kwargs) -%} {%- if key not in kwargs -%}
{{- qs | urlencode -}} {%- for value in values -%}
{{ key }}={{ value }}&amp;
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
{%- for key, value in kwargs.items() -%}
{{ key }}={{ value }}&amp;
{%- endfor -%}
{% endmacro %} {% endmacro %}

View File

@@ -1,14 +1,7 @@
{% extends "core/base.jinja" %} {% extends "core/base.jinja" %}
{%- block additional_js -%}
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
{%- endblock -%}
{%- block additional_css -%} {%- block additional_css -%}
<link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}"> <link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}">
{# importing ajax-select-index is necessary for it to be applied after HTMX reload #}
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
{%- endblock -%} {%- endblock -%}
{% block title %} {% block title %}

View File

@@ -57,7 +57,7 @@
</table> </table>
<br> <br>
{% if is_paginated %} {% if is_paginated %}
{{ paginate_jinja(request, page_obj, paginator) }} {{ paginate_jinja(page_obj, paginator) }}
{% endif %} {% endif %}
{% else %} {% else %}
{% trans %}There is no cash register summary in this website.{% endtrans %} {% trans %}There is no cash register summary in this website.{% endtrans %}

View File

@@ -28,7 +28,7 @@
{%- endfor %} {%- endfor %}
</table> </table>
{% if is_paginated %} {% if is_paginated %}
{{ paginate_jinja(request, page_obj, paginator) }} {{ paginate_jinja(page_obj, paginator) }}
{% endif %} {% endif %}
{%- endblock %} {%- endblock %}

View File

@@ -46,7 +46,7 @@
</section> </section>
{%- endfor %} {%- endfor %}
{% if is_paginated %} {% if is_paginated %}
{{ paginate_jinja(request, page_obj, paginator) }} {{ paginate_jinja(page_obj, paginator) }}
{% endif %} {% endif %}
{%- endblock %} {%- endblock %}

View File

@@ -27,7 +27,7 @@
</p> </p>
{{ display_search_bar(request) }} {{ display_search_bar(request) }}
{{ paginate_jinja(request, msgs, msgs.paginator) }} {{ paginate_jinja(msgs, msgs.paginator) }}
<main class="message-list"> <main class="message-list">
{% for m in msgs %} {% for m in msgs %}
@@ -44,9 +44,7 @@
<p><a class="ib button" href="{{ url('forum:new_message', topic_id=topic.id) }}">{% trans %}Reply{% endtrans %}</a></p> <p><a class="ib button" href="{{ url('forum:new_message', topic_id=topic.id) }}">{% trans %}Reply{% endtrans %}</a></p>
{% if is_paginated %} {{ paginate_jinja(msgs, msgs.paginator) }}
{{ paginate_jinja(request, msgs, msgs.paginator) }}
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -4,54 +4,54 @@ version = "3"
description = "Le web Sith de l'AE" description = "Le web Sith de l'AE"
readme = "README.md" readme = "README.md"
authors = [ authors = [
{ name = "Skia", email = "skia@hya.sk" }, { name = "Skia", email = "skia@hya.sk" },
{ name = "klmp200", email = "antoine@bartuccio.fr" }, { name = "klmp200", email = "antoine@bartuccio.fr" },
{ name = "Krophil", email = "pierre.brunet@krophil.fr" }, { name = "Krophil", email = "pierre.brunet@krophil.fr" },
{ name = "Maréchal", email = "thgirod@hotmail.com" }, { name = "Maréchal", email = "thgirod@hotmail.com" },
{ name = "Och", email = "francescowitz68@gmail.com" }, { name = "Och", email = "francescowitz68@gmail.com" },
{ name = "tleb", email = "tleb@openmailbox.org" }, { name = "tleb", email = "tleb@openmailbox.org" },
{ name = "Soldat", email = "ryan-68@live.fr" }, { name = "Soldat", email = "ryan-68@live.fr" },
{ name = "Nabos", email = "gnikwo@hotmail.com" }, { name = "Nabos", email = "gnikwo@hotmail.com" },
{ name = "Terre", email = "jbaptiste.lenglet+git@gmail.com" }, { name = "Terre", email = "jbaptiste.lenglet+git@gmail.com" },
{ name = "Lo-J", email = "renaudg779@gmail.com" }, { name = "Lo-J", email = "renaudg779@gmail.com" },
{ name = "Vial", email = "robin.trioux@utbm.fr" }, { name = "Vial", email = "robin.trioux@utbm.fr" },
] ]
license = { text = "GPL-3.0-only" } license = { text = "GPL-3.0-only" }
requires-python = "<4.0,>=3.12" requires-python = "<4.0,>=3.12"
dependencies = [ dependencies = [
"django>=5.2.12,<6.0.0", "django>=5.2.12,<6.0.0",
"django-ninja>=1.5.3,<6.0.0", "django-ninja>=1.5.3,<6.0.0",
"django-ninja-extra>=0.31.0", "django-ninja-extra>=0.31.0",
"Pillow>=12.1.1,<13.0.0", "Pillow>=12.1.1,<13.0.0",
"mistune>=3.2.0,<4.0.0", "mistune>=3.2.0,<4.0.0",
"django-jinja<3.0.0,>=2.11.0", "django-jinja<3.0.0,>=2.11.0",
"cryptography>=46.0.5,<47.0.0", "cryptography>=46.0.5,<47.0.0",
"django-phonenumber-field>=8.4.0,<9.0.0", "django-phonenumber-field>=8.4.0,<9.0.0",
"phonenumbers>=9.0.25,<10.0.0", "phonenumbers>=9.0.25,<10.0.0",
"reportlab>=4.4.10,<5.0.0", "reportlab>=4.4.10,<5.0.0",
"django-haystack<4.0.0,>=3.3.0", "django-haystack<4.0.0,>=3.3.0",
"xapian-haystack<4.0.0,>=3.1.0", "xapian-haystack<4.0.0,>=3.1.0",
"libsass<1.0.0,>=0.23.0", "libsass<1.0.0,>=0.23.0",
"django-ordered-model<4.0.0,>=3.7.4", "django-ordered-model<4.0.0,>=3.7.4",
"django-simple-captcha<1.0.0,>=0.6.3", "django-simple-captcha<1.0.0,>=0.6.3",
"python-dateutil<3.0.0.0,>=2.9.0.post0", "python-dateutil<3.0.0.0,>=2.9.0.post0",
"sentry-sdk>=2.54.0,<3.0.0", "sentry-sdk>=2.54.0,<3.0.0",
"jinja2<4.0.0,>=3.1.6", "jinja2<4.0.0,>=3.1.6",
"django-countries>=8.2.0,<9.0.0", "django-countries>=8.2.0,<9.0.0",
"dict2xml>=1.7.8,<2.0.0", "dict2xml>=1.7.8,<2.0.0",
"Sphinx<6,>=5", "Sphinx<6,>=5",
"tomli>=2.4.0,<3.0.0", "tomli>=2.4.0,<3.0.0",
"django-honeypot>=1.3.0,<2", "django-honeypot>=1.3.0,<2",
"pydantic-extra-types>=2.11.0,<3.0.0", "pydantic-extra-types>=2.11.0,<3.0.0",
"ical>=11.1.0,<12", "ical>=11.1.0,<12",
"redis[hiredis]>=5.3.0,<8.0.0", "redis[hiredis]>=5.3.0,<8.0.0",
"environs[django]>=14.5.0,<15.0.0", "environs[django]>=14.5.0,<15.0.0",
"requests>=2.32.5,<3.0.0", "requests>=2.32.5,<3.0.0",
"honcho>=2.0.0", "honcho>=2.0.0",
"psutil>=7.2.2,<8.0.0", "psutil>=7.2.2,<8.0.0",
"celery[redis]>=5.6.2,<7", "celery[redis]>=5.6.2,<7",
"django-celery-results>=2.5.1", "django-celery-results>=2.5.1",
"django-celery-beat>=2.9.0", "django-celery-beat>=2.9.0",
] ]
[project.urls] [project.urls]
@@ -59,74 +59,73 @@ homepage = "https://ae.utbm.fr/"
documentation = "https://sith-ae.readthedocs.io/" documentation = "https://sith-ae.readthedocs.io/"
[dependency-groups] [dependency-groups]
prod = ["psycopg[c]>=3.3.3,<4.0.0"] prod = [
"psycopg[c]>=3.3.3,<4.0.0",
]
dev = [ dev = [
"django-debug-toolbar>=6.2.0,<7", "django-debug-toolbar>=6.2.0,<7",
"ipython>=9.11.0,<10.0.0", "ipython>=9.11.0,<10.0.0",
"pre-commit>=4.5.1,<5.0.0", "pre-commit>=4.5.1,<5.0.0",
"ruff>=0.15.5,<1.0.0", "ruff>=0.15.5,<1.0.0",
"djhtml>=3.0.10,<4.0.0", "djhtml>=3.0.10,<4.0.0",
"faker>=40.8.0,<41.0.0", "faker>=40.8.0,<41.0.0",
"rjsmin>=1.2.5,<2.0.0", "rjsmin>=1.2.5,<2.0.0",
] ]
tests = [ tests = [
"freezegun>=1.5.5,<2.0.0", "freezegun>=1.5.5,<2.0.0",
"pytest>=9.0.2,<10.0.0", "pytest>=9.0.2,<10.0.0",
"pytest-cov>=7.0.0,<8.0.0", "pytest-cov>=7.0.0,<8.0.0",
"pytest-django<5.0.0,>=4.12.0", "pytest-django<5.0.0,>=4.12.0",
"model-bakery<2.0.0,>=1.23.3", "model-bakery<2.0.0,>=1.23.3",
"beautifulsoup4>=4.14.3,<5", "beautifulsoup4>=4.14.3,<5",
"lxml>=6.0.2,<7", "lxml>=6.0.2,<7",
] ]
docs = [ docs = [
"mkdocs<2.0.0,>=1.6.1", "mkdocs<2.0.0,>=1.6.1",
"mkdocs-material>=9.7.5,<10.0.0", "mkdocs-material>=9.7.5,<10.0.0",
"mkdocstrings>=1.0.3,<2.0.0", "mkdocstrings>=1.0.3,<2.0.0",
"mkdocstrings-python>=2.0.3,<3.0.0", "mkdocstrings-python>=2.0.3,<3.0.0",
"mkdocs-include-markdown-plugin>=7.2.1,<8.0.0", "mkdocs-include-markdown-plugin>=7.2.1,<8.0.0",
] ]
[tool.uv] [tool.uv]
default-groups = ["dev", "tests", "docs"] default-groups = ["dev", "tests", "docs"]
[tool.xapian] [tool.xapian]
version = "1.4.31" version = "1.4.29"
core-sha256 = "fecf609ea2efdc8a64be369715aac733336a11f7480a6545244964ae6bc80811"
bindings-sha256 = "a38cc7ba4188cc0bd27dc7369f03906772047087a1c54f1b93355d5e9103c304"
[tool.ruff] [tool.ruff]
output-format = "concise" # makes ruff error logs easier to read output-format = "concise" # makes ruff error logs easier to read
[tool.ruff.lint] [tool.ruff.lint]
select = [ select = [
"A", # shadowing of Python builtins "A", # shadowing of Python builtins
"B", "B",
"C4", # use comprehensions when possible "C4", # use comprehensions when possible
"DJ", # django-specific rules, "DJ", # django-specific rules,
"E", # pycodestyle (https://docs.astral.sh/ruff/rules/#pycodestyle-e-w) "E", # pycodestyle (https://docs.astral.sh/ruff/rules/#pycodestyle-e-w)
"ERA", # commented code "ERA", # commented code
"F", # pyflakes (https://docs.astral.sh/ruff/rules/#pyflakes-f) "F", # pyflakes (https://docs.astral.sh/ruff/rules/#pyflakes-f)
"FBT", # boolean trap "FBT", # boolean trap
"FLY", # f-string instead of str.join "FLY", # f-string instead of str.join
"FURB", # https://docs.astral.sh/ruff/rules/#refurb-furb "FURB", # https://docs.astral.sh/ruff/rules/#refurb-furb
"I", # isort "I", # isort
"INT", # gettext "INT", # gettext
"PERF", # performance "PERF", # performance
"PLW", # pylint warnings (https://docs.astral.sh/ruff/rules/#pylint-pl) "PLW", # pylint warnings (https://docs.astral.sh/ruff/rules/#pylint-pl)
"RUF", # Ruff specific rules "RUF", # Ruff specific rules
"SIM", # simplify (https://docs.astral.sh/ruff/rules/#flake8-simplify-sim) "SIM", # simplify (https://docs.astral.sh/ruff/rules/#flake8-simplify-sim)
"T100", # breakpoint() "T100", # breakpoint()
"T2", # print statements "T2", # print statements
"TCH", # type-checking block "TCH", # type-checking block
"UP008", # Use super() instead of super(__class__, self) "UP008", # Use super() instead of super(__class__, self)
"UP009", # utf-8 encoding declaration is unnecessary "UP009", # utf-8 encoding declaration is unnecessary
] ]
ignore = [ ignore = [
"DJ001", # null=True in CharField/TextField. this one would require a migration "DJ001", # null=True in CharField/TextField. this one would require a migration
"E501", # line too long. The rule is too harsh, and the formatter deals with it in most cases "E501", # line too long. The rule is too harsh, and the formatter deals with it in most cases
"RUF012", # mutable class attributes. This rule doesn't integrate well with django "RUF012" # mutable class attributes. This rule doesn't integrate well with django
] ]
[tool.ruff.lint.pydocstyle] [tool.ruff.lint.pydocstyle]
@@ -142,4 +141,4 @@ sith = "sith.pytest"
[tool.pytest.ini_options] [tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "sith.settings" DJANGO_SETTINGS_MODULE = "sith.settings"
python_files = ["tests.py", "test_*.py", "*_tests.py"] python_files = ["tests.py", "test_*.py", "*_tests.py"]
markers = ["slow"] markers = ["slow"]

View File

@@ -28,7 +28,5 @@
</table> </table>
<br> <br>
{% if is_paginated %} {{ paginate_jinja(page_obj, paginator) }}
{{ paginate_jinja(request, page_obj, paginator) }}
{% endif %}
{% endblock content %} {% endblock content %}

View File

@@ -6,14 +6,8 @@
{% trans %}New subscription{% endtrans %} {% trans %}New subscription{% endtrans %}
{% endblock %} {% endblock %}
{# The following statics are bundled with our autocomplete select.
However, if one tries to swap a form by another, then the urls in script-once
and link-once disappear.
So we give them here.
If the aforementioned bug is resolved, you can remove this. #}
{% block additional_js %} {% block additional_js %}
<script type="module" src="{{ static('bundled/core/components/tabs-index.ts') }}"></script> <script type="module" src="{{ static('bundled/core/components/tabs-index.ts') }}"></script>
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
<script <script
type="module" type="module"
src="{{ static("bundled/subscription/creation-form-existing-user-index.ts") }}" src="{{ static("bundled/subscription/creation-form-existing-user-index.ts") }}"
@@ -21,8 +15,6 @@
{% endblock %} {% endblock %}
{% block additional_css %} {% block additional_css %}
<link rel="stylesheet" href="{{ static("core/components/tabs.scss") }}"> <link rel="stylesheet" href="{{ static("core/components/tabs.scss") }}">
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
<link rel="stylesheet" href="{{ static("subscription/css/subscription.scss") }}"> <link rel="stylesheet" href="{{ static("subscription/css/subscription.scss") }}">
{% endblock %} {% endblock %}