6
0

Result.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import json
  2. import typing as T
  3. from pycs import db
  4. from pycs.database.base import BaseModel
  5. from pycs.database.util import commit_on_return
  6. class ResultConfirmation(BaseModel):
  7. """ DB Model for user confirmations of results """
  8. result_id = db.Column(
  9. db.Integer,
  10. db.ForeignKey("result.id", ondelete="CASCADE"),
  11. nullable=False)
  12. confirming_user = db.Column(db.String, nullable=False)
  13. serialize_only = BaseModel.serialize_only + (
  14. "result_id",
  15. "confirming_user",
  16. )
  17. class Result(BaseModel):
  18. """ DB Model for projects """
  19. file_id = db.Column(
  20. db.Integer,
  21. db.ForeignKey("file.id", ondelete="CASCADE"),
  22. nullable=False)
  23. origin = db.Column(db.String, nullable=False)
  24. origin_user = db.Column(db.String, nullable=True)
  25. type = db.Column(db.String, nullable=False)
  26. label_id = db.Column(
  27. db.Integer,
  28. db.ForeignKey("label.id", ondelete="SET NULL"),
  29. nullable=True)
  30. data_encoded = db.Column(db.String)
  31. result_confirmations = db.relationship("ResultConfirmation",
  32. backref="result",
  33. lazy="dynamic",
  34. passive_deletes=True,
  35. )
  36. serialize_only = BaseModel.serialize_only + (
  37. "file_id",
  38. "origin",
  39. "origin_user",
  40. "type",
  41. "label_id",
  42. "data",
  43. "confirmations"
  44. )
  45. def serialize(self):
  46. """ extends the default serialize with the decoded data attribute """
  47. result = super().serialize()
  48. result["data"] = self.data
  49. result["confirmations"] = self.confirmations
  50. return result
  51. @property
  52. def data(self) -> T.Optional[dict]:
  53. """ getter for the decoded data attribute """
  54. return None if self.data_encoded is None else json.loads(self.data_encoded)
  55. @data.setter
  56. def data(self, value):
  57. """
  58. setter for the decoded data attribute
  59. The attribute is encoded property before assigned to the object.
  60. """
  61. if isinstance(value, str) or value is None:
  62. self.data_encoded = value
  63. elif isinstance(value, (dict, list)):
  64. self.data_encoded = json.dumps(value)
  65. else:
  66. raise ValueError(f"Not supported type: {type(value)}")
  67. @commit_on_return
  68. def set_origin(self, origin: str, origin_user: str = None):
  69. """
  70. set this results origin
  71. :param origin: either 'user' or 'pipeline'
  72. :param origin_user: None if origin is 'pipeline' else name of the user
  73. :return:
  74. """
  75. if origin == "pipeline" and not origin_user is None:
  76. raise ValueError("If an annotation was made by the pipeline no user"\
  77. "can be specified!")
  78. self.origin = origin
  79. self.origin_user = origin_user
  80. self.reset_confirmations()
  81. @commit_on_return
  82. def set_label(self, label: int):
  83. """
  84. set this results origin
  85. :param label: label ID
  86. :return:
  87. """
  88. if self.label_id != label:
  89. self.reset_confirmations()
  90. self.label_id = label
  91. @property
  92. def confirmations(self) -> T.List[ResultConfirmation]:
  93. """
  94. Returns all confirmations for this results
  95. :return: list of result confirmations
  96. """
  97. confirmations = db.session.query(ResultConfirmation).filter(
  98. ResultConfirmation.result.has(Result.id==self.id))
  99. _confirmations = [c.serialize() for c in confirmations.all()]
  100. _confirmations = [{k:v for k, v in c.items()
  101. if k in ('id', 'confirming_user')}
  102. for c in _confirmations]
  103. return _confirmations
  104. def reset_confirmations(self) -> T.List[ResultConfirmation]:
  105. """
  106. Resets all confirmations
  107. :return: list of result confirmation objects
  108. """
  109. confirmations = ResultConfirmation.query.filter(
  110. ResultConfirmation.result_id == self.id)
  111. _confirmations = [c.serialize() for c in confirmations.all()]
  112. confirmations.delete()
  113. return _confirmations
  114. @commit_on_return
  115. def confirm(self, user: str):
  116. """
  117. Result is confirmed by the given user. This sets the origin to "user".
  118. If no username was specified before, the given username is used.
  119. A confirmation is only added if it does not already exist. The result
  120. has be labeled to be confirmed.
  121. :param user: username
  122. """
  123. if user is None:
  124. raise ValueError("When confirming a result the username has to" \
  125. "be specified.")
  126. if self.origin == "pipeline":
  127. self.set_origin(origin="user", origin_user=user)
  128. # Get current confirmations.
  129. confirmations = self.confirmations
  130. # Results can only be confirmed if the result is labeled.
  131. # Also, the original annotator cannot confirm the result and we want
  132. # to avoid duplicates.
  133. if self.label_id is not None and self.origin_user != user and not len(confirmations) > 0:
  134. ResultConfirmation.new(commit=False,
  135. result_id=self.id,
  136. confirming_user=user)