Rename project (#41)
continuous-integration/drone/push Build is passing Details

The old naming scheme was causing confusion and probably errors.

Reviewed-on: #41
This commit is contained in:
Lukáš Kucharczyk 2023-01-19 19:35:25 +00:00
parent c3c9ae0632
commit 2f00be455d
46 changed files with 1266 additions and 163 deletions

View File

@ -5,4 +5,4 @@
.venv .venv
.vscode .vscode
node_modules node_modules
src/web/static/* src/timetracker/static/*

2
.gitignore vendored
View File

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

View File

@ -5,7 +5,7 @@
:8000 { :8000 {
handle_path /static/* { handle_path /static/* {
root * src/web/static/ root * src/timetracker/static/
file_server file_server
} }
handle { handle {

View File

@ -2,7 +2,7 @@ FROM node as css
WORKDIR /app WORKDIR /app
COPY . /app COPY . /app
RUN npm install && \ RUN npm install && \
npx tailwindcss -i ./src/input.css -o ./src/web/tracker/static/base.css --minify npx tailwindcss -i ./src/input.css -o ./src/timetracker/games/static/base.css --minify
FROM python:3.10.9-slim-bullseye FROM python:3.10.9-slim-bullseye
@ -26,7 +26,7 @@ RUN useradd -m --uid 1000 timetracker
WORKDIR /home/timetracker/app WORKDIR /home/timetracker/app
COPY . /home/timetracker/app/ COPY . /home/timetracker/app/
RUN chown -R timetracker:timetracker /home/timetracker/app RUN chown -R timetracker:timetracker /home/timetracker/app
COPY --from=css /app/src/web/tracker/static/base.css /home/timetracker/app/src/web/tracker/static/base.css COPY --from=css /app/src/timetracker/games/static/base.css /home/timetracker/app/src/timetracker/games/static/base.css
COPY entrypoint.sh / COPY entrypoint.sh /
RUN chmod +x /entrypoint.sh RUN chmod +x /entrypoint.sh

View File

@ -2,49 +2,49 @@ all: css migrate
initialize: npm css migrate sethookdir loadplatforms initialize: npm css migrate sethookdir loadplatforms
HTMLFILES := $(shell find src/web/tracker/templates -type f) HTMLFILES := $(shell find src/timetracker/games/templates -type f)
npm: npm:
npm install npm install
css: src/input.css css: src/input.css
npx tailwindcss -i ./src/input.css -o ./src/web/tracker/static/base.css npx tailwindcss -i ./src/input.css -o ./src/timetracker/games/static/base.css
css-dev: css css-dev: css
npx tailwindcss -i ./src/input.css -o ./src/web/tracker/static/base.css --watch npx tailwindcss -i ./src/input.css -o ./src/timetracker/games/static/base.css --watch
makemigrations: makemigrations:
poetry run python src/web/manage.py makemigrations poetry run python src/timetracker/manage.py makemigrations
migrate: makemigrations migrate: makemigrations
poetry run python src/web/manage.py migrate poetry run python src/timetracker/manage.py migrate
dev: migrate dev: migrate
poetry run python src/web/manage.py runserver poetry run python src/timetracker/manage.py runserver
caddy: caddy:
caddy run --watch caddy run --watch
dev-prod: migrate collectstatic dev-prod: migrate collectstatic
cd src/web/; PROD=1 poetry run python -m gunicorn --bind 0.0.0.0:8001 web.asgi:application -k uvicorn.workers.UvicornWorker cd src/timetracker/; PROD=1 poetry run python -m gunicorn --bind 0.0.0.0:8001 root.asgi:application -k uvicorn.workers.UvicornWorker
dumptracker: dumpgames:
poetry run python src/web/manage.py dumpdata --format yaml tracker --output tracker_fixture.yaml poetry run python src/timetracker/manage.py dumpdata --format yaml games --output tracker_fixture.yaml
loadplatforms: loadplatforms:
poetry run python src/web/manage.py loaddata platforms.yaml poetry run python src/timetracker/manage.py loaddata platforms.yaml
loadsample: loadsample:
poetry run python src/web/manage.py loaddata sample.yaml poetry run python src/timetracker/manage.py loaddata sample.yaml
createsuperuser: createsuperuser:
poetry run python src/web/manage.py createsuperuser poetry run python src/timetracker/manage.py createsuperuser
shell: shell:
poetry run python src/web/manage.py shell poetry run python src/timetracker/manage.py shell
collectstatic: collectstatic:
poetry run python src/web/manage.py collectstatic --clear --no-input poetry run python src/timetracker/manage.py collectstatic --clear --no-input
poetry.lock: pyproject.toml poetry.lock: pyproject.toml
poetry install poetry install
@ -56,6 +56,6 @@ date:
poetry run python -c 'import datetime; from zoneinfo import ZoneInfo; print(datetime.datetime.isoformat(datetime.datetime.now(ZoneInfo("Europe/Prague")), timespec="minutes", sep=" "))' poetry run python -c 'import datetime; from zoneinfo import ZoneInfo; print(datetime.datetime.isoformat(datetime.datetime.now(ZoneInfo("Europe/Prague")), timespec="minutes", sep=" "))'
cleanstatic: cleanstatic:
rm -r src/web/static/* rm -r src/timetracker/static/*
clean: cleanstatic clean: cleanstatic

View File

@ -11,7 +11,7 @@ services:
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz" - CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz"
user: "1000" user: "1000"
# volumes: # volumes:
# - "db:/home/timetracker/app/src/web/db.sqlite3" # - "db:/home/timetracker/app/src/timetracker/db.sqlite3"
ports: ports:
- "8000:8000" - "8000:8000"
restart: unless-stopped restart: unless-stopped

View File

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

View File

@ -5,6 +5,7 @@ description = "A simple time tracker."
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"] authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
license = "GPL" license = "GPL"
readme = "README.md" readme = "README.md"
packages = [{include = "timetracker", from = "src"}]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"

View File

@ -1,3 +0,0 @@
import logging
logging.basicConfig(level=logging.ERROR, filename="timelogger.log")

View File

@ -7,7 +7,7 @@ import matplotlib.dates as mdates
import pandas as pd import pandas as pd
from django.db.models import F, IntegerField, QuerySet, Sum from django.db.models import F, IntegerField, QuerySet, Sum
from django.db.models.functions import TruncDay from django.db.models.functions import TruncDay
from tracker.models import Session from games.models import Session
def key_value_to_value_value(data): def key_value_to_value_value(data):

View File

@ -1,6 +1,6 @@
from django.apps import AppConfig from django.apps import AppConfig
class TrackerConfig(AppConfig): class GamesConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField" default_auto_field = "django.db.models.BigAutoField"
name = "tracker" name = "games"

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +1,28 @@
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Steam name: Steam
group: PC group: PC
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Xbox Gamepass name: Xbox Gamepass
group: PC group: PC
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Epic Games Store name: Epic Games Store
group: PC group: PC
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Playstation 5 name: Playstation 5
group: Playstation group: Playstation
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Playstation 4 name: Playstation 4
group: Playstation group: Playstation
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Nintendo Switch name: Nintendo Switch
group: Nintendo group: Nintendo
- model: tracker.Platform - model: games.Platform
fields: fields:
name: Nintendo 3DS name: Nintendo 3DS
group: Nintendo group: Nintendo

View File

@ -0,0 +1,71 @@
- model: games.game
pk: 1
fields:
name: Nioh 2
wikidata: Q67482292
- model: games.game
pk: 2
fields:
name: Elden Ring
wikidata: Q64826862
- model: games.game
pk: 3
fields:
name: Cyberpunk 2077
wikidata: Q3182559
- model: games.purchase
pk: 1
fields:
game: 1
platform: 1
date_purchased: 2021-02-13
date_refunded: null
- model: games.purchase
pk: 2
fields:
game: 2
platform: 1
date_purchased: 2022-02-24
date_refunded: null
- model: games.purchase
pk: 3
fields:
game: 3
platform: 1
date_purchased: 2020-12-07
date_refunded: null
- model: games.platform
pk: 1
fields:
name: Steam
group: PC
- model: games.platform
pk: 3
fields:
name: Xbox Gamepass
group: PC
- model: games.platform
pk: 4
fields:
name: Epic Games Store
group: PC
- model: games.platform
pk: 5
fields:
name: Playstation 5
group: Playstation
- model: games.platform
pk: 6
fields:
name: Playstation 4
group: Playstation
- model: games.platform
pk: 7
fields:
name: Nintendo Switch
group: Nintendo
- model: games.platform
pk: 8
fields:
name: Nintendo 3DS
group: Nintendo

View File

@ -60,14 +60,14 @@ class Migration(migrations.Migration):
( (
"game", "game",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="tracker.game" on_delete=django.db.models.deletion.CASCADE, to="games.game"
), ),
), ),
( (
"platform", "platform",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="tracker.platform", to="games.platform",
), ),
), ),
], ],
@ -93,7 +93,7 @@ class Migration(migrations.Migration):
"purchase", "purchase",
models.ForeignKey( models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
to="tracker.purchase", to="games.purchase",
), ),
), ),
], ],

View File

@ -8,7 +8,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("tracker", "0001_initial"), ("games", "0001_initial"),
] ]
operations = [ operations = [

View File

@ -6,7 +6,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("tracker", "0002_alter_session_duration_manual"), ("games", "0002_alter_session_duration_manual"),
] ]
operations = [ operations = [

View File

@ -8,7 +8,7 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("tracker", "0003_alter_session_duration_manual_and_more"), ("games", "0003_alter_session_duration_manual_and_more"),
] ]
operations = [ operations = [

View File

@ -6,7 +6,7 @@ from django.db import migrations
def set_duration_calculated_none_to_zero(apps, schema_editor): def set_duration_calculated_none_to_zero(apps, schema_editor):
Session = apps.get_model("tracker", "Session") Session = apps.get_model("games", "Session")
for session in Session.objects.all(): for session in Session.objects.all():
if session.duration_calculated == None: if session.duration_calculated == None:
session.duration_calculated = timedelta(0) session.duration_calculated = timedelta(0)
@ -14,7 +14,7 @@ def set_duration_calculated_none_to_zero(apps, schema_editor):
def revert_set_duration_calculated_none_to_zero(apps, schema_editor): def revert_set_duration_calculated_none_to_zero(apps, schema_editor):
Session = apps.get_model("tracker", "Session") Session = apps.get_model("games", "Session")
for session in Session.objects.all(): for session in Session.objects.all():
if session.duration_calculated == timedelta(0): if session.duration_calculated == timedelta(0):
session.duration_calculated = None session.duration_calculated = None
@ -24,7 +24,7 @@ def revert_set_duration_calculated_none_to_zero(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("tracker", "0004_alter_session_duration_manual"), ("games", "0004_alter_session_duration_manual"),
] ]
operations = [ operations = [

View File

@ -6,7 +6,7 @@ from django.db import migrations
def set_duration_manual_none_to_zero(apps, schema_editor): def set_duration_manual_none_to_zero(apps, schema_editor):
Session = apps.get_model("tracker", "Session") Session = apps.get_model("games", "Session")
for session in Session.objects.all(): for session in Session.objects.all():
if session.duration_manual == None: if session.duration_manual == None:
session.duration_manual = timedelta(0) session.duration_manual = timedelta(0)
@ -14,7 +14,7 @@ def set_duration_manual_none_to_zero(apps, schema_editor):
def revert_set_duration_manual_none_to_zero(apps, schema_editor): def revert_set_duration_manual_none_to_zero(apps, schema_editor):
Session = apps.get_model("tracker", "Session") Session = apps.get_model("games", "Session")
for session in Session.objects.all(): for session in Session.objects.all():
if session.duration_manual == timedelta(0): if session.duration_manual == timedelta(0):
session.duration_manual = None session.duration_manual = None
@ -24,7 +24,7 @@ def revert_set_duration_manual_none_to_zero(apps, schema_editor):
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("tracker", "0005_auto_20230109_1843"), ("games", "0005_auto_20230109_1843"),
] ]
operations = [ operations = [

View File

@ -0,0 +1,35 @@
# Generated by Django 4.1.5 on 2023-01-19 18:30
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("games", "0006_auto_20230109_1904"),
]
operations = [
migrations.AlterField(
model_name="purchase",
name="game",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="games.game"
),
),
migrations.AlterField(
model_name="purchase",
name="platform",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="games.platform"
),
),
migrations.AlterField(
model_name="session",
name="purchase",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to="games.purchase"
),
),
]

View File

@ -2,7 +2,7 @@ from datetime import datetime, timedelta
from typing import Any from typing import Any
from zoneinfo import ZoneInfo from zoneinfo import ZoneInfo
from common.util.time import format_duration from timetracker.common.util.time import format_duration
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.db.models import F, Manager, Sum from django.db.models import F, Manager, Sum

View File

@ -21,9 +21,15 @@ def model_counts(request):
def add_session(request): def add_session(request):
context = {} context = {}
initial = {}
now = now_with_tz() now = now_with_tz()
initial["timestamp_start"] = now
last = Session.objects.all().last() last = Session.objects.all().last()
initial = {"timestamp_start": now, "purchase": last.purchase} if last != None:
initial["purchase"] = last.purchase
form = SessionForm(request.POST or None, initial=initial) form = SessionForm(request.POST or None, initial=initial)
if form.is_valid(): if form.is_valid():
form.save() form.save()

View File

@ -6,7 +6,7 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "root.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:

View File

@ -1,5 +1,5 @@
""" """
ASGI config for web project. ASGI config for root project.
It exposes the ASGI callable as a module-level variable named ``application``. It exposes the ASGI callable as a module-level variable named ``application``.
@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "root.settings")
application = get_asgi_application() application = get_asgi_application()

View File

@ -1,5 +1,5 @@
""" """
Django settings for web project. Django settings for timetracker project.
Generated by 'django-admin startproject' using Django 4.1.4. Generated by 'django-admin startproject' using Django 4.1.4.
@ -32,7 +32,7 @@ ALLOWED_HOSTS = ["*"]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"tracker.apps.TrackerConfig", "games.apps.GamesConfig",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
"django.contrib.sessions", "django.contrib.sessions",
@ -54,7 +54,7 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
ROOT_URLCONF = "web.urls" ROOT_URLCONF = "root.urls"
TEMPLATES = [ TEMPLATES = [
{ {
@ -67,13 +67,13 @@ TEMPLATES = [
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"tracker.views.model_counts", "games.views.model_counts",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = "web.wsgi.application" WSGI_APPLICATION = "root.wsgi.application"
# Database # Database

View File

@ -1,4 +1,4 @@
"""web URL Configuration """timetracker URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see: The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.1/topics/http/urls/ https://docs.djangoproject.com/en/4.1/topics/http/urls/
@ -20,7 +20,7 @@ from django.views.generic import RedirectView
urlpatterns = [ urlpatterns = [
path("", RedirectView.as_view(url="/tracker/list-sessions")), path("", RedirectView.as_view(url="/tracker/list-sessions")),
path("tracker/", include("tracker.urls")), path("tracker/", include("games.urls")),
] ]
if settings.DEBUG: if settings.DEBUG:

View File

@ -1,5 +1,5 @@
""" """
WSGI config for web project. WSGI config for timetracker project.
It exposes the WSGI callable as a module-level variable named ``application``. It exposes the WSGI callable as a module-level variable named ``application``.
@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "web.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "root.settings")
application = get_wsgi_application() application = get_wsgi_application()

View File

@ -1,98 +0,0 @@
- model: tracker.game
pk: 1
fields:
name: Nioh 2
wikidata: Q67482292
- model: tracker.game
pk: 2
fields:
name: Elden Ring
wikidata: Q64826862
- model: tracker.game
pk: 3
fields:
name: Cyberpunk 2077
wikidata: Q3182559
- model: tracker.purchase
pk: 1
fields:
game: 1
platform: 1
date_purchased: 2021-02-13
date_refunded: null
- model: tracker.purchase
pk: 2
fields:
game: 2
platform: 1
date_purchased: 2022-02-24
date_refunded: null
- model: tracker.purchase
pk: 3
fields:
game: 3
platform: 1
date_purchased: 2020-12-07
date_refunded: null
- model: tracker.platform
pk: 1
fields:
name: Steam
group: PC
- model: tracker.platform
pk: 3
fields:
name: Xbox Gamepass
group: PC
- model: tracker.platform
pk: 4
fields:
name: Epic Games Store
group: PC
- model: tracker.platform
pk: 5
fields:
name: Playstation 5
group: Playstation
- model: tracker.platform
pk: 6
fields:
name: Playstation 4
group: Playstation
- model: tracker.platform
pk: 7
fields:
name: Nintendo Switch
group: Nintendo
- model: tracker.platform
pk: 8
fields:
name: Nintendo 3DS
group: Nintendo
- model: tracker.session
pk: 1
fields:
purchase: 2
timestamp_start: 2022-12-31 14:25:58+00:00
timestamp_end: 2022-12-31 16:25:22+00:00
duration_manual: null
duration_calculated: null
note: ''
- model: tracker.session
pk: 3
fields:
purchase: 3
timestamp_start: 2023-01-01 22:00:23+00:00
timestamp_end: 2023-01-01 23:28:23+00:00
duration_manual: null
duration_calculated: null
note: ''
- model: tracker.session
pk: 4
fields:
purchase: 3
timestamp_start: 2020-01-01 23:29:17+00:00
timestamp_end: 2020-01-01 23:29:17+00:00
duration_manual: '12:00:00'
duration_calculated: null
note: ''

View File

@ -1,7 +1,7 @@
import unittest import unittest
from datetime import timedelta from datetime import timedelta
from web.common.util.time import format_duration from timetracker.common.util.time import format_duration
class FormatDurationTest(unittest.TestCase): class FormatDurationTest(unittest.TestCase):