8 Commits

Author SHA1 Message Date
215374167b Version 1.0.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-20 19:58:40 +01:00
77268ae92f Add make loadall 2023-01-20 19:58:31 +01:00
c42687a072 Change ENTRYPOINT to CMD 2023-01-20 19:58:09 +01:00
ca16345374 Fix start session button starting different game than it says
Fixes #44
2023-01-20 19:57:45 +01:00
3a3045be91 Sort form fields alphabetically
All checks were successful
continuous-integration/drone/push Build is passing
Fixes #39
Fixes #40
2023-01-20 18:27:30 +01:00
d40612af72 Remove Caddy
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-20 17:15:53 +01:00
18e8f93261 Additional fixes
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-20 15:06:42 +01:00
56e5dfaa03 Rename project, part 2 (#42)
Some checks failed
continuous-integration/drone/push Build is failing
Reviewed-on: #42
2023-01-20 13:37:46 +00:00
16 changed files with 68 additions and 32 deletions

2
.gitignore vendored
View File

@ -5,4 +5,4 @@ __pycache__
node_modules
package-lock.json
db.sqlite3
src/timetracker/static
static

View File

@ -1,3 +1,11 @@
## 1.0.0 / 2023-01-20 19:54+01:00
* Breaking
* Due to major re-arranging and re-naming of the folder structure, tables also had to be renamed.
* Fixed
* Sort form fields alphabetically (https://git.kucharczyk.xyz/lukas/timetracker/issues/39, https://git.kucharczyk.xyz/lukas/timetracker/issues/40)
* Start session button starts different game than it says (#44)
## 0.2.5 / 2023-01-18 17:01+01:00
* New

View File

@ -5,10 +5,10 @@
:8000 {
handle_path /static/* {
root * src/timetracker/static/
root * /usr/share/caddy
file_server
}
handle {
reverse_proxy :8001
reverse_proxy backend:8001
}
}

View File

@ -2,11 +2,11 @@ FROM node as css
WORKDIR /app
COPY . /app
RUN npm install && \
npx tailwindcss -i ./src/input.css -o ./src/timetracker/games/static/base.css --minify
npx tailwindcss -i ./common/input.css -o ./static/base.css --minify
FROM python:3.10.9-slim-bullseye
ENV VERSION_NUMBER 0.2.5
ENV VERSION_NUMBER 1.0.0
ENV PROD 1
ENV PYTHONUNBUFFERED=1
@ -15,18 +15,13 @@ RUN apt update && \
bash \
vim \
curl && \
apt install -y debian-keyring debian-archive-keyring apt-transport-https && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list && \
apt update && \
apt install caddy && \
rm -rf /var/lib/apt/lists/*
RUN useradd -m --uid 1000 timetracker
WORKDIR /home/timetracker/app
COPY . /home/timetracker/app/
RUN chown -R timetracker:timetracker /home/timetracker/app
COPY --from=css /app/src/timetracker/games/static/base.css /home/timetracker/app/src/timetracker/games/static/base.css
COPY --from=css ./app/static/base.css /home/timetracker/app/static/base.css
COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh
@ -36,4 +31,4 @@ RUN pip install --no-cache-dir poetry
RUN poetry install --without dev
EXPOSE 8000
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "/entrypoint.sh" ]

View File

@ -34,6 +34,9 @@ dumpgames:
loadplatforms:
poetry run python manage.py loaddata platforms.yaml
loadall:
poetry run python manage.py loaddata data.yaml
loadsample:
poetry run python manage.py loaddata sample.yaml

View File

@ -0,0 +1,17 @@
---
services:
timetracker:
image: registry.kucharczyk.xyz/timetracker
build:
context: .
dockerfile: Dockerfile
container_name: timetracker
environment:
- TZ=Europe/Prague
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz"
user: "1000"
# volumes:
# - "db:/home/timetracker/app/src/timetracker/db.sqlite3"
ports:
- "8000:8000"
restart: unless-stopped

View File

@ -1,17 +1,28 @@
---
services:
timetracker:
backend:
image: registry.kucharczyk.xyz/timetracker
build:
context: .
dockerfile: Dockerfile
container_name: timetracker
environment:
- TZ=Europe/Prague
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz"
user: "1000"
# volumes:
# - "db:/home/timetracker/app/src/timetracker/db.sqlite3"
volumes:
- "static-files:/home/timetracker/app/static"
restart: unless-stopped
frontend:
image: caddy
volumes:
- "static-files:/usr/share/caddy"
- "$PWD/Caddyfile:/etc/caddy/Caddyfile"
ports:
- "8000:8000"
restart: unless-stopped
depends_on:
- backend
volumes:
static-files:

View File

@ -2,12 +2,10 @@
# Apply database migrations
set -euo pipefail
echo "Apply database migrations"
poetry run python src/timetracker/manage.py migrate
poetry run python manage.py migrate
echo "Collect static files"
poetry run python src/timetracker/manage.py collectstatic --clear --no-input
poetry run python manage.py collectstatic --clear --no-input
echo "Starting server"
caddy start
cd src/timetracker || exit
poetry run python -m gunicorn --bind 0.0.0.0:8001 root.asgi:application -k uvicorn.workers.UvicornWorker --access-logfile - --error-logfile -
echo "Starting app"
poetry run python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker --access-logfile - --error-logfile -

View File

@ -4,6 +4,8 @@ from games.models import Game, Platform, Purchase, Session
class SessionForm(forms.ModelForm):
purchase = forms.ModelChoiceField(queryset=Purchase.objects.order_by("game__name"))
class Meta:
model = Session
fields = [
@ -16,6 +18,9 @@ class SessionForm(forms.ModelForm):
class PurchaseForm(forms.ModelForm):
game = forms.ModelChoiceField(queryset=Game.objects.order_by("name"))
platform = forms.ModelChoiceField(queryset=Platform.objects.order_by("name"))
class Meta:
model = Purchase
fields = ["game", "platform", "date_purchased", "date_refunded"]

View File

@ -18,7 +18,7 @@
{% if purchase %}<a class="dark:text-white hover:underline block" href="{% url 'list_sessions_by_game' purchase.game.id %}">See all platforms</a>{% endif %}
{% endif %}
{% if dataset.count >= 1 %}
<a class="clear-both" href="{% url 'start_session' dataset.last.purchase.id %}">
<a class="clear-both" href="{% url 'start_session' last.purchase.id %}">
<button type="button" title="Track last tracked" class="mt-10 py-1 px-2 bg-green-600 hover:bg-green-700 focus:ring-green-500 focus:ring-offset-blue-200 text-white transition ease-in duration-200 text-center text-base font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-lg ">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="self-center w-6 h-6 inline">
<path stroke-linecap="round" stroke-linejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z" />

View File

@ -13,9 +13,7 @@ def version_date():
"%d-%b-%Y %H:%m",
time.gmtime(
os.path.getmtime(
os.path.abspath(
os.path.join(settings.BASE_DIR, "..", "..", "pyproject.toml")
)
os.path.abspath(os.path.join(settings.BASE_DIR, "pyproject.toml"))
)
),
)

View File

@ -85,7 +85,8 @@ def list_sessions(request, filter="", purchase_id="", platform_id="", game_id=""
# cannot use dataset[0] here because that might be only partial QuerySet
context["last"] = Session.objects.all().order_by("timestamp_start").last()
# charts are always oldest->newest
context["chart"] = playtime_over_time_chart(dataset.order_by("timestamp_start"))
if Session.objects.count() >= 2:
context["chart"] = playtime_over_time_chart(dataset.order_by("timestamp_start"))
return render(request, "list_sessions.html", context)

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "timetracker"
version = "0.2.5"
version = "1.0.0"
description = "A simple time tracker."
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
license = "GPL"
@ -31,4 +31,4 @@ requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry.scripts]
timetracker-import = "timetracker.common.import_data:import_from_file"
timetracker-import = "common.import_data:import_from_file"

View File

@ -1,6 +1,6 @@
module.exports = {
darkMode: 'class',
content: ["./src/**/*.{html,js}"],
content: ["./**/*.{html,js}"],
theme: {
fontFamily: {
sans: ['Inter', 'sans-serif'],

View File

@ -14,7 +14,7 @@ import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve()
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production