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.

137 lines
3.4 KiB

  1. import abc
  2. from typing import Any, Dict, Optional, Union
  3. from pydantic import BaseModel, validator
  4. from api.models import Category, CategoryType
  5. from api.models import Label as LabelModel
  6. from api.models import Project, Span, SpanType
  7. from api.models import TextLabel as TL
  8. class Label(BaseModel, abc.ABC):
  9. @abc.abstractmethod
  10. def has_name(self) -> bool:
  11. raise NotImplementedError()
  12. @property
  13. @abc.abstractmethod
  14. def name(self) -> str:
  15. raise NotImplementedError()
  16. @classmethod
  17. def parse(cls, obj: Any):
  18. raise NotImplementedError()
  19. @abc.abstractmethod
  20. def create(self, project: Project) -> Optional[LabelModel]:
  21. raise NotImplementedError()
  22. @abc.abstractmethod
  23. def create_annotation(self, user, example, mapping):
  24. raise NotImplementedError
  25. def __hash__(self):
  26. return hash(tuple(self.dict()))
  27. class CategoryLabel(Label):
  28. label: str
  29. @validator('label')
  30. def label_is_not_empty(cls, value: str):
  31. if value:
  32. return value
  33. else:
  34. raise ValueError('is not empty.')
  35. def has_name(self) -> bool:
  36. return True
  37. @property
  38. def name(self) -> str:
  39. return self.label
  40. @classmethod
  41. def parse(cls, obj: Any):
  42. if isinstance(obj, str):
  43. return cls(label=obj)
  44. elif isinstance(obj, int):
  45. return cls(label=str(obj))
  46. else:
  47. raise TypeError(f'{obj} is not str.')
  48. def create(self, project: Project) -> Optional[LabelModel]:
  49. return CategoryType(text=self.label, project=project)
  50. def create_annotation(self, user, example, mapping: Dict[str, LabelModel]):
  51. return Category(
  52. user=user,
  53. example=example,
  54. label=mapping[self.label]
  55. )
  56. class SpanLabel(Label):
  57. label: Union[str, int]
  58. start_offset: int
  59. end_offset: int
  60. def has_name(self) -> bool:
  61. return True
  62. @property
  63. def name(self) -> str:
  64. return self.label
  65. @classmethod
  66. def parse(cls, obj: Any):
  67. if isinstance(obj, list) or isinstance(obj, tuple):
  68. columns = ['start_offset', 'end_offset', 'label']
  69. obj = zip(columns, obj)
  70. return cls.parse_obj(obj)
  71. elif isinstance(obj, dict):
  72. return cls.parse_obj(obj)
  73. else:
  74. raise TypeError(f'{obj} is invalid type.')
  75. def create(self, project: Project) -> Optional[LabelModel]:
  76. return SpanType(text=self.label, project=project)
  77. def create_annotation(self, user, example, mapping: Dict[str, LabelModel]):
  78. return Span(
  79. user=user,
  80. example=example,
  81. start_offset=self.start_offset,
  82. end_offset=self.end_offset,
  83. label=mapping[self.label]
  84. )
  85. class TextLabel(Label):
  86. text: str
  87. def has_name(self) -> bool:
  88. return False
  89. @property
  90. def name(self) -> str:
  91. return self.text
  92. @classmethod
  93. def parse(cls, obj: Any):
  94. if isinstance(obj, str) and obj:
  95. return cls(text=obj)
  96. else:
  97. raise TypeError(f'{obj} is not str or empty.')
  98. def create(self, project: Project) -> Optional[LabelModel]:
  99. return None
  100. def create_annotation(self, user, example, mapping):
  101. return TL(
  102. user=user,
  103. example=example,
  104. text=self.text
  105. )