bornhack-website/src/facilities/models.py

213 lines
6.7 KiB
Python

import base64
import io
import logging
import qrcode
from django.contrib.gis.db.models import PointField
from django.db import models
from django.shortcuts import reverse
from maps.utils import LeafletMarkerChoices
from utils.models import CampRelatedModel, UUIDModel
from utils.slugs import unique_slugify
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:
# we need a unique slug for each team due to the url structure in backoffice
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.",
)
marker = models.CharField(
max_length=10,
choices=LeafletMarkerChoices.choices,
default=LeafletMarkerChoices.BLUE,
help_text="The name/colour of the Leaflet marker to use for this facility type.",
)
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 = unique_slugify(
self.name,
slugs_in_use=self.__class__.objects.filter(
responsible_team=self.responsible_team
).values_list("slug", flat=True),
)
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"