Add custom timedelta filter and styles

This commit is contained in:
Nathan Chapman 2021-02-05 20:32:27 -07:00
parent ec573cafdc
commit 2065c6d1ee
18 changed files with 192 additions and 148 deletions

View File

@ -29,10 +29,9 @@ class Period(models.Model):
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True) modified = models.DateTimeField(auto_now=True)
# def duration(self): @property
# duration = self.clocked_out - self.clocked_in def p_duration(self):
# duration_in_s = duration.total_seconds() return round((self.clocked_out - self.clocked_in).seconds / 3600, 2)
# return divmod(duration_in_s, 3600)[0]
def get_absolute_url(self): def get_absolute_url(self):
return reverse('period-detail', kwargs={'pk': self.pk}) return reverse('period-detail', kwargs={'pk': self.pk})

View File

@ -1,10 +1,9 @@
{% extends 'application.html' %} {% extends 'application.html' %}
{% block content %} {% block content %}
<h1>Scan code to clock-in/out</h1> <section class="clockin panel">
<h1>Scan code to clock-in/out</h1>
<section> <form class="clockin__form" method="post" action="{% url 'attendance-update' %}">
<form method="post" action="{% url 'attendance-update' %}">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}

View File

@ -1,99 +1,102 @@
<section> {% load timedelta_filter %}
<header> <article class="instructor">
<h1>{{ user.instructor.department.name }}</h1> <section class="instructor__header panel">
<h1 class="instructor__department">{{ user.instructor.department.name }}</h1>
<p> <p>
<a href="">Generate Reports</a> <a class="instructor__generate-reports" href="">Generate Reports</a>
</p> </p>
</header> </header>
<div class="instructor__active_periods">
<h3>Active sessions</h3> <h3>Students clocked-in</h3>
<p> <p>
<a class="action-button" href="{% url 'period-create' %}">+ Add new session</a> <a class="action-button" href="{% url 'period-create' %}">+ Add new session</a>
</p> </p>
<table> <table>
<thead> <thead>
<tr>
<th>Student</th>
<th>Station</th>
<th>Clocked in</th>
<th colspan="2">Duration</th>
</tr>
</thead>
<tbody>
{% for period in period_list %}
{% if not period.clocked_out %}
<tr> <tr>
<td>{{ period.student }}</td> <th>Student</th>
<td>{{ period.station_number }}</td> <th>Station</th>
<td>{{ period.clocked_in }}</td> <th>Clocked in</th>
{% if period.clocked_out %} <th colspan="2">Duration</th>
<td>{{ period.clocked_out }}</td>
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
{% else %}
<td>Current sesson: {{ period.clocked_in|timesince }}</td>
{% endif %}
<td><a href="{% url 'period-detail' period.id %}">View</a></td>
</tr> </tr>
{% endif %} </thead>
{% empty %} <tbody>
<tr><td colspan="2">No periods yet.</td></tr> {% for period in period_list %}
{% endfor %} {% if not period.clocked_out %}
</tbody> <tr>
</table> <td>{{ period.student }}</td>
</section> <td>{{ period.station_number }}</td>
<article> <td>{{ period.clocked_in }}</td>
<div> {% if period.clocked_out %}
<h3>Attendance log</h3> <td>{{ period.clocked_out }}</td>
<table> <td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
<thead> {% else %}
<tr> <td>Current sesson: {{ period.clocked_in|timesince }}</td>
<th>Student</th> {% endif %}
<th>Station</th> <td><a href="{% url 'period-detail' period.id %}">View</a></td>
<th>Clocked in</th> </tr>
<th>Clocked out</th>
<th colspan="2">Duration</th>
</tr>
</thead>
<tbody>
{% for period in period_list %}
{% if period.clocked_out %}
<tr>
<td>{{ period.student }}</td>
<td>{{ period.station_number }}</td>
<td>{{ period.clocked_in }}</td>
{% if period.clocked_out %}
<td>{{ period.clocked_out }}</td>
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
{% else %}
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
{% endif %} {% endif %}
<td><a href="{% url 'period-detail' period.id %}">View</a></td> {% empty %}
</tr> <tr><td colspan="2">No periods yet.</td></tr>
{% endif %} {% endfor %}
{% empty %} </tbody>
<tr><td colspan="2">No periods yet.</td></tr> </table>
{% endfor %} </div>
</tbody> </section>
</table> <section class="instructor__attendance_log">
</div> <div>
</article> <h3>Attendance log</h3>
<article> <table>
<table> <thead>
<thead>
<tr>
<th>Student</th>
<th>Total hours</th>
</tr>
</thead>
<tbody>
{% for student in student_list %}
<tr> <tr>
<td>{{ student.user.first_name }} {{ student.user.last_name }}</td> <th>Student</th>
<td>{{ student.total_hours }}</td> <th>Station</th>
<th>Clocked in</th>
<th>Clocked out</th>
<th colspan="2">Duration</th>
</tr> </tr>
{% empty %} </thead>
<tr><td colspan="2">No periods yet.</td></tr> <tbody>
{% endfor %} {% for period in period_list %}
</tbody> {% if period.clocked_out %}
</table> <tr>
<td>{{ period.student }}</td>
<td>{{ period.station_number }}</td>
<td>{{ period.clocked_in }}</td>
{% if period.clocked_out %}
<td>{{ period.clocked_out }}</td>
<td>{{ period.clocked_in|timesince:period.clocked_out }}</td>
{% else %}
<td colspan="2">Current sesson: {{ period.clocked_in|timesince }}</td>
{% endif %}
<td><a href="{% url 'period-detail' period.id %}">View</a></td>
</tr>
{% endif %}
{% empty %}
<tr><td colspan="2">No periods yet.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<section class="instructor__total_hours">
<table>
<thead>
<tr>
<th>Student</th>
<th>Total hours</th>
</tr>
</thead>
<tbody>
{% for student in student_list %}
<tr>
<td>{{ student.user.first_name }} {{ student.user.last_name }}</td>
<td>{{ student.total_hours|dimedelta_format }}</td>
</tr>
{% empty %}
<tr><td colspan="2">No periods yet.</td></tr>
{% endfor %}
</tbody>
</table>
</section>
</article> </article>

