feat: Add case review messaging system with feedback and query capabilities
- Introduced CaseReviewMessage model to handle feedback and queries for cases. - Implemented views for displaying user and CID messages, including outstanding feedback. - Created templates for collection feedback overview and user messages. - Added functionality for acknowledging messages and sending feedback or queries. - Enhanced collection detail view to show outstanding messages and user message statistics. - Added tests for handling duplicate DICOM uploads without series.
This commit is contained in:
@@ -2024,6 +2024,71 @@ class SelfReview(models.Model):
|
||||
)
|
||||
|
||||
|
||||
class CaseReviewMessage(models.Model):
|
||||
"""Conversation message for one learner attempt on one case.
|
||||
|
||||
Supervisors/collection authors use this for per-case feedback and replies.
|
||||
Learners use it to raise questions/queries. Supervisor messages may require
|
||||
acknowledgement so they continue to flag on the learner-facing pages until
|
||||
explicitly acknowledged.
|
||||
"""
|
||||
|
||||
class MessageType(models.TextChoices):
|
||||
FEEDBACK = "feedback", _("Feedback")
|
||||
QUERY = "query", _("Query")
|
||||
REPLY = "reply", _("Reply")
|
||||
|
||||
cid_user_exam = models.ForeignKey(
|
||||
CidUserExam,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="case_review_messages",
|
||||
)
|
||||
case = models.ForeignKey(
|
||||
Case,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="case_review_messages",
|
||||
)
|
||||
author = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="case_review_messages_authored",
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
author_label = models.CharField(max_length=255, blank=True)
|
||||
message_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=MessageType.choices,
|
||||
default=MessageType.QUERY,
|
||||
)
|
||||
message = models.TextField()
|
||||
requires_acknowledgement = models.BooleanField(default=False)
|
||||
acknowledged_at = models.DateTimeField(null=True, blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ("created_at",)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.get_message_type_display()} for {self.case}"
|
||||
|
||||
@property
|
||||
def is_acknowledged(self) -> bool:
|
||||
return self.acknowledged_at is not None
|
||||
|
||||
def get_author_display(self) -> str:
|
||||
if self.author is not None:
|
||||
full_name = self.author.get_full_name().strip()
|
||||
return full_name or self.author.username
|
||||
return self.author_label or "Learner"
|
||||
|
||||
def acknowledge(self):
|
||||
if self.acknowledged_at is None:
|
||||
self.acknowledged_at = timezone.now()
|
||||
self.save(update_fields=["acknowledged_at"])
|
||||
|
||||
|
||||
class UncategorisedDicom(models.Model):
|
||||
# We use image to maintain consitency across apps
|
||||
image = models.FileField(upload_to=uncategorised_dicom_directory_path)
|
||||
|
||||
Reference in New Issue
Block a user