Compare commits
3 Commits
1.0.0
...
7d6b53cd1d
| Author | SHA1 | Date | |
|---|---|---|---|
|
7d6b53cd1d
|
|||
|
274520c57a
|
|||
|
458ee11a71
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,4 +5,4 @@ __pycache__
|
|||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
static
|
src/timetracker/static
|
||||||
@ -1,11 +1,3 @@
|
|||||||
## 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
|
## 0.2.5 / 2023-01-18 17:01+01:00
|
||||||
|
|
||||||
* New
|
* New
|
||||||
|
|||||||
@ -5,10 +5,10 @@
|
|||||||
|
|
||||||
:8000 {
|
:8000 {
|
||||||
handle_path /static/* {
|
handle_path /static/* {
|
||||||
root * /usr/share/caddy
|
root * src/timetracker/static/
|
||||||
file_server
|
file_server
|
||||||
}
|
}
|
||||||
handle {
|
handle {
|
||||||
reverse_proxy backend:8001
|
reverse_proxy :8001
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
13
Dockerfile
13
Dockerfile
@ -2,11 +2,11 @@ FROM node as css
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
RUN npm install && \
|
RUN npm install && \
|
||||||
npx tailwindcss -i ./common/input.css -o ./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
|
||||||
|
|
||||||
ENV VERSION_NUMBER 1.0.0
|
ENV VERSION_NUMBER 0.2.5
|
||||||
ENV PROD 1
|
ENV PROD 1
|
||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
@ -15,13 +15,18 @@ RUN apt update && \
|
|||||||
bash \
|
bash \
|
||||||
vim \
|
vim \
|
||||||
curl && \
|
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/*
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN useradd -m --uid 1000 timetracker
|
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/static/base.css /home/timetracker/app/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
|
||||||
|
|
||||||
@ -31,4 +36,4 @@ RUN pip install --no-cache-dir poetry
|
|||||||
RUN poetry install --without dev
|
RUN poetry install --without dev
|
||||||
|
|
||||||
EXPOSE 8000
|
EXPOSE 8000
|
||||||
CMD [ "/entrypoint.sh" ]
|
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||||
3
Makefile
3
Makefile
@ -34,9 +34,6 @@ dumpgames:
|
|||||||
loadplatforms:
|
loadplatforms:
|
||||||
poetry run python manage.py loaddata platforms.yaml
|
poetry run python manage.py loaddata platforms.yaml
|
||||||
|
|
||||||
loadall:
|
|
||||||
poetry run python manage.py loaddata data.yaml
|
|
||||||
|
|
||||||
loadsample:
|
loadsample:
|
||||||
poetry run python manage.py loaddata sample.yaml
|
poetry run python manage.py loaddata sample.yaml
|
||||||
|
|
||||||
|
|||||||
@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
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
|
|
||||||
@ -1,28 +1,17 @@
|
|||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
backend:
|
timetracker:
|
||||||
image: registry.kucharczyk.xyz/timetracker
|
image: registry.kucharczyk.xyz/timetracker
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
container_name: timetracker
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Prague
|
- TZ=Europe/Prague
|
||||||
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz"
|
- CSRF_TRUSTED_ORIGINS="https://tracker.kucharczyk.xyz"
|
||||||
user: "1000"
|
user: "1000"
|
||||||
volumes:
|
# volumes:
|
||||||
- "static-files:/home/timetracker/app/static"
|
# - "db:/home/timetracker/app/src/timetracker/db.sqlite3"
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
frontend:
|
|
||||||
image: caddy
|
|
||||||
volumes:
|
|
||||||
- "static-files:/usr/share/caddy"
|
|
||||||
- "$PWD/Caddyfile:/etc/caddy/Caddyfile"
|
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
depends_on:
|
restart: unless-stopped
|
||||||
- backend
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
static-files:
|
|
||||||
|
|
||||||
|
|||||||
@ -2,10 +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 manage.py migrate
|
poetry run python src/timetracker/manage.py migrate
|
||||||
|
|
||||||
echo "Collect static files"
|
echo "Collect static files"
|
||||||
poetry run python manage.py collectstatic --clear --no-input
|
poetry run python src/timetracker/manage.py collectstatic --clear --no-input
|
||||||
|
|
||||||
echo "Starting app"
|
echo "Starting server"
|
||||||
poetry run python -m gunicorn --bind 0.0.0.0:8001 timetracker.asgi:application -k uvicorn.workers.UvicornWorker --access-logfile - --error-logfile -
|
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 -
|
||||||
|
|||||||
@ -4,8 +4,6 @@ from games.models import Game, Platform, Purchase, Session
|
|||||||
|
|
||||||
|
|
||||||
class SessionForm(forms.ModelForm):
|
class SessionForm(forms.ModelForm):
|
||||||
purchase = forms.ModelChoiceField(queryset=Purchase.objects.order_by("game__name"))
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Session
|
model = Session
|
||||||
fields = [
|
fields = [
|
||||||
@ -18,9 +16,6 @@ class SessionForm(forms.ModelForm):
|
|||||||
|
|
||||||
|
|
||||||
class PurchaseForm(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:
|
class Meta:
|
||||||
model = Purchase
|
model = Purchase
|
||||||
fields = ["game", "platform", "date_purchased", "date_refunded"]
|
fields = ["game", "platform", "date_purchased", "date_refunded"]
|
||||||
|
|||||||
@ -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 %}
|
{% 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 %}
|
{% endif %}
|
||||||
{% if dataset.count >= 1 %}
|
{% if dataset.count >= 1 %}
|
||||||
<a class="clear-both" href="{% url 'start_session' last.purchase.id %}">
|
<a class="clear-both" href="{% url 'start_session' dataset.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 ">
|
<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">
|
<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" />
|
<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" />
|
||||||
|
|||||||
@ -13,7 +13,9 @@ def version_date():
|
|||||||
"%d-%b-%Y %H:%m",
|
"%d-%b-%Y %H:%m",
|
||||||
time.gmtime(
|
time.gmtime(
|
||||||
os.path.getmtime(
|
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")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -85,8 +85,7 @@ def list_sessions(request, filter="", purchase_id="", platform_id="", game_id=""
|
|||||||
# cannot use dataset[0] here because that might be only partial QuerySet
|
# cannot use dataset[0] here because that might be only partial QuerySet
|
||||||
context["last"] = Session.objects.all().order_by("timestamp_start").last()
|
context["last"] = Session.objects.all().order_by("timestamp_start").last()
|
||||||
# charts are always oldest->newest
|
# charts are always oldest->newest
|
||||||
if Session.objects.count() >= 2:
|
context["chart"] = playtime_over_time_chart(dataset.order_by("timestamp_start"))
|
||||||
context["chart"] = playtime_over_time_chart(dataset.order_by("timestamp_start"))
|
|
||||||
|
|
||||||
return render(request, "list_sessions.html", context)
|
return render(request, "list_sessions.html", context)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "timetracker"
|
name = "timetracker"
|
||||||
version = "1.0.0"
|
version = "0.2.5"
|
||||||
description = "A simple time tracker."
|
description = "A simple time tracker."
|
||||||
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
|
||||||
license = "GPL"
|
license = "GPL"
|
||||||
@ -31,4 +31,4 @@ requires = ["poetry-core"]
|
|||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
timetracker-import = "common.import_data:import_from_file"
|
timetracker-import = "timetracker.common.import_data:import_from_file"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
content: ["./**/*.{html,js}"],
|
content: ["./src/**/*.{html,js}"],
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['Inter', 'sans-serif'],
|
sans: ['Inter', 'sans-serif'],
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import os
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve()
|
||||||
|
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
|
|||||||
Reference in New Issue
Block a user