|  | @@ -229,6 +229,7 @@ class Person(NamedTuple):
 | 
	
		
			
			| 229 | 229 |              return NetworkError.HttpFailure
 | 
	
		
			
			| 230 | 230 |  
 | 
	
		
			
			| 231 | 231 |          except ValueError as e:
 | 
	
		
			
			|  | 232 | +            LOG.exception(e)
 | 
	
		
			
			| 232 | 233 |              return NetworkError.InvalidData
 | 
	
		
			
			| 233 | 234 |  
 | 
	
		
			
			| 234 | 235 |      @classmethod
 | 
	
	
		
			
			|  | @@ -546,3 +547,124 @@ class Settlement(NamedTuple):
 | 
	
		
			
			| 546 | 547 |          data["settlement"]["count_info"] = data["count_info"]
 | 
	
		
			
			| 547 | 548 |  
 | 
	
		
			
			| 548 | 549 |          return cls.from_dict(data["settlement"])
 | 
	
		
			
			|  | 550 | +
 | 
	
		
			
			|  | 551 | +
 | 
	
		
			
			|  | 552 | +@dataclass(frozen=True)
 | 
	
		
			
			|  | 553 | +class AardbeiActivity:
 | 
	
		
			
			|  | 554 | +    aardbei_id: int
 | 
	
		
			
			|  | 555 | +    name: str
 | 
	
		
			
			|  | 556 | +
 | 
	
		
			
			|  | 557 | +    @classmethod
 | 
	
		
			
			|  | 558 | +    def from_dict(cls, data: Dict[str, Any]) -> AardbeiActivity:
 | 
	
		
			
			|  | 559 | +        return cls(data["activity"]["id"], data["activity"]["name"])
 | 
	
		
			
			|  | 560 | +
 | 
	
		
			
			|  | 561 | +    @classmethod
 | 
	
		
			
			|  | 562 | +    def get_available(
 | 
	
		
			
			|  | 563 | +        cls, token: str, endpoint: str
 | 
	
		
			
			|  | 564 | +    ) -> Union[List[AardbeiActivity], NetworkError]:
 | 
	
		
			
			|  | 565 | +        try:
 | 
	
		
			
			|  | 566 | +            req = requests.post(
 | 
	
		
			
			|  | 567 | +                urljoin(SERVER_URL, "/aardbei/get_activities"),
 | 
	
		
			
			|  | 568 | +                json={"endpoint": endpoint, "token": token},
 | 
	
		
			
			|  | 569 | +            )
 | 
	
		
			
			|  | 570 | +
 | 
	
		
			
			|  | 571 | +            req.raise_for_status()
 | 
	
		
			
			|  | 572 | +            return [cls.from_dict(x) for x in req.json()["activities"]]
 | 
	
		
			
			|  | 573 | +
 | 
	
		
			
			|  | 574 | +        except requests.ConnectionError as e:
 | 
	
		
			
			|  | 575 | +            LOG.exception(e)
 | 
	
		
			
			|  | 576 | +            return NetworkError.ConnectionFailure
 | 
	
		
			
			|  | 577 | +
 | 
	
		
			
			|  | 578 | +        except requests.HTTPError as e:
 | 
	
		
			
			|  | 579 | +            LOG.exception(e)
 | 
	
		
			
			|  | 580 | +            return NetworkError.HttpFailure
 | 
	
		
			
			|  | 581 | +
 | 
	
		
			
			|  | 582 | +        except ValueError as e:
 | 
	
		
			
			|  | 583 | +            LOG.exception(e)
 | 
	
		
			
			|  | 584 | +            return NetworkError.InvalidData
 | 
	
		
			
			|  | 585 | +
 | 
	
		
			
			|  | 586 | +    @classmethod
 | 
	
		
			
			|  | 587 | +    def apply_activity(
 | 
	
		
			
			|  | 588 | +        cls, token: str, endpoint: str, activity_id: int
 | 
	
		
			
			|  | 589 | +    ) -> Union[int, NetworkError]:
 | 
	
		
			
			|  | 590 | +        try:
 | 
	
		
			
			|  | 591 | +            req = requests.post(
 | 
	
		
			
			|  | 592 | +                urljoin(SERVER_URL, "/aardbei/apply_activity"),
 | 
	
		
			
			|  | 593 | +                json={"activity_id": activity_id, "token": token, "endpoint": endpoint},
 | 
	
		
			
			|  | 594 | +            )
 | 
	
		
			
			|  | 595 | +            req.raise_for_status()
 | 
	
		
			
			|  | 596 | +            data = req.json()
 | 
	
		
			
			|  | 597 | +
 | 
	
		
			
			|  | 598 | +            return data["activity"]["response_counts"]["present"]
 | 
	
		
			
			|  | 599 | +
 | 
	
		
			
			|  | 600 | +        except requests.ConnectionError as e:
 | 
	
		
			
			|  | 601 | +            LOG.exception(e)
 | 
	
		
			
			|  | 602 | +            return NetworkError.ConnectionFailure
 | 
	
		
			
			|  | 603 | +
 | 
	
		
			
			|  | 604 | +        except requests.HTTPError as e:
 | 
	
		
			
			|  | 605 | +            LOG.exception(e)
 | 
	
		
			
			|  | 606 | +            return NetworkError.HttpFailure
 | 
	
		
			
			|  | 607 | +
 | 
	
		
			
			|  | 608 | +        except ValueError as e:
 | 
	
		
			
			|  | 609 | +            LOG.exception(e)
 | 
	
		
			
			|  | 610 | +            return NetworkError.InvalidData
 | 
	
		
			
			|  | 611 | +
 | 
	
		
			
			|  | 612 | +
 | 
	
		
			
			|  | 613 | +@dataclass(frozen=True)
 | 
	
		
			
			|  | 614 | +class AardbeiPeopleDiff:
 | 
	
		
			
			|  | 615 | +    altered_name: List[str]
 | 
	
		
			
			|  | 616 | +    link_existing: List[str]
 | 
	
		
			
			|  | 617 | +    new_people: List[str]
 | 
	
		
			
			|  | 618 | +    num_changes: int
 | 
	
		
			
			|  | 619 | +
 | 
	
		
			
			|  | 620 | +    @classmethod
 | 
	
		
			
			|  | 621 | +    def from_dict(cls, data: Dict[str, Any]) -> AardbeiPeopleDiff:
 | 
	
		
			
			|  | 622 | +        return cls(**data)
 | 
	
		
			
			|  | 623 | +
 | 
	
		
			
			|  | 624 | +    @classmethod
 | 
	
		
			
			|  | 625 | +    def get_diff(cls, token: str, endpoint: str) -> Union[AardbeiPeopleDiff, NetworkError]:
 | 
	
		
			
			|  | 626 | +        try:
 | 
	
		
			
			|  | 627 | +            req = requests.post(
 | 
	
		
			
			|  | 628 | +                urljoin(SERVER_URL, "/aardbei/diff_people"),
 | 
	
		
			
			|  | 629 | +                json={"endpoint": endpoint, "token": token},
 | 
	
		
			
			|  | 630 | +            )
 | 
	
		
			
			|  | 631 | +            req.raise_for_status()
 | 
	
		
			
			|  | 632 | +            data = req.json()
 | 
	
		
			
			|  | 633 | +
 | 
	
		
			
			|  | 634 | +            return cls.from_dict(data)
 | 
	
		
			
			|  | 635 | +
 | 
	
		
			
			|  | 636 | +        except requests.ConnectionError as e:
 | 
	
		
			
			|  | 637 | +            LOG.exception(e)
 | 
	
		
			
			|  | 638 | +            return NetworkError.ConnectionFailure
 | 
	
		
			
			|  | 639 | +
 | 
	
		
			
			|  | 640 | +        except requests.HTTPError as e:
 | 
	
		
			
			|  | 641 | +            LOG.exception(e)
 | 
	
		
			
			|  | 642 | +            return NetworkError.HttpFailure
 | 
	
		
			
			|  | 643 | +
 | 
	
		
			
			|  | 644 | +        except ValueError as e:
 | 
	
		
			
			|  | 645 | +            LOG.exception(e)
 | 
	
		
			
			|  | 646 | +            return NetworkError.InvalidData
 | 
	
		
			
			|  | 647 | +
 | 
	
		
			
			|  | 648 | +    @classmethod
 | 
	
		
			
			|  | 649 | +    def sync(cls, token: str, endpoint: str) -> Union[AardbeiPeopleDiff, NetworkError]:
 | 
	
		
			
			|  | 650 | +        try:
 | 
	
		
			
			|  | 651 | +            req = requests.post(
 | 
	
		
			
			|  | 652 | +                urljoin(SERVER_URL, "/aardbei/sync_people"),
 | 
	
		
			
			|  | 653 | +                json={"endpoint": endpoint, "token": token},
 | 
	
		
			
			|  | 654 | +            )
 | 
	
		
			
			|  | 655 | +            req.raise_for_status()
 | 
	
		
			
			|  | 656 | +            data = req.json()
 | 
	
		
			
			|  | 657 | +
 | 
	
		
			
			|  | 658 | +            return cls.from_dict(data)
 | 
	
		
			
			|  | 659 | +
 | 
	
		
			
			|  | 660 | +        except requests.ConnectionError as e:
 | 
	
		
			
			|  | 661 | +            LOG.exception(e)
 | 
	
		
			
			|  | 662 | +            return NetworkError.ConnectionFailure
 | 
	
		
			
			|  | 663 | +
 | 
	
		
			
			|  | 664 | +        except requests.HTTPError as e:
 | 
	
		
			
			|  | 665 | +            LOG.exception(e)
 | 
	
		
			
			|  | 666 | +            return NetworkError.HttpFailure
 | 
	
		
			
			|  | 667 | +
 | 
	
		
			
			|  | 668 | +        except ValueError as e:
 | 
	
		
			
			|  | 669 | +            LOG.exception(e)
 | 
	
		
			
			|  | 670 | +            return NetworkError.InvalidData
 |