7 Commits

6 changed files with 65 additions and 70 deletions

View File

@@ -2,26 +2,27 @@
# uv pip compile requirements-server.in # uv pip compile requirements-server.in
annotated-types==0.7.0 annotated-types==0.7.0
# via pydantic # via pydantic
anyio==4.6.2.post1 anyio==4.8.0
# via # via
# httpx # httpx
# starlette # starlette
# watchfiles # watchfiles
certifi==2024.8.30 certifi==2025.1.31
# via # via
# httpcore # httpcore
# httpx # httpx
click==8.1.7 click==8.1.8
# via # via
# rich-toolkit
# typer # typer
# uvicorn # uvicorn
dnspython==2.7.0 dnspython==2.7.0
# via email-validator # via email-validator
email-validator==2.2.0 email-validator==2.2.0
# via fastapi # via fastapi
fastapi==0.115.5 fastapi==0.115.8
# via -r requirements.in # via -r requirements.in
fastapi-cli==0.0.5 fastapi-cli==0.0.7
# via fastapi # via fastapi
gunicorn==23.0.0 gunicorn==23.0.0
# via -r requirements-server.in # via -r requirements-server.in
@@ -33,14 +34,14 @@ httpcore==1.0.7
# via httpx # via httpx
httptools==0.6.4 httptools==0.6.4
# via uvicorn # via uvicorn
httpx==0.27.2 httpx==0.28.1
# via fastapi # via fastapi
idna==3.10 idna==3.10
# via # via
# anyio # anyio
# email-validator # email-validator
# httpx # httpx
jinja2==3.1.4 jinja2==3.1.5
# via fastapi # via fastapi
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via rich # via rich
@@ -50,49 +51,52 @@ mdurl==0.1.2
# via markdown-it-py # via markdown-it-py
packaging==24.2 packaging==24.2
# via gunicorn # via gunicorn
pydantic==2.10.1 pydantic==2.10.6
# via # via
# fastapi # fastapi
# pydantic-settings # pydantic-settings
pydantic-core==2.27.1 pydantic-core==2.27.2
# via pydantic # via pydantic
pydantic-settings==2.6.1 pydantic-settings==2.7.1
# via -r requirements.in # via -r requirements.in
pygments==2.18.0 pygments==2.19.1
# via rich # via rich
python-dotenv==1.0.1 python-dotenv==1.0.1
# via # via
# pydantic-settings # pydantic-settings
# uvicorn # uvicorn
python-multipart==0.0.17 python-multipart==0.0.20
# via fastapi # via fastapi
pyyaml==6.0.2 pyyaml==6.0.2
# via uvicorn # via uvicorn
rich==13.9.4 rich==13.9.4
# via typer # via
# rich-toolkit
# typer
rich-toolkit==0.13.2
# via fastapi-cli
shellingham==1.5.4 shellingham==1.5.4
# via typer # via typer
sniffio==1.3.1 sniffio==1.3.1
# via # via anyio
# anyio starlette==0.45.3
# httpx
starlette==0.41.3
# via fastapi # via fastapi
typer==0.13.1 typer==0.15.1
# via fastapi-cli # via fastapi-cli
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# fastapi # fastapi
# pydantic # pydantic
# pydantic-core # pydantic-core
# rich-toolkit
# typer # typer
uvicorn==0.32.1 uvicorn==0.34.0
# via # via
# fastapi # fastapi
# fastapi-cli # fastapi-cli
uvloop==0.21.0 uvloop==0.21.0
# via uvicorn # via uvicorn
watchfiles==0.24.0 watchfiles==1.0.4
# via uvicorn # via uvicorn
websockets==14.1 websockets==14.2
# via uvicorn # via uvicorn

View File

