Skip to content

Commit 24769a5

Browse files
committed
[lichess] WIP: stop using Berserk, add integration to more endpoints
We can now create correspondence games on Lichess, and display the ongoing games. [bugfix] Our assets are now correctly served straight from the "static/" folder of each app in development mode, even though we stopped using "runserver" - thanks to WhiteNoise. Misc other bugfixes here and there.
1 parent 3be18be commit 24769a5

File tree

21 files changed

+516
-145
lines changed

21 files changed

+516
-145
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,8 @@ FROM python:3.11-slim-bookworm AS assets_download
115115
# By having a separate build stage for downloading assets, we can cache them
116116
# as long as the `download_assets.py` doesn't change.
117117

118-
# should preferably be the same as in `poetry.lock`:
119-
ENV PYTHON_HTTPX_VERSION=0.26.0
118+
# should preferably be the same as in `uv.lock`:
119+
ENV PYTHON_HTTPX_VERSION=0.27.2
120120

121121
RUN pip install -U pip httpx==${PYTHON_HTTPX_VERSION}
122122

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ backend/watch: address ?= localhost
4343
backend/watch: port ?= 8000
4444
backend/watch: dotenv_file ?= .env.local
4545
backend/watch: ## Start Django via Uvicorn, in "watch" mode
46-
@@DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} ${env_vars} \
46+
@DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE} ${env_vars} \
4747
${UV} run uvicorn \
4848
--reload --reload-dir src/ \
4949
--host ${address} --port ${port} \

pyproject.toml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ dependencies= [
2828
"msgspec==0.18.*",
2929
"zakuchess",
3030
"authlib==1.*",
31-
"berserk>=0.13.2",
31+
"httpx==0.27.*",
3232
]
3333

3434