View File

@ -1,22 +1,25 @@
<section class="student panel"> {% load timedelta_filter %}
<h1 class="student__department">{{ user.student.department.name }}</h1> <article class="student">
{% if user.student.is_clocked_in %} <section class="student__header panel">
<div class="student__statusbar status"> <h1 class="student__department">{{ user.student.department.name }}</h1>
<p><span class="status__clocked--clocked-in">You are currently clocked in.</span></p> {% if user.student.is_clocked_in %}
<p> <div class="student__statusbar status">
Clocked in at: <strong>{{ current_period.clocked_in|date:"P" }}</strong><br> <p><span class="status__clocked--clocked-in">You are currently clocked in.</span></p>
Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong> <p class="status__clocked-info">
</p> Clocked in at: <strong>{{ current_period.clocked_in|date:"P" }}</strong><br>
</div> Current sesson: <strong>{{ current_period.clocked_in|timesince }}</strong>
{% include 'attendance/_student_code.html' with clocked_in=True %} </p>
{% else %} </div>
<div class="status__clocked"></div>You are not clocked in.</p> {% include 'attendance/_student_code.html' with clocked_in=True %}
{% include 'attendance/_student_code.html' with clocked_in=False %} {% else %}
{% endif %} <div class="status__clocked"></div>You are not clocked in.</p>
</section> {% include 'attendance/_student_code.html' with clocked_in=False %}
<section class="attendance"> {% endif %}
<h2 class="attendance__title">Attendance log</h2> </section>
<p class="attendance__total">Total hours for the month: <strong>{{period_total}}</strong><br> <section class="student__attendance attendance">
<small>(Does not include current session.)</small></p> <h2 class="attendance__title">Attendance log</h2>
{% include 'attendance/_student_periods.html' %} <p class="attendance__total">Total hours for the month: <strong>{{ period_total.total|dimedelta_format:2 }}</strong><br>
</section> <small>(Does not include current session.)</small></p>
{% include 'attendance/_student_periods.html' %}
</section>
</article>

View File

@ -2,11 +2,10 @@
{% load qr_code %} {% load qr_code %}
{% block content %} {% block content %}
<h1>Clock in</h1> <section class="code-detail panel">
<h1>Clock in</h1>
<section class="code_detail"> <p class="code-detail__instructions"><em>Scan code at clock-in station</em></p>
<p><em>Scan code at clock-in station</em></p> <span class="code-detail__qrcode">{% qr_from_text code.qr_code_str size=24 version=2 %}</span>
<span>{% qr_from_text code.qr_code_str size=24 version=2 %}</span> <span class="code-detail__done"><a class="action-button" href="{% url 'attendance-overview' %}">Done</a></span>
<span><a class="action-button" href="{% url 'attendance-overview' %}">Done</a></span>
</section> </section>
{% endblock %} {% endblock %}

View File

