import json
from django.conf import settings
from django.db import models
from django.db.models import Case as djCase
from django.db.models import CharField, F, Q, Value, When
from django.db.models.query import QuerySet
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from teryt_tree.dal_ext.filters import AreaMultipleFilter
from poradnia.cases.models import Case
from poradnia.teryt.models import JST
from poradnia.utils.mixins import FormattedDatetimeMixin, UserPrettyNameMixin
[dokumentacja]
class AbstractCategory(models.Model):
name = models.CharField(max_length=100, verbose_name=_("Name"))
active = models.BooleanField(default=True, verbose_name=_("Active"))
tag_helper = models.TextField(
verbose_name=_("Tag helper"),
null=True,
blank=True,
help_text=_("Helper text how to apply the tag."),
)
class Meta:
abstract = True
def __str__(self):
return self.name
@classmethod
def active_as_json(cls):
items = list(cls.objects.filter(active=True).values("id", "name", "tag_helper"))
return json.dumps(items)
[dokumentacja]
class Issue(AbstractCategory):
is_dip = models.BooleanField(default=True, verbose_name=_("Is FOI issue?"))
is_local_government = models.BooleanField(
default=False, verbose_name=_("Is local government issue?")
)
class Meta:
verbose_name = _("The thematic scope of the request")
verbose_name_plural = _("Thematic scopes of requests")
@classmethod
def active_as_json(cls):
items = list(
cls.objects.filter(active=True).values(
"id", "name", "tag_helper", "is_dip", "is_local_government"
)
)
return json.dumps(items)
[dokumentacja]
class Area(AbstractCategory):
is_dip = models.BooleanField(default=True, verbose_name=_("Is FOI area?"))
is_local_government = models.BooleanField(
default=False, verbose_name=_("Is local government area?")
)
class Meta:
verbose_name = _("Problem regarding the right to information")
verbose_name_plural = _("Problems regarding the right to information")
@classmethod
def active_as_json(cls):
items = list(
cls.objects.filter(active=True).values(
"id", "name", "tag_helper", "is_dip", "is_local_government"
)
)
return json.dumps(items)
[dokumentacja]
class PersonKind(AbstractCategory):
class Meta:
verbose_name = _("Type of person who reporting the advice")
verbose_name_plural = _("Types of people who report to for advice")
[dokumentacja]
class InstitutionKind(AbstractCategory):
class Meta:
verbose_name = _("Institution kind")
verbose_name_plural = _("Institution kinds")
[dokumentacja]
class AdviceQuerySet(FormattedDatetimeMixin, UserPrettyNameMixin, QuerySet):
def for_user(self, user):
if user.has_perm("advicer.can_view_all_advices"):
return self
return self.filter(Q(advicer=user.pk) | Q(created_by=user.pk))
def visible(self):
return self.filter(visible=True)
def area_filter(self, jst):
return self.filter(
jst__tree_id=jst.tree_id, jst__lft__range=(jst.lft, jst.rght)
)
def area_in(self, jsts):
if not jsts:
# Show all results if filter is empty.
return self
else:
return AreaMultipleFilter.filter_area_in(self, jsts, "jst")
def with_helped_str(self):
return self.annotate(
helped_str=djCase(
When(helped=True, then=Value(_("Yes"))),
When(helped=False, then=Value(_("No"))),
default=Value("-"),
output_field=CharField(),
),
)
def with_visible_str(self):
return self.annotate(
visible_str=djCase(
When(visible=True, then=Value(_("Yes"))),
When(visible=False, then=Value(_("No"))),
default=Value("-"),
output_field=CharField(),
),
)
def with_jst_name_str(self):
return self.annotate(
jst_name=djCase(
When(jst__name__isnull=False, then=F("jst__name")),
default=Value("-"),
output_field=CharField(),
),
)
[dokumentacja]
class Advice(models.Model):
case = models.OneToOneField(
to=Case, null=True, blank=True, on_delete=models.CASCADE, verbose_name=_("Case")
)
subject = models.CharField(
max_length=100, verbose_name=_("Tag subject"), null=True, blank=True
)
summary = models.TextField(verbose_name=_("Summary"), null=True, blank=True)
issues = models.ManyToManyField(
to=Issue, verbose_name=Issue._meta.verbose_name_plural, blank=True
)
area = models.ManyToManyField(
to=Area, verbose_name=Area._meta.verbose_name_plural, blank=True
)
person_kind = models.ForeignKey(
PersonKind,
null=True,
blank=True,
on_delete=models.CASCADE,
verbose_name=PersonKind._meta.verbose_name,
)
institution_kind = models.ForeignKey(
to=InstitutionKind,
verbose_name=InstitutionKind._meta.verbose_name,
null=True,
blank=True,
on_delete=models.CASCADE,
)
advicer = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
verbose_name=_("Advicer"),
help_text=_("Person who give a advice"),
limit_choices_to={"is_staff": True},
on_delete=models.CASCADE,
)
grant_on = models.DateTimeField(default=now, verbose_name=_("Grant on"))
created_by = models.ForeignKey(
to=settings.AUTH_USER_MODEL,
verbose_name=_("Created by"),
related_name="advice_created_by",
on_delete=models.CASCADE,
)
created_on = models.DateTimeField(
auto_now_add=True, verbose_name=_("Creation date")
)
# advice_table filters for interesting_case and for_knowledge_base:
# "Yes" → flag=True; "No" → flag=False or NULL
# (NULL treated as not set, i.e. negative)
interesting_case = models.BooleanField(
verbose_name=_("Interesting case"), null=True, blank=True
)
for_knowledge_base = models.BooleanField(
verbose_name=_("For knowledge base"), null=True, blank=True
)
helped = models.BooleanField(verbose_name=_("We helped?"), null=True, blank=True)
modified_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
verbose_name=_("Modified by"),
related_name="advice_modified_by",
on_delete=models.CASCADE,
)
modified_on = models.DateTimeField(
auto_now=True, null=True, blank=True, verbose_name=_("Modification date")
)
visible = models.BooleanField(default=True, verbose_name=_("Visible"))
comment = models.TextField(verbose_name=_("Comment"), null=True, blank=True)
jst = models.ForeignKey(
to=JST,
null=True,
blank=True,
on_delete=models.CASCADE,
verbose_name=_("Unit of administrative division"),
db_index=True,
)
ai_assistant_tags = models.JSONField(
verbose_name=_("AI assistant tags"),
null=True,
blank=True,
help_text=_("Tags generated by AI assistant"),
)
objects = AdviceQuerySet.as_manager()
class Meta:
ordering = ["-created_on"]
permissions = (("can_view_all_advices", _("Can view all advices")),)
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
def __str__(self):
return self.subject or _("Tag #%d") % (self.pk)
def get_absolute_url(self):
return reverse("advicer:detail", kwargs={"pk": self.pk})
def render_advice_link(self):
url = self.get_absolute_url()
label = self.subject or _("Tag #%d") % (self.pk)
return f'<a href="{url}">{label}</a>'
def render_helped(self):
if self.helped is None:
return '<span class="fas fa-question"></span>'
elif self.helped:
return '<span class="fas fa-check" style="color: green;"></span>'
return '<span class="fas fa-xmark" style="color: red;"></span>'
def render_visible(self):
if self.visible:
return '<span class="fas fa-check" style="color: green;"></span>'
return '<span class="fas fa-xmark" style="color: red;"></span>'
def _ai_tag(self, key):
if self.ai_assistant_tags:
return self.ai_assistant_tags.get(key)
return None
@property
def ai_subject(self):
return self._ai_tag("subject")
@property
def ai_summary(self):
return self._ai_tag("summary")
@property
def ai_issues(self):
ids = self._ai_tag("issues")
if not ids:
return None
return list(Issue.objects.filter(pk__in=ids).values_list("name", flat=True))
@property
def ai_area(self):
ids = self._ai_tag("area")
if not ids:
return None
return list(Area.objects.filter(pk__in=ids).values_list("name", flat=True))
@property
def ai_person_kind(self):
pk = self._ai_tag("person_kind")
if pk is None:
return None
return PersonKind.objects.filter(pk=pk).values_list("name", flat=True).first()
@property
def ai_institution_kind(self):
pk = self._ai_tag("institution_kind")
if pk is None:
return None
return (
InstitutionKind.objects.filter(pk=pk).values_list("name", flat=True).first()
)
@property
def ai_jst(self):
jst_id = self._ai_tag("jst")
if jst_id is None:
return None
jst = JST.objects.filter(pk=jst_id).first()
return str(jst) if jst else None
@property
def ai_comment(self):
return self._ai_tag("comment")