Add signal for period created to auto-clockin Student

This commit is contained in:
Nathan Chapman 2021-02-08 20:28:18 -07:00
parent b9630092d5
commit 76339dc65a
15 changed files with 65 additions and 33 deletions

View File

@ -2,6 +2,7 @@ from django.db import models
from django.urls import reverse from django.urls import reverse
from django.contrib.auth.models import User from django.contrib.auth.models import User
class Department(models.Model): class Department(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)

View File

@ -10,7 +10,7 @@
{% elif user.instructor %} {% elif user.instructor %}
<p>{{ user.instructor.department }}</p> <p>{{ user.instructor.department }}</p>
{% endif %} {% endif %}
{% if periods %} {% if periods and periods.total %}
<p>Total clocked hours: <strong>{{ periods.total|timedelta_format }}</strong></p> <p>Total clocked hours: <strong>{{ periods.total|timedelta_format }}</strong></p>
{% endif %} {% endif %}
<p> <p>

View File

@ -2,6 +2,8 @@ from datetime import datetime
from django.db import models from django.db import models
from django.urls import reverse from django.urls import reverse
from accounts.models import Student from accounts.models import Student
from django.db.models.signals import post_save
from django.dispatch import receiver
class Code(models.Model): class Code(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE) student = models.ForeignKey(Student, on_delete=models.CASCADE)
@ -46,3 +48,10 @@ class Period(models.Model):
if self.clocked_out != None: if self.clocked_out != None:
self.duration = self.clocked_out - self.clocked_in self.duration = self.clocked_out - self.clocked_in
super(Period, self).save(*args, **kwargs) super(Period, self).save(*args, **kwargs)
@receiver(post_save, sender=Period)
def auto_clockin_student(sender, instance, created, **kwargs):
if created:
instance.student.is_clocked_in = True
instance.student.current_period_id = instance.pk
instance.student.save()

View File

@ -7,7 +7,7 @@
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
<input type="submit" value="clock-in/out"> <input type="submit" value="Submit">
</form> </form>
</section> </section>
{% endblock %} {% endblock %}

View File

@ -93,7 +93,9 @@
{% for student in student_list %} {% for student in student_list %}
<tr> <tr>
<td>{{ student.user.first_name }} {{ student.user.last_name }}</td> <td>{{ student.user.first_name }} {{ student.user.last_name }}</td>
{% if student.total_hours %}
<td><strong>{{ student.total_hours|timedelta_format }}</strong></td> <td><strong>{{ student.total_hours|timedelta_format }}</strong></td>
{% endif %}
</tr> </tr>
{% empty %} {% empty %}
<tr><td colspan="2">No periods yet.</td></tr> <tr><td colspan="2">No periods yet.</td></tr>

View File

@ -20,7 +20,11 @@
</section> </section>
<section class="student__attendance attendance"> <section class="student__attendance attendance">
<h2 class="attendance__title">Attendance log</h2> <h2 class="attendance__title">Attendance log</h2>
<p class="attendance__total">Total hours for the month: <strong>{{ period_total.total|timedelta_format:2 }}</strong> <small>(Does not include current session.)</small></p> {% if monthly_total.duration %}
<p class="attendance__total">Total hours for the month: <strong>{{ monthly_total.duration|timedelta_format }}</strong>
<small>(Does not include current session.)</small>
</p>
{% endif %}
{% include 'attendance/_student_periods.html' %} {% include 'attendance/_student_periods.html' %}
<p> <p>
<a class="action-button" href="{% url 'period-list' %}">See previous →</a> <a class="action-button" href="{% url 'period-list' %}">See previous →</a>

View File

@ -2,9 +2,13 @@
{% load static %} {% load static %}
{% block content %} {% block content %}
<section class="panel">
<h1>Delete Session</h1>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p> <p>Are you sure you want to delete "{{ object }}"?</p>
<input type="submit" value="Confirm"> <input type="submit" value="Confirm"> or
<a href="{% url 'period-detail' object.pk %}">Cancel</a>
</form> </form>
</section>
{% endblock %} {% endblock %}

View File

@ -1,26 +1,32 @@
{% extends 'application.html' %} {% extends 'application.html' %}
{% load timedelta_filter %}
{% block content %} {% block content %}
<h1>Period</h1>
<section> <section class="period panel">
<header> <h1>Session</h1>
<header class="period__header">
<a href="{% url 'attendance-overview' %}">← Back</a> <a href="{% url 'attendance-overview' %}">← Back</a>
<div> <div>
<a href="{% url 'period-update' period.id %}">Edit</a> <a href="{% url 'period-update' period.id %}">Edit</a>
<a href="{% url 'period-delete' period.id %}">Delete</a> <a href="{% url 'period-delete' period.id %}">Delete</a>
</div> </div>
</header> </header>
<dl> <dl class="period__data">
<dt>Student</dt> <dt>Student</dt>
<dd>{{ period.student }}</dd> <dd>{{ period.student }}</dd>
<dt>Clocked in</dt> <dt>Clocked in</dt>
<dd>{{ period.clocked_in }}</dd> <dd>{{ period.clocked_in }}</dd>
{% if period.clocked_out %}
<dt>Clocked out</dt> <dt>Clocked out</dt>
<dd>{{ period.clocked_out }}</dd> <dd>{{ period.clocked_out }}</dd>
{% else %}
<dt>Not clocked out.</dt>
{% endif %}
{% if period.duration %}
<dt>Duration</dt> <dt>Duration</dt>
<dd>{{ period.duration }}</dd> <dd>{{ period.duration|timedelta_format }} hours</dd>
{% endif %}
</dl> </dl>
</section> </section>
{% endblock %} {% endblock %}

