Compare commits

...

5 commits

42 changed files with 179 additions and 142 deletions

View file

@ -40,7 +40,7 @@ repos:
hooks: hooks:
- id: djhtml - id: djhtml
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0 rev: v0.2.1
hooks: hooks:
- id: ruff - id: ruff
args: [--fix] args: [--fix]
@ -50,7 +50,7 @@ repos:
- id: prettier - id: prettier
types_or: [javascript, css] types_or: [javascript, css]
- repo: https://github.com/pre-commit/mirrors-eslint - repo: https://github.com/pre-commit/mirrors-eslint
rev: v9.0.0-alpha.2 rev: v9.0.0-beta.0
hooks: hooks:
- id: eslint - id: eslint
args: [--fix] args: [--fix]

View file

@ -14,14 +14,5 @@ inv test
./src/manage.py createsuperuser ./src/manage.py createsuperuser
``` ```
## With preview mode
```shell
caddy --config contrib/Caddyfile.local
./src/manage.py runserver
cd ~/Projects/charasheet-go
make run/live
```
# Reuse # Reuse
If you do reuse my work, please consider linking back to this repository 🙂 If you do reuse my work, please consider linking back to this repository 🙂

View file

@ -24,7 +24,7 @@ confusable-homoglyphs==3.3.1
# via django-registration # via django-registration
cryptography==42.0.2 cryptography==42.0.2
# via django-anymail # via django-anymail
django==4.2.9 django==5.0.2
# via # via
# -r requirements.in # -r requirements.in
# django-anymail # django-anymail
@ -75,7 +75,7 @@ requests==2.31.0
# via # via
# -r requirements.in # -r requirements.in
# django-anymail # django-anymail
selenium==4.9.1 selenium==4.17.2
# via -r requirements.in # via -r requirements.in
sniffio==1.3.0 sniffio==1.3.0
# via trio # via trio
@ -89,6 +89,8 @@ trio==0.24.0
# trio-websocket # trio-websocket
trio-websocket==0.11.1 trio-websocket==0.11.1
# via selenium # via selenium
typing-extensions==4.9.0
# via selenium
urllib3==2.2.0 urllib3==2.2.0
# via # via
# django-anymail # django-anymail

View file