@ -1,10 +1,10 @@
{% extends 'application.html' %} {% extends 'application.html' %}
{% block content %} {% block content %}
<h1>Generate code</h1>
<section> <section class="code-update panel">
<form method="post" action="{% url 'code-create' %}"> <h1>Generate code</h1>
<form method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}

View File

View File

@ -0,0 +1,8 @@
from django import template
register = template.Library()
@register.filter()
def dimedelta_format(value, arg=2):
"""Format timedelta"""
return round((value).seconds / 3600, arg)

View File

@ -27,7 +27,7 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
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'] = User.objects.get(pk=self.request.user.id) context['user'] = self.request.user
if hasattr(self.request.user, 'instructor'): if hasattr(self.request.user, 'instructor'):
context['student_list'] = Student.objects.filter( context['student_list'] = Student.objects.filter(
department=self.request.user.instructor.department department=self.request.user.instructor.department
@ -36,18 +36,18 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
elif hasattr(self.request.user, 'student'): elif hasattr(self.request.user, 'student'):
student = self.request.user.student student = self.request.user.student
# sum all duration fields for student # sum all duration fields for student
periods_duration_sum = Period.objects.filter( context['period_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_duration=Sum('duration')) ).aggregate(total=Sum('duration'))
hours = 0 # hours = 0
# Convert to hours floating point # # Convert to hours floating point
if periods_duration_sum['total_duration'] != None: # if periods_duration_sum['total_duration'] != None:
hours = round((periods_duration_sum['total_duration'].total_seconds() / 3600), 2) # hours = round((periods_duration_sum['total_duration'].total_seconds() / 3600), 2)
context['period_list'] = Period.objects.filter( context['period_list'] = Period.objects.filter(
student=student student=student
@ -56,7 +56,6 @@ class AttendanceOverview(LoginRequiredMixin, TemplateView):
).filter( ).filter(
clocked_in__month=timezone.now().month clocked_in__month=timezone.now().month
).order_by('-clocked_in') ).order_by('-clocked_in')
context['period_total'] = hours
if student.current_period_id != None: if student.current_period_id != None:
context['current_period'] = Period.objects.get(pk=student.current_period_id) context['current_period'] = Period.objects.get(pk=student.current_period_id)
return context return context
@ -141,7 +140,7 @@ class CodeDetailView(LoginRequiredMixin, DetailView):
class CodeUpdateView(LoginRequiredMixin, UpdateView): class CodeUpdateView(LoginRequiredMixin, UpdateView):
model = Code model = Code
fields = ['station_number'] fields = ['station_number']
template_name = 'attendance/code_update_form.html' template_name = 'attendance/code_form.html'
def get_success_url(self): def get_success_url(self):
pk = self.kwargs["pk"] pk = self.kwargs["pk"]

View File

View File

@ -0,0 +1,16 @@
.code-detail {
display: flex;
flex-direction: column;
align-items: center;
&__instructions {
}
&__qrcode {
}
&__done {
margin: 1rem 0;
}
}

View File

@ -51,6 +51,7 @@ input[type=submit],
color: white; color: white;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
border-radius: 3.75rem;
} }
input:focus, input:focus,

View File

@ -51,7 +51,7 @@ hr {
} }
// BLOCK ELEMENTS // BLOCK ELEMENTS
main { main, aside {
max-width: 58rem; max-width: 58rem;
margin: 0 auto; margin: 0 auto;
} }

View File

@ -0,0 +1,14 @@
.instructor {
&__active_periods {
}
&__attendance_log {
margin: 0 1rem;
}
&__total_hours {
margin: 0 1rem;
}
}

View File

@ -0,0 +1,3 @@
.messages {
}

View File

@ -1,9 +1,7 @@
.student { .student {
&__header { &__header {
display: flex;
justify-content: space-between;
align-items: baseline;
} }
&__department { &__department {

View File

@ -3,9 +3,11 @@
@import "helpers"; @import "helpers";
@import "forms"; @import "forms";
@import "nav"; @import "nav";
@import "messages";
@import "registration"; @import "registration";
@import "students"; @import "students";
@import "instructors"; @import "instructors";
@import "attendance";
@import "periods"; @import "periods";
@import "codes"; @import "codes";

View File

@ -32,11 +32,11 @@
<aside> <aside>
{% if messages %} {% if messages %}
<div class="messages"> <div class="messages panel">
{% for message in messages %} {% for message in messages %}
<span {% if message.tags %} class="{{ message.tags }} messages__message"{% endif %}>{{ message }}</span> <span {% if message.tags %} class="{{ message.tags }} messages__message"{% endif %}>{{ message }}</span>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% block aside %} {% block aside %}
{% endblock %} {% endblock %}