Compare commits
	
		
			4 Commits
		
	
	
		
			d4ab0596da
			...
			c337d2200f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c337d2200f | |||
| 8a8b05b0bd | |||
| 9446065271 | |||
| 755093845d | 
@ -1,5 +1,8 @@
 | 
				
			|||||||
## Unreleased
 | 
					## Unreleased
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					* Add support for device info (https://git.kucharczyk.xyz/lukas/timetracker/issues/49)
 | 
				
			||||||
 | 
					* Add support for purchase ownership information (https://git.kucharczyk.xyz/lukas/timetracker/issues/48)
 | 
				
			||||||
 | 
					* Add support for purchase prices
 | 
				
			||||||
* Add support for game editions (https://git.kucharczyk.xyz/lukas/timetracker/issues/28)
 | 
					* Add support for game editions (https://git.kucharczyk.xyz/lukas/timetracker/issues/28)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## 1.0.1 / 2023-01-30 22:17+01:00
 | 
					## 1.0.1 / 2023-01-30 22:17+01:00
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
from django import forms
 | 
					from django import forms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from games.models import Game, Platform, Purchase, Session, Edition
 | 
					from games.models import Game, Platform, Purchase, Session, Edition, Device
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class SessionForm(forms.ModelForm):
 | 
					class SessionForm(forms.ModelForm):
 | 
				
			||||||
@ -15,6 +15,7 @@ class SessionForm(forms.ModelForm):
 | 
				
			|||||||
            "timestamp_start",
 | 
					            "timestamp_start",
 | 
				
			||||||
            "timestamp_end",
 | 
					            "timestamp_end",
 | 
				
			||||||
            "duration_manual",
 | 
					            "duration_manual",
 | 
				
			||||||
 | 
					            "device",
 | 
				
			||||||
            "note",
 | 
					            "note",
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,7 +26,15 @@ class PurchaseForm(forms.ModelForm):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Purchase
 | 
					        model = Purchase
 | 
				
			||||||
        fields = ["edition", "platform", "date_purchased", "date_refunded"]
 | 
					        fields = [
 | 
				
			||||||
 | 
					            "edition",
 | 
				
			||||||
 | 
					            "platform",
 | 
				
			||||||
 | 
					            "date_purchased",
 | 
				
			||||||
 | 
					            "date_refunded",
 | 
				
			||||||
 | 
					            "price",
 | 
				
			||||||
 | 
					            "price_currency",
 | 
				
			||||||
 | 
					            "ownership_type",
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EditionForm(forms.ModelForm):
 | 
					class EditionForm(forms.ModelForm):
 | 
				
			||||||
@ -44,3 +53,9 @@ class PlatformForm(forms.ModelForm):
 | 
				
			|||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        model = Platform
 | 
					        model = Platform
 | 
				
			||||||
        fields = ["name", "group"]
 | 
					        fields = ["name", "group"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DeviceForm(forms.ModelForm):
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        model = Device
 | 
				
			||||||
 | 
					        fields = ["name", "type"]
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# Generated by Django 4.1.5 on 2023-02-18 19:53
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("games", "0011_rename_game_purchase_edition"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name="purchase",
 | 
				
			||||||
 | 
					            name="price",
 | 
				
			||||||
 | 
					            field=models.IntegerField(default=0),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name="purchase",
 | 
				
			||||||
 | 
					            name="price_currency",
 | 
				
			||||||
 | 
					            field=models.CharField(default="USD", max_length=3),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										31
									
								
								games/migrations/0013_purchase_ownership_type.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								games/migrations/0013_purchase_ownership_type.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					# Generated by Django 4.1.5 on 2023-02-18 19:54
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("games", "0012_purchase_price_purchase_price_currency"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name="purchase",
 | 
				
			||||||
 | 
					            name="ownership_type",
 | 
				
			||||||
 | 
					            field=models.CharField(
 | 
				
			||||||
 | 
					                choices=[
 | 
				
			||||||
 | 
					                    ("ph", "Physical"),
 | 
				
			||||||
 | 
					                    ("di", "Digital"),
 | 
				
			||||||
 | 
					                    ("du", "Digital Upgrade"),
 | 
				
			||||||
 | 
					                    ("re", "Rented"),
 | 
				
			||||||
 | 
					                    ("bo", "Borrowed"),
 | 
				
			||||||
 | 
					                    ("tr", "Trial"),
 | 
				
			||||||
 | 
					                    ("de", "Demo"),
 | 
				
			||||||
 | 
					                    ("pi", "Pirated"),
 | 
				
			||||||
 | 
					                ],
 | 
				
			||||||
 | 
					                default="di",
 | 
				
			||||||
 | 
					                max_length=2,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										52
									
								
								games/migrations/0014_device_session_device.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								games/migrations/0014_device_session_device.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					# Generated by Django 4.1.5 on 2023-02-18 19:59
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("games", "0013_purchase_ownership_type"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name="Device",
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "id",
 | 
				
			||||||
 | 
					                    models.BigAutoField(
 | 
				
			||||||
 | 
					                        auto_created=True,
 | 
				
			||||||
 | 
					                        primary_key=True,
 | 
				
			||||||
 | 
					                        serialize=False,
 | 
				
			||||||
 | 
					                        verbose_name="ID",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                ("name", models.CharField(max_length=255)),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "type",
 | 
				
			||||||
 | 
					                    models.CharField(
 | 
				
			||||||
 | 
					                        choices=[
 | 
				
			||||||
 | 
					                            ("pc", "PC"),
 | 
				
			||||||
 | 
					                            ("co", "Console"),
 | 
				
			||||||
 | 
					                            ("ha", "Handheld"),
 | 
				
			||||||
 | 
					                            ("mo", "Mobile"),
 | 
				
			||||||
 | 
					                            ("sbc", "Single-board computer"),
 | 
				
			||||||
 | 
					                        ],
 | 
				
			||||||
 | 
					                        default="pc",
 | 
				
			||||||
 | 
					                        max_length=3,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name="session",
 | 
				
			||||||
 | 
					            name="device",
 | 
				
			||||||
 | 
					            field=models.ForeignKey(
 | 
				
			||||||
 | 
					                null=True,
 | 
				
			||||||
 | 
					                on_delete=django.db.models.deletion.CASCADE,
 | 
				
			||||||
 | 
					                to="games.device",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
@ -26,13 +26,37 @@ class Edition(models.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Purchase(models.Model):
 | 
					class Purchase(models.Model):
 | 
				
			||||||
 | 
					    PHYSICAL = "ph"
 | 
				
			||||||
 | 
					    DIGITAL = "di"
 | 
				
			||||||
 | 
					    DIGITALUPGRADE = "du"
 | 
				
			||||||
 | 
					    RENTED = "re"
 | 
				
			||||||
 | 
					    BORROWED = "bo"
 | 
				
			||||||
 | 
					    TRIAL = "tr"
 | 
				
			||||||
 | 
					    DEMO = "de"
 | 
				
			||||||
 | 
					    PIRATED = "pi"
 | 
				
			||||||
 | 
					    OWNERSHIP_TYPES = [
 | 
				
			||||||
 | 
					        (PHYSICAL, "Physical"),
 | 
				
			||||||
 | 
					        (DIGITAL, "Digital"),
 | 
				
			||||||
 | 
					        (DIGITALUPGRADE, "Digital Upgrade"),
 | 
				
			||||||
 | 
					        (RENTED, "Rented"),
 | 
				
			||||||
 | 
					        (BORROWED, "Borrowed"),
 | 
				
			||||||
 | 
					        (TRIAL, "Trial"),
 | 
				
			||||||
 | 
					        (DEMO, "Demo"),
 | 
				
			||||||
 | 
					        (PIRATED, "Pirated"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    edition = models.ForeignKey("Edition", on_delete=models.CASCADE)
 | 
					    edition = models.ForeignKey("Edition", on_delete=models.CASCADE)
 | 
				
			||||||
    platform = models.ForeignKey("Platform", on_delete=models.CASCADE)
 | 
					    platform = models.ForeignKey("Platform", on_delete=models.CASCADE)
 | 
				
			||||||
    date_purchased = models.DateField()
 | 
					    date_purchased = models.DateField()
 | 
				
			||||||
    date_refunded = models.DateField(blank=True, null=True)
 | 
					    date_refunded = models.DateField(blank=True, null=True)
 | 
				
			||||||
 | 
					    price = models.IntegerField(default=0)
 | 
				
			||||||
 | 
					    price_currency = models.CharField(max_length=3, default="USD")
 | 
				
			||||||
 | 
					    ownership_type = models.CharField(
 | 
				
			||||||
 | 
					        max_length=2, choices=OWNERSHIP_TYPES, default=DIGITAL
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return f"{self.edition} ({self.platform})"
 | 
					        return f"{self.edition} ({self.platform}, {self.get_ownership_type_display()})"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Platform(models.Model):
 | 
					class Platform(models.Model):
 | 
				
			||||||
@ -57,6 +81,7 @@ class Session(models.Model):
 | 
				
			|||||||
    timestamp_end = models.DateTimeField(blank=True, null=True)
 | 
					    timestamp_end = models.DateTimeField(blank=True, null=True)
 | 
				
			||||||
    duration_manual = models.DurationField(blank=True, null=True, default=timedelta(0))
 | 
					    duration_manual = models.DurationField(blank=True, null=True, default=timedelta(0))
 | 
				
			||||||
    duration_calculated = models.DurationField(blank=True, null=True)
 | 
					    duration_calculated = models.DurationField(blank=True, null=True)
 | 
				
			||||||
 | 
					    device = models.ForeignKey("Device", on_delete=models.CASCADE, null=True)
 | 
				
			||||||
    note = models.TextField(blank=True, null=True)
 | 
					    note = models.TextField(blank=True, null=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    objects = SessionQuerySet.as_manager()
 | 
					    objects = SessionQuerySet.as_manager()
 | 
				
			||||||
@ -94,3 +119,23 @@ class Session(models.Model):
 | 
				
			|||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.duration_calculated = timedelta(0)
 | 
					            self.duration_calculated = timedelta(0)
 | 
				
			||||||
        super(Session, self).save(*args, **kwargs)
 | 
					        super(Session, self).save(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Device(models.Model):
 | 
				
			||||||
 | 
					    PC = "pc"
 | 
				
			||||||
 | 
					    CONSOLE = "co"
 | 
				
			||||||
 | 
					    HANDHELD = "ha"
 | 
				
			||||||
 | 
					    MOBILE = "mo"
 | 
				
			||||||
 | 
					    SBC = "sbc"
 | 
				
			||||||
 | 
					    DEVICE_TYPES = [
 | 
				
			||||||
 | 
					        (PC, "PC"),
 | 
				
			||||||
 | 
					        (CONSOLE, "Console"),
 | 
				
			||||||
 | 
					        (HANDHELD, "Handheld"),
 | 
				
			||||||
 | 
					        (MOBILE, "Mobile"),
 | 
				
			||||||
 | 
					        (SBC, "Single-board computer"),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					    name = models.CharField(max_length=255)
 | 
				
			||||||
 | 
					    type = models.CharField(max_length=3, choices=DEVICE_TYPES, default=PC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self):
 | 
				
			||||||
 | 
					        return f"{self.name} ({self.get_type_display()})"
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,7 @@
 | 
				
			|||||||
                            {% endif %}
 | 
					                            {% endif %}
 | 
				
			||||||
                            {% if purchase_available %}
 | 
					                            {% if purchase_available %}
 | 
				
			||||||
                                <li><a class="block py-2 pl-3 pr-4 hover:underline" href="{% url 'add_session' %}">New Session</a></li>
 | 
					                                <li><a class="block py-2 pl-3 pr-4 hover:underline" href="{% url 'add_session' %}">New Session</a></li>
 | 
				
			||||||
 | 
					                                <li><a class="block py-2 pl-3 pr-4 hover:underline" href="{% url 'add_device' %}">New Device</a></li>
 | 
				
			||||||
                            {% endif %}
 | 
					                            {% endif %}
 | 
				
			||||||
                            {% if session_count > 0 %}
 | 
					                            {% if session_count > 0 %}
 | 
				
			||||||
                                <li><a class="block py-2 pl-3 pr-4 hover:underline" href="{% url 'list_sessions' %}">All Sessions</a></li>
 | 
					                                <li><a class="block py-2 pl-3 pr-4 hover:underline" href="{% url 'list_sessions' %}">All Sessions</a></li>
 | 
				
			||||||
 | 
				
			|||||||
@ -30,6 +30,7 @@ urlpatterns = [
 | 
				
			|||||||
    ),
 | 
					    ),
 | 
				
			||||||
    path("add-purchase/", views.add_purchase, name="add_purchase"),
 | 
					    path("add-purchase/", views.add_purchase, name="add_purchase"),
 | 
				
			||||||
    path("add-edition/", views.add_edition, name="add_edition"),
 | 
					    path("add-edition/", views.add_edition, name="add_edition"),
 | 
				
			||||||
 | 
					    path("add-device/", views.add_device, name="add_device"),
 | 
				
			||||||
    path("edit-session/<int:session_id>", views.edit_session, name="edit_session"),
 | 
					    path("edit-session/<int:session_id>", views.edit_session, name="edit_session"),
 | 
				
			||||||
    path("list-sessions/", views.list_sessions, name="list_sessions"),
 | 
					    path("list-sessions/", views.list_sessions, name="list_sessions"),
 | 
				
			||||||
    path(
 | 
					    path(
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,14 @@ from common.time import now as now_with_tz
 | 
				
			|||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.shortcuts import redirect, render
 | 
					from django.shortcuts import redirect, render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .forms import GameForm, PlatformForm, PurchaseForm, SessionForm, EditionForm
 | 
					from .forms import (
 | 
				
			||||||
 | 
					    GameForm,
 | 
				
			||||||
 | 
					    PlatformForm,
 | 
				
			||||||
 | 
					    PurchaseForm,
 | 
				
			||||||
 | 
					    SessionForm,
 | 
				
			||||||
 | 
					    EditionForm,
 | 
				
			||||||
 | 
					    DeviceForm,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from .models import Game, Platform, Purchase, Session, Edition
 | 
					from .models import Game, Platform, Purchase, Session, Edition
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -160,5 +167,17 @@ def add_platform(request):
 | 
				
			|||||||
    return render(request, "add.html", context)
 | 
					    return render(request, "add.html", context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def add_device(request):
 | 
				
			||||||
 | 
					    context = {}
 | 
				
			||||||
 | 
					    form = DeviceForm(request.POST or None)
 | 
				
			||||||
 | 
					    if form.is_valid():
 | 
				
			||||||
 | 
					        form.save()
 | 
				
			||||||
 | 
					        return redirect("index")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context["form"] = form
 | 
				
			||||||
 | 
					    context["title"] = "Add New Device"
 | 
				
			||||||
 | 
					    return render(request, "add.html", context)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def index(request):
 | 
					def index(request):
 | 
				
			||||||
    return redirect("list_sessions_recent")
 | 
					    return redirect("list_sessions_recent")
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user