@@ -2,26 +2,27 @@
# uv pip compile requirements.in # uv pip compile requirements.in
annotated-types==0.7.0 annotated-types==0.7.0
# via pydantic # via pydantic
anyio==4.6.2.post1 anyio==4.8.0
# via # via
# httpx # httpx
# starlette # starlette
# watchfiles # watchfiles
certifi==2024.8.30 certifi==2025.1.31
# via # via
# httpcore # httpcore
# httpx # httpx
click==8.1.7 click==8.1.8
# via # via
# rich-toolkit
# typer # typer
# uvicorn # uvicorn
dnspython==2.7.0 dnspython==2.7.0
# via email-validator # via email-validator
email-validator==2.2.0 email-validator==2.2.0
# via fastapi # via fastapi
fastapi==0.115.5 fastapi==0.115.8
# via -r requirements.in # via -r requirements.in
fastapi-cli==0.0.5 fastapi-cli==0.0.7
# via fastapi # via fastapi
h11==0.14.0 h11==0.14.0
# via # via
@@ -31,14 +32,14 @@ httpcore==1.0.7
# via httpx # via httpx
httptools==0.6.4 httptools==0.6.4
# via uvicorn # via uvicorn
httpx==0.27.2 httpx==0.28.1
# via fastapi # via fastapi
idna==3.10 idna==3.10
# via # via
# anyio # anyio
# email-validator # email-validator
# httpx # httpx
jinja2==3.1.4 jinja2==3.1.5
# via fastapi # via fastapi
markdown-it-py==3.0.0 markdown-it-py==3.0.0
# via rich # via rich
@@ -46,49 +47,52 @@ markupsafe==3.0.2
# via jinja2 # via jinja2
mdurl==0.1.2 mdurl==0.1.2
# via markdown-it-py # via markdown-it-py
pydantic==2.10.1 pydantic==2.10.6
# via # via
# fastapi # fastapi
# pydantic-settings # pydantic-settings
pydantic-core==2.27.1 pydantic-core==2.27.2
# via pydantic # via pydantic
pydantic-settings==2.6.1 pydantic-settings==2.7.1
# via -r requirements.in # via -r requirements.in
pygments==2.18.0 pygments==2.19.1
# via rich # via rich
python-dotenv==1.0.1 python-dotenv==1.0.1
# via # via
# pydantic-settings # pydantic-settings
# uvicorn # uvicorn
python-multipart==0.0.17 python-multipart==0.0.20
# via fastapi # via fastapi
pyyaml==6.0.2 pyyaml==6.0.2
# via uvicorn # via uvicorn
rich==13.9.4 rich==13.9.4
# via typer # via
# rich-toolkit
# typer
rich-toolkit==0.13.2
# via fastapi-cli
shellingham==1.5.4 shellingham==1.5.4
# via typer # via typer
sniffio==1.3.1 sniffio==1.3.1
# via # via anyio
# anyio starlette==0.45.3
# httpx
starlette==0.41.3
# via fastapi # via fastapi
typer==0.13.1 typer==0.15.1
# via fastapi-cli # via fastapi-cli
typing-extensions==4.12.2 typing-extensions==4.12.2
# via # via
# fastapi # fastapi
# pydantic # pydantic
# pydantic-core # pydantic-core
# rich-toolkit
# typer # typer
uvicorn==0.32.1 uvicorn==0.34.0
# via # via
# fastapi # fastapi
# fastapi-cli # fastapi-cli
uvloop==0.21.0 uvloop==0.21.0
# via uvicorn # via uvicorn
watchfiles==0.24.0 watchfiles==1.0.4
# via uvicorn # via uvicorn
websockets==14.1 websockets==14.2
# via uvicorn # via uvicorn

View File

@@ -2,6 +2,7 @@
import logging import logging
from datetime import date, datetime, timezone from datetime import date, datetime, timezone
from typing import Union from typing import Union
from zoneinfo import ZoneInfo
from fastapi import FastAPI, Request, HTTPException from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import HTMLResponse from fastapi.responses import HTMLResponse
@@ -11,7 +12,9 @@ from pydantic import DirectoryPath, FilePath
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
VERSION = '0.3.0' VERSION = '0.3.2'
AMSTERDAM = ZoneInfo('Europe/Amsterdam')
class Settings(BaseSettings): class Settings(BaseSettings):
@@ -54,15 +57,15 @@ if settings.debug:
def get_game_id(): def get_game_id():
"""Calculate the index for the game/word we are handling today.""" """Calculate the index for the game/word we are handling today."""
today = datetime.now(timezone.utc).date() today = datetime.now(tz=AMSTERDAM).date()
# Calculate the amount of days since the start of the games so we know which word is used today # Calculate the amount of days since the start of the games so we know which word is used today
return (today - settings.start_date).days return (today - settings.start_date).days
def get_game_deadline(): def get_game_deadline():
"""Calculate the amount of time left for the current game.""" """Calculate the amount of time left for the current game."""
this_moment = datetime.now(timezone.utc) this_moment = datetime.now(tz=AMSTERDAM)
midnight = datetime.now(timezone.utc).replace(hour=23, minute=59, second=59, microsecond=0) midnight = datetime.now(tz=AMSTERDAM).replace(hour=23, minute=59, second=59, microsecond=0)
# Calculate the amount of time left till midnight (and the start of the next game) # Calculate the amount of time left till midnight (and the start of the next game)
return midnight - this_moment return midnight - this_moment

