Add home; overwrite save method on Period model

This commit is contained in:
Nathan Chapman 2021-01-30 17:40:40 -07:00
parent 787066d474
commit 5e56fc0139
23 changed files with 133 additions and 50 deletions

View File

@ -29,6 +29,7 @@ class Student(models.Model):
student_number = models.IntegerField()
department = models.ForeignKey(Department, on_delete=models.CASCADE)
is_clocked_in = models.BooleanField(default=False)
current_period_id = models.IntegerField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)

View File

@ -7,3 +7,20 @@ class AttendanceUpdateForm(forms.Form):
max_length=100,
widget=forms.TextInput(attrs={'autofocus': True})
)
class PeriodForm(forms.ModelForm):
class Meta:
model = Period
fields = ['student', 'station_number', 'clocked_in', 'clocked_out']
widgets = {
'clocked_in': forms.DateTimeInput(format = '%Y-%m-%d %H:%M', attrs = {
'placeholder': '2006-10-25 14:30'
}),
'clocked_out': forms.DateTimeInput(format = '%Y-%m-%d %H:%M', attrs = {
'placeholder': '2006-10-25 14:30'
}),
}
labels = {
'clocked_in': 'Clocked in (example 2006-10-25 14:30):',
'clocked_out': 'Clocked out (example 2006-10-25 14:30):',
}

View File

