202 lines
6.3 KiB
Python
202 lines
6.3 KiB
Python
|
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"
|