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.

207 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. @property
  81. def is_text_project(self) -> bool:
  82. return True
  83. @property
  84. def can_define_label(self) -> bool:
  85. return True
  86. @property
  87. def can_define_span(self) -> bool:
  88. return True
  89. class Seq2seqProject(Project):
  90. @property
  91. def is_text_project(self) -> bool:
  92. return True
  93. class IntentDetectionAndSlotFillingProject(Project):
  94. @property
  95. def is_text_project(self) -> bool:
  96. return True
  97. @property
  98. def can_define_label(self) -> bool:
  99. return True
  100. @property
  101. def can_define_category(self) -> bool:
  102. return True
  103. @property
  104. def can_define_span(self) -> bool:
  105. return True
  106. class Speech2textProject(Project):
  107. @property
  108. def is_text_project(self) -> bool:
  109. return False
  110. class ImageClassificationProject(Project):
  111. @property
  112. def is_text_project(self) -> bool:
  113. return False
  114. @property
  115. def can_define_label(self) -> bool:
  116. return True
  117. @property
  118. def can_define_category(self) -> bool:
  119. return True
  120. class Tag(models.Model):
  121. text = models.TextField()
  122. project = models.ForeignKey(to=Project, on_delete=models.CASCADE, related_name="tags")
  123. def __str__(self):
  124. return self.text
  125. class MemberManager(Manager):
  126. def can_update(self, project: int, member_id: int, new_role: str) -> bool:
  127. """The project needs at least 1 admin.
  128. Args:
  129. project: The project id.
  130. member_id: The member id.
  131. new_role: The new role name.
  132. Returns:
  133. Whether the mapping can be updated or not.
  134. """
  135. queryset = self.filter(project=project, role__name=settings.ROLE_PROJECT_ADMIN)
  136. if queryset.count() > 1:
  137. return True
  138. else:
  139. admin = queryset.first()
  140. # we can change the role except for the only admin.
  141. return admin.id != member_id or new_role == settings.ROLE_PROJECT_ADMIN
  142. def has_role(self, project_id: int, user: User, role_name: str):
  143. return self.filter(project=project_id, user=user, role__name=role_name).exists()
  144. class Member(models.Model):
  145. user = models.ForeignKey(to=User, on_delete=models.CASCADE, related_name="role_mappings")
  146. project = models.ForeignKey(to=Project, on_delete=models.CASCADE, related_name="role_mappings")
  147. role = models.ForeignKey(to=Role, on_delete=models.CASCADE)
  148. created_at = models.DateTimeField(auto_now_add=True)
  149. updated_at = models.DateTimeField(auto_now=True)
  150. objects = MemberManager()
  151. def clean(self):
  152. members = self.__class__.objects.exclude(id=self.id)
  153. if members.filter(user=self.user, project=self.project).exists():
  154. message = "This user is already assigned to a role in this project."
  155. raise ValidationError(message)
  156. @property
  157. def username(self):
  158. return self.user.username
  159. class Meta:
  160. unique_together = ("user", "project")