bornhack-website/src/facilities/models.py

202 lines
6.3 KiB
Python
Raw Normal View History

import base64
import io
import logging
import qrcode
from django.contrib.gis.db.models import PointField
from django.core.exceptions import ValidationError
from django.db import models
from django.shortcuts import reverse
from django.utils.text import slugify
from utils.models import CampRelatedModel, UUIDModel
logger = logging.getLogger("bornhack.%s" % __name__)
class FacilityQuickFeedback(models.Model):
"""
This model contains the various options for giving quick feedback which we present to the user
when giving feedback on facilities. Think "Needs cleaning" or "Doesn't work" and such.
This model is not Camp specific.
"""
feedback = models.CharField(max_length=100)
icon = models.CharField(
max_length=100,
default="fas fa-exclamation",
blank=True,
help_text="Name of the fontawesome icon to use, including the 'fab fa-' or 'fas fa-' part. Defaults to an exclamation mark icon.",
)
def __str__(self):
return self.feedback
class FacilityType(CampRelatedModel):
"""
Facility types are used to group similar facilities, like Toilets, Showers, Thrashcans...
facilities.Type has a m2m relationship with FeedbackChoice which determines which choices
are presented for giving feedback for this Facility
"""
class Meta:
unique_together = [("slug", "responsible_team")]
name = models.CharField(max_length=100, help_text="The name of this facility type")
slug = models.SlugField(
blank=True,
help_text="The url slug for this facility type. Leave blank to autogenerate one.",
)
description = models.TextField(help_text="Description of this facility type")
icon = models.CharField(
max_length=100,
default="fas fa-list",
blank=True,
help_text="Name of the fontawesome icon to use, including the 'fab fa-' or 'fas fa-' part.",
)
responsible_team = models.ForeignKey(
"teams.Team",
on_delete=models.PROTECT,
help_text="The Team responsible for this type of facility. This team will get the notification when we get a new FacilityFeedback for a Facility of this type.",
)
quickfeedback_options = models.ManyToManyField(
to="facilities.FacilityQuickFeedback",
help_text="Pick the quick feedback options the user should be presented with when submitting Feedback for a Facility of this type. Pick at least the 'N/A' option if none of the other applies.",
)
@property
def camp(self):
return self.responsible_team.camp
camp_filter = "responsible_team__camp"
def __str__(self):
return f"{self.name} ({self.camp})"
def save(self, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
if not self.slug:
raise ValidationError("Unable to slugify")
super().save(**kwargs)
class Facility(CampRelatedModel, UUIDModel):
"""
Facilities are toilets, thrashcans, cooking and dishwashing areas, and any other part of the event which could need attention or maintenance.
"""
facility_type = models.ForeignKey(
"facilities.FacilityType", related_name="facilities", on_delete=models.PROTECT
)
name = models.CharField(
max_length=100, help_text="Name or description of this facility",
)
description = models.TextField(help_text="Description of this facility")
location = PointField(help_text="The location of this facility")
@property
def team(self):
return self.facility_type.responsible_team
@property
def camp(self):
return self.facility_type.camp
camp_filter = "facility_type__responsible_team__camp"
def __str__(self):
return self.name
def get_feedback_url(self, request):
return request.build_absolute_uri(
reverse(
"facilities:facility_feedback",
kwargs={
"camp_slug": self.facility_type.responsible_team.camp.slug,
"facility_type_slug": self.facility_type.slug,
"facility_uuid": self.uuid,
},
)
)
def get_feedback_qr(self, request):
qr = qrcode.make(
self.get_feedback_url(request),
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
).resize((250, 250))
file_like = io.BytesIO()
qr.save(file_like, format="png")
qrcode_base64 = base64.b64encode(file_like.getvalue()).decode("utf-8")
return f"data:image/png;base64,{qrcode_base64}"
class FacilityFeedback(CampRelatedModel):
"""
This model contains participant feedback for Facilities.
It is linked to the user and the facility, and to the
quick_feedback choice the user picked (if any).
"""
user = models.ForeignKey(
"auth.User",
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="facility_feebacks",
help_text="The User this feedback came from, empty if the user submits anonymously",
)
facility = models.ForeignKey(
"facilities.Facility",
related_name="feedbacks",
on_delete=models.PROTECT,
help_text="The Facility this feeback is about",
)
quick_feedback = models.ForeignKey(
"facilities.FacilityQuickFeedback",
on_delete=models.PROTECT,
related_name="feedbacks",
help_text="Quick feedback options. Elaborate in comment field as needed.",
)
comment = models.TextField(
blank=True, help_text="Any comments or feedback about this facility? (optional)"
)
urgent = models.BooleanField(
default=False,
help_text="Check if this is an urgent issue. Will trigger immediate notifications to the responsible team.",
)
handled = models.BooleanField(
default=False,
help_text="True if this feedback has been handled by the responsible team, False if not",
)
handled_by = models.ForeignKey(
"auth.User",
null=True,
blank=True,
on_delete=models.PROTECT,
related_name="facility_feebacks_handled",
help_text="The User who handled this feedback",
)
@property
def camp(self):
return self.facility.camp
camp_filter = "facility__facility_type__responsible_team__camp"