@ -1,15 +0,0 @@
{
email gabriel@augendre.info
http_port 8000
}
http://localhost:8080 {
@preview {
header Cookie *preview=enabled;*
path /test /test/* /character/1/notes_change/
}
route {
reverse_proxy @preview localhost:4444
reverse_proxy localhost:8000
}
}

View file

@ -4,6 +4,8 @@
[tool.ruff] [tool.ruff]
src = ["src"] src = ["src"]
target-version = "py311" target-version = "py311"
[tool.ruff.lint]
select = ["ALL"] select = ["ALL"]
unfixable = ["T20", "RUF001", "RUF002", "RUF003"] unfixable = ["T20", "RUF001", "RUF002", "RUF003"]
@ -18,7 +20,7 @@ ignore = [
"RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar`
] ]
[tool.ruff.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"**/tests/*" = [ "**/tests/*" = [
"S101", # Use of assert detected. "S101", # Use of assert detected.
"S106", # Possible hardcoded password. "S106", # Possible hardcoded password.
@ -36,10 +38,10 @@ ignore = [
"RUF001", # String contains ambiguous unicode character "RUF001", # String contains ambiguous unicode character
] ]
[tool.ruff.pydocstyle] [tool.ruff.lint.pydocstyle]
convention = "pep257" convention = "pep257"
[tool.ruff.mccabe] [tool.ruff.lint.mccabe]
max-complexity = 10 max-complexity = 10
[tool.pytest.ini_options] [tool.pytest.ini_options]

View file

@ -5,7 +5,7 @@ pytest-cov>=3.0.0
pytest-django>=4.1.0 pytest-django>=4.1.0
pytest-selenium>=4.0.0 pytest-selenium>=4.0.0
# pytest-selenium 4.0.1 has an implicit dependency on `py` # pytest-selenium 4.0.1 has an implicit dependency on `py`
py>=1.11.0 # py>=1.11.0
pre-commit>=2.1 pre-commit>=2.1
model-bakery>=1.3.1 model-bakery>=1.3.1
bpython>=0.22.1 bpython>=0.22.1

View file

@ -50,7 +50,7 @@ cwcwidth==0.1.9
# curtsies # curtsies
distlib==0.3.8 distlib==0.3.8
# via virtualenv # via virtualenv
django==4.2.9 django==5.0.2
# via # via
# -c constraints.txt # -c constraints.txt
# django-browser-reload # django-browser-reload
@ -68,9 +68,9 @@ h11==0.14.0
# via # via
# -c constraints.txt # -c constraints.txt
# wsproto # wsproto
hypothesis==6.98.1 hypothesis==6.98.3
# via -r requirements-dev.in # via -r requirements-dev.in
identify==2.5.33 identify==2.5.34
# via pre-commit # via pre-commit
idna==3.6 idna==3.6
# via # via
@ -111,9 +111,7 @@ platformdirs==4.2.0
# virtualenv # virtualenv
pluggy==1.4.0 pluggy==1.4.0
# via pytest # via pytest
pre-commit==3.6.0 pre-commit==3.6.1
# via -r requirements-dev.in
py==1.11.0
# via -r requirements-dev.in # via -r requirements-dev.in
pygments==2.17.2 pygments==2.17.2
# via bpython # via bpython
@ -143,7 +141,7 @@ pytest-html==4.1.1
# via pytest-selenium # via pytest-selenium
pytest-metadata==3.1.0 pytest-metadata==3.1.0
# via pytest-html # via pytest-html
pytest-selenium==4.0.1 pytest-selenium==4.1.0
# via -r requirements-dev.in # via -r requirements-dev.in
pytest-variables==3.1.0 pytest-variables==3.1.0
# via pytest-selenium # via pytest-selenium
@ -157,9 +155,9 @@ requests==2.31.0
# bpython # bpython
# pytest-base-url # pytest-base-url
# pytest-selenium # pytest-selenium
ruff==0.2.0 ruff==0.2.1
# via -r requirements-dev.in # via -r requirements-dev.in
selenium==4.9.1 selenium==4.17.2
# via # via
# -c constraints.txt # -c constraints.txt
# pytest-selenium # pytest-selenium
@ -190,6 +188,10 @@ trio-websocket==0.11.1
# via # via
# -c constraints.txt # -c constraints.txt
# selenium # selenium
typing-extensions==4.9.0
# via
# -c constraints.txt
# selenium
urllib3[socks]==2.2.0 urllib3[socks]==2.2.0
# via # via
# -c constraints.txt # -c constraints.txt

View file

@ -1,14 +1,11 @@
django>=4.1,<5.0 django>=4.1
django-cleanup>=6.0 django-cleanup>=6.0
django-environ>=0.9.0 django-environ>=0.9.0
django-htmx>=1.12.2 django-htmx>=1.12.2
django-linear-migrations>=2.2.0 django-linear-migrations>=2.2.0
django-extensions>=3.1.5 django-extensions>=3.1.5
whitenoise>=6.2 whitenoise>=6.2
# pytest-selenium is broken with selenium 4.10.0 selenium>=4.5.0
# TypeError: WebDriver.__init__() got an unexpected keyword argument 'service_log_path'
# https://github.com/robotframework/SeleniumLibrary/issues/1835
selenium>=4.5.0,<4.10.0
Markdown>=3.2 Markdown>=3.2
django-registration>=3.3 django-registration>=3.3
django-anymail[mailgun]>=8.6 django-anymail[mailgun]>=8.6

View file

@ -24,7 +24,7 @@ confusable-homoglyphs==3.3.1
# via django-registration # via django-registration
cryptography==42.0.2 cryptography==42.0.2
# via django-anymail # via django-anymail
django==4.2.9 django==5.0.2
# via # via
# -r requirements.in # -r requirements.in
# django-anymail # django-anymail
@ -75,7 +75,7 @@ requests==2.31.0
# via # via
# -r requirements.in # -r requirements.in
# django-anymail # django-anymail
selenium==4.9.1 selenium==4.17.2
# via -r requirements.in # via -r requirements.in
sniffio==1.3.0 sniffio==1.3.0
# via trio # via trio
@ -89,6 +89,8 @@ trio==0.24.0
# trio-websocket # trio-websocket
trio-websocket==0.11.1 trio-websocket==0.11.1
# via selenium # via selenium
typing-extensions==4.9.0
# via selenium
urllib3[socks]==2.2.0 urllib3[socks]==2.2.0
# via # via
# django-anymail # django-anymail

View file

@ -79,6 +79,12 @@ class CharacterForm(forms.ModelForm):
"value_intelligence", "value_intelligence",
"value_wisdom", "value_wisdom",
"value_charisma", "value_charisma",
"bonus_strength",
"bonus_dexterity",
"bonus_constitution",
"bonus_intelligence",
"bonus_wisdom",
"bonus_charisma",
"health_max", "health_max",
"racial_capability", "racial_capability",
"weapons", "weapons",

View file

@ -0,0 +1,61 @@
# Generated by Django 4.2.9 on 2024-02-11 10:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("character", "0043_character_paths"),
]
operations = [
migrations.AddField(
model_name="character",
name="bonus_charisma",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus charisme",
),
),
migrations.AddField(
model_name="character",
name="bonus_constitution",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus constitution",
),
),
migrations.AddField(
model_name="character",
name="bonus_dexterity",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus dextérité",
),
),
migrations.AddField(
model_name="character",
name="bonus_intelligence",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus intelligence",
),
),
migrations.AddField(
model_name="character",
name="bonus_strength",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus force",
),
),
migrations.AddField(
model_name="character",
name="bonus_wisdom",
field=models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus sagesse",
),
),
]

View file

@ -1 +1 @@
0043_character_paths 0044_character_bonus_charisma_and_more

View file

@ -223,6 +223,31 @@ class Character(models.Model):
value_wisdom = models.PositiveSmallIntegerField(verbose_name="valeur sagesse") value_wisdom = models.PositiveSmallIntegerField(verbose_name="valeur sagesse")
value_charisma = models.PositiveSmallIntegerField(verbose_name="valeur charisme") value_charisma = models.PositiveSmallIntegerField(verbose_name="valeur charisme")
bonus_strength = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus force",
)
bonus_dexterity = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus dextérité",
)
bonus_constitution = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus constitution",
)
bonus_intelligence = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus intelligence",
)
bonus_wisdom = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus sagesse",
)
bonus_charisma = models.PositiveSmallIntegerField(
default=0,
verbose_name="bonus charisme",
)
health_max = models.PositiveSmallIntegerField(verbose_name="points de vie max") health_max = models.PositiveSmallIntegerField(verbose_name="points de vie max")
health_remaining = models.PositiveSmallIntegerField( health_remaining = models.PositiveSmallIntegerField(
verbose_name="points de vie restants", verbose_name="points de vie restants",
@ -314,27 +339,27 @@ class Character(models.Model):
@property @property
def modifier_strength(self) -> int: def modifier_strength(self) -> int:
return modifier(self.value_strength) return modifier(self.value_strength) + self.bonus_strength
@property @property
def modifier_dexterity(self) -> int: def modifier_dexterity(self) -> int:
return modifier(self.value_dexterity) return modifier(self.value_dexterity) + self.bonus_dexterity
@property @property
def modifier_constitution(self) -> int: def modifier_constitution(self) -> int:
return modifier(self.value_constitution) return modifier(self.value_constitution) + self.bonus_constitution
@property @property
def modifier_intelligence(self) -> int: def modifier_intelligence(self) -> int:
return modifier(self.value_intelligence) return modifier(self.value_intelligence) + self.bonus_intelligence
@property @property
def modifier_wisdom(self) -> int: def modifier_wisdom(self) -> int:
return modifier(self.value_wisdom) return modifier(self.value_wisdom) + self.bonus_wisdom
@property @property
def modifier_charisma(self) -> int: def modifier_charisma(self) -> int:
return modifier(self.value_charisma) return modifier(self.value_charisma) + self.bonus_charisma
@property @property
def modifier_initiative(self) -> int: def modifier_initiative(self) -> int:

View file

@ -31,12 +31,18 @@
</div> </div>
<h2>Compétences</h2> <h2>Compétences</h2>
<div class="row"> <div class="row">
<div class="col-md-2">{% bootstrap_field form.value_strength %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_strength %}</div>
<div class="col-md-2">{% bootstrap_field form.value_dexterity %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_strength %}</div>
<div class="col-md-2">{% bootstrap_field form.value_constitution %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_dexterity %}</div>
<div class="col-md-2">{% bootstrap_field form.value_intelligence %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_dexterity %}</div>
<div class="col-md-2">{% bootstrap_field form.value_wisdom %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_constitution %}</div>
<div class="col-md-2">{% bootstrap_field form.value_charisma %}</div> <div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_constitution %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_intelligence %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_intelligence %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_wisdom %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_wisdom %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.value_charisma %}</div>
<div class="col-lg-2 col-md-3 col-6">{% bootstrap_field form.bonus_charisma %}</div>
</div> </div>
<h2>Protection</h2> <h2>Protection</h2>
<div class="row"> <div class="row">

View file

@ -1,12 +1,5 @@
from django.conf import settings from django.conf import settings
from django.core.handlers.wsgi import WSGIRequest
def app(_): def app(_):
return settings.APP return settings.APP
def preview(request: WSGIRequest):
return {
"preview_enabled": request.session.get("preview", False),
}

View file

@ -1,6 +1,4 @@
from django.conf import settings from django.conf import settings
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
def debug_toolbar_bypass_internal_ips(_) -> bool: def debug_toolbar_bypass_internal_ips(_) -> bool:
@ -11,31 +9,3 @@ def debug_toolbar_bypass_internal_ips(_) -> bool:
This is impossible to predict in a docker/k8s environment so we bypass this check. This is impossible to predict in a docker/k8s environment so we bypass this check.
""" """
return settings.DEBUG_TOOLBAR return settings.DEBUG_TOOLBAR
class PreviewMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request: WSGIRequest):
enable_preview = "enable_preview" in request.GET
disable_preview = "disable_preview" in request.GET
new_value = None
if enable_preview:
new_value = True
elif disable_preview:
new_value = False
if new_value is not None:
request.session["preview"] = new_value
response: HttpResponse = self.get_response(request)
if new_value is True:
response.set_cookie("preview", "enabled")
elif new_value is False:
response.delete_cookie("preview")
return response

View file

@ -97,7 +97,6 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"django_htmx.middleware.HtmxMiddleware", "django_htmx.middleware.HtmxMiddleware",
"charasheet.middleware.PreviewMiddleware",
] ]
if DEBUG_TOOLBAR: if DEBUG_TOOLBAR:
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
@ -118,7 +117,6 @@ TEMPLATES = [
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"charasheet.context_processors.app", "charasheet.context_processors.app",
"charasheet.context_processors.preview",
], ],
}, },
}, },

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
/*! /*!
* Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com * Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2022 Fonticons, Inc. * Copyright 2023 Fonticons, Inc.
*/ */
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900} :host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -10,10 +10,10 @@
{% include "common/icons.html" %} {% include "common/icons.html" %}
{% include "common/opengraph.html" %} {% include "common/opengraph.html" %}
<link rel="stylesheet" href="{% static "vendor/bootstrap-5.2.2/bootstrap.min.css" %}"> <link rel="stylesheet" href="{% static "vendor/bootstrap-5.3.2/bootstrap.min.css" %}">
<link rel="stylesheet" href="{% static "vendor/fontawesome-6.2.0/css/fontawesome.min.css" %}"> <link rel="stylesheet" href="{% static "vendor/fontawesome-6.5.1/css/fontawesome.min.css" %}">
<link rel="stylesheet" href="{% static "vendor/fontawesome-6.2.0/css/brands.min.css" %}"> <link rel="stylesheet" href="{% static "vendor/fontawesome-6.5.1/css/brands.min.css" %}">
<link rel="stylesheet" href="{% static "vendor/fontawesome-6.2.0/css/solid.min.css" %}"> <link rel="stylesheet" href="{% static "vendor/fontawesome-6.5.1/css/solid.min.css" %}">
<style> <style>
body { body {
margin-bottom: 2em; margin-bottom: 2em;
@ -35,7 +35,7 @@
</main> </main>
{% include "common/footer.html" %} {% include "common/footer.html" %}
<script src="{% static 'vendor/htmx-1.8.2.min.js' %}" defer></script> <script src="{% static 'vendor/htmx-1.9.10.min.js' %}" defer></script>
{% django_htmx_script %} {% django_htmx_script %}
{% if debug %} {% if debug %}
<script type="javascript"> <script type="javascript">
@ -51,7 +51,7 @@
} }
</script> </script>
{% endif %} {% endif %}
<script src="{% static "vendor/bootstrap-5.2.2/bootstrap.bundle.min.js" %}"></script> <script src="{% static "vendor/bootstrap-5.3.2/bootstrap.bundle.min.js" %}"></script>
<script type="application/javascript" defer> <script type="application/javascript" defer>
let tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); let tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
let tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); let tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl));

View file

@ -3,11 +3,6 @@
<span> <span>
Version : {{ build.describe }} Version : {{ build.describe }}
&bull; {{ build.date }} &bull; {{ build.date }}
{% if preview_enabled %}
&bull; <a class="text-muted" href="?disable_preview">Disable preview</a>
{% else %}
&bull; <a class="text-muted" href="?enable_preview">Enable preview</a>
{% endif %}
</span> </span>
<div> <div>
<a href="https://github.com/Crocmagnon/charasheet" class="text-muted"><i class="fa-brands fa-github"></i></a> <a href="https://github.com/Crocmagnon/charasheet" class="text-muted"><i class="fa-brands fa-github"></i></a>