added basic pages + task import

This commit is contained in:
2025-01-26 00:53:17 +01:00
parent 6810fc976a
commit cd604e39a0
23 changed files with 425 additions and 6 deletions

36
dispatcher/core.py Normal file
View File

@ -0,0 +1,36 @@
from dispatcher.models import Task, Project
def import_tasks(csv_content: str):
tasks = []
for line in csv_content.splitlines()[1:]:
if len(line.strip()) == 0:
continue
date, duration, project_name, name = line.split(";")
if duration == "-":
duration = 0
else:
hours, mins = duration.split(":")
duration = int(hours) * 60 + int(mins)
project, created = Project.objects.get_or_create(name=project_name)
if created:
print(f"Created new project {project}")
tasks.append(Task(
date=date,
duration=duration,
project=project,
name=name
))
Task.objects.bulk_create(
tasks,
update_conflicts=True,
unique_fields=["date", "project", "name"],
update_fields=["duration"]
)
print(f"Imported {len(tasks)} tasks")

12
dispatcher/forms.py Normal file
View File

@ -0,0 +1,12 @@
from django import forms
from dispatcher.models import Project
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = "__all__"
class ImportForm(forms.Form):
file = forms.FileField()

View File

@ -0,0 +1,19 @@
# Generated by Django 5.1.5 on 2025-01-25 00:15
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dispatcher', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='project',
name='parent',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='dispatcher.parent'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.5 on 2025-01-25 12:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dispatcher', '0002_alter_project_parent'),
]
operations = [
migrations.AddField(
model_name='task',
name='name',
field=models.CharField(default='', max_length=512),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 5.1.5 on 2025-01-25 21:29
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dispatcher', '0003_task_name'),
]
operations = [
migrations.AddConstraint(
model_name='task',
constraint=models.UniqueConstraint(fields=('date', 'project', 'name'), name='unique_daily_task'),
),
]

View File

@ -4,13 +4,29 @@ class Parent(models.Model):
project_num = models.CharField(max_length=32)
name = models.CharField(max_length=256)
def __str__(self):
return self.name
class Project(models.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
parent = models.ForeignKey(
Parent,
on_delete=models.CASCADE,
null=True
)
name = models.CharField(max_length=256)
def __str__(self):
return self.name
class Task(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
date = models.DateField(null=False)
duration = models.IntegerField(null=False, default=0)
name = models.CharField(max_length=512, default="")
class Meta:
constraints = [
models.UniqueConstraint(fields=["date", "project", "name"], name="unique_daily_task")
]

View File

@ -0,0 +1,82 @@
:root {
--dark1: #232323;
--dark2: #2f2f2f;
--dark3: #444444;
--dark4: #656565;
--light1: #ffffff;
--light2: #e5e5e5;
--light3: #cccccc;
--light4: #c5c5c5;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: var(--dark1);
color: var(--light1);
height: 100vh;
display: flex;
flex-direction: column;
font-family: "Segoe UI", system-ui, sans-serif;
font-size: 12pt;
}
header {
background-color: var(--dark2);
font-size: 120%;
}
nav {
display: flex;
}
nav a {
color: var(--light1);
text-decoration: none;
display: grid;
place-items: center;
padding: 1.2em;
transition: background-color 0.2s;
}
nav a.active {
box-shadow: 0 -2px var(--light1) inset;
}
nav a:hover {
background-color: var(--dark3);
}
footer {
background-color: var(--dark2);
min-height: 3em;
}
main {
flex-grow: 1;
display: flex;
flex-direction: column;
padding: 2em;
}
button, input, select {
font-family: inherit;
font-size: inherit;
padding: 0.2em 0.4em;
background-color: var(--light1);
color: var(--dark1);
border: solid var(--dark3) 1px;
border-radius: 4px;
}
button:hover, select:hover {
background-color: var(--light2);
}
button, select {
cursor: pointer;
}

View File

View File

View File

@ -0,0 +1,20 @@
.list {
display: flex;
width: 100%;
max-width: 40em;
align-self: center;
gap: 0.8em;
flex-direction: column;
}
.list li {
display: flex;
padding: 0.8em 1.2em;
marker: none;
gap: 0.8em;
border: solid var(--dark4) 1px;
}
.list li .actions {
margin-left: auto;
}

View File

@ -0,0 +1,24 @@
.projects {
width: 100%;
max-width: 40em;
border-collapse: collapse;
align-self: center;
}
.projects thead {
border-bottom: solid var(--light3) 2px;
}
.projects thead th:not(:first-child) {
border-left: solid var(--light3) 2px;
}
.projects th, .projects td {
text-align: left;
padding: 0.4em 0.8em;
}
.projects tbody tr:nth-child(even) {
background-color: var(--dark2);
}

View File

@ -0,0 +1,8 @@
window.addEventListener("load", () => {
document.querySelectorAll(".projects tbody tr").forEach(row => {
let id = row.dataset.id
row.querySelector("button.see").addEventListener("click", () => {
window.location.href = `${id}/`
})
})
})

View File

@ -1,5 +1,46 @@
from django.shortcuts import render
from django.core.files.uploadedfile import UploadedFile
from django.shortcuts import render, get_object_or_404, redirect
from django.views import generic
from dispatcher.core import import_tasks
from dispatcher.forms import ProjectForm, ImportForm
from dispatcher.models import Project, Parent
def dashboard_view(request):
return render(request, "dashboard.html")
context = {
"projects": Project.objects.all(),
"parents": Parent.objects.all()
}
return render(request, "dashboard.html", context)
def projects_view(request):
context = {
"projects": Project.objects.all(),
"parents": Parent.objects.all()
}
return render(request, "projects.html", context)
def project_view(request, id):
project = get_object_or_404(Project, id=id)
context = {}
form = ProjectForm(request.POST or None, request.FILES or None, instance=project)
if form.is_valid():
form.save()
context["form"] = ProjectForm(instance=project)
return render(request, "project.html", context)
def import_view(request):
if request.method == "POST":
form = ImportForm(request.POST, request.FILES)
if form.is_valid():
print(request.FILES)
import_tasks(request.FILES["file"].read().decode("utf-8"))
return redirect("dashboard")
return render(request, "import.html")
class ParentsView(generic.ListView):
model = Parent
template_name = "parents.html"
context_object_name = "elements"