Skip to content

Commit

Permalink
Refactor files to organize by gos / scra / field builder (#4)
Browse files Browse the repository at this point in the history
* Refactor files to organize by gos / scra / field builder

* Duplicate signin info to google sheets

* Update database
  • Loading branch information
pjreiniger authored Jan 21, 2025
1 parent f9139ca commit 87f2a87
Show file tree
Hide file tree
Showing 13 changed files with 216 additions and 151 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,6 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

# End of https://www.toptal.com/developers/gitignore/api/django
# End of https://www.toptal.com/developers/gitignore/api/django

credentials.json
3 changes: 3 additions & 0 deletions attendance/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .gos import * # noqa
from .scra import * # noqa
from .field_builders import * # noqa
15 changes: 15 additions & 0 deletions attendance/models/field_builders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.db import models

from attendance.models.mixins import AttendanceMixin


class FieldBuilder(models.Model):
full_name = models.CharField(max_length=100)
forms_completed = models.BooleanField(default=False)

def __str__(self):
return self.full_name


class FieldBuilderAttendance(AttendanceMixin):
field_builder = models.ForeignKey(FieldBuilder, on_delete=models.CASCADE)
58 changes: 58 additions & 0 deletions attendance/models/gos.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import datetime

from django.db import models
from django.utils import timezone

from attendance.models.mixins import InOutTimeMixin, AttendanceMixin
from attendance.models.sheets_backend import GoogleSheetsBackend


class GosStudent(models.Model, InOutTimeMixin):
rfid = models.IntegerField()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)

def __str__(self):
return self.full_name()

def full_name(self):
return f"{self.first_name} {self.last_name}"

def num_meetings(self):
return len(self.gosattendance_set.all())

def num_hours(self):
total_time = sum(
[x.get_duration() for x in self.gosattendance_set.all()],
datetime.timedelta(),
)
return total_time.total_seconds() / 3600

def _attendance_model(self):
return self.gosattendance_set

def _log_in(self):
attendance = GosAttendance.objects.create(student=self, time_in=timezone.now())
sheets_backend = GoogleSheetsBackend()
sheets_backend.gos_signin(
attendance.time_in, attendance.student.rfid, attendance.student.full_name()
)

def _log_out(self):
last_login = self.get_last_login()
last_login.time_out = timezone.now()
last_login.save()

sheets_backend = GoogleSheetsBackend()
sheets_backend.gos_signout(last_login.time_out, last_login.student.full_name())

def _full_name(self):
return self.full_name()


class GosAttendance(AttendanceMixin):
student = models.ForeignKey(GosStudent, on_delete=models.CASCADE)
purpose = models.CharField(max_length=100)

def __str__(self):
return f"{self.student} {self.time_in}-{self.time_out}"
69 changes: 0 additions & 69 deletions attendance/models.py → attendance/models/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,58 +77,6 @@ def _full_name(self):
raise NotImplementedError()


class GosStudent(models.Model, InOutTimeMixin):
rfid = models.IntegerField()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)

def __str__(self):
return self.full_name()

def full_name(self):
return f"{self.first_name} {self.last_name}"

def num_meetings(self):
return len(self.gosattendance_set.all())

def num_hours(self):
total_time = sum(
[x.get_duration() for x in self.gosattendance_set.all()],
datetime.timedelta(),
)
return total_time.total_seconds() / 3600

def _attendance_model(self):
return self.gosattendance_set

def _log_in(self):
GosAttendance.objects.create(student=self, time_in=timezone.now())

def _log_out(self):
last_login = self.get_last_login()
last_login.time_out = timezone.now()
last_login.save()

def _full_name(self):
return self.full_name()


class FieldBuilder(models.Model):
full_name = models.CharField(max_length=100)
forms_completed = models.BooleanField(default=False)

def __str__(self):
return self.full_name


class ScraVisitor(models.Model):
full_name = models.CharField(max_length=100)
team_number = models.IntegerField()

def __str__(self):
return self.full_name


class AttendanceMixin(models.Model):
time_in = models.DateTimeField("Time In")
time_out = models.DateTimeField("Time Out", null=True)
Expand All @@ -139,20 +87,3 @@ def get_duration(self):

class Meta:
abstract = True


class GosAttendance(AttendanceMixin):
student = models.ForeignKey(GosStudent, on_delete=models.CASCADE)
purpose = models.CharField(max_length=100)

def __str__(self):
return f"{self.student} {self.time_in}-{self.time_out}"


class FieldBuilderAttendance(AttendanceMixin):
field_builder = models.ForeignKey(FieldBuilder, on_delete=models.CASCADE)


class ScraVisitorAttendance(AttendanceMixin):
scra_visitor = models.ForeignKey(ScraVisitor, on_delete=models.CASCADE)
purpose = models.CharField(max_length=100)
16 changes: 16 additions & 0 deletions attendance/models/scra.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.db import models

from attendance.models.mixins import InOutTimeMixin, AttendanceMixin


class ScraVisitor(models.Model, InOutTimeMixin):
full_name = models.CharField(max_length=100)
team_number = models.IntegerField()

def __str__(self):
return self.full_name


class ScraVisitorAttendance(AttendanceMixin):
scra_visitor = models.ForeignKey(ScraVisitor, on_delete=models.CASCADE)
purpose = models.CharField(max_length=100)
34 changes: 34 additions & 0 deletions attendance/models/sheets_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import gspread
import logging
import os
import pytz
from django.utils import timezone