@ -1,3 +1,4 @@
from datetime import datetime
from django.db import models
from django.urls import reverse
from accounts.models import Student
@ -20,7 +21,7 @@ class Code(models.Model):
class Period(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
clocked_in = models.DateTimeField(auto_now_add=True)
clocked_in = models.DateTimeField()
clocked_out = models.DateTimeField(blank=True, null=True)
station_number = models.IntegerField()
duration = models.DurationField(blank=True, null=True)
@ -38,3 +39,8 @@ class Period(models.Model):
def __str__(self):
return f'{self.clocked_in}: {self.student.user.first_name} {self.student.user.last_name}'
def save(self, *args, **kwargs):
if isinstance(self.clocked_out, datetime):
self.duration = self.clocked_out - self.clocked_in
super(Period, self).save(*args, **kwargs)

View File

@ -7,6 +7,9 @@
</header>
<h3>Active sessions</h3>
<p>
<a href="{% url 'period-create' %}">+ Add new session</a>
</p>
<table>
<thead>
<tr>

View File

@ -1,13 +1,17 @@
<section>
<header>
<header class="header">
<h1>{{ user.first_name }} {{ user.last_name }}</h1>
<a href="{% url 'account-update' user.id %}">Update profile</a>
</header>
<h5>{{ user.student.department.name }}</h5>
{% if user.student.is_clocked_in %}
{% if user.student.is_clocked_in and user.student.code_set.last %}
<p>You are currently clocked in.<br>
Current sesson: {{ period_list.first.clocked_in|timesince }}</p>
Current sesson: {{ current_period.clocked_in|timesince }}</p>
<a href="{% url 'code-update' user.student.code_set.last.id %}">Clock out →</a>
{% elif user.student.is_clocked_in and not user.student.code_set.last %}
<p><span class="message info">You are currently clocked in.</span><br>
Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong></p>
<a href="{% url 'code-create' %}">Clock out →</a>
{% elif user.student.code_set.last %}
<p>You are not clocked in.</p>
<a href="{% url 'code-update' user.student.code_set.last.id %}">Clock in →</a>

View File

@ -8,7 +8,8 @@
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Generate code" />
<input type="submit" value="Generate code"> or
<a href="{{request.META.HTTP_REFERER}}">Cancel</a>
</form>
</section>
{% endblock %}

View File

@ -19,6 +19,8 @@
<dd>{{ period.clocked_in }}</dd>
<dt>Clocked out</dt>
<dd>{{ period.clocked_out }}</dd>
<dt>Duration</dt>
<dd>{{ period.duration }}</dd>
</dl>
</section>
{% endblock %}

View File

@ -4,11 +4,12 @@
<h1>Generate Period</h1>
<section>
<form method="post" action="{% url 'period-update' period.id %}">
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save changes" />
<input type="submit" value="Save changes" /> or
<a href="{{request.META.HTTP_REFERER}}">Cancel</a>
</form>
</section>
{% endblock %}

View File

@ -10,6 +10,7 @@ from . import views
urlpatterns = [
path('', views.AttendanceOverview.as_view(), name='attendance-overview'),
path('update/', views.AttendanceUpdateView.as_view(), name='attendance-update'),
path('periods/new/', views.PeriodCreateView.as_view(), name='period-create'),
path('periods/<int:pk>/', include([
path('', views.PeriodDetailView.as_view(), name='period-detail'),
path('update/', views.PeriodUpdateView.as_view(), name='period-update'),

View File

@ -12,7 +12,7 @@ from django.contrib import messages
from django.contrib.auth.models import User
from accounts.models import Instructor, Student
from .models import Code, Period
from .forms import AttendanceUpdateForm
from .forms import AttendanceUpdateForm, PeriodForm
# EXAMPLE PERMISSION MIXIN
# class MyView(PermissionRequiredMixin, View):
@ -32,19 +32,18 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
context['period_list'] = Period.objects.order_by('-clocked_in')
elif hasattr(self.request.user, 'student'):
# sum all duration fields for student
total_duration = Period.objects.filter(
periods_duration_sum = Period.objects.filter(
student = self.request.user.student
).filter(
clocked_in__year=timezone.now().year
).filter(
clocked_in__month=timezone.now().month
).order_by('-clocked_in'
).aggregate(total_duration=Sum('duration'))
hours = ""
hours = 0
# Convert to hours floating point
if hasattr(total_duration, 'total_duration'):
hours = round((total_duration['total_duration'].total_seconds() / 3600), 2)
if periods_duration_sum['total_duration'] != None:
hours = round((periods_duration_sum['total_duration'].total_seconds() / 3600), 2)
context['period_list'] = Period.objects.filter(
student=self.request.user.student
@ -54,6 +53,7 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
clocked_in__month=timezone.now().month
).order_by('-clocked_in')
context['period_total'] = hours
context['current_period'] = Period.objects.get(pk=self.request.user.student.current_period_id)
return context
class AttendanceUpdateView(LoginRequiredMixin, FormView):
@ -67,14 +67,14 @@ class AttendanceUpdateView(LoginRequiredMixin, FormView):
student = Student.objects.get(student_number=student_number)
if student.is_clocked_in:
student.is_clocked_in=False
period = student.period_set.last()
period = student.period_set.get(pk=student.current_period_id)
period.clocked_out=timezone.now()
period.duration=period.clocked_out-period.clocked_in
student.save()
period.save()
messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked out.')
else:
student.period_set.create(student=student, station_number=station_number)
c_p = student.period_set.create(student=student, clocked_in=timezone.now(), station_number=station_number)
student.current_period_id = c_p.id
student.is_clocked_in=True
student.save()
messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked in.')
@ -89,7 +89,7 @@ class AttendanceUpdateView(LoginRequiredMixin, FormView):
# PERIODS
class PeriodCreateView(LoginRequiredMixin, CreateView):
model = Period
fields = ['student']
form_class = PeriodForm
template_name = 'attendance/period_form.html'
class PeriodDetailView(LoginRequiredMixin, DetailView):
@ -98,9 +98,17 @@ class PeriodDetailView(LoginRequiredMixin, DetailView):
class PeriodUpdateView(LoginRequiredMixin, UpdateView):
model = Period
fields = ['clocked_out']
form_class = PeriodForm
template_name = 'attendance/period_form.html'
# When editing a period, check if it matches the current period of the student and clock them out
def form_valid(self, form):
student = form.instance.student
if form.instance.id == student.current_period_id:
student.is_clocked_in = False
student.save()
return super().form_valid(form)
def get_success_url(self):
pk = self.kwargs["pk"]
return reverse('period-detail', kwargs={'pk': pk})

0
home/__init__.py Normal file
View File

3
home/admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

5
home/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class HomeConfig(AppConfig):
name = 'home'

View File

3
home/models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View File

@ -0,0 +1,6 @@
{% extends 'application.html' %}
{% load static %}
{% block content %}
<h1>Welcome home</h1>
{% endblock %}

3
home/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
home/urls.py Normal file
View File

@ -0,0 +1,12 @@
from django.urls import include, path
from . import views
# list/ /users/
# create/ /users/new/
# detail/ /users/1/
# update/ /users/1/update/ (update shift preferences)
# delete/ /users/1/delete/
urlpatterns = [
path('', views.HomeView.as_view(), name='home'),
]

5
home/views.py Normal file
View File

@ -0,0 +1,5 @@
from django.shortcuts import render
from django.views.generic.base import TemplateView
class HomeView(TemplateView):
template_name = "home/home.html"

View File

@ -6,10 +6,8 @@ accent 2 (orange) #9b6f45
accent 3 (blue) #242e34
*/
@import url('https://fonts.googleapis.com/css2?family=Heebo:wght@400;700;900&display=swap');
html {
font-size: 1.125em;
font-size: 100%;
}
body {
@ -17,46 +15,36 @@ body {
margin: 0.25in auto;
background-color: #f7f7f7;
color: #323834;
font-family: 'Heebo', sans-serif;
font-family: 'Lato', sans-serif;
font-weight: 400;
line-height: 1.65;
line-height: 1.75;
}
header {
display: flex;
align-items: baseline;
justify-content: space-between;
}
/* Text Elements */
p {
margin-bottom: 1.15rem;
margin-bottom: 1rem;
}
h1, h2, h3, h4, h5 {
/*margin: 2.75rem 0 1.05rem;*/
margin: 0 0 1.05rem;
font-weight: 800;
line-height: 1.15;
margin: 3rem 0 1.38rem;
font-family: 'Lato', sans-serif;
line-height: 1.3;
}
h1 {
margin-top: 0;
font-size: 2.488em;
font-size: 2.488rem;
}
h2 {
font-size: 2.074em;
font-size: 2.074rem;
}
h3 {
font-size: 1.728em;
font-size: 1.728rem;
}
h4 {
font-size: 1.44em;
font-size: 1.44rem;
}
h5 {
font-size: 1.2em;
font-size: 1.2rem;
}
small {
font-size: 0.833em;
font-size: 0.833rem;
}
a {
@ -65,14 +53,23 @@ a {
white-space: nowrap;
}
small {
font-size: 0.8em;
}
hr {
margin: 0;
border: 0.8px solid #bdc3c7;
}
.navigation {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.header {
display: flex;
align-items: top;
justify-content: space-between;
}
blockquote {
text-align: left;
padding: 0 15px;
@ -109,6 +106,7 @@ label {
input[type=text],
input[type=email],
input[type=number],
input[type=password],
select[multiple=multiple],
textarea,
@ -219,4 +217,4 @@ th {
/* Messages */
.messages > .info {color: green;}
.info {color: green;}

View File

@ -9,11 +9,13 @@
<link rel="shortcut icon" type="image/png" href="{% static 'favicon.ico' %}"/>
{% block head %}
{% endblock %}
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="{% static 'css/base.css' %}">
</head>
<body>
<header>
<h4>BTech Time Tracker</h4>
<header class="navigation">
<p><strong>BTech Time Tracker</strong></p>
{% if user.is_authenticated %}
<nav>
<a href="{% url 'account-update' user.id %}">{{ user.first_name }} {{ user.last_name }}</a> /

View File

@ -39,6 +39,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'compressor',
'qr_code',
'home.apps.HomeConfig',
'accounts.apps.AccountsConfig',
'attendance.apps.AttendanceConfig',
]

View File

@ -17,6 +17,7 @@ from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('home.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('accounts.urls')),
path('attendance/', include('attendance.urls')),