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.

248 lines
6.7 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
  1. import abc
  2. from django.conf import settings
  3. from django.contrib.auth.models import User
  4. from django.core.exceptions import ValidationError
  5. from django.db import models
  6. from django.db.models import Manager
  7. from polymorphic.models import PolymorphicModel
  8. from roles.models import Role
  9. DOCUMENT_CLASSIFICATION = "DocumentClassification"
  10. SEQUENCE_LABELING = "SequenceLabeling"
  11. SEQ2SEQ = "Seq2seq"
  12. SPEECH2TEXT = "Speech2text"
  13. IMAGE_CLASSIFICATION = "ImageClassification"
  14. BOUNDING_BOX = "BoundingBox"
  15. SEGMENTATION = "Segmentation"
  16. IMAGE_CAPTIONING = "ImageCaptioning"
  17. INTENT_DETECTION_AND_SLOT_FILLING = "IntentDetectionAndSlotFilling"
  18. PROJECT_CHOICES = (
  19. (DOCUMENT_CLASSIFICATION, "document classification"),
  20. (SEQUENCE_LABELING, "sequence labeling"),
  21. (SEQ2SEQ, "sequence to sequence"),
  22. (INTENT_DETECTION_AND_SLOT_FILLING, "intent detection and slot filling"),
  23. (SPEECH2TEXT, "speech to text"),
  24. (IMAGE_CLASSIFICATION, "image classification"),
  25. (BOUNDING_BOX, "bounding box"),
  26. (SEGMENTATION, "segmentation"),
  27. (IMAGE_CAPTIONING, "image captioning"),
  28. )
  29. class Project(PolymorphicModel):
  30. name = models.CharField(max_length=100)
  31. description = models.TextField(default="")
  32. guideline = models.TextField(default="", blank=True)
  33. created_at = models.DateTimeField(auto_now_add=True)
  34. updated_at = models.DateTimeField(auto_now=True)
  35. created_by = models.ForeignKey(
  36. User,
  37. on_delete=models.SET_NULL,
  38. null=True,
  39. )
  40. project_type = models.CharField(max_length=30, choices=PROJECT_CHOICES)
  41. random_order = models.BooleanField(default=False)
  42. collaborative_annotation = models.BooleanField(default=False)
  43. single_class_classification = models.BooleanField(default=False)
  44. def add_admin(self):
  45. admin_role = Role.objects.get(name=settings.ROLE_PROJECT_ADMIN)
  46. Member.objects.create(
  47. project=self,
  48. user=self.created_by,
  49. role=admin_role,
  50. )
  51. @property
  52. @abc.abstractmethod
  53. def is_text_project(self) -> bool:
  54. return False
  55. @property
  56. def can_define_label(self) -> bool:
  57. """Whether or not the project can define label(ignoring the type of label)"""
  58. return False
  59. @property
  60. def can_define_relation(self) -> bool:
  61. """Whether or not the project can define relation."""
  62. return False
  63. @property
  64. def can_define_category(self) -> bool:
  65. """Whether or not the project can define category."""
  66. return False
  67. @property
  68. def can_define_span(self) -> bool:
  69. """Whether or not the project can define span."""
  70. return False
  71. def __str__(self):
  72. return self.name
  73. class TextClassificationProject(Project):
  74. @property
  75. def is_text_project(self) -> bool:
  76. return True
  77. @property
  78. def can_define_label(self) -> bool:
  79. return True
  80. @property
  81. def can_define_category(self) -> bool:
  82. return True
  83. class SequenceLabelingProject(Project):
  84. allow_overlapping = models.BooleanField(default=False)
  85. grapheme_mode = models.BooleanField(default=False)
  86. use_relation = models.BooleanField(default=False)
  87. @property
  88. def is_text_project(self) -> bool:
  89. return True
  90. @property
  91. def can_define_label(self) -> bool:
  92. return True
  93. @property
  94. def can_define_span(self) -> bool:
  95. return True
  96. class Seq2seqProject(Project):
  97. @property
  98. def is_text_project(self) -> bool:
  99. return True
  100. class IntentDetectionAndSlotFillingProject(Project):
  101. @property
  102. def is_text_project(self) -> bool:
  103. return True
  104. @property
  105. def can_define_label(self) -> bool:
  106. return True
  107. @property
  108. def can_define_category(self) -> bool:
  109. return True
  110. @property
  111. def can_define_span(self) -> bool:
  112. return True
  113. class Speech2textProject(Project):
  114. @property
  115. def is_text_project(self) -> bool:
  116. return False
  117. class ImageClassificationProject(Project):
  118. @property
  119. def is_text_project(self) -> bool:
  120. return False
  121. @property
  122. def can_define_label(self) -> bool:
  123. return True
  124. @property
  125. def can_define_category(self) -> bool:
  126. return True
  127. class BoundingBoxProject(Project):
  128. @property
  129. def is_text_project(self) -> bool:
  130. return False
  131. @property
  132. def can_define_label(self) -> bool:
  133. return True
  134. @property
  135. def can_define_category(self) -> bool:
  136. return True
  137. class SegmentationProject(Project):
  138. @property
  139. def is_text_project(self) -> bool:
  140. return False
  141. @property
  142. def can_define_label(self) -> bool:
  143. return True
  144. @property
  145. def can_define_category(self) -> bool:
  146. return True
  147. class ImageCaptioningProject(Project):
  148. @property
  149. def is_text_project(self) -> bool:
  150. return False
  151. class Tag(models.Model):
  152. text = models.TextField()
  153. project = models.ForeignKey(to=Project, on_delete=models.CASCADE, related_name="tags")
  154. def __str__(self):
  155. return self.text
  156. class MemberManager(Manager):
  157. def can_update(self, project: int, member_id: int, new_role: str) -> bool:
  158. """The project needs at least 1 admin.
  159. Args:
  160. project: The project id.
  161. member_id: The member id.
  162. new_role: The new role name.
  163. Returns:
  164. Whether the mapping can be updated or not.
  165. """
  166. queryset = self.filter(project=project, role__name=settings.ROLE_PROJECT_ADMIN)
  167. if queryset.count() > 1:
  168. return True
  169. else:
  170. admin = queryset.first()
  171. # we can change the role except for the only admin.
  172. return admin.id != member_id or new_role == settings.ROLE_PROJECT_ADMIN
  173. def has_role(self, project_id: int, user: User, role_name: str):
  174. return self.filter(project=project_id, user=user, role__name=role_name).exists()
  175. class Member(models.Model):
  176. user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name="role_mappings")
  177. project = models.ForeignKey(to=Project, on_delete=models.CASCADE, related_name="role_mappings")
  178. role = models.ForeignKey(to=Role, on_delete=models.CASCADE)
  179. created_at = models.DateTimeField(auto_now_add=True)
  180. updated_at = models.DateTimeField(auto_now=True)
  181. objects = MemberManager()
  182. def clean(self):
  183. members = self.__class__.objects.exclude(id=self.id)
  184. if members.filter(user=self.user, project=self.project).exists():
  185. message = "This user is already assigned to a role in this project."
  186. raise ValidationError(message)
  187. @property
  188. def username(self):
  189. return self.user.username
  190. class Meta:
  191. unique_together = ("user", "project")