SPREADSHEET_KEY = "1ztlyayX_A59oDQQsRPfWNKSZ-efkdWKgML-J9WtB66s"


class GoogleSheetsBackend:
def __init__(self):
logging.info("Connecting to sheets")
gc = gspread.service_account(filename=os.path.abspath("credentials.json"))
self.spreadsheet = gc.open_by_key(SPREADSHEET_KEY)

def format_time(self, current_time) -> str:
utc = current_time.replace(tzinfo=pytz.UTC)
in_local = utc.astimezone(timezone.get_current_timezone())
return in_local.strftime("%m/%d/%y %I:%M %p")

def gos_signin(self, timestamp, rfid, full_name):
sheet_tab = self.spreadsheet.worksheet("GoS Attendance")

row_contents = [self.format_time(timestamp), rfid, full_name, "General Meeting"]
sheet_tab.append_row(row_contents, value_input_option="USER_ENTERED")

def gos_signout(self, timestamp, full_name):
sheet_tab = self.spreadsheet.worksheet("GoS Attendance")
cell_list = sheet_tab.findall(full_name)
if cell_list:
last_cell = cell_list[-1]
last_row_num = last_cell.row
sheet_tab.update_cell(last_row_num, 5, self.format_time(timestamp))
17 changes: 9 additions & 8 deletions attendance/urls.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
from django.urls import path

from . import views
from .views import gos
from .views import top_level

urlpatterns = [
path("", views.IndexView.as_view(), name="index"),
path("manifest/", views.ActiveManifest.as_view(), name="active_manifest"),
path("gos/", views.GosStudentSummaryView.as_view(), name="gos_student_summary"),
path("gos/signin", views.gos_signin, name="gos_signin"),
path("", top_level.IndexView.as_view(), name="index"),
path("manifest/", top_level.ActiveManifest.as_view(), name="active_manifest"),
path("gos/", gos.GosStudentSummaryView.as_view(), name="gos_student_summary"),
path("gos/signin", gos.gos_signin, name="gos_signin"),
path(
"gos/log_attendance_rfid",
views.gos_log_attendance_rfid,
gos.gos_log_attendance_rfid,
name="gos_log_attendance_rfid",
),
path(
"gos/log_attendance_name",
views.gos_log_attendance_name,
gos.gos_log_attendance_name,
name="gos_log_attendance_name",
),
path(
"gos/<int:rfid>/",
views.GosStudentDetailView.as_view(),
gos.GosStudentDetailView.as_view(),
name="gos_student_detail",
),
]
73 changes: 1 addition & 72 deletions attendance/views.py → attendance/views/gos.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,9 @@
import datetime
from typing import Optional

from django.db.models import Count
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from django.views import generic

from attendance.models import GosStudent, GosAttendance


class IndexView(generic.TemplateView):
template_name = "attendance/index.html"

def get_context_data(self, **kwargs):
calendar_events = []

calendar_events.extend(
create_calendar_events_from_attendance(GosAttendance, "GOS", "blue")
)

context = super().get_context_data(**kwargs)
context["calendar_events"] = calendar_events
return context
from attendance.models.gos import GosStudent


class GosStudentSummaryView(generic.ListView):
Expand Down Expand Up @@ -92,55 +73,3 @@ def __gos_handle_login(request, student):
request.session["result_msg"] = msg
request.session["good_result"] = good_result
return HttpResponseRedirect(reverse("gos_signin"))


class ActiveManifest(generic.TemplateView):
template_name = "attendance/signed_in_list.html"

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["gos_students"] = [
student for student in GosStudent.objects.all() if student.is_logged_in()
]
return context


class CalendarEvent:
def __init__(
self,
time_in: datetime.datetime,
time_out: Optional[datetime.datetime],
title: str,
color: Optional[str] = None,
show_as_all_day: bool = False,
):
self.time_in = time_in
self.time_out = time_out
self.title = title
self.color = color
self.show_as_all_day = show_as_all_day

def __repr__(self):
return f"CalendarEvent - {type(self.time_in)}"


def create_calendar_events_from_attendance(attendance_model, title, color):
visits = attendance_model.objects.values("time_in__date").annotate(
xyz=Count("time_in__date")
)

calendar_events = []
for att in visits:
calendar_events.append(
CalendarEvent(
time_in=datetime.datetime.fromisoformat(
att["time_in__date"].isoformat()
),
time_out=None,
title=f"{att['xyz']} " + title,
color=color,
show_as_all_day=True,
)
)

return calendar_events
32 changes: 32 additions & 0 deletions attendance/views/top_level.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from django.views import generic

from attendance.models.gos import GosStudent, GosAttendance
from attendance.views.utils import create_calendar_events_from_attendance


class IndexView(generic.TemplateView):
template_name = "attendance/index.html"

def get_context_data(self, **kwargs):
calendar_events = []

calendar_events.extend(
create_calendar_events_from_attendance(
GosAttendance.objects.all(), "GOS", "blue"
)
)

context = super().get_context_data(**kwargs)
context["calendar_events"] = calendar_events
return context


class ActiveManifest(generic.TemplateView):
template_name = "attendance/signed_in_list.html"

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["gos_students"] = [
student for student in GosStudent.objects.all() if student.is_logged_in()
]
return context
Loading

0 comments on commit 87f2a87

Please sign in to comment.