Label.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. from __future__ import annotations
  2. import typing as T
  3. from datetime import datetime
  4. from pycs import db
  5. from pycs.database.base import NamedBaseModel
  6. from pycs.database.util import commit_on_return
  7. def compare_children(start_label: Label, identifier: int) -> bool:
  8. """ check for cyclic relationships """
  9. labels_to_check = [start_label]
  10. while labels_to_check:
  11. label = labels_to_check.pop(0)
  12. if label.id == identifier:
  13. return False
  14. labels_to_check.extend(label.children)
  15. return True
  16. def _label_id():
  17. return Label.id
  18. class Label(NamedBaseModel):
  19. """ DB Model for labels """
  20. id = db.Column(db.Integer, primary_key=True)
  21. project_id = db.Column(
  22. db.Integer,
  23. db.ForeignKey("project.id", ondelete="CASCADE"),
  24. nullable=False)
  25. parent_id = db.Column(
  26. db.Integer,
  27. db.ForeignKey("label.id", ondelete="SET NULL"))
  28. created = db.Column(db.DateTime, default=datetime.utcnow,
  29. index=True, nullable=False)
  30. reference = db.Column(db.String)
  31. hierarchy_level = db.Column(db.String)
  32. # contraints
  33. __table_args__ = (
  34. db.UniqueConstraint('project_id', 'reference'),
  35. )
  36. # relationships to other models
  37. parent = db.relationship("Label",
  38. backref="children",
  39. remote_side=_label_id,
  40. )
  41. results = db.relationship("Result",
  42. backref="label",
  43. passive_deletes=True,
  44. lazy="dynamic",
  45. )
  46. serialize_only = NamedBaseModel.serialize_only + (
  47. "project_id",
  48. "parent_id",
  49. "parent_reference",
  50. "reference",
  51. "hierarchy_level",
  52. # "children",
  53. )
  54. @property
  55. def parent_reference(self):
  56. return None if self.parent is None else self.parent.reference
  57. @commit_on_return
  58. def set_parent(self, parent: T.Optional[T.Union[int, str, Label]] = None) -> None:
  59. """
  60. set this labels parent
  61. :param parent: parent label. Can be a reference, an id or a Label instance
  62. :return:
  63. """
  64. parent_id = None
  65. if parent is not None:
  66. if isinstance(parent, Label):
  67. parent_id = parent.id
  68. elif isinstance(parent, str):
  69. parent_id = Label.query.filter(Label.reference == parent).one().id
  70. elif isinstance(parent, int):
  71. parent_id = parent
  72. if not compare_children(self, parent_id):
  73. raise ValueError('Cyclic relationship detected!')
  74. self.parent_id = parent_id