Digitale bierlijst

aardbei_sync.py 5.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from __future__ import annotations
  2. from typing import List, Dict, Any, Tuple, Optional
  3. from dataclasses import dataclass
  4. import logging
  5. import requests
  6. from piket_server import Person, db
  7. @dataclass(frozen=True)
  8. class SparseAardbeiPerson:
  9. full_name: str
  10. display_name: str
  11. aardbei_id: int
  12. is_leader: bool
  13. @classmethod
  14. def from_aardbei_dict(cls, data: Dict[str, Any]) -> SparseAardbeiPerson:
  15. return cls(
  16. full_name=data["member"]["person"]["full_name"],
  17. display_name=data["member"]["display_name"],
  18. aardbei_id=data["member"]["person"]["id"],
  19. is_leader=data["member"]["is_leader"],
  20. )
  21. @dataclass(frozen=True)
  22. class AardbeiMatch:
  23. local: Person
  24. remote: SparseAardbeiPerson
  25. @dataclass(frozen=True)
  26. class AardbeiLink:
  27. matches: List[AardbeiMatch]
  28. """People that exist on both sides, but aren't linked in the people table."""
  29. altered_name: List[AardbeiMatch]
  30. """People that are already linked but changed one of their names."""
  31. remote_only: List[SparseAardbeiPerson]
  32. """People that only exist on the remote."""
  33. def get_aardbei_people(token: str) -> List[SparseAardbeiPerson]:
  34. resp = requests.get(
  35. "https://aardbei.app/api/groups/0/", headers={"Authorization": f"Group {token}"}
  36. )
  37. resp.raise_for_status()
  38. members = resp.json()["group"]["members"]
  39. return [SparseAardbeiPerson.from_aardbei_dict(x) for x in members]
  40. def match_local_aardbei(aardbei_people: List[SparseAardbeiPerson]) -> AardbeiLink:
  41. matches: List[AardbeiMatch] = []
  42. altered_name: List[AardbeiMatch] = []
  43. remote_only: List[SparseAardbeiPerson] = []
  44. for aardbei_person in aardbei_people:
  45. p: Optional[Person] = Person.query.filter_by(
  46. aardbei_id=aardbei_person.aardbei_id
  47. ).one_or_none()
  48. if p is not None:
  49. if (
  50. p.full_name != aardbei_person.full_name
  51. or p.display_name != aardbei_person.display_name
  52. ):
  53. altered_name.append(AardbeiMatch(p, aardbei_person))
  54. else:
  55. logging.info(
  56. "OK: %s / %s (L%s/R%s)",
  57. p.full_name,
  58. p.display_name,
  59. p.person_id,
  60. p.aardbei_id,
  61. )
  62. continue
  63. p = Person.query.filter_by(full_name=aardbei_person.full_name).one_or_none()
  64. if p is not None:
  65. matches.append(AardbeiMatch(p, aardbei_person))
  66. else:
  67. remote_only.append(aardbei_person)
  68. return AardbeiLink(matches, altered_name, remote_only)
  69. def link_matches(matches: List[AardbeiMatch]) -> None:
  70. for match in matches:
  71. match.local.aardbei_id = match.remote.aardbei_id
  72. match.local.display_name = match.remote.display_name
  73. logging.info(
  74. "Linking local %s (%s) to remote %s (%s)",
  75. match.local.full_name,
  76. match.local.person_id,
  77. match.remote.display_name,
  78. match.remote.aardbei_id,
  79. )
  80. db.session.add(match.local)
  81. def create_missing(missing: List[SparseAardbeiPerson]) -> None:
  82. for person in missing:
  83. pnew = Person(
  84. full_name=person.full_name,
  85. display_name=person.display_name,
  86. aardbei_id=person.aardbei_id,
  87. active=False,
  88. )
  89. logging.info(
  90. "Creating new person for %s (%s)", person.full_name, person.aardbei_id
  91. )
  92. db.session.add(pnew)
  93. def update_names(matches: List[AardbeiMatch]) -> None:
  94. for match in matches:
  95. p = match.local
  96. aardbei_person = match.remote
  97. changed = False
  98. if p.full_name != aardbei_person.full_name:
  99. logging.info(
  100. "Updating %s (L%s/R%s) full name %s to %s",
  101. aardbei_person.full_name,
  102. p.person_id,
  103. aardbei_person.aardbei_id,
  104. p.full_name,
  105. aardbei_person.full_name,
  106. )
  107. p.full_name = aardbei_person.full_name
  108. changed = True
  109. if p.display_name != aardbei_person.display_name:
  110. logging.info(
  111. "Updating %s (L%s/R%s) display name %s to %s",
  112. aardbei_person.full_name,
  113. p.person_id,
  114. aardbei_person.aardbei_id,
  115. p.display_name,
  116. aardbei_person.display_name,
  117. )
  118. p.display_name = aardbei_person.display_name
  119. changed = True
  120. assert changed, "got match but didn't update anything"
  121. db.session.add(p)
  122. if __name__ == "__main__":
  123. logging.basicConfig(level=logging.INFO)
  124. token = input("Token: ")
  125. aardbei_people = get_aardbei_people(token)
  126. link = match_local_aardbei(aardbei_people)
  127. link_matches(link.matches)
  128. create_missing(link.remote_only)
  129. update_names(link.altered_name)
  130. confirm = input("Commit? Y/N")
  131. if confirm.lower() == "y":
  132. print("Committing.")
  133. db.session.commit()
  134. else:
  135. print("Not committing.")