import datetime from datetime import timedelta 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 from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from dispatcher.core import import_tasks from dispatcher.forms import ProjectForm, ImportForm, ParentForm from dispatcher.models import Project, Parent, Clocking, Task from dispatcher.serializers import ClockingSerializer def dashboard_view(request): context = { "projects": Project.objects.all(), "parents": Parent.objects.all() } return render(request, "dashboard.html", context) def projects_view(request): context = { "class_name": "parent", "projects": Project.objects.all(), "parents": Parent.objects.all() } return render(request, "projects.html", context) def parent_view(request, id): parent = get_object_or_404(Parent, id=id) if request.method == "DELETE": parent.delete() return JsonResponse({"status": "success"}) context = { "class": "parent", "id": id } form = ParentForm(request.POST or None, request.FILES or None, instance=parent) if form.is_valid(): form.save() return redirect("parents") context["form"] = ParentForm(instance=parent) return render(request, "edit.html", context) def parent_on_delete_view(request, id): parent = get_object_or_404(Parent, id=id) projects = parent.project_set.count() tasks = Task.objects.filter(project__parent=parent).count() return JsonResponse({ "status": "success", "projects": projects, "tasks": tasks }) def project_on_delete_view(request, id): project = get_object_or_404(Project, id=id) tasks = project.task_set.count() return JsonResponse({ "status": "success", "tasks": tasks }) def new_parent_view(request): context = { "class": "parent" } form = ParentForm(request.POST or None, request.FILES or None) if form.is_valid(): form.save() return redirect("parents") context["form"] = form return render(request, "add.html", context) def project_view(request, id): project = get_object_or_404(Project, id=id) if request.method == "DELETE": project.delete() return JsonResponse({"status": "success"}) context = { "class": "project", "id": id } 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, "edit.html", context) def new_project_view(request): context = { "class": "project" } form = ProjectForm(request.POST or None, request.FILES or None) if form.is_valid(): form.save() return redirect("projects") context["form"] = form return render(request, "add.html", context) def table_view(request): return render(request, "table.html") @require_POST def set_parent(request, id): project = get_object_or_404(Project, id=id) parent_id = request.POST.get("parent_id") or None try: parent = Parent.objects.get(id=parent_id) except Parent.DoesNotExist: parent = None project.parent = parent project.save() return JsonResponse({"status": "success"}) 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") def get_stats_by_month(request, year: int, month: int): if month < 1 or month > 12: return JsonResponse({"status": "error", "error": f"Invalid month {month}"}) parents = Parent.objects.annotate( total_duration=Sum( "project__task__duration", filter=Q( project__task__date__year=year, project__task__date__month=month ) ) ) projects = Project.objects.annotate( total_duration=Sum( "task__duration", filter=Q( task__date__year=year, task__date__month=month ) ) ) clockings = Clocking.objects.filter( date__year=year, date__month=month ) return JsonResponse({"status": "success", "data": format_stats(parents, projects, clockings)}) def get_stats_between(request, start_date: datetime.date, end_date: datetime.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) ) ) ) clockings = Clocking.objects.filter( date__gte=start_date, date__lt=end_date + timedelta(days=1) ) return JsonResponse({"status": "success", "data": format_stats(parents, projects, clockings)}) def format_stats(parents: QuerySet[Parent], projects: QuerySet[Project], clockings: QuerySet[Clocking]): data = { "parents": [ { "id": parent.id, "name": parent.name, "project_num": parent.project_num, "is_productive": parent.is_productive, "duration": parent.total_duration } for parent in parents ], "projects": [ { "id": project.id, "name": project.name, "duration": project.total_duration } for project in projects ], "clockings": ClockingSerializer(clockings, many=True).data } Clocking.objects.filter() return data class ParentsView(generic.ListView): model = Parent template_name = "parents.html" context_object_name = "elements" class ProjectsView(generic.ListView): model = Project template_name = "projects.html" context_object_name = "elements" ordering = ["parent"] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["parents"] = Parent.objects.all() return context def get_table_data(request, start_date: datetime.date, end_date: datetime.date): end_date = end_date + timedelta(days=1) clockings = Clocking.objects.filter( date__gte=start_date, date__lte=end_date ) parents = Parent.objects.all().order_by("id") data = { "parents": [ { "id": parent.id, "name": parent.name, "project_num": parent.project_num, "is_productive": parent.is_productive, "projects": [ { "id": project.id, "name": project.name, "tasks": [ { "date": task["date"], "duration": task["duration"] } for task in project.task_set.filter( date__gte=start_date, date__lt=end_date ).values("date").order_by("date").annotate(duration=Sum("duration")) ] } for project in parent.project_set.order_by("id") ] } for parent in parents ], "clockings": ClockingSerializer(clockings, many=True).data } return JsonResponse({"status": "success", "data": data}) @require_POST @csrf_exempt def set_clocking(request, date: datetime.date): clocking, created = Clocking.objects.get_or_create(date=date) clocking.in_am = request.POST.get("in_am", clocking.in_am) or None clocking.out_am = request.POST.get("out_am", clocking.out_am) or None clocking.in_pm = request.POST.get("in_pm", clocking.in_pm) or None clocking.out_pm = request.POST.get("out_pm", clocking.out_pm) or None clocking.remote = request.POST.get("remote", clocking.remote) or None clocking.save() clocking.refresh_from_db() return JsonResponse({ "status": "success", "clocking": ClockingSerializer(clocking).data })