You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

106 lines
4.1 KiB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
  1. from django.contrib.auth.models import User
  2. from django.core.exceptions import ValidationError
  3. from django.db import models
  4. from .managers import CategoryManager, LabelManager, SpanManager, TextLabelManager
  5. from examples.models import Example
  6. from label_types.models import CategoryType, RelationType, SpanType
  7. from projects.models import Project
  8. class Label(models.Model):
  9. objects = LabelManager()
  10. prob = models.FloatField(default=0.0)
  11. manual = models.BooleanField(default=False)
  12. user = models.ForeignKey(User, on_delete=models.CASCADE)
  13. created_at = models.DateTimeField(auto_now_add=True)
  14. updated_at = models.DateTimeField(auto_now=True)
  15. class Meta:
  16. abstract = True
  17. class Category(Label):
  18. objects = CategoryManager()
  19. example = models.ForeignKey(to=Example, on_delete=models.CASCADE, related_name="categories")
  20. label = models.ForeignKey(to=CategoryType, on_delete=models.CASCADE)
  21. class Meta:
  22. unique_together = ("example", "user", "label")
  23. class Span(Label):
  24. objects = SpanManager()
  25. example = models.ForeignKey(to=Example, on_delete=models.CASCADE, related_name="spans")
  26. label = models.ForeignKey(to=SpanType, on_delete=models.CASCADE)
  27. start_offset = models.IntegerField()
  28. end_offset = models.IntegerField()
  29. def validate_unique(self, exclude=None):
  30. allow_overlapping = getattr(self.example.project, "allow_overlapping", False)
  31. is_collaborative = self.example.project.collaborative_annotation
  32. if allow_overlapping:
  33. super().validate_unique(exclude=exclude)
  34. return
  35. overlapping_span = (
  36. Span.objects.exclude(id=self.id)
  37. .filter(example=self.example)
  38. .filter(
  39. models.Q(start_offset__gte=self.start_offset, start_offset__lt=self.end_offset)
  40. | models.Q(end_offset__gt=self.start_offset, end_offset__lte=self.end_offset)
  41. | models.Q(start_offset__lte=self.start_offset, end_offset__gte=self.end_offset)
  42. )
  43. )
  44. if is_collaborative:
  45. if overlapping_span.exists():
  46. raise ValidationError("This overlapping is not allowed in this project.")
  47. else:
  48. if overlapping_span.filter(user=self.user).exists():
  49. raise ValidationError("This overlapping is not allowed in this project.")
  50. def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
  51. self.full_clean()
  52. super().save(force_insert, force_update, using, update_fields)
  53. def is_overlapping(self, other: "Span"):
  54. return (
  55. (other.start_offset <= self.start_offset < other.end_offset)
  56. or (other.start_offset < self.end_offset <= other.end_offset)
  57. or (self.start_offset < other.start_offset and other.end_offset < self.end_offset)
  58. )
  59. class Meta:
  60. constraints = [
  61. models.CheckConstraint(check=models.Q(start_offset__gte=0), name="startOffset >= 0"),
  62. models.CheckConstraint(check=models.Q(end_offset__gte=0), name="endOffset >= 0"),
  63. models.CheckConstraint(check=models.Q(start_offset__lt=models.F("end_offset")), name="start < end"),
  64. ]
  65. class TextLabel(Label):
  66. objects = TextLabelManager()
  67. example = models.ForeignKey(to=Example, on_delete=models.CASCADE, related_name="texts")
  68. text = models.TextField()
  69. def is_same_text(self, other: "TextLabel"):
  70. return self.text == other.text
  71. class Meta:
  72. unique_together = ("example", "user", "text")
  73. class Relation(models.Model):
  74. annotation_id_1 = models.IntegerField()
  75. annotation_id_2 = models.IntegerField()
  76. type = models.ForeignKey(RelationType, related_name="annotation_relations", on_delete=models.CASCADE)
  77. timestamp = models.DateTimeField()
  78. user = models.ForeignKey(User, related_name="annotation_relations", on_delete=models.CASCADE)
  79. project = models.ForeignKey(Project, related_name="annotation_relations", on_delete=models.CASCADE)
  80. def __str__(self):
  81. return self.__dict__.__str__()
  82. class Meta:
  83. unique_together = ("annotation_id_1", "annotation_id_2", "type", "project")