@@ -41,16 +41,20 @@ dev = [
4141
"ipython==8.*",
4242
"types-requests==2.*",
4343
"django-extensions==3.*",
44-
# (httpx is only used in "scripts/download_assets.py" for parallel downloads)
45-
"httpx==0.26.*",
4644
"sqlite-utils==3.*",
45+
# N.B. As it turns out that Lichess' "Berserk" package misses some features we need,
46+
# such as the creation of correspondence Seeks...
47+
# And as we had to wrap evey call in an `sync_to_async` anyway, which was not great...
48+
# We only use for its `types` module at the momemt.
49+
"berserk==0.13.*",
4750
]
4851
test = [
49-
"pytest==7.*",
52+
"pytest==8.3.*",
5053
"pytest-django==4.*",
5154
"pytest-cov==4.*",
5255
"time-machine==2.*",
5356
"pytest-blockage==0.2.*",
57+
"pytest-asyncio==0.24.*",
5458
]
5559
load-testing = [
5660
"locust==2.*",
@@ -127,9 +131,17 @@ testpaths = [
127131
"src/project/tests/",
128132
]
129133
python_files = ["test_*.py"]
134+
# pytest-django settings:
135+
# https://pytest-django.readthedocs.io/
130136
addopts = "--reuse-db"
131137
DJANGO_SETTINGS_MODULE = "project.settings.test"
132-
blockage = true # https://github.com/rob-b/pytest-blockage
138+
# pytest-asyncio settings:
139+
# https://pytest-asyncio.readthedocs.io/en/stable/reference/configuration.html
140+
asyncio_default_fixture_loop_scope = "function"
141+
asyncio_mode = "auto"
142+
# pytest-blockage settings:
143+
# https://github.com/rob-b/pytest-blockage
144+
blockage = true
133145

134146
[tool.coverage.run]
135147
# @link https://coverage.readthedocs.io/en/latest/excluding.html
File renamed without changes.

src/apps/daily_challenge/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from .components.misc_ui.help_modal import help_modal
2626
from .components.misc_ui.stats_modal import stats_modal
2727
from .components.misc_ui.user_prefs_modal import user_prefs_modal
28-
from .components.pages.daily_chess import (
28+
from .components.pages.daily_chess_pages import (
2929
daily_challenge_moving_parts_fragment,
3030
daily_challenge_page,
3131
)

src/apps/lichess_bridge/components/pages/lichess.py

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
from typing import TYPE_CHECKING
2+
3+
from django.urls import reverse
4+
from dominate.tags import (
5+
a,
6+
button,
7+
div,
8+
fieldset,
9+
form,
10+
input_,
11+
label,
12+
legend,
13+
li,
14+
p,
15+
section,
16+
ul,
17+
)
18+
19+
from apps.webui.components import common_styles
20+
from apps.webui.components.forms_common import csrf_hidden_input
21+
from apps.webui.components.layout import page
22+
23+
from ... import lichess_api
24+
from ...models import LichessCorrespondenceGameDaysChoice
25+
from ..misc_ui import detach_lichess_account_form
26+
from ..svg_icons import ICON_SVG_CREATE, ICON_SVG_LOG_IN
27+
28+
if TYPE_CHECKING:
29+
from django.http import HttpRequest
30+
31+
from ...models import LichessAccessToken
32+
33+
34+
def lichess_no_account_linked_page(
35+
*,
36+
request: "HttpRequest",
37+
) -> str:
38+
return page(
39+
section(
40+
form(
41+
csrf_hidden_input(request),
42+
p("Click here to log in to Lichess"),
43+
button(
44+
"Log in via Lichess",
45+
" ",
46+
ICON_SVG_LOG_IN,
47+
type="submit",
48+
cls=common_styles.BUTTON_CLASSES,
49+
),
50+
action=reverse("lichess_bridge:oauth2_start_flow"),
51+
method="POST",
52+
),
53+
cls="text-slate-50",
54+
),
55+
request=request,
56+
title="Lichess - no account linked",
57+
)
58+
59+
60+
async def lichess_account_linked_homepage(
61+
*,
62+
request: "HttpRequest",
63+
access_token: "LichessAccessToken",
64+
) -> str:
65+
me = await lichess_api.get_my_account(access_token=access_token)
66+
ongoing_games = await lichess_api.get_my_ongoing_games(access_token=access_token)
67+
68+
return page(
69+
div(
70+
section(
71+
f"Hello {me.username}!",
72+
ul(
73+
*[li(game.gameId) for game in ongoing_games],
74+
),
75+
p(
76+
a(
77+
"Create a new game",
78+
href=reverse("lichess_bridge:create_correspondence_game"),
79+
cls=common_styles.BUTTON_CLASSES,
80+
),
81+
),
82+
cls="text-slate-50",
83+
),
84+
div(
85+
detach_lichess_account_form(request),
86+
cls="mt-4",
87+
),
88+
cls="w-full mx-auto bg-slate-900 min-h-48 "
89+
"md:max-w-3xl xl:max-w-7xl xl:border xl:rounded-md xl:border-neutral-800",
90+
),
91+
request=request,
92+
title="Lichess - account linked",
93+
)
94+
95+
96+
def lichess_correspondence_game_creation_page(
97+
request: "HttpRequest", form_errors: dict
98+
) -> str:
99+
return page(
100+
div(
101+
section(
102+
form(
103+
csrf_hidden_input(request),
104+
div(
105+
fieldset(
106+
legend("Days per turn."),
107+
(
108+
p(form_errors["days_per_turn"], cls="text-red-600 ")
109+
if "days_per_turn" in form_errors
110+
else ""
111+
),
112+
div(
113+
*[
114+
div(
115+
input_(
116+
id=f"days-per-turn-{value}",
117+
type="radio",
118+
name="days_per_turn",
119+
value=value,
120+
checked=(
121+
value
122+
== LichessCorrespondenceGameDaysChoice.THREE_DAYS.value # type: ignore[attr-defined]
123+
),
124+
),
125+
label(
126+
display, html_for=f"days-per-turn-{value}"
127+
),
128+
)
129+
for value, display in LichessCorrespondenceGameDaysChoice.choices
130+
],
131+
cls="flex gap-3",
132+
),
133+
cls="block text-sm font-bold mb-2",
134+
),
135+
input_(
136+
id="days-per-turn",
137+
cls="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline",
138+
),
139+
cls="mb-4",
140+
),
141+
button(
142+
"Create",
143+
" ",
144+
ICON_SVG_CREATE,
145+
type="submit",
146+
cls=common_styles.BUTTON_CLASSES,
147+
),
148+
action=reverse("lichess_bridge:create_correspondence_game"),
149+
method="POST",
150+
),
151+
cls="text-slate-50",
152+
),
153+
div(
154+
detach_lichess_account_form(request),
155+
cls="mt-4",
156+
),
157+
cls="w-full mx-auto bg-slate-900 min-h-48 "
158+
"md:max-w-3xl xl:max-w-7xl xl:border xl:rounded-md xl:border-neutral-800",
159+
),
160+
request=request,
161+
title="Lichess - new correspondence game",
162+
)

src/apps/lichess_bridge/components/svg_icons.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,10 @@
1313
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 9V5.25A2.25 2.25 0 0 1 10.5 3h6a2.25 2.25 0 0 1 2.25 2.25v13.5A2.25 2.25 0 0 1 16.5 21h-6a2.25 2.25 0 0 1-2.25-2.25V15m-3 0-3-3m0 0 3-3m-3 3H15" />
1414
</svg>"""
1515
)
16+
17+
# https://heroicons.com/, icon `plus-circle`
18+
ICON_SVG_CREATE = raw(
19+
r"""<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="inline w-5 h-5">
20+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
21+
</svg>"""
22+
)

src/apps/lichess_bridge/forms.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from django import forms
2+
3+
from .models import LichessCorrespondenceGameDaysChoice
4+
5+
6+
class LichessCorrespondenceGameCreationForm(forms.Form):
7+
days_per_turn = forms.ChoiceField(
8+
choices=LichessCorrespondenceGameDaysChoice.choices
9+
)

0 commit comments

Comments
 (0)