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.

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