View File

@ -3,7 +3,7 @@
{% block content %} {% block content %}
<section class="period panel"> <section class="period panel">
<h1>Generate Period</h1> <h1>Session</h1>
<form method="post" action=""> <form method="post" action="">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}

View File

@ -16,19 +16,11 @@ from accounts.models import Instructor, Student
from .models import Code, Period from .models import Code, Period
from .forms import AttendanceUpdateForm, PeriodForm from .forms import AttendanceUpdateForm, PeriodForm
# EXAMPLE PERMISSION MIXIN
# class MyView(PermissionRequiredMixin, View):
# permission_required = 'polls.add_choice'
# # Or multiple of permissions:
# permission_required = ('polls.view_choice', 'polls.change_choice')
# OVERVIEW # OVERVIEW
class AttendanceOverview(LoginRequiredMixin, TemplateView): class AttendanceOverview(LoginRequiredMixin, TemplateView):
template_name = 'attendance/attendance_overview.html' template_name = 'attendance/attendance_overview.html'
def get_queryset(self):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['user'] = self.request.user context['user'] = self.request.user
@ -50,13 +42,13 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
student = self.request.user.student student = self.request.user.student
# sum all duration fields for student # sum all duration fields for student
context['period_total'] = Period.objects.filter( context['monthly_total'] = Period.objects.filter(
student = student student = student
).filter( ).filter(
clocked_in__year=timezone.now().year clocked_in__year=timezone.now().year
).filter( ).filter(
clocked_in__month=timezone.now().month clocked_in__month=timezone.now().month
).aggregate(total=Sum('duration')) ).aggregate(duration=Sum('duration'))
context['period_list'] = Period.objects.filter( context['period_list'] = Period.objects.filter(
student=student student=student
@ -74,7 +66,6 @@ class AttendanceUpdateView(LoginRequiredMixin, FormView):
form_class = AttendanceUpdateForm form_class = AttendanceUpdateForm
def form_valid(self, form): def form_valid(self, form):
# update checked in
student_number = form.cleaned_data['qr_string'].split(':')[0] student_number = form.cleaned_data['qr_string'].split(':')[0]
station_number = form.cleaned_data['qr_string'].split(':')[1] station_number = form.cleaned_data['qr_string'].split(':')[1]
student = Student.objects.get(student_number=student_number) student = Student.objects.get(student_number=student_number)
@ -86,8 +77,8 @@ class AttendanceUpdateView(LoginRequiredMixin, FormView):
period.save() period.save()
messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked out.') messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked out.')
else: else:
c_p = student.period_set.create(student=student, clocked_in=timezone.now(), station_number=station_number) period = student.period_set.create(student=student, clocked_in=timezone.now(), station_number=station_number)
student.current_period_id = c_p.id student.current_period_id = period.pk
student.is_clocked_in=True student.is_clocked_in=True
student.save() student.save()
messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked in.') messages.add_message(self.request, messages.INFO, f'{student.user.first_name} {student.user.last_name} clocked in.')

View File

@ -5,18 +5,18 @@
<article class="home"> <article class="home">
<header class="panel"> <header class="panel">
<h1>Welcome to<br> <h1>Welcome to<br>
Btech Time Tracker</h1> BTech Time Tracker</h1>
<h2>Know exactly how many hours you've clocked, anytime, anywhere.</h2> <h2>Know exactly how many hours you've clocked, anytime, anywhere.</h2>
</header> </header>
<section> <section class="home__section">
<h3>No more paper</h3> <h3>No more paper</h3>
<p>No more reading bad handwriting or putting the wrong date or time.</p> <p>No more reading bad handwriting or putting the wrong date or time.</p>
</section> </section>
<section> <section class="home__section">
<h3>No more questions</h3> <h3>No more questions</h3>
<p>No more asking your instructor, or being asked by your student how many hours they have.</p> <p>No more asking your instructor, or being asked by your student how many hours they have.</p>
</section> </section>
<section> <section class="home__section">
<h3>No more manual entry</h3> <h3>No more manual entry</h3>
<p>We invented computers to do that for us.</p> <p>We invented computers to do that for us.</p>
</section> </section>

View File

@ -2,4 +2,8 @@
h1, h2 { h1, h2 {
text-align: center; text-align: center;
} }
&__section {
margin: 0 1rem;
}
} }

View File

@ -0,0 +1,6 @@
.period {
&__header {
display: flex;
justify-content: space-between;
}
}

View File

@ -8,6 +8,11 @@ class TimezoneMiddleware:
def __call__(self, request): def __call__(self, request):
tzname = request.session.get('django_timezone') tzname = request.session.get('django_timezone')
if request.user.is_authenticated:
if hasattr(request.user, 'instructor'):
tzname = request.user.instructor.timezone
if hasattr(request.user, 'student'):
tzname = request.user.student.timezone
if tzname: if tzname:
timezone.activate(pytz.timezone(tzname)) timezone.activate(pytz.timezone(tzname))
else: else:

View File

@ -149,4 +149,4 @@ MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media' MEDIA_ROOT = BASE_DIR / 'media'
LOGIN_REDIRECT_URL = '/accounts/timezone/' LOGIN_REDIRECT_URL = '/attendance/'