mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-12 12:59:24 +00:00
Compare commits
3 Commits
docker
...
windows-up
Author | SHA1 | Date | |
---|---|---|---|
1d03fcf6ea | |||
a6ba65a494 | |||
c90fcc838e |
83
.env.example
83
.env.example
@ -1,83 +0,0 @@
|
||||
HTTPS=off
|
||||
DEBUG=true
|
||||
|
||||
# This is not the real key used in prod
|
||||
SECRET_KEY=(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2
|
||||
|
||||
DATABASE_URL=sqlite:///db.sqlite3
|
||||
# uncomment the next line if you want to use a postgres database
|
||||
#DATABASE_URL=postgres://user:password@127.0.0.1:5432/sith
|
||||
CACHE_URL=redis://127.0.0.1:6379/0
|
||||
|
||||
MEDIA_ROOT=data
|
||||
STATIC_ROOT=static
|
||||
|
||||
DEFAULT_FROM_EMAIL=bibou@git.an
|
||||
SITH_COM_EMAIL=bibou_com@git.an
|
||||
|
||||
HONEYPOT_VALUE=content
|
||||
HONEYPOT_FIELD_NAME=body2
|
||||
HONEYPOT_FIELD_NAME_FORUM=message2
|
||||
|
||||
EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
|
||||
EMAIL_HOST=localhost
|
||||
EMAIL_PORT=25
|
||||
|
||||
SITH_URL=127.0.0.1:8000
|
||||
SITH_NAME="AE UTBM"
|
||||
|
||||
SITH_MAIN_CLUB_ID=1
|
||||
|
||||
SITH_GROUP_ROOT_ID=1
|
||||
SITH_GROUP_PUBLIC_ID=2
|
||||
SITH_GROUP_SUBSCRIBERS_ID=3
|
||||
SITH_GROUP_OLD_SUBSCRIBERS_ID=4
|
||||
SITH_GROUP_ACCOUNTING_ADMIN_ID=5
|
||||
SITH_GROUP_COM_ADMIN_ID=6
|
||||
SITH_GROUP_COUNTER_ADMIN_ID=7
|
||||
SITH_GROUP_SAS_ADMIN_ID=8
|
||||
SITH_GROUP_FORUM_ADMIN_ID=9
|
||||
SITH_GROUP_PEDAGOGY_ADMIN_ID=10
|
||||
|
||||
SITH_GROUP_BANNED_ALCOHOL_ID=11
|
||||
SITH_GROUP_BANNED_COUNTER_ID=12
|
||||
SITH_GROUP_BANNED_SUBSCRIPTION_ID=13
|
||||
|
||||
SITH_CLUB_REFOUND_ID=89
|
||||
SITH_COUNTER_REFOUND_ID=38
|
||||
SITH_PRODUCT_REFOUND_ID=5
|
||||
|
||||
# Counter
|
||||
|
||||
SITH_COUNTER_ACCOUNT_DUMP_ID=39
|
||||
|
||||
# Defines which product type is the refilling type, and thus increases the account amount
|
||||
SITH_COUNTER_PRODUCTTYPE_REFILLING=3
|
||||
|
||||
SITH_ECOCUP_CONS=1152
|
||||
SITH_ECOCUP_DECO=1151
|
||||
|
||||
# Defines which product is the one year subscription and which one is the six month subscription
|
||||
SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER=1
|
||||
SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS=2
|
||||
SITH_PRODUCTTYPE_SUBSCRIPTION=2
|
||||
|
||||
# Defines which clubs let its members the ability to see users subscription history
|
||||
SITH_CAN_CREATE_SUBSCRIPTION_HISTORY=1
|
||||
SITH_CAN_READ_SUBSCRIPTION_HISTORY=1
|
||||
|
||||
# SAS variables
|
||||
SITH_SAS_ROOT_DIR_ID=4
|
||||
|
||||
# ET variables
|
||||
SITH_EBOUTIC_CB_ENABLED=true
|
||||
SITH_EBOUTIC_ET_URL="https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi"
|
||||
SITH_EBOUTIC_PBX_SITE=1999888
|
||||
SITH_EBOUTIC_PBX_RANG=32
|
||||
SITH_EBOUTIC_PBX_IDENTIFIANT=2
|
||||
SITH_EBOUTIC_HMAC_KEY=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
|
||||
SITH_EBOUTIC_PUB_KEY_PATH=sith/et_keys/pubkey.pem
|
||||
|
||||
SITH_MAILING_FETCH_KEY=IloveMails
|
||||
SENTRY_DSN=
|
||||
SENTRY_ENV=production
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -7,10 +7,6 @@ on:
|
||||
branches: [master, taiste]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
SECRET_KEY: notTheRealOne
|
||||
DATABASE_URL: sqlite:///db.sqlite3
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
name: Launch pre-commits checks (ruff)
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,4 +21,3 @@ node_modules/
|
||||
|
||||
# compiled documentation
|
||||
site/
|
||||
.env
|
||||
|
@ -1,10 +1,10 @@
|
||||
from django.db.models.signals import post_delete, post_save
|
||||
from django.db.models.base import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from com.calendar import IcsCalendar
|
||||
from com.models import News
|
||||
|
||||
|
||||
@receiver([post_save, post_delete], sender=News, dispatch_uid="update_internal_ics")
|
||||
@receiver(post_save, sender=News, dispatch_uid="update_internal_ics")
|
||||
def update_internal_ics(*args, **kwargs):
|
||||
_ = IcsCalendar.make_internal()
|
||||
|
@ -125,7 +125,7 @@
|
||||
<i class="fa-brands fa-discord fa-xl"></i>
|
||||
<a rel="nofollow" target="#" href="https://discord.gg/QvTm3XJrHR">{% trans %}Discord AE{% endtrans %}</a>
|
||||
{% if user.was_subscribed %}
|
||||
- <a rel="nofollow" target="#" href="https://discord.gg/u6EuMfyGaJ">{% trans %}Dev Team{% endtrans %}</a>
|
||||
- <a rel="nofollow" target="#" href="https://discord.gg/XK9WfPsUFm">{% trans %}Dev Team{% endtrans %}</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
|
@ -13,12 +13,42 @@
|
||||
#
|
||||
#
|
||||
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Self
|
||||
|
||||
import tomli
|
||||
from django.core.management.base import BaseCommand, CommandParser
|
||||
import urllib3
|
||||
from django.core.management.base import BaseCommand, CommandParser, OutputWrapper
|
||||
from urllib3.response import HTTPException
|
||||
|
||||
|
||||
@dataclass
|
||||
class XapianSpec:
|
||||
version: str
|
||||
core_sha1: str
|
||||
bindings_sha1: str
|
||||
|
||||
@classmethod
|
||||
def from_pyproject(cls) -> Self:
|
||||
with open(
|
||||
Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb"
|
||||
) as f:
|
||||
pyproject = tomli.load(f)
|
||||
spec = pyproject["tool"]["xapian"]
|
||||
return cls(
|
||||
version=spec["version"],
|
||||
core_sha1=spec["core-sha1"],
|
||||
bindings_sha1=spec["bindings-sha1"],
|
||||
)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -39,13 +69,6 @@ class Command(BaseCommand):
|
||||
return None
|
||||
return xapian.version_string()
|
||||
|
||||
def _desired_version(self) -> str:
|
||||
with open(
|
||||
Path(__file__).parent.parent.parent.parent / "pyproject.toml", "rb"
|
||||
) as f:
|
||||
pyproject = tomli.load(f)
|
||||
return pyproject["tool"]["xapian"]["version"]
|
||||
|
||||
def handle(self, *args, force: bool, **options):
|
||||
if not os.environ.get("VIRTUAL_ENV", None):
|
||||
self.stdout.write(
|
||||
@ -53,20 +76,185 @@ class Command(BaseCommand):
|
||||
)
|
||||
return
|
||||
|
||||
desired = self._desired_version()
|
||||
if desired == self._current_version():
|
||||
desired = XapianSpec.from_pyproject()
|
||||
if desired.version == self._current_version():
|
||||
if not force:
|
||||
self.stdout.write(
|
||||
f"Version {desired} is already installed, use --force to re-install"
|
||||
f"Version {desired.version} is already installed, use --force to re-install"
|
||||
)
|
||||
return
|
||||
self.stdout.write(f"Version {desired} is already installed, re-installing")
|
||||
self.stdout.write(
|
||||
f"Installing xapian version {desired} at {os.environ['VIRTUAL_ENV']}"
|
||||
)
|
||||
subprocess.run(
|
||||
[str(Path(__file__).parent / "install_xapian.sh"), desired],
|
||||
env=dict(os.environ),
|
||||
check=True,
|
||||
)
|
||||
self.stdout.write(
|
||||
f"Version {desired.version} is already installed, re-installing"
|
||||
)
|
||||
XapianInstaller(desired, self.stdout, self.stderr).run()
|
||||
self.stdout.write("Installation success")
|
||||
|
||||
|
||||
class XapianInstaller:
|
||||
def __init__(
|
||||
self,
|
||||
spec: XapianSpec,
|
||||
stdout: OutputWrapper,
|
||||
stderr: OutputWrapper,
|
||||
):
|
||||
self._version = spec.version
|
||||
self._core_sha1 = spec.core_sha1
|
||||
self._bindings_sha1 = spec.bindings_sha1
|
||||
|
||||
self._stdout = stdout
|
||||
self._stderr = stderr
|
||||
self._virtual_env = os.environ.get("VIRTUAL_ENV", None)
|
||||
|
||||
if not self._virtual_env:
|
||||
raise RuntimeError("You are not inside a virtual environment")
|
||||
self._virtual_env = Path(self._virtual_env)
|
||||
|
||||
self._dest_dir = Path(self._virtual_env) / "packages"
|
||||
self._core = f"xapian-core-{self._version}"
|
||||
self._bindings = f"xapian-bindings-{self._version}"
|
||||
|
||||
@property
|
||||
def _is_windows(self) -> bool:
|
||||
return platform.system() == "Windows"
|
||||
|
||||
def _util_download(self, url: str, dest: Path, sha1_hash: str) -> None:
|
||||
resp = urllib3.request("GET", url)
|
||||
if resp.status != 200:
|
||||
raise HTTPException(f"Could not download {url}")
|
||||
if hashlib.sha1(resp.data).hexdigest() != sha1_hash:
|
||||
raise ValueError(f"File downloaded from {url} is compromised")
|
||||
with open(dest, "wb") as f:
|
||||
f.write(resp.data)
|
||||
|
||||
def _setup_env(self):
|
||||
os.environ.update(
|
||||
{
|
||||
"CPATH": "",
|
||||
"LIBRARY_PATH": "",
|
||||
"CFLAGS": "",
|
||||
"LDFLAGS": "",
|
||||
"CCFLAGS": "",
|
||||
"CXXFLAGS": "",
|
||||
"CPPFLAGS": "",
|
||||
}
|
||||
)
|
||||
|
||||
def _prepare_dest_folder(self):
|
||||
shutil.rmtree(self._dest_dir, ignore_errors=True)
|
||||
self._dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def _download(self):
|
||||
self._stdout.write("Downloading source…")
|
||||
|
||||
core = self._dest_dir / f"{self._core}.tar.xz"
|
||||
bindings = self._dest_dir / f"{self._bindings}.tar.xz"
|
||||
self._util_download(
|
||||
f"https://oligarchy.co.uk/xapian/{self._version}/{self._core}.tar.xz",
|
||||
core,
|
||||
self._core_sha1,
|
||||
)
|
||||
self._util_download(
|
||||
f"https://oligarchy.co.uk/xapian/{self._version}/{self._bindings}.tar.xz",
|
||||
bindings,
|
||||
self._bindings_sha1,
|
||||
)
|
||||
self._stdout.write("Extracting source …")
|
||||
with tarfile.open(core) as tar:
|
||||
tar.extractall(self._dest_dir)
|
||||
with tarfile.open(bindings) as tar:
|
||||
tar.extractall(self._dest_dir)
|
||||
|
||||
os.remove(core)
|
||||
os.remove(bindings)
|
||||
|
||||
def _install(self):
|
||||
self._stdout.write("Installing Xapian-core…")
|
||||
def configure() -> list[str]:
|
||||
if self._is_windows:
|
||||
return ["sh", "configure"]
|
||||
return ["./configure"]
|
||||
def enable_static() -> list[str]:
|
||||
if self._is_windows:
|
||||
return ["--enable-shared", "--disable-static"]
|
||||
return []
|
||||
|
||||
# Make sure that xapian finds the correct executable
|
||||
os.environ["PYTHON3"] = str(Path(sys.executable).as_posix())
|
||||
|
||||
subprocess.run(
|
||||
[*configure(), "--prefix", str(self._virtual_env.as_posix()), *enable_static(),],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._core,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
).check_returncode()
|
||||
subprocess.run(
|
||||
[
|
||||
"make",
|
||||
"-j",
|
||||
str(multiprocessing.cpu_count()),
|
||||
],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._core,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
).check_returncode()
|
||||
subprocess.run(
|
||||
["make", "install"],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._core,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
|
||||
).check_returncode()
|
||||
|
||||
|
||||
self._stdout.write("Installing Xapian-bindings")
|
||||
subprocess.run(
|
||||
[
|
||||
*configure(),
|
||||
"--prefix",
|
||||
str(self._virtual_env.as_posix()),
|
||||
"--with-python3",
|
||||
f"XAPIAN_CONFIG={(self._virtual_env / 'bin'/'xapian-config').as_posix()}",
|
||||
*enable_static(),
|
||||
],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._bindings,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
).check_returncode()
|
||||
subprocess.run(
|
||||
[
|
||||
"make",
|
||||
"-j",
|
||||
str(multiprocessing.cpu_count()),
|
||||
],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._bindings,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
).check_returncode()
|
||||
subprocess.run(
|
||||
["make", "install"],
|
||||
env=dict(os.environ),
|
||||
cwd=self._dest_dir / self._bindings,
|
||||
check=False,
|
||||
shell=self._is_windows,
|
||||
).check_returncode()
|
||||
|
||||
def _post_clean(self):
|
||||
shutil.rmtree(self._dest_dir, ignore_errors=True)
|
||||
|
||||
def _test(self):
|
||||
subprocess.run(
|
||||
[sys.executable, "-c", "import xapian"], check=False, shell=self._is_windows,
|
||||
).check_returncode()
|
||||
|
||||
def run(self):
|
||||
self._setup_env()
|
||||
self._prepare_dest_folder()
|
||||
self._download()
|
||||
self._install()
|
||||
self._post_clean()
|
||||
self._test()
|
||||
|
@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Originates from https://gist.github.com/jorgecarleitao/ab6246c86c936b9c55fd
|
||||
# first argument of the script is Xapian version (e.g. 1.2.19)
|
||||
VERSION="$1"
|
||||
|
||||
# Cleanup env vars for auto discovery mechanism
|
||||
export CPATH=
|
||||
export LIBRARY_PATH=
|
||||
export CFLAGS=
|
||||
export LDFLAGS=
|
||||
export CCFLAGS=
|
||||
export CXXFLAGS=
|
||||
export CPPFLAGS=
|
||||
|
||||
# prepare
|
||||
rm -rf "$VIRTUAL_ENV/packages"
|
||||
mkdir -p "$VIRTUAL_ENV/packages" && cd "$VIRTUAL_ENV/packages" || exit 1
|
||||
|
||||
CORE=xapian-core-$VERSION
|
||||
BINDINGS=xapian-bindings-$VERSION
|
||||
|
||||
# download
|
||||
echo "Downloading source..."
|
||||
curl -O "https://oligarchy.co.uk/xapian/$VERSION/${CORE}.tar.xz"
|
||||
curl -O "https://oligarchy.co.uk/xapian/$VERSION/${BINDINGS}.tar.xz"
|
||||
|
||||
# extract
|
||||
echo "Extracting source..."
|
||||
tar xf "${CORE}.tar.xz"
|
||||
tar xf "${BINDINGS}.tar.xz"
|
||||
|
||||
# install
|
||||
echo "Installing Xapian-core..."
|
||||
cd "$VIRTUAL_ENV/packages/${CORE}" || exit 1
|
||||
./configure --prefix="$VIRTUAL_ENV" && make -j"$(nproc)" && make install
|
||||
|
||||
PYTHON_FLAG=--with-python3
|
||||
|
||||
echo "Installing Xapian-bindings..."
|
||||
cd "$VIRTUAL_ENV/packages/${BINDINGS}" || exit 1
|
||||
./configure --prefix="$VIRTUAL_ENV" $PYTHON_FLAG XAPIAN_CONFIG="$VIRTUAL_ENV/bin/xapian-config" && make -j"$(nproc)" && make install
|
||||
|
||||
# clean
|
||||
rm -rf "$VIRTUAL_ENV/packages"
|
||||
|
||||
# test
|
||||
python -c "import xapian"
|
@ -460,7 +460,6 @@ Welcome to the wiki page!
|
||||
limit_age=18,
|
||||
)
|
||||
cons = Product.objects.create(
|
||||
id=settings.SITH_ECOCUP_CONS,
|
||||
name="Consigne Eco-cup",
|
||||
code="CONS",
|
||||
product_type=verre,
|
||||
@ -470,7 +469,6 @@ Welcome to the wiki page!
|
||||
club=main_club,
|
||||
)
|
||||
dcons = Product.objects.create(
|
||||
id=settings.SITH_ECOCUP_DECO,
|
||||
name="Déconsigne Eco-cup",
|
||||
code="DECO",
|
||||
product_type=verre,
|
||||
|
@ -16,7 +16,6 @@
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import connection
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@ -30,7 +29,7 @@ class Command(BaseCommand):
|
||||
if not data_dir.is_dir():
|
||||
data_dir.mkdir()
|
||||
db_path = settings.BASE_DIR / "db.sqlite3"
|
||||
if db_path.exists() or connection.vendor != "sqlite":
|
||||
if db_path.exists():
|
||||
call_command("flush", "--noinput")
|
||||
self.stdout.write("Existing database reset")
|
||||
call_command("migrate")
|
||||
|
@ -1,73 +0,0 @@
|
||||
import clip from "@arendjr/text-clipper";
|
||||
|
||||
/*
|
||||
This script adds a way to have a 'show more / show less' button
|
||||
on some text content.
|
||||
|
||||
The usage is very simple, you just have to add the attribute `show-more`
|
||||
with the desired max size to the element you want to add the button to.
|
||||
This script does html matching and is able to properly cut rendered markdown.
|
||||
|
||||
Example usage:
|
||||
<p show-more="20">
|
||||
My very long text will be cut by this script
|
||||
</p>
|
||||
*/
|
||||
|
||||
function showMore(element: HTMLElement) {
|
||||
if (!element.hasAttribute("show-more")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark element as loaded so we can hide unloaded
|
||||
// tags with css and avoid blinking text
|
||||
element.setAttribute("show-more-loaded", "");
|
||||
|
||||
const fullContent = element.innerHTML;
|
||||
const clippedContent = clip(
|
||||
element.innerHTML,
|
||||
Number.parseInt(element.getAttribute("show-more") as string),
|
||||
{
|
||||
html: true,
|
||||
},
|
||||
);
|
||||
|
||||
// If already at the desired size, we don't do anything
|
||||
if (clippedContent === fullContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const actionLink = document.createElement("a");
|
||||
actionLink.setAttribute("class", "show-more-link");
|
||||
|
||||
let opened = false;
|
||||
|
||||
const setText = () => {
|
||||
if (opened) {
|
||||
element.innerHTML = fullContent;
|
||||
actionLink.innerText = gettext("Show less");
|
||||
} else {
|
||||
element.innerHTML = clippedContent;
|
||||
actionLink.innerText = gettext("Show more");
|
||||
}
|
||||
element.appendChild(document.createElement("br"));
|
||||
element.appendChild(actionLink);
|
||||
};
|
||||
|
||||
const toggle = () => {
|
||||
opened = !opened;
|
||||
setText();
|
||||
};
|
||||
|
||||
setText();
|
||||
actionLink.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
toggle();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
for (const elem of document.querySelectorAll("[show-more]")) {
|
||||
showMore(elem as HTMLElement);
|
||||
}
|
||||
});
|
@ -1,27 +1,11 @@
|
||||
.ts-wrapper.multi .ts-control {
|
||||
min-width: calc(100% - 0.2rem);
|
||||
}
|
||||
|
||||
/* This also requires ajax-select-index.css */
|
||||
.ts-dropdown {
|
||||
width: calc(100% - 0.2rem);
|
||||
left: 0.1rem;
|
||||
top: calc(100% - 0.2rem - var(--nf-input-border-bottom-width));
|
||||
border: var(--nf-input-border-color) var(--nf-input-border-width) solid;
|
||||
border-top: none;
|
||||
border-bottom-width: var(--nf-input-border-bottom-width);
|
||||
|
||||
.option.active {
|
||||
background-color: #e5eafa;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.select-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
height: 40px;
|
||||
@ -32,44 +16,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.ts-wrapper.single {
|
||||
> .ts-control {
|
||||
box-shadow: none;
|
||||
max-width: 300px;
|
||||
background-color: var(--nf-input-background-color);
|
||||
|
||||
&::after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
> .ts-dropdown {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper input[type="text"] {
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ts-wrapper.multi, .ts-wrapper.single {
|
||||
.ts-control:has(input:focus) {
|
||||
outline: none;
|
||||
border-color: var(--nf-input-focus-border-color);
|
||||
box-shadow: none;
|
||||
}
|
||||
width: 263px; // same length as regular text inputs
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-remove_button:not(.rtl) .item .remove {
|
||||
border-left: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.ts-wrapper.multi.has-items .ts-control {
|
||||
padding: calc(var(--nf-input-size) * 0.65);
|
||||
display: flex;
|
||||
gap: calc(var(--nf-input-size) / 3);
|
||||
|
||||
.ts-wrapper.multi .ts-control {
|
||||
[data-value],
|
||||
[data-value].active {
|
||||
background-image: none;
|
||||
@ -78,17 +37,19 @@
|
||||
border: 1px solid #aaa;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
text-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
.remove {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper.focus .ts-control {
|
||||
box-shadow: none;
|
||||
}
|
||||
.ts-dropdown {
|
||||
.option.active {
|
||||
background-color: #e5eafa;
|
||||
color: inherit;
|
||||
}
|
||||
}
|
@ -48,8 +48,7 @@
|
||||
|
||||
input,
|
||||
textarea[type="text"],
|
||||
[type="number"],
|
||||
.ts-control {
|
||||
[type="number"] {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
background-color: $background-button-color;
|
||||
@ -70,7 +69,7 @@
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
select, .ts-control {
|
||||
select {
|
||||
border: none;
|
||||
text-decoration: none;
|
||||
font-size: 1.2em;
|
||||
@ -178,7 +177,7 @@ form {
|
||||
}
|
||||
|
||||
// wrap texts
|
||||
label, legend, ul.errorlist > li, .helptext {
|
||||
label, legend, ul.errorlist>li, .helptext {
|
||||
text-wrap: wrap;
|
||||
}
|
||||
|
||||
@ -219,25 +218,23 @@ form {
|
||||
}
|
||||
}
|
||||
|
||||
:not(.ts-control) > {
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
input[type="url"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="date"],
|
||||
input[type="week"],
|
||||
input[type="time"],
|
||||
input[type="search"],
|
||||
textarea,
|
||||
input[type="month"],
|
||||
select {
|
||||
min-width: 300px;
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
input[type="tel"],
|
||||
input[type="url"],
|
||||
input[type="password"],
|
||||
input[type="number"],
|
||||
input[type="date"],
|
||||
input[type="week"],
|
||||
input[type="time"],
|
||||
input[type="month"],
|
||||
input[type="search"],
|
||||
textarea,
|
||||
select {
|
||||
min-width: 300px;
|
||||
|
||||
&.grow {
|
||||
width: 95%;
|
||||
}
|
||||
&.grow {
|
||||
width: 95%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -256,8 +253,7 @@ form {
|
||||
input[type="month"],
|
||||
input[type="search"],
|
||||
textarea,
|
||||
select,
|
||||
.ts-control {
|
||||
select {
|
||||
background: var(--nf-input-background-color);
|
||||
font-size: var(--nf-input-font-size);
|
||||
border-color: var(--nf-input-border-color);
|
||||
@ -717,11 +713,7 @@ form {
|
||||
|
||||
// ---------------- SELECT
|
||||
|
||||
select,
|
||||
.ts-wrapper.multi .ts-control,
|
||||
.ts-wrapper.single .ts-control,
|
||||
.ts-wrapper.single.input-active .ts-control {
|
||||
background-color: var(--nf-input-background-color);
|
||||
select {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='feather feather-chevron-down'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
||||
background-position: right calc(var(--nf-input-size) * 0.75) bottom 50%;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -131,10 +131,6 @@ body {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
[show-more]:not([show-more-loaded]) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*--------------------------------HEADER-------------------------------*/
|
||||
|
||||
#popupheader {
|
||||
|
@ -125,14 +125,15 @@
|
||||
navbar.style.setProperty("display", current === "none" ? "block" : "none");
|
||||
}
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
// Looking at the `s` key when not typing in a form
|
||||
if (e.keyCode !== 83 || ["INPUT", "TEXTAREA", "SELECT"].includes(e.target.nodeName)) {
|
||||
return;
|
||||
$(document).keydown(function (e) {
|
||||
if ($(e.target).is('input')) { return }
|
||||
if ($(e.target).is('textarea')) { return }
|
||||
if ($(e.target).is('select')) { return }
|
||||
if (e.keyCode === 83) {
|
||||
$("#search").focus();
|
||||
return false;
|
||||
}
|
||||
document.getElementById("search").focus();
|
||||
e.preventDefault(); // Don't type the character in the focused search input
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
</body>
|
||||
|
@ -57,4 +57,13 @@
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
{% if popup %}
|
||||
<script>
|
||||
parent.$(".choose_file_widget").css("height", "75%");
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
|
@ -244,30 +244,27 @@
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script>
|
||||
// Image selection
|
||||
for (const img of document.querySelectorAll("#small_pictures img")){
|
||||
img.addEventListener("click", (e) => {
|
||||
const displayed = document.querySelector("#big_picture img");
|
||||
displayed.src = e.target.src;
|
||||
displayed.alt = e.target.alt;
|
||||
displayed.title = e.target.title;
|
||||
})
|
||||
}
|
||||
|
||||
let keys = [];
|
||||
const pattern = "71,85,89,71,85,89";
|
||||
|
||||
document.addEventListener("keydown", (e) => {
|
||||
keys.push(e.keyCode);
|
||||
if (keys.toString() === pattern) {
|
||||
keys = [];
|
||||
document.querySelector("#big_picture img").src = "{{ static('core/img/yug.jpg') }}";
|
||||
}
|
||||
if (keys.length === 6) {
|
||||
keys.shift();
|
||||
}
|
||||
$(function () {
|
||||
var keys = [];
|
||||
var pattern = "71,85,89,71,85,89";
|
||||
$(document).keydown(function (e) {
|
||||
keys.push(e.keyCode);
|
||||
if (keys.toString() == pattern) {
|
||||
keys = [];
|
||||
$("#big_picture img").attr("src", "{{ static('core/img/yug.jpg') }}");
|
||||
}
|
||||
if (keys.length == 6) {
|
||||
keys.shift();
|
||||
}
|
||||
});
|
||||
});
|
||||
$(function () {
|
||||
$("#small_pictures img").click(function () {
|
||||
$("#big_picture img").attr("src", $(this)[0].src);
|
||||
$("#big_picture img").attr("alt", $(this)[0].alt);
|
||||
$("#big_picture img").attr("title", $(this)[0].title);
|
||||
})
|
||||
});
|
||||
|
||||
$(function () {
|
||||
$("#drop_gifts").accordion({
|
||||
heightStyle: "content",
|
||||
|
@ -23,7 +23,7 @@
|
||||
<li><a href="{{ url('rootplace:operation_logs') }}">{% trans %}Operation logs{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('rootplace:delete_forum_messages') }}">{% trans %}Delete user's forum messages{% endtrans %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.has_perm("core.view_userban") %}
|
||||
{% if user.has_perm("core:view_userban") %}
|
||||
<li><a href="{{ url("rootplace:ban_list") }}">{% trans %}Bans{% endtrans %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.can_create_subscription or user.is_root %}
|
||||
|
@ -76,15 +76,7 @@ export class CounterProductSelect extends AutoCompleteSelectBase {
|
||||
return {
|
||||
...super.tomSelectSettings(),
|
||||
openOnFocus: false,
|
||||
// We make searching on exact code matching a higher priority
|
||||
// We need to manually set weights or it results on an inconsistent
|
||||
// behavior between production and development environment
|
||||
searchField: [
|
||||
// @ts-ignore documentation says it's fine, specified type is wrong
|
||||
{ field: "code", weight: 2 },
|
||||
// @ts-ignore documentation says it's fine, specified type is wrong
|
||||
{ field: "text", weight: 0.5 },
|
||||
],
|
||||
searchField: ["code", "text"],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -236,10 +236,6 @@ class TestCounterClick(TestFullClickBase):
|
||||
BanGroup.objects.get(pk=settings.SITH_GROUP_BANNED_COUNTER_ID)
|
||||
)
|
||||
|
||||
cls.gift = product_recipe.make(
|
||||
selling_price="-1.5",
|
||||
special_selling_price="-1.5",
|
||||
)
|
||||
cls.beer = product_recipe.make(
|
||||
limit_age=18, selling_price="1.5", special_selling_price="1"
|
||||
)
|
||||
@ -257,12 +253,7 @@ class TestCounterClick(TestFullClickBase):
|
||||
limit_age=0, selling_price="1.5", special_selling_price="1"
|
||||
)
|
||||
|
||||
cls.cons = Product.objects.get(id=settings.SITH_ECOCUP_CONS)
|
||||
cls.dcons = Product.objects.get(id=settings.SITH_ECOCUP_DECO)
|
||||
|
||||
cls.counter.products.add(
|
||||
cls.gift, cls.beer, cls.beer_tap, cls.snack, cls.cons, cls.dcons
|
||||
)
|
||||
cls.counter.products.add(cls.beer, cls.beer_tap, cls.snack)
|
||||
|
||||
cls.other_counter.products.add(cls.snack)
|
||||
|
||||
@ -603,84 +594,6 @@ class TestCounterClick(TestFullClickBase):
|
||||
else:
|
||||
assert not counter.has_annotated_barman
|
||||
|
||||
def test_selling_ordering(self):
|
||||
# Cheaper items should be processed with a higher priority
|
||||
self.login_in_bar(self.barmen)
|
||||
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[
|
||||
BasketItem(self.beer.id, 1),
|
||||
BasketItem(self.gift.id, 1),
|
||||
],
|
||||
).status_code
|
||||
== 302
|
||||
)
|
||||
|
||||
assert self.updated_amount(self.customer) == 0
|
||||
|
||||
def test_recordings(self):
|
||||
self.refill_user(self.customer, self.cons.selling_price * 3)
|
||||
self.login_in_bar(self.barmen)
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[BasketItem(self.cons.id, 3)],
|
||||
).status_code
|
||||
== 302
|
||||
)
|
||||
assert self.updated_amount(self.customer) == 0
|
||||
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[BasketItem(self.dcons.id, 3)],
|
||||
).status_code
|
||||
== 302
|
||||
)
|
||||
|
||||
assert self.updated_amount(self.customer) == self.dcons.selling_price * -3
|
||||
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[BasketItem(self.dcons.id, settings.SITH_ECOCUP_LIMIT)],
|
||||
).status_code
|
||||
== 302
|
||||
)
|
||||
|
||||
assert self.updated_amount(self.customer) == self.dcons.selling_price * (
|
||||
-3 - settings.SITH_ECOCUP_LIMIT
|
||||
)
|
||||
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[BasketItem(self.dcons.id, 1)],
|
||||
).status_code
|
||||
== 200
|
||||
)
|
||||
|
||||
assert self.updated_amount(self.customer) == self.dcons.selling_price * (
|
||||
-3 - settings.SITH_ECOCUP_LIMIT
|
||||
)
|
||||
|
||||
assert (
|
||||
self.submit_basket(
|
||||
self.customer,
|
||||
[
|
||||
BasketItem(self.cons.id, 1),
|
||||
BasketItem(self.dcons.id, 1),
|
||||
],
|
||||
).status_code
|
||||
== 302
|
||||
)
|
||||
|
||||
assert self.updated_amount(self.customer) == self.dcons.selling_price * (
|
||||
-3 - settings.SITH_ECOCUP_LIMIT
|
||||
)
|
||||
|
||||
|
||||
class TestCounterStats(TestCase):
|
||||
@classmethod
|
||||
|
@ -194,11 +194,7 @@ class CounterClick(CounterTabsMixin, CanViewMixin, SingleObjectMixin, FormView):
|
||||
with transaction.atomic():
|
||||
self.request.session["last_basket"] = []
|
||||
|
||||
# We sort items from cheap to expensive
|
||||
# This is important because some items have a negative price
|
||||
# Negative priced items gives money to the customer and should
|
||||
# be processed first so that we don't throw a not enough money error
|
||||
for form in sorted(formset, key=lambda form: form.product.price):
|
||||
for form in formset:
|
||||
self.request.session["last_basket"].append(
|
||||
f"{form.cleaned_data['quantity']} x {form.product.name}"
|
||||
)
|
||||
|
@ -1,36 +0,0 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:16.6
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
ports:
|
||||
- "5431:5432"
|
||||
environment:
|
||||
POSTGRES_USER: sith
|
||||
POSTGRES_PASSWORD: sith
|
||||
POSTGRES_DB: sith
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
ports:
|
||||
- "6378:6379"
|
||||
command: redis-server
|
||||
volumes:
|
||||
- redis_data:/var/lib/redis/data/
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
|
@ -2,13 +2,13 @@
|
||||
|
||||
Pour connecter l'application à une instance de sentry (ex: https://sentry.io),
|
||||
il est nécessaire de configurer la variable `SENTRY_DSN`
|
||||
dans le fichier `.env`.
|
||||
dans le fichier `settings_custom.py`.
|
||||
Cette variable est composée d'un lien complet vers votre projet sentry.
|
||||
|
||||
## Récupérer les statiques
|
||||
|
||||
Nous utilisons du SCSS dans le projet.
|
||||
En environnement de développement (`DEBUG=true`),
|
||||
En environnement de développement (`DEBUG=True`),
|
||||
le SCSS est compilé à chaque fois que le fichier est demandé.
|
||||
Pour la production, le projet considère
|
||||
que chacun des fichiers est déjà compilé.
|
||||
|
@ -47,19 +47,19 @@ Commencez par installer les dépendances système :
|
||||
=== "Debian/Ubuntu"
|
||||
|
||||
```bash
|
||||
sudo apt install postgresql libq-dev nginx
|
||||
sudo apt install postgresql redis libq-dev nginx
|
||||
```
|
||||
|
||||
=== "Arch Linux"
|
||||
|
||||
```bash
|
||||
sudo pacman -S postgresql nginx
|
||||
sudo pacman -S postgresql redis nginx
|
||||
```
|
||||
|
||||
=== "macOS"
|
||||
|
||||
```bash
|
||||
brew install postgresql lipbq nginx
|
||||
brew install postgresql redis lipbq nginx
|
||||
export PATH="/usr/local/opt/libpq/bin:$PATH"
|
||||
source ~/.zshrc
|
||||
```
|
||||
@ -77,6 +77,34 @@ uv sync --group prod
|
||||
C'est parce que ces dépendances compilent certains modules
|
||||
à l'installation.
|
||||
|
||||
## Configurer Redis
|
||||
|
||||
Redis est utilisé comme cache.
|
||||
Assurez-vous qu'il tourne :
|
||||
|
||||
```bash
|
||||
sudo systemctl redis status
|
||||
```
|
||||
|
||||
Et s'il ne tourne pas, démarrez-le :
|
||||
|
||||
```bash
|
||||
sudo systemctl start redis
|
||||
sudo systemctl enable redis # si vous voulez que redis démarre automatiquement au boot
|
||||
```
|
||||
|
||||
Puis ajoutez le code suivant à la fin de votre fichier
|
||||
`settings_custom.py` :
|
||||
|
||||
```python
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.redis.RedisCache",
|
||||
"LOCATION": "redis://127.0.0.1:6379",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configurer PostgreSQL
|
||||
|
||||
PostgreSQL est utilisé comme base de données.
|
||||
@ -111,19 +139,26 @@ en étant connecté en tant que postgres :
|
||||
psql -d sith -c "GRANT ALL PRIVILEGES ON SCHEMA public to sith";
|
||||
```
|
||||
|
||||
Puis modifiez votre `.env`.
|
||||
Dedans, décommentez l'url de la base de données
|
||||
de postgres et commentez l'url de sqlite :
|
||||
Puis ajoutez le code suivant à la fin de votre
|
||||
`settings_custom.py` :
|
||||
|
||||
```dotenv
|
||||
#DATABASE_URL=sqlite:///db.sqlite3
|
||||
DATABASE_URL=postgres://sith:password@localhost:5432/sith
|
||||
```python
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "sith",
|
||||
"USER": "sith",
|
||||
"PASSWORD": "password",
|
||||
"HOST": "localhost",
|
||||
"PORT": "", # laissez ce champ vide pour que le choix du port soit automatique
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Enfin, créez vos données :
|
||||
|
||||
```bash
|
||||
uv run ./manage.py setup
|
||||
uv run ./manage.py populate
|
||||
```
|
||||
|
||||
!!! note
|
||||
@ -212,7 +247,7 @@ Puis lancez ou relancez nginx :
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
Dans votre `.env`, remplacez `DEBUG=true` par `DEBUG=false`.
|
||||
Dans votre `settings_custom.py`, remplacez `DEBUG=True` par `DEBUG=False`.
|
||||
|
||||
Enfin, démarrez le serveur Django :
|
||||
|
||||
|
@ -7,7 +7,6 @@ Certaines dépendances sont nécessaires niveau système :
|
||||
- libjpeg
|
||||
- zlib1g-dev
|
||||
- gettext
|
||||
- redis
|
||||
|
||||
### Installer WSL
|
||||
|
||||
@ -66,8 +65,8 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
||||
|
||||
```bash
|
||||
sudo apt install curl build-essential libssl-dev \
|
||||
libjpeg-dev zlib1g-dev npm libffi-dev pkg-config \
|
||||
gettext git redis
|
||||
libjpeg-dev zlib1g-dev npm libffi-dev pkg-config \
|
||||
gettext git
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh
|
||||
```
|
||||
|
||||
@ -76,7 +75,7 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
||||
```bash
|
||||
sudo pacman -Syu # on s'assure que les dépôts et le système sont à jour
|
||||
|
||||
sudo pacman -S uv gcc git gettext pkgconf npm redis
|
||||
sudo pacman -S uv gcc git gettext pkgconf npm
|
||||
```
|
||||
|
||||
=== "macOS"
|
||||
@ -85,7 +84,7 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
||||
Il est également nécessaire d'avoir installé xcode
|
||||
|
||||
```bash
|
||||
brew install git uv npm redis
|
||||
brew install git uv npm
|
||||
|
||||
# Pour bien configurer gettext
|
||||
brew link gettext # (suivez bien les instructions supplémentaires affichées)
|
||||
@ -100,24 +99,6 @@ cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
||||
Python ne fait pas parti des dépendances puisqu'il est automatiquement
|
||||
installé par uv.
|
||||
|
||||
Parmi les dépendances installées se trouve redis (que nous utilisons comme cache).
|
||||
Redis est un service qui doit être activé pour être utilisé.
|
||||
Pour cela, effectuez les commandes :
|
||||
|
||||
```bash
|
||||
sudo systemctl start redis
|
||||
sudo systemctl enable redis # si vous voulez que redis démarre automatiquement au boot
|
||||
```
|
||||
|
||||
Parmi les dépendances installées se trouve redis (que nous utilisons comme cache).
|
||||
Redis est un service qui doit être activé pour être utilisé.
|
||||
Pour cela, effectuez les commandes :
|
||||
|
||||
```bash
|
||||
sudo systemctl start redis
|
||||
sudo systemctl enable redis # si vous voulez que redis démarre automatiquement au boot
|
||||
```
|
||||
|
||||
## Finaliser l'installation
|
||||
|
||||
Clonez le projet (depuis votre console WSL, si vous utilisez WSL)
|
||||
@ -139,24 +120,20 @@ uv run ./manage.py install_xapian
|
||||
de texte à l'écran.
|
||||
C'est normal, il ne faut pas avoir peur.
|
||||
|
||||
Une fois les dépendances installées, il faut encore
|
||||
mettre en place quelques éléments de configuration,
|
||||
qui peuvent varier d'un environnement à l'autre.
|
||||
Ces variables sont stockées dans un fichier `.env`.
|
||||
Pour le créer, vous pouvez copier le fichier `.env.example` :
|
||||
Maintenant que les dépendances sont installées, nous
|
||||
allons créer la base de données, la remplir avec des données de test,
|
||||
et compiler les traductions.
|
||||
Cependant, avant de faire cela, il est nécessaire de modifier
|
||||
la configuration pour signifier que nous sommes en mode développement.
|
||||
Pour cela, nous allons créer un fichier `sith/settings_custom.py`
|
||||
et l'utiliser pour surcharger les settings de base.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
echo "DEBUG=True" > sith/settings_custom.py
|
||||
echo 'SITH_URL = "localhost:8000"' >> sith/settings_custom.py
|
||||
```
|
||||
|
||||
Les variables par défaut contenues dans le fichier `.env`
|
||||
devraient convenir pour le développement, sans modification.
|
||||
|
||||
Maintenant que les dépendances sont installées
|
||||
et la configuration remplie, nous allons pouvoir générer
|
||||
des données utiles pendant le développement.
|
||||
|
||||
Pour cela, lancez les commandes suivantes :
|
||||
Enfin, nous pouvons lancer les commandes suivantes :
|
||||
|
||||
```bash
|
||||
# Prépare la base de données
|
||||
@ -194,30 +171,6 @@ uv run ./manage.py runserver
|
||||
[http://localhost:8000/api/docs](http://localhost:8000/api/docs),
|
||||
une interface swagger, avec toutes les routes de l'API.
|
||||
|
||||
!!! question "Pourquoi l'installation est aussi complexe ?"
|
||||
|
||||
Cette question nous a été posée de nombreuses fois par des personnes
|
||||
essayant d'installer le projet.
|
||||
Il y a en effet un certain nombre d'étapes à suivre,
|
||||
de paquets à installer et de commandes à exécuter.
|
||||
|
||||
Le processus d'installation peut donc sembler complexe.
|
||||
|
||||
En réalité, il est difficile de faire plus simple.
|
||||
En effet, un site web a besoin de beaucoup de composants
|
||||
pour être développé : il lui faut au minimum
|
||||
une base de données, un cache, un bundler Javascript
|
||||
et un interpréteur pour le code du serveur.
|
||||
Pour nos besoin particuliers, nous utilisons également
|
||||
un moteur de recherche full-text.
|
||||
|
||||
Nous avons tenté au maximum de limiter le nombre de dépendances
|
||||
et de sélecionner les plus simples à installer.
|
||||
Cependant, il est impossible de retirer l'intégralité
|
||||
de la complexité du processus.
|
||||
Si vous rencontrez des difficulté lors de l'installation,
|
||||
n'hésitez pas à demander de l'aide.
|
||||
|
||||
## Générer la documentation
|
||||
|
||||
La documentation est automatiquement mise en ligne à chaque envoi de code sur GitHub.
|
||||
|
@ -72,14 +72,12 @@ sith/
|
||||
├── .gitattributes
|
||||
├── .gitignore
|
||||
├── .mailmap
|
||||
├── .env (26)
|
||||
├── .env.example (27)
|
||||
├── manage.py (28)
|
||||
├── mkdocs.yml (29)
|
||||
├── manage.py (26)
|
||||
├── mkdocs.yml (27)
|
||||
├── uv.lock
|
||||
├── pyproject.toml (30)
|
||||
├── .venv/ (31)
|
||||
├── .python-version (32)
|
||||
├── pyproject.toml (28)
|
||||
├── .venv/ (29)
|
||||
├── .python-version (30)
|
||||
└── README.md
|
||||
```
|
||||
</div>
|
||||
@ -123,19 +121,15 @@ sith/
|
||||
de manière transparente pour l'utilisateur.
|
||||
24. Fichier de configuration de coverage.
|
||||
25. Fichier de configuration de direnv.
|
||||
26. Contient les variables d'environnement, qui sont susceptibles
|
||||
de varier d'une machine à l'autre.
|
||||
27. Contient des valeurs par défaut pour le `.env`
|
||||
pouvant convenir à un environnment de développement local
|
||||
28. Fichier généré automatiquement par Django. C'est lui
|
||||
26. Fichier généré automatiquement par Django. C'est lui
|
||||
qui permet d'appeler des commandes de gestion du projet
|
||||
avec la syntaxe `python ./manage.py <nom de la commande>`
|
||||
29. Le fichier de configuration de la documentation,
|
||||
27. Le fichier de configuration de la documentation,
|
||||
avec ses plugins et sa table des matières.
|
||||
30. Le fichier où sont déclarés les dépendances et la configuration
|
||||
28. Le fichier où sont déclarés les dépendances et la configuration
|
||||
de certaines d'entre elles.
|
||||
31. Dossier d'environnement virtuel généré par uv
|
||||
32. Fichier qui contrôle quelle version de python utiliser pour le projet
|
||||
29. Dossier d'environnement virtuel généré par uv
|
||||
30. Fichier qui contrôle quel version de python utiliser pour le projet
|
||||
|
||||
|
||||
## L'application principale
|
||||
@ -150,9 +144,10 @@ Il est organisé comme suit :
|
||||
```
|
||||
sith/
|
||||
├── settings.py (1)
|
||||
├── toolbar_debug.py (2)
|
||||
├── urls.py (3)
|
||||
└── wsgi.py (4)
|
||||
├── settings_custom.py (2)
|
||||
├── toolbar_debug.py (3)
|
||||
├── urls.py (4)
|
||||
└── wsgi.py (5)
|
||||
```
|
||||
</div>
|
||||
|
||||
@ -160,10 +155,13 @@ sith/
|
||||
Ce fichier contient les paramètres de configuration du projet.
|
||||
Par exemple, il contient la liste des applications
|
||||
installées dans le projet.
|
||||
2. Configuration de la barre de debug.
|
||||
2. Configuration maison pour votre environnement.
|
||||
Toute variable que vous définissez dans ce fichier sera prioritaire
|
||||
sur la configuration donnée dans `settings.py`.
|
||||
3. Configuration de la barre de debug.
|
||||
C'est inutilisé en prod, mais c'est très pratique en développement.
|
||||
3. Fichier de configuration des urls du projet.
|
||||
4. Fichier de configuration pour le serveur WSGI.
|
||||
4. Fichier de configuration des urls du projet.
|
||||
5. Fichier de configuration pour le serveur WSGI.
|
||||
WSGI est un protocole de communication entre le serveur
|
||||
et les applications.
|
||||
Ce fichier ne vous servira sans doute pas sur un environnement
|
||||
|
@ -15,8 +15,8 @@ $min_col_width: 100px;
|
||||
flex-direction: row;
|
||||
gap: $gap;
|
||||
|
||||
>input,
|
||||
>label {
|
||||
> input,
|
||||
> label {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -25,12 +25,12 @@ $min_col_width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
#page #content {
|
||||
overflow-x: scroll;
|
||||
.election_vote {
|
||||
overflow-x: scroll !important;
|
||||
}
|
||||
|
||||
.election_table {
|
||||
width: inherit;
|
||||
width: 100%;
|
||||
|
||||
>.lists {
|
||||
display: flex;
|
||||
@ -93,30 +93,16 @@ $min_col_width: 100px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 0;
|
||||
row-gap: 10px;
|
||||
padding: $padding;
|
||||
width: 100%;
|
||||
|
||||
|
||||
>.role_text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
>h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.role_description {
|
||||
flex-grow: 1;
|
||||
>p {
|
||||
margin-top: .5em;
|
||||
text-wrap: auto;
|
||||
text-align: left;
|
||||
|
||||
// Show more/less element
|
||||
a {
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,9 +112,9 @@ $min_col_width: 100px;
|
||||
align-items: center;
|
||||
gap: $gap;
|
||||
|
||||
>button,
|
||||
>button>i,
|
||||
>a {
|
||||
> button,
|
||||
> button > i,
|
||||
> a {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #e9e9e9;
|
||||
@ -141,23 +127,23 @@ $min_col_width: 100px;
|
||||
justify-content: center;
|
||||
|
||||
&:hover,
|
||||
&:hover>i {
|
||||
&:hover > i {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
>button {
|
||||
> button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
>button[disabled] {
|
||||
> button[disabled] {
|
||||
background-color: #eee;
|
||||
cursor: not-allowed;
|
||||
|
||||
>i,
|
||||
&:hover,
|
||||
&:hover>i {
|
||||
&:hover > i {
|
||||
background-color: #eee;
|
||||
}
|
||||
}
|
||||
@ -192,12 +178,12 @@ $min_col_width: 100px;
|
||||
width: 100%;
|
||||
gap: $gap;
|
||||
|
||||
>input[type="radio"]:checked+label,
|
||||
>input[type="checkbox"]:checked+label {
|
||||
>input[type="radio"]:checked + label,
|
||||
>input[type="checkbox"]:checked + label {
|
||||
background-color: lightgray;
|
||||
border-radius: 10px;
|
||||
|
||||
>figure>.edit_btns>a:hover {
|
||||
>figure>.edit_btns>a:hover{
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
@ -229,9 +215,7 @@ $min_col_width: 100px;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.candidate_program {
|
||||
text-wrap: auto;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
@ -244,7 +228,7 @@ $min_col_width: 100px;
|
||||
right: $gap;
|
||||
gap: $gap;
|
||||
|
||||
>a {
|
||||
> a {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #e9e9e9;
|
||||
@ -269,44 +253,40 @@ $min_col_width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
#content {
|
||||
.election_details {
|
||||
margin: .5em 0;
|
||||
}
|
||||
|
||||
.election_details {
|
||||
margin: .5em 0;
|
||||
}
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: $gap;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: $gap;
|
||||
}
|
||||
.button {
|
||||
border: none;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
background-color: $primary-neutral-light-color;
|
||||
padding: 0.4em;
|
||||
margin: 0.1em;
|
||||
font-size: 1.18em;
|
||||
border-radius: 5px;
|
||||
box-shadow: #dfdfdf 0 0 1px;
|
||||
cursor: pointer;
|
||||
|
||||
.button {
|
||||
border: none;
|
||||
&:hover {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
background-color: $primary-neutral-light-color;
|
||||
padding: 0.4em;
|
||||
margin: 0.1em;
|
||||
font-size: 1.18em;
|
||||
border-radius: 5px;
|
||||
box-shadow: #dfdfdf 0 0 1px;
|
||||
cursor: pointer;
|
||||
background: #d4d4d4;
|
||||
}
|
||||
|
||||
&_send {
|
||||
background-color: #59aee2;
|
||||
&:hover {
|
||||
color: black;
|
||||
background: #d4d4d4;
|
||||
}
|
||||
|
||||
&_send {
|
||||
background-color: #59aee2;
|
||||
|
||||
&:hover {
|
||||
background-color: rgb(130, 186, 235);
|
||||
}
|
||||
background-color: rgb(130, 186, 235);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,12 +4,13 @@
|
||||
{{ object.title }}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_js %}
|
||||
<script type="module" src="{{ static('bundled/core/read-more-index.ts') }}"></script>
|
||||
{% endblock %}
|
||||
{% block head %}
|
||||
{{ super() -}}
|
||||
<link rel="stylesheet" href="{{ static('election/css/election.scss') }}">
|
||||
{%- endblock %}
|
||||
|
||||
{% block additional_css %}
|
||||
<link rel="stylesheet" href="{{ static('election/css/election.scss') }}">
|
||||
<script src="{{ static('bundled/vendored/jquery.shorten.min.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -46,6 +47,7 @@
|
||||
{% csrf_token %}
|
||||
<table class="election_table">
|
||||
{%- set election_lists = election.election_lists.all() -%}
|
||||
<caption></caption>
|
||||
<thead class="lists">
|
||||
<tr>
|
||||
<th class="column" style="width: {{ 100 / (election_lists.count() + 1) }}%">{% trans %}Blank vote{% endtrans %}</th>
|
||||
@ -68,7 +70,7 @@
|
||||
<td class="role_title">
|
||||
<div class="role_text">
|
||||
<h4>{{ role.title }}</h4>
|
||||
<p class="role_description" show-more="300">{{ role.description }}</p>
|
||||
<p class="role_description">{{ role.description }}</p>
|
||||
{%- if role.max_choice > 1 and not election.has_voted(user) and election.can_vote(user) %}
|
||||
<strong>{% trans %}You may choose up to{% endtrans %} {{ role.max_choice }} {% trans %}people.{% endtrans %}</strong>
|
||||
{%- endif %}
|
||||
@ -139,9 +141,7 @@
|
||||
<figcaption class="candidate__details">
|
||||
<h5>{{ candidature.user.first_name }} <em>{{candidature.user.nick_name or ''}} </em>{{ candidature.user.last_name }}</h5>
|
||||
{%- if not election.is_vote_finished %}
|
||||
<q class="candidate_program" show-more="200">
|
||||
{{ candidature.program|markdown or '' }}
|
||||
</q>
|
||||
<q class="candidate_program">{{ candidature.program | markdown or '' }}</q>
|
||||
{%- endif %}
|
||||
</figcaption>
|
||||
{%- if user.can_edit(candidature) -%}
|
||||
@ -200,6 +200,18 @@
|
||||
|
||||
{% block script %}
|
||||
{{ super() }}
|
||||
<script type="text/javascript">
|
||||
$('.role_description').shorten({
|
||||
moreText: "{% trans %}Show more{% endtrans %}",
|
||||
lessText: "{% trans %}Show less{% endtrans %}",
|
||||
showChars: 50
|
||||
});
|
||||
$('.candidate_program').shorten({
|
||||
moreText: "{% trans %}Show more{% endtrans %}",
|
||||
lessText: "{% trans %}Show less{% endtrans %}",
|
||||
showChars: 200
|
||||
});
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
document.querySelectorAll('.role__multiple-choices').forEach(setupRestrictions);
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-08 12:23+0100\n"
|
||||
"POT-Creation-Date: 2025-01-05 16:39+0100\n"
|
||||
"PO-Revision-Date: 2016-07-18\n"
|
||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@ -935,10 +935,6 @@ msgstr "rôle"
|
||||
msgid "description"
|
||||
msgstr "description"
|
||||
|
||||
#: club/models.py
|
||||
msgid "past member"
|
||||
msgstr "ancien membre"
|
||||
|
||||
#: club/models.py
|
||||
msgid "Email address"
|
||||
msgstr "Adresse email"
|
||||
@ -2066,12 +2062,16 @@ msgid "reason"
|
||||
msgstr "raison"
|
||||
|
||||
#: core/models.py
|
||||
#, fuzzy
|
||||
#| msgid "user"
|
||||
msgid "user ban"
|
||||
msgstr "utilisateur banni"
|
||||
msgstr "utilisateur"
|
||||
|
||||
#: core/models.py
|
||||
#, fuzzy
|
||||
#| msgid "user"
|
||||
msgid "user bans"
|
||||
msgstr "utilisateurs bannis"
|
||||
msgstr "utilisateur"
|
||||
|
||||
#: core/models.py
|
||||
msgid "receive the Weekmail"
|
||||
@ -3328,8 +3328,8 @@ msgstr "Nom d'utilisateur, email, ou numéro de compte AE"
|
||||
|
||||
#: core/views/forms.py
|
||||
msgid ""
|
||||
"Profile: you need to be visible on the picture, in order to be recognized "
|
||||
"(e.g. by the barmen)"
|
||||
"Profile: you need to be visible on the picture, in order to be recognized (e."
|
||||
"g. by the barmen)"
|
||||
msgstr ""
|
||||
"Photo de profil: vous devez être visible sur la photo afin d'être reconnu "
|
||||
"(par exemple par les barmen)"
|
||||
@ -3935,8 +3935,8 @@ msgstr ""
|
||||
#: counter/templates/counter/mails/account_dump.jinja
|
||||
msgid "If you think this was a mistake, please mail us at ae@utbm.fr."
|
||||
msgstr ""
|
||||
"Si vous pensez qu'il s'agit d'une erreur, veuillez envoyer un mail à "
|
||||
"ae@utbm.fr."
|
||||
"Si vous pensez qu'il s'agit d'une erreur, veuillez envoyer un mail à ae@utbm."
|
||||
"fr."
|
||||
|
||||
#: counter/templates/counter/mails/account_dump.jinja
|
||||
msgid ""
|
||||
@ -4456,6 +4456,14 @@ msgstr "Ajouter un nouveau rôle"
|
||||
msgid "Submit the vote !"
|
||||
msgstr "Envoyer le vote !"
|
||||
|
||||
#: election/templates/election/election_detail.jinja
|
||||
msgid "Show more"
|
||||
msgstr "Montrer plus"
|
||||
|
||||
#: election/templates/election/election_detail.jinja
|
||||
msgid "Show less"
|
||||
msgstr "Montrer moins"
|
||||
|
||||
#: election/templates/election/election_list.jinja
|
||||
msgid "Election list"
|
||||
msgstr "Liste des élections"
|
||||
|
@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-08 12:23+0100\n"
|
||||
"POT-Creation-Date: 2025-01-04 23:07+0100\n"
|
||||
"PO-Revision-Date: 2024-09-17 11:54+0200\n"
|
||||
"Last-Translator: Sli <antoine@bartuccio.fr>\n"
|
||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||
@ -113,14 +113,6 @@ msgstr "Guide markdown"
|
||||
msgid "Unsupported NFC card"
|
||||
msgstr "Carte NFC non supportée"
|
||||
|
||||
#: core/static/bundled/core/read-more-index.ts
|
||||
msgid "Show less"
|
||||
msgstr "Montrer moins"
|
||||
|
||||
#: core/static/bundled/core/read-more-index.ts
|
||||
msgid "Show more"
|
||||
msgstr "Montrer plus"
|
||||
|
||||
#: core/static/bundled/user/family-graph-index.js
|
||||
msgid "family_tree.%(extension)s"
|
||||
msgstr "arbre_genealogique.%(extension)s"
|
||||
|
13
package-lock.json
generated
13
package-lock.json
generated
@ -10,7 +10,6 @@
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@alpinejs/sort": "^3.14.7",
|
||||
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
@ -31,6 +30,7 @@
|
||||
"htmx.org": "^2.0.3",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery.shorten": "^1.0.0",
|
||||
"native-file-system-adapter": "^3.0.1",
|
||||
"three": "^0.169.0",
|
||||
"three-spritetext": "^1.9.0",
|
||||
@ -85,12 +85,6 @@
|
||||
"url": "https://github.com/sponsors/philsturgeon"
|
||||
}
|
||||
},
|
||||
"node_modules/@arendjr/text-clipper": {
|
||||
"name": "@jsr/arendjr__text-clipper",
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://npm.jsr.io/~/11/@jsr/arendjr__text-clipper/3.0.0.tgz",
|
||||
"integrity": "sha512-Uu3CYSvFrNdDkYKEaEKHAk0decaxVFlSSqf50Okte/9vJjO2rESzPF1ngQjS9H1aX45RIXRGMYOXJ/LPDFwUdQ=="
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.24.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz",
|
||||
@ -4398,6 +4392,11 @@
|
||||
"jquery": ">=1.12.0 <5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jquery.shorten": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jquery.shorten/-/jquery.shorten-1.0.0.tgz",
|
||||
"integrity": "sha512-49rJlpcyVI/Y2eQRwSexyz6l+fwTKfurO0XttXK4XnG9eQxIuE2Fb4rwNqnsnzStJ8M7ynlhH31fWE9P70B9rg=="
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
|
@ -36,7 +36,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alpinejs/sort": "^3.14.7",
|
||||
"@arendjr/text-clipper": "npm:@jsr/arendjr__text-clipper@^3.0.0",
|
||||
"@fortawesome/fontawesome-free": "^6.6.0",
|
||||
"@fullcalendar/core": "^6.1.15",
|
||||
"@fullcalendar/daygrid": "^6.1.15",
|
||||
@ -57,6 +56,7 @@
|
||||
"htmx.org": "^2.0.3",
|
||||
"jquery": "^3.7.1",
|
||||
"jquery-ui": "^1.14.0",
|
||||
"jquery.shorten": "^1.0.0",
|
||||
"native-file-system-adapter": "^3.0.1",
|
||||
"three": "^0.169.0",
|
||||
"three-spritetext": "^1.9.0",
|
||||
|
@ -44,8 +44,6 @@ dependencies = [
|
||||
"django-honeypot<2.0.0,>=1.2.1",
|
||||
"pydantic-extra-types<3.0.0,>=2.10.1",
|
||||
"ical<9.0.0,>=8.3.0",
|
||||
"redis[hiredis]<6.0.0,>=5.2.0",
|
||||
"environs[django]<15.0.0,>=14.1.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@ -55,6 +53,7 @@ documentation = "https://sith-ae.readthedocs.io/"
|
||||
[dependency-groups]
|
||||
prod = [
|
||||
"psycopg[c]<4.0.0,>=3.2.3",
|
||||
"redis[hiredis]<6.0.0,>=5.2.0",
|
||||
]
|
||||
dev = [
|
||||
"django-debug-toolbar<5.0.0,>=4.4.6",
|
||||
@ -85,6 +84,8 @@ default-groups = ["dev", "tests", "docs"]
|
||||
|
||||
[tool.xapian]
|
||||
version = "1.4.25"
|
||||
core-sha1 = "e2b4b4cf6076873ec9402cab7b9a3b71dcf95e20"
|
||||
bindings-sha1 = "782f568d2ea3ca751c519a2814a35c7dc86df3a4"
|
||||
|
||||
[tool.ruff]
|
||||
output-format = "concise" # makes ruff error logs easier to read
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% if user.has_perm("core.add_userban") %}
|
||||
{% if user.has_perm("core:add_userban") %}
|
||||
<a href="{{ url("rootplace:ban_create") }}" class="btn btn-red margin-bottom">
|
||||
<i class="fa fa-person-circle-xmark"></i>
|
||||
{% trans %}Ban a user{% endtrans %}
|
||||
@ -44,7 +44,7 @@
|
||||
<summary class="clickable">{% trans %}Reason{% endtrans %}</summary>
|
||||
<p>{{ user_ban.reason }}</p>
|
||||
</details>
|
||||
{% if user.has_perm("core.delete_userban") %}
|
||||
{% if user.has_perm("core:delete_userban") %}
|
||||
<span>
|
||||
<a
|
||||
href="{{ url("rootplace:ban_remove", ban_id=user_ban.id) }}"
|
||||
|
161
sith/settings.py
161
sith/settings.py
@ -34,6 +34,7 @@ https://docs.djangoproject.com/en/1.8/ref/settings/
|
||||
"""
|
||||
|
||||
import binascii
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
@ -42,32 +43,25 @@ from pathlib import Path
|
||||
import sentry_sdk
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from environs import Env
|
||||
from sentry_sdk.integrations.django import DjangoIntegration
|
||||
|
||||
from .honeypot import custom_honeypot_error
|
||||
|
||||
env = Env()
|
||||
env.read_env()
|
||||
|
||||
BASE_DIR = Path(__file__).parent.parent.resolve()
|
||||
|
||||
os.environ["HTTPS"] = "off"
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = env.str("SECRET_KEY")
|
||||
SECRET_KEY = "(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = env.bool("DEBUG", default=False)
|
||||
DEBUG = False
|
||||
TESTING = "pytest" in sys.modules
|
||||
INTERNAL_IPS = ["127.0.0.1"]
|
||||
|
||||
# force csrf tokens and cookies to be secure when in https
|
||||
CSRF_COOKIE_SECURE = env.bool("HTTPS", default=True)
|
||||
SESSION_COOKIE_SECURE = env.bool("HTTPS", default=True)
|
||||
X_FRAME_OPTIONS = "SAMEORIGIN"
|
||||
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
|
||||
# Application definition
|
||||
@ -214,12 +208,12 @@ WSGI_APPLICATION = "sith.wsgi.application"
|
||||
# Database
|
||||
|
||||
DATABASES = {
|
||||
"default": env.dj_db_url("DATABASE_URL", conn_max_age=None, conn_health_checks=True)
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
},
|
||||
}
|
||||
|
||||
if "CACHE_URL" in os.environ:
|
||||
CACHES = {"default": env.dj_cache_url("CACHE_URL")}
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
|
||||
|
||||
# Logging
|
||||
@ -271,13 +265,13 @@ PHONENUMBER_DEFAULT_REGION = "FR"
|
||||
|
||||
# Medias
|
||||
MEDIA_URL = "/data/"
|
||||
MEDIA_ROOT = env.path("MEDIA_ROOT", default=BASE_DIR / "data")
|
||||
MEDIA_ROOT = BASE_DIR / "data"
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.8/howto/static-files/
|
||||
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = env.path("STATIC_ROOT", default=BASE_DIR / "static")
|
||||
STATIC_ROOT = BASE_DIR / "static"
|
||||
|
||||
# Static files finders which allow to see static folder in all apps
|
||||
STATICFILES_FINDERS = [
|
||||
@ -301,28 +295,24 @@ AUTHENTICATION_BACKENDS = ["core.auth_backends.SithModelBackend"]
|
||||
LOGIN_URL = "/login"
|
||||
LOGOUT_URL = "/logout"
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
DEFAULT_FROM_EMAIL = env.str("DEFAULT_FROM_EMAIL", default="bibou@git.an")
|
||||
SITH_COM_EMAIL = env.str("SITH_COM_EMAIL", default="bibou_com@git.an")
|
||||
DEFAULT_FROM_EMAIL = "bibou@git.an"
|
||||
SITH_COM_EMAIL = "bibou_com@git.an"
|
||||
|
||||
# Those values are to be changed in production to be more effective
|
||||
HONEYPOT_FIELD_NAME = env.str("HONEYPOT_FIELD_NAME", default="body2")
|
||||
HONEYPOT_VALUE = env.str("HONEYPOT_VALUE", default="content")
|
||||
HONEYPOT_FIELD_NAME = "body2"
|
||||
HONEYPOT_VALUE = "content"
|
||||
HONEYPOT_RESPONDER = custom_honeypot_error # Make honeypot errors less suspicious
|
||||
HONEYPOT_FIELD_NAME_FORUM = env.str(
|
||||
"HONEYPOT_FIELD_NAME_FORUM", default="message2"
|
||||
) # Only used on forum
|
||||
HONEYPOT_FIELD_NAME_FORUM = "message2" # Only used on forum
|
||||
|
||||
# Email
|
||||
EMAIL_BACKEND = env.str(
|
||||
"EMAIL_BACKEND", default="django.core.mail.backends.dummy.EmailBackend"
|
||||
)
|
||||
EMAIL_HOST = env.str("EMAIL_HOST", default="localhost")
|
||||
EMAIL_PORT = env.int("EMAIL_PORT", default=25)
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
EMAIL_HOST = "localhost"
|
||||
EMAIL_PORT = 25
|
||||
|
||||
# Below this line, only Sith-specific variables are defined
|
||||
|
||||
SITH_URL = env.str("SITH_URL", default="127.0.0.1:8000")
|
||||
SITH_NAME = env.str("SITH_NAME", default="AE UTBM")
|
||||
SITH_URL = "my.url.git.an"
|
||||
SITH_NAME = "Sith website"
|
||||
SITH_TWITTER = "@ae_utbm"
|
||||
|
||||
# Enable experimental features
|
||||
@ -331,7 +321,7 @@ SITH_ENABLE_GALAXY = False
|
||||
|
||||
# AE configuration
|
||||
# TODO: keep only that first setting, with the ID, and do the same for the other clubs
|
||||
SITH_MAIN_CLUB_ID = env.int("SITH_MAIN_CLUB_ID", default=1)
|
||||
SITH_MAIN_CLUB_ID = 1
|
||||
SITH_MAIN_CLUB = {
|
||||
"name": "AE",
|
||||
"unix_name": "ae",
|
||||
@ -366,28 +356,26 @@ SITH_SCHOOL_START_YEAR = 1999
|
||||
# id of the Root account
|
||||
SITH_ROOT_USER_ID = 0
|
||||
|
||||
SITH_GROUP_ROOT_ID = env.int("SITH_GROUP_ROOT_ID", default=1)
|
||||
SITH_GROUP_PUBLIC_ID = env.int("SITH_GROUP_PUBLIC_ID", default=2)
|
||||
SITH_GROUP_SUBSCRIBERS_ID = env.int("SITH_GROUP_SUBSCRIBERS_ID", default=3)
|
||||
SITH_GROUP_OLD_SUBSCRIBERS_ID = env.int("SITH_GROUP_OLD_SUBSCRIBERS_ID", default=4)
|
||||
SITH_GROUP_ACCOUNTING_ADMIN_ID = env.int("SITH_GROUP_ACCOUNTING_ADMIN_ID", default=5)
|
||||
SITH_GROUP_COM_ADMIN_ID = env.int("SITH_GROUP_COM_ADMIN_ID", default=6)
|
||||
SITH_GROUP_COUNTER_ADMIN_ID = env.int("SITH_GROUP_COUNTER_ADMIN_ID", default=7)
|
||||
SITH_GROUP_SAS_ADMIN_ID = env.int("SITH_GROUP_SAS_ADMIN_ID", default=8)
|
||||
SITH_GROUP_FORUM_ADMIN_ID = env.int("SITH_GROUP_FORUM_ADMIN_ID", default=9)
|
||||
SITH_GROUP_PEDAGOGY_ADMIN_ID = env.int("SITH_GROUP_PEDAGOGY_ADMIN_ID", default=10)
|
||||
SITH_GROUP_ROOT_ID = 1
|
||||
SITH_GROUP_PUBLIC_ID = 2
|
||||
SITH_GROUP_SUBSCRIBERS_ID = 3
|
||||
SITH_GROUP_OLD_SUBSCRIBERS_ID = 4
|
||||
SITH_GROUP_ACCOUNTING_ADMIN_ID = 5
|
||||
SITH_GROUP_COM_ADMIN_ID = 6
|
||||
SITH_GROUP_COUNTER_ADMIN_ID = 7
|
||||
SITH_GROUP_SAS_ADMIN_ID = 8
|
||||
SITH_GROUP_FORUM_ADMIN_ID = 9
|
||||
SITH_GROUP_PEDAGOGY_ADMIN_ID = 10
|
||||
|
||||
SITH_GROUP_BANNED_ALCOHOL_ID = env.int("SITH_GROUP_BANNED_ALCOHOL_ID", default=11)
|
||||
SITH_GROUP_BANNED_COUNTER_ID = env.int("SITH_GROUP_BANNED_COUNTER_ID", default=12)
|
||||
SITH_GROUP_BANNED_SUBSCRIPTION_ID = env.int(
|
||||
"SITH_GROUP_BANNED_SUBSCRIPTION_ID", default=13
|
||||
)
|
||||
SITH_GROUP_BANNED_ALCOHOL_ID = 11
|
||||
SITH_GROUP_BANNED_COUNTER_ID = 12
|
||||
SITH_GROUP_BANNED_SUBSCRIPTION_ID = 13
|
||||
|
||||
SITH_CLUB_REFOUND_ID = env.int("SITH_CLUB_REFOUND_ID", default=89)
|
||||
SITH_COUNTER_REFOUND_ID = env.int("SITH_COUNTER_REFOUND_ID", default=38)
|
||||
SITH_PRODUCT_REFOUND_ID = env.int("SITH_PRODUCT_REFOUND_ID", default=5)
|
||||
SITH_CLUB_REFOUND_ID = 89
|
||||
SITH_COUNTER_REFOUND_ID = 38
|
||||
SITH_PRODUCT_REFOUND_ID = 5
|
||||
|
||||
SITH_COUNTER_ACCOUNT_DUMP_ID = env.int("SITH_COUNTER_ACCOUNT_DUMP_ID", default=39)
|
||||
SITH_COUNTER_ACCOUNT_DUMP_ID = 39
|
||||
|
||||
# Pages
|
||||
SITH_CORE_PAGE_SYNTAX = "Aide_sur_la_syntaxe"
|
||||
@ -397,7 +385,7 @@ SITH_CORE_PAGE_SYNTAX = "Aide_sur_la_syntaxe"
|
||||
SITH_FORUM_PAGE_LENGTH = 30
|
||||
|
||||
# SAS variables
|
||||
SITH_SAS_ROOT_DIR_ID = env.int("SITH_SAS_ROOT_DIR_ID", default=4)
|
||||
SITH_SAS_ROOT_DIR_ID = 4
|
||||
SITH_SAS_IMAGES_PER_PAGE = 60
|
||||
|
||||
SITH_BOARD_SUFFIX = "-bureau"
|
||||
@ -504,9 +492,9 @@ SITH_LOG_OPERATION_TYPE = [
|
||||
|
||||
SITH_PEDAGOGY_UTBM_API = "https://extranet1.utbm.fr/gpedago/api/guide"
|
||||
|
||||
SITH_ECOCUP_CONS = env.int("SITH_ECOCUP_CONS", default=1151)
|
||||
SITH_ECOCUP_CONS = 1152
|
||||
|
||||
SITH_ECOCUP_DECO = env.int("SITH_ECOCUP_DECO", default=1152)
|
||||
SITH_ECOCUP_DECO = 1151
|
||||
|
||||
# The limit is the maximum difference between cons and deco possible for a customer
|
||||
SITH_ECOCUP_LIMIT = 3
|
||||
@ -521,31 +509,21 @@ SITH_ACCOUNT_DUMP_DELTA = timedelta(days=30)
|
||||
|
||||
# Defines which product type is the refilling type,
|
||||
# and thus increases the account amount
|
||||
SITH_COUNTER_PRODUCTTYPE_REFILLING = env.int(
|
||||
"SITH_COUNTER_PRODUCTTYPE_REFILLING", default=3
|
||||
)
|
||||
SITH_COUNTER_PRODUCTTYPE_REFILLING = 3
|
||||
|
||||
# Defines which product is the one year subscription
|
||||
# and which one is the six month subscription
|
||||
SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER = env.int(
|
||||
"SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER", default=1
|
||||
)
|
||||
SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS = env.int(
|
||||
"SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS", default=2
|
||||
)
|
||||
SITH_PRODUCTTYPE_SUBSCRIPTION = env.int("SITH_PRODUCTTYPE_SUBSCRIPTION", default=2)
|
||||
SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER = 1
|
||||
SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS = 2
|
||||
SITH_PRODUCTTYPE_SUBSCRIPTION = 2
|
||||
|
||||
# Defines which club lets its member the ability to make subscriptions
|
||||
# Elements of this list are club's id
|
||||
SITH_CAN_CREATE_SUBSCRIPTIONS = env.list(
|
||||
"SITH_CAN_CREATE_SUBSCRIPTION_HISTORY", default=[1]
|
||||
)
|
||||
SITH_CAN_CREATE_SUBSCRIPTIONS = [1]
|
||||
|
||||
# Defines which clubs lets its members the ability to see users subscription history
|
||||
# Elements of this list are club's id
|
||||
SITH_CAN_READ_SUBSCRIPTION_HISTORY = env.list(
|
||||
"SITH_CAN_READ_SUBSCRIPTION_HISTORY", default=[1]
|
||||
)
|
||||
SITH_CAN_READ_SUBSCRIPTION_HISTORY = []
|
||||
|
||||
# Number of weeks before the end of a subscription when the subscriber can resubscribe
|
||||
SITH_SUBSCRIPTION_END = 10
|
||||
@ -654,29 +632,21 @@ SITH_BARMAN_TIMEOUT = 30
|
||||
SITH_LAST_OPERATIONS_LIMIT = 10
|
||||
|
||||
# ET variables
|
||||
SITH_EBOUTIC_CB_ENABLED = env.bool("SITH_EBOUTIC_CB_ENABLED", default=True)
|
||||
SITH_EBOUTIC_ET_URL = env.str(
|
||||
"SITH_EBOUTIC_ET_URL",
|
||||
default="https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi",
|
||||
SITH_EBOUTIC_CB_ENABLED = True
|
||||
SITH_EBOUTIC_ET_URL = (
|
||||
"https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi"
|
||||
)
|
||||
SITH_EBOUTIC_PBX_SITE = env.str("SITH_EBOUTIC_PBX_SITE", default="1999888")
|
||||
SITH_EBOUTIC_PBX_RANG = env.str("SITH_EBOUTIC_PBX_RANG", default="32")
|
||||
SITH_EBOUTIC_PBX_IDENTIFIANT = env.str("SITH_EBOUTIC_PBX_IDENTIFIANT", default="2")
|
||||
SITH_EBOUTIC_PBX_SITE = "1999888"
|
||||
SITH_EBOUTIC_PBX_RANG = "32"
|
||||
SITH_EBOUTIC_PBX_IDENTIFIANT = "2"
|
||||
SITH_EBOUTIC_HMAC_KEY = binascii.unhexlify(
|
||||
env.str(
|
||||
"SITH_EBOUTIC_HMAC_KEY",
|
||||
default=(
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
),
|
||||
)
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
"0123456789ABCDEF0123456789ABCDEF"
|
||||
)
|
||||
SITH_EBOUTIC_PUB_KEY = ""
|
||||
with open(
|
||||
env.path("SITH_EBOUTIC_PUB_KEY_PATH", default=BASE_DIR / "sith/et_keys/pubkey.pem")
|
||||
) as f:
|
||||
with open(os.path.join(os.path.dirname(__file__), "et_keys/pubkey.pem")) as f:
|
||||
SITH_EBOUTIC_PUB_KEY = f.read()
|
||||
|
||||
# Launderette variables
|
||||
@ -718,17 +688,24 @@ SITH_QUICK_NOTIF = {
|
||||
# Mailing related settings
|
||||
|
||||
SITH_MAILING_DOMAIN = "utbm.fr"
|
||||
SITH_MAILING_FETCH_KEY = env.str("SITH_MAILING_FETCH_KEY", default="ILoveMails")
|
||||
SITH_MAILING_FETCH_KEY = "IloveMails"
|
||||
|
||||
SITH_GIFT_LIST = [("AE Tee-shirt", _("AE tee-shirt"))]
|
||||
|
||||
SENTRY_DSN = env.str("SENRY_DSN", default=None)
|
||||
SENTRY_ENV = env.str("SENTRY_ENV", default="production")
|
||||
SENTRY_DSN = ""
|
||||
SENTRY_ENV = "production"
|
||||
|
||||
TOXIC_DOMAINS_PROVIDERS = [
|
||||
"https://www.stopforumspam.com/downloads/toxic_domains_whole.txt",
|
||||
]
|
||||
|
||||
try:
|
||||
from .settings_custom import * # noqa F403 (this star-import is actually useful)
|
||||
|
||||
logging.getLogger("django").info("Custom settings imported")
|
||||
except ImportError:
|
||||
logging.getLogger("django").warning("Custom settings failed")
|
||||
|
||||
if DEBUG:
|
||||
INSTALLED_APPS += ("debug_toolbar",)
|
||||
MIDDLEWARE = ("debug_toolbar.middleware.DebugToolbarMiddleware", *MIDDLEWARE)
|
||||
|
@ -81,7 +81,7 @@ def sentry_debug(request):
|
||||
The error will be displayed on Sentry
|
||||
inside the "development" environment
|
||||
|
||||
NOTE : you need to specify the SENTRY_DSN setting in .env
|
||||
NOTE : you need to specify the SENTRY_DSN setting in settings_custom.py
|
||||
"""
|
||||
if settings.SENTRY_ENV != "development" or not settings.SENTRY_DSN:
|
||||
raise Http404
|
||||
|
@ -1,6 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
import platform
|
||||
from dataclasses import dataclass
|
||||
from hashlib import sha1
|
||||
from itertools import chain
|
||||
@ -94,7 +95,7 @@ class JSBundler:
|
||||
@staticmethod
|
||||
def compile():
|
||||
"""Bundle js files with the javascript bundler for production."""
|
||||
process = subprocess.Popen(["npm", "run", "compile"])
|
||||
process = subprocess.Popen(["npm", "run", "compile"], shell=platform.system() == "Windows")
|
||||
process.wait()
|
||||
if process.returncode:
|
||||
raise RuntimeError(f"Bundler failed with returncode {process.returncode}")
|
||||
@ -103,7 +104,7 @@ class JSBundler:
|
||||
def runserver() -> subprocess.Popen:
|
||||
"""Bundle js files automatically in background when called in debug mode."""
|
||||
logging.getLogger("django").info("Running javascript bundling server")
|
||||
return subprocess.Popen(["npm", "run", "serve"])
|
||||
return subprocess.Popen(["npm", "run", "serve"], shell=platform.system() == "Windows")
|
||||
|
||||
@staticmethod
|
||||
def get_manifest() -> JSBundlerManifest:
|
||||
@ -197,4 +198,4 @@ class OpenApi:
|
||||
with open(out, "w") as f:
|
||||
_ = f.write(schema)
|
||||
|
||||
subprocess.run(["npx", "openapi-ts"], check=True)
|
||||
subprocess.run(["npx", "openapi-ts"], check=True, shell=platform.system() == "Windows")
|
||||
|
86
uv.lock
generated
86
uv.lock
generated
@ -155,7 +155,7 @@ name = "click"
|
||||
version = "8.1.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "platform_system == 'Windows'" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
|
||||
wheels = [
|
||||
@ -276,28 +276,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dj-database-url"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/98/9f/fc9905758256af4f68a55da94ab78a13e7775074edfdcaddd757d4921686/dj_database_url-2.3.0.tar.gz", hash = "sha256:ae52e8e634186b57e5a45e445da5dc407a819c2ceed8a53d1fac004cc5288787", size = 10980 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/91/641a4e5c8903ed59f6cbcce571003bba9c5d2f731759c31db0ba83bb0bdb/dj_database_url-2.3.0-py3-none-any.whl", hash = "sha256:bb0d414ba0ac5cd62773ec7f86f8cc378a9dbb00a80884c2fc08cc570452521e", size = 7793 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dj-email-url"
|
||||
version = "1.0.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/14/ef/8eb478accd9b0369d46a98d1b43027ee0c254096149265c78e6b2e2fa3b0/dj-email-url-1.0.6.tar.gz", hash = "sha256:55ffe3329e48f54f8a75aa36ece08f365e09d61f8a209773ef09a1d4760e699a", size = 15590 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f9/fcb9745099d821f9a26092d3d6f4df8f10049885045c3a93ff726d2e40a6/dj_email_url-1.0.6-py2.py3-none-any.whl", hash = "sha256:cbd08327fbb08b104eac160fb4703f375532e4c0243eb230f5b960daee7a96db", size = 6296 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django"
|
||||
version = "4.2.17"
|
||||
@ -312,15 +290,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/85/457360cb3de496382e35db4c2af054066df5c40e26df31400d0109a0500c/Django-4.2.17-py3-none-any.whl", hash = "sha256:3a93350214ba25f178d4045c0786c61573e7dbfa3c509b3551374f1e11ba8de0", size = 7993390 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-cache-url"
|
||||
version = "3.4.5"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/20/28/d420aaa89028d2ec0cf17c1510d06ff6a8ed0bf1abfb7f33c999e1c5befa/django-cache-url-3.4.5.tar.gz", hash = "sha256:eb9fb194717524348c95cad9905b70b647452741c1d9e481fac6d2125f0ad917", size = 7230 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/48/90/01755e4a42558b763f7021e9369aa6aa94c2ede7313deed56cb7483834ab/django_cache_url-3.4.5-py2.py3-none-any.whl", hash = "sha256:5f350759978483ab85dc0e3e17b3d53eed3394a28148f6bf0f53d11d0feb5b3c", size = 4760 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-countries"
|
||||
version = "7.6.1"
|
||||
@ -470,26 +439,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/93/69/e391bd51bc08ed9141ecd899a0ddb61ab6465309f1eb470905c0c8868081/docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc", size = 570472 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "environs"
|
||||
version = "14.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "marshmallow" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/3c/8f/952bd034eac79c8b68b6c770cb78c2bdcb3140d31ff224847f3520077d75/environs-14.1.0.tar.gz", hash = "sha256:a5f2afe9d5a21b468e74a3cceacf5d2371fd67dbb9a7e54fe62290c75a09cdfa", size = 30985 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/ad/57cfa3e8a006df88e723524127dbab2971a4877c97e7bad070257e15cb6c/environs-14.1.0-py3-none-any.whl", hash = "sha256:a7edda1668ddf1fbfcb7662bdc242dac25648eff2c7fdbaa5d959693afed7a3e", size = 15332 },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
django = [
|
||||
{ name = "dj-database-url" },
|
||||
{ name = "dj-email-url" },
|
||||
{ name = "django-cache-url" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "executing"
|
||||
version = "2.1.0"
|
||||
@ -759,18 +708,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "marshmallow"
|
||||
version = "3.25.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "packaging" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bd/5c/cbfa41491d6c83b36471f2a2f75602349d20a8f88afd94f83c1e68bbc298/marshmallow-3.25.0.tar.gz", hash = "sha256:5ba94a4eb68894ad6761a505eb225daf7e5cb7b4c32af62d4a45e9d42665bc31", size = 176751 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/76/26/b347619b719d4c048e038929769f8f6b28c6d930149b40d950bbdde31d48/marshmallow-3.25.0-py3-none-any.whl", hash = "sha256:50894cd57c6b097a6c6ed2bf216af47d10146990a54db52d03e32edb0448c905", size = 49480 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matplotlib-inline"
|
||||
version = "0.1.7"
|
||||
@ -807,7 +744,7 @@ version = "1.6.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "colorama", marker = "platform_system == 'Windows'" },
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "ghp-import" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "markdown" },
|
||||
@ -1292,15 +1229,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dotenv"
|
||||
version = "1.0.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyyaml"
|
||||
version = "6.0.2"
|
||||
@ -1500,7 +1428,6 @@ dependencies = [
|
||||
{ name = "django-ordered-model" },
|
||||
{ name = "django-phonenumber-field" },
|
||||
{ name = "django-simple-captcha" },
|
||||
{ name = "environs", extra = ["django"] },
|
||||
{ name = "ical" },
|
||||
{ name = "jinja2" },
|
||||
{ name = "libsass" },
|
||||
@ -1509,7 +1436,6 @@ dependencies = [
|
||||
{ name = "pillow" },
|
||||
{ name = "pydantic-extra-types" },
|
||||
{ name = "python-dateutil" },
|
||||
{ name = "redis", extra = ["hiredis"] },
|
||||
{ name = "reportlab" },
|
||||
{ name = "sentry-sdk" },
|
||||
{ name = "sphinx" },
|
||||
@ -1536,6 +1462,7 @@ docs = [
|
||||
]
|
||||
prod = [
|
||||
{ name = "psycopg", extra = ["c"] },
|
||||
{ name = "redis", extra = ["hiredis"] },
|
||||
]
|
||||
tests = [
|
||||
{ name = "freezegun" },
|
||||
@ -1559,7 +1486,6 @@ requires-dist = [
|
||||
{ name = "django-ordered-model", specifier = ">=3.7.4,<4.0.0" },
|
||||
{ name = "django-phonenumber-field", specifier = ">=8.0.0,<9.0.0" },
|
||||
{ name = "django-simple-captcha", specifier = ">=0.6.0,<1.0.0" },
|
||||
{ name = "environs", extras = ["django"], specifier = ">=14.1.0,<15.0.0" },
|
||||
{ name = "ical", specifier = ">=8.3.0,<9.0.0" },
|
||||
{ name = "jinja2", specifier = ">=3.1.4,<4.0.0" },
|
||||
{ name = "libsass", specifier = ">=0.23.0,<1.0.0" },
|
||||
@ -1568,7 +1494,6 @@ requires-dist = [
|
||||
{ name = "pillow", specifier = ">=11.0.0,<12.0.0" },
|
||||
{ name = "pydantic-extra-types", specifier = ">=2.10.1,<3.0.0" },
|
||||
{ name = "python-dateutil", specifier = ">=2.9.0.post0,<3.0.0.0" },
|
||||
{ name = "redis", extras = ["hiredis"], specifier = ">=5.2.0,<6.0.0" },
|
||||
{ name = "reportlab", specifier = ">=4.2.5,<5.0.0" },
|
||||
{ name = "sentry-sdk", specifier = ">=2.19.2,<3.0.0" },
|
||||
{ name = "sphinx", specifier = ">=5,<6" },
|
||||
@ -1593,7 +1518,10 @@ docs = [
|
||||
{ name = "mkdocstrings", specifier = ">=0.27.0,<1.0.0" },
|
||||
{ name = "mkdocstrings-python", specifier = ">=1.12.2,<2.0.0" },
|
||||
]
|
||||
prod = [{ name = "psycopg", extras = ["c"], specifier = ">=3.2.3,<4.0.0" }]
|
||||
prod = [
|
||||
{ name = "psycopg", extras = ["c"], specifier = ">=3.2.3,<4.0.0" },
|
||||
{ name = "redis", extras = ["hiredis"], specifier = ">=5.2.0,<6.0.0" },
|
||||
]
|
||||
tests = [
|
||||
{ name = "freezegun", specifier = ">=1.5.1,<2.0.0" },
|
||||
{ name = "model-bakery", specifier = ">=1.20.0,<2.0.0" },
|
||||
|
@ -1,5 +1,5 @@
|
||||
// biome-ignore lint/correctness/noNodejsModules: this is backend side
|
||||
import { parse, resolve } from "node:path";
|
||||
import { parse, resolve, sep } from "node:path";
|
||||
import inject from "@rollup/plugin-inject";
|
||||
import { glob } from "glob";
|
||||
import { type AliasOptions, type UserConfig, defineConfig } from "vite";
|
||||
@ -31,7 +31,7 @@ function getAliases(): AliasOptions {
|
||||
function getRelativeAssetPath(path: string): string {
|
||||
let relativePath: string[] = [];
|
||||
const fullPath = parse(path);
|
||||
for (const dir of fullPath.dir.split("/").reverse()) {
|
||||
for (const dir of fullPath.dir.split(sep).reverse()) {
|
||||
if (dir === "bundled") {
|
||||
break;
|
||||
}
|
||||
@ -40,7 +40,7 @@ function getRelativeAssetPath(path: string): string {
|
||||
// We collected folders in reverse order, we put them back in the original order
|
||||
relativePath = relativePath.reverse();
|
||||
relativePath.push(fullPath.name);
|
||||
return relativePath.join("/");
|
||||
return relativePath.join(sep);
|
||||
}
|
||||
|
||||
// biome-ignore lint/style/noDefaultExport: this is recommended by documentation
|
||||
@ -97,6 +97,10 @@ export default defineConfig((config: UserConfig) => {
|
||||
src: resolve(nodeModules, "jquery-ui/dist/jquery-ui.min.js"),
|
||||
dest: vendored,
|
||||
},
|
||||
{
|
||||
src: resolve(nodeModules, "jquery.shorten/src/jquery.shorten.min.js"),
|
||||
dest: vendored,
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
|
Reference in New Issue
Block a user