View File

@@ -33,12 +33,13 @@ a.title {
padding: 2rem 0 0 0; padding: 2rem 0 0 0;
} }
.guessesheading { .guessesheading, .copied {
color: #CCC;; color: #CCC;
} }
.guessesheading, .guessesbefore, .guessesafter { .guessesheading, .guessesbefore, .guessesafter {
text-align: center; text-align: center;
clear: both;
} }
input[type="text"] { input[type="text"] {

View File

@@ -24,6 +24,8 @@ document.addEventListener('alpine:init', () => {
resultGuesses: Alpine.$persist('').as('resultGuesses'), resultGuesses: Alpine.$persist('').as('resultGuesses'),
resultTimeTaken: Alpine.$persist('').as('resultTimeTaken'), resultTimeTaken: Alpine.$persist('').as('resultTimeTaken'),
resultsCopied: false,
async init() { async init() {
/** Initialise the application after loading */ /** Initialise the application after loading */
await this.getGameID(); await this.getGameID();
@@ -108,7 +110,6 @@ document.addEventListener('alpine:init', () => {
this.resultGuesses = '🤔 '+ this.nrGuesses + ' gokken'; this.resultGuesses = '🤔 '+ this.nrGuesses + ' gokken';
let winTimeDate = new Date(this.winTime); let winTimeDate = new Date(this.winTime);
let startTimeDate = new Date(this.startTime); let startTimeDate = new Date(this.startTime);
// this.resultTimeTaken = '⏱️ ' + getFormattedTime(this.winTime - this.startTime);
this.resultTimeTaken = '⏱️ ' + this.getFormattedTime(winTimeDate - startTimeDate); this.resultTimeTaken = '⏱️ ' + this.getFormattedTime(winTimeDate - startTimeDate);
} }
}, },
@@ -190,23 +191,6 @@ document.addEventListener('alpine:init', () => {
}); });
/* Clipboard stuff **/
let clip = new ClipboardJS('.copy');
clip.on("success", function(e) {
document.getElementById('copyresults').innerHTML = '<p style="font-size:var(--small);opacity:50%">Gekopieerd! Deel je resultaat.</p>';
e.clearSelection();
});
clip.on("error", function() {
document.getElementById('copyresults').innerHTML = '<p style="font-size:var(--small);opacity:50%">Fout. Graag handmatig kopi&euml;ren...</p>';
});
/* Get current gameID etc **/
// document.addEventListener('alpine:initialized', () => { // document.addEventListener('alpine:initialized', () => {
/* On AlpineJS completely loaded, do all this */ /* On AlpineJS completely loaded, do all this */
// Alpine.store('alfagok').getGameID(); // Alpine.store('alfagok').getGameID();

View File

@@ -12,6 +12,7 @@
<link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon-16x16.png"> <link rel="icon" type="image/png" sizes="16x16" href="/static/images/favicon-16x16.png">
<link rel="manifest" href="/static/images/site.webmanifest"> <link rel="manifest" href="/static/images/site.webmanifest">
<script src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@alpinejs/persist@3.x.x/dist/cdn.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-clipboard@2.x.x/dist/alpine-clipboard.js" defer></script>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head> </head>
<body> <body>
@@ -49,8 +50,8 @@
<p>🔗 <span class="link">alfagok.diginaut.net</span></p> <p>🔗 <span class="link">alfagok.diginaut.net</span></p>
</div> </div>
</div> </div>
<div id="copyresults"></div> <div class="copied" x-show="$store.alfagok.resultsCopied">Gekopieerd! Deel je resultaat.</div>
<button class="copy" data-clipboard-target="#results"> <button class="copy" @click="$clipboard($store.alfagok.resultGameID + '\n' + $store.alfagok.resultGuesses + '\n' + $store.alfagok.resultTimeTaken + '\n' + '🔗 alfagok.diginaut.net'); $store.alfagok.resultsCopied = true">
Tik om te kopi&euml;ren en te delen ❤️ Tik om te kopi&euml;ren en te delen ❤️
</button> </button>
</div> </div>
@@ -64,12 +65,10 @@
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
<script src="/static/game.js?v={{ version }}"></script> <script src="/static/game.js?v={{ version }}"></script>
{# {#
<button x-data @click="$store.darkMode.toggle()">Toggle Dark Mode</button> <button x-data @click="$store.darkMode.toggle()">Toggle Dark Mode</button>
<div x-data :class="$store.darkMode.on && 'bg-black'">...</div> <div x-data :class="$store.darkMode.on && 'bg-black'">...</div>
#} #}
</body> </body>
</html> </html>