added weekly view

This commit is contained in:
2025-01-27 18:50:24 +01:00
parent 500ff54537
commit 01db4285c2
6 changed files with 213 additions and 60 deletions

View File

@ -1,4 +1,4 @@
.monthly {
.by-range {
display: flex;
flex-direction: column;
padding: 1.2em;
@ -6,13 +6,23 @@
gap: 1.2em;
}
.monthly .controls {
.by-range .controls {
display: flex;
gap: 1.2em;
gap: 2.4em;
align-items: center;
}
.monthly .controls button {
.by-range .controls .group {
display: flex;
gap: 0.8em;
align-items: center;
}
.by-range .controls .group.hidden {
display: none;
}
.by-range .controls button, .by-range .controls select {
background-color: var(--dark3);
color: var(--light1);
padding: 0.4em 0.8em;
@ -20,24 +30,24 @@
cursor: pointer;
}
.monthly .controls button:hover {
.by-range .controls button:hover, .by-range .controls select:hover {
background-color: var(--dark4);
}
.monthly #list {
.by-range #list {
display: flex;
flex-direction: column;
gap: 0.8em;
}
.monthly #list .row {
.by-range #list .row {
display: flex;
gap: 1.2em;
padding: 0.4em 0.8em;
background-color: var(--dark3);
}
.monthly #list .no-data {
.by-range #list .no-data {
font-style: italic;
padding: 0.4em 0.8em;
background-color: var(--dark2);

View File

@ -1,22 +1,36 @@
let prevBtn, nextBtn, month
let prevMonthBtn, nextMonthBtn, month
let prevWeekBtn, nextWeekBtn, week
let curYear = new Date().getFullYear()
let curMonth = new Date().getMonth()
let curWeekDate = new Date()
const SEC_MS = 1000
const MIN_MS = SEC_MS * 60
const HOUR_MS = MIN_MS * 60
const DAY_MS = HOUR_MS * 24
const MONTHS = [
"Janvier",
"Février",
"Mars",
"Avril",
"Mai",
"Juin",
"Juillet",
"At",
"Septembre",
"Octobre",
"Novembre",
"Décembre"
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
]
function formatDate(date) {
let year = date.getFullYear().toString().padStart(4, "0")
let month = (date.getMonth() + 1).toString().padStart(2, "0")
let day = date.getDate().toString().padStart(2, "0")
return `${year}-${month}-${day}`
}
function formatDuration(duration) {
let hours = Math.floor(duration / 60)
duration -= hours * 60
@ -36,7 +50,7 @@ function prevMonth() {
curMonth += 12
curYear -= 1
}
updateList()
updateListMonthly()
}
function nextMonth() {
curMonth = curMonth + 1
@ -44,10 +58,18 @@ function nextMonth() {
curMonth -= 12
curYear += 1
}
updateList()
updateListMonthly()
}
function prevWeek() {
curWeekDate = new Date(curWeekDate.valueOf() - 7 * DAY_MS)
updateListWeekly()
}
function nextWeek() {
curWeekDate = new Date(curWeekDate.valueOf() + 7 * DAY_MS)
updateListWeekly()
}
function updateList() {
function updateListMonthly() {
let year = curYear.toString().padStart(4, "0")
let month = (curMonth + 1).toString().padStart(2, "0")
let today = new Date()
@ -63,32 +85,82 @@ function updateList() {
if (res.status !== "success") {
return
}
let data = res.data.filter(parent => parent.duration !== null)
let list = document.getElementById("list")
let template = document.querySelector(".monthly .template.row").cloneNode(true)
template.classList.remove("template")
list.innerHTML = ""
if (data.length === 0) {
let noData = document.querySelector(".monthly .template.no-data").cloneNode(true)
noData.classList.remove("template")
list.appendChild(noData)
setListElements(res.data.parents)
})
}
function updateListWeekly() {
let weekDay = (curWeekDate.getDay() + 6) % 7
let startDate = new Date(curWeekDate.valueOf() - weekDay * DAY_MS)
let endDate = new Date(startDate.valueOf() + 6 * DAY_MS)
let today = new Date()
let txt = `Week of ${MONTHS[startDate.getMonth()]} ${startDate.getDate()}`
if (startDate.getFullYear() !== today.getFullYear()) {
txt += " " + startDate.getFullYear().toString().padStart(4, "0")
}
document.getElementById("week").innerText = txt
fetch(`stats/between/${formatDate(startDate)}/${formatDate(endDate)}/`).then(res => {
return res.json()
}).then(res => {
if (res.status !== "success") {
return
}
data.forEach(parent => {
let row = template.cloneNode(true)
row.querySelector(".name").innerText = `${parent.name} (${parent.project_num})`
row.querySelector(".duration").innerText = formatDuration(parent.duration)
list.appendChild(row)
})
setListElements(res.data.parents)
})
}
function setListElements(data) {
data = data.filter(parent => parent.duration !== null)
let list = document.getElementById("list")
let template = document.querySelector(".by-range .template.row").cloneNode(true)
template.classList.remove("template")
list.innerHTML = ""
if (data.length === 0) {
let noData = document.querySelector(".by-range .template.no-data").cloneNode(true)
noData.classList.remove("template")
list.appendChild(noData)
return
}
data.forEach(parent => {
let row = template.cloneNode(true)
row.querySelector(".name").innerText = `${parent.name} (${parent.project_num})`
row.querySelector(".duration").innerText = formatDuration(parent.duration)
list.appendChild(row)
})
}
window.addEventListener("load", () => {
prevBtn = document.getElementById("prev")
nextBtn = document.getElementById("next")
prevMonthBtn = document.getElementById("prev-month")
nextMonthBtn = document.getElementById("next-month")
month = document.getElementById("month")
prevBtn.addEventListener("click", () => prevMonth())
nextBtn.addEventListener("click", () => nextMonth())
updateList()
prevWeekBtn = document.getElementById("prev-week")
nextWeekBtn = document.getElementById("next-week")
week = document.getElementById("week")
prevMonthBtn.addEventListener("click", () => prevMonth())
nextMonthBtn.addEventListener("click", () => nextMonth())
prevWeekBtn.addEventListener("click", () => prevWeek())
nextWeekBtn.addEventListener("click", () => nextWeek())
let monthGrp = document.getElementById("month-sel")
let weekGrp = document.getElementById("week-sel")
rangeSel = document.getElementById("range-sel")
rangeSel.addEventListener("change", () => {
let mode = rangeSel.value
if (mode === "weekly") {
monthGrp.classList.add("hidden")
weekGrp.classList.remove("hidden")
updateListWeekly()
} else {
monthGrp.classList.remove("hidden")
weekGrp.classList.add("hidden")
updateListMonthly()
}
})
updateListMonthly()
})

View File

@ -1,5 +1,7 @@
from datetime import timedelta
from django.core.files.uploadedfile import UploadedFile
from django.db.models import Sum, Q
from django.db.models import Sum, Q, QuerySet
from django.http import JsonResponse
from django.shortcuts import render, get_object_or_404, redirect
from django.views import generic
@ -71,17 +73,59 @@ def get_stats_by_month(request, year: int, month: int):
)
)
)
data = [
{
"id": parent.id,
"name": parent.name,
"project_num": parent.project_num,
"duration": parent.total_duration
}
for parent in parents
]
return JsonResponse({"status": "success", "data": data})
projects = Project.objects.annotate(
total_duration=Sum(
"task__duration",
filter=Q(
task__date__year=year,
task__date__month=month
)
)
)
return JsonResponse({"status": "success", "data": format_stats(parents, projects)})
def get_stats_between(request, start_date, end_date):
parents = Parent.objects.annotate(
total_duration=Sum(
"project__task__duration",
filter=Q(
project__task__date__gte=start_date,
project__task__date__lt=end_date + timedelta(days=1)
)
)
)
projects = Project.objects.annotate(
total_duration=Sum(
"task__duration",
filter=Q(
task__date__gte=start_date,
task__date__lt=end_date + timedelta(days=1)
)
)
)
return JsonResponse({"status": "success", "data": format_stats(parents, projects)})
def format_stats(parents: QuerySet[Parent], projects: QuerySet[Project]):
data = {
"parents": [
{
"id": parent.id,
"name": parent.name,
"project_num": parent.project_num,
"duration": parent.total_duration
}
for parent in parents
],
"projects": [
{
"id": project.id,
"name": project.name,
"duration": project.total_duration
}
for project in projects
]
}
return data
class ParentsView(generic.ListView):
model = Parent