瀏覽代碼

Add activities command to cli

Maarten van den Berg 5 年之前
父節點
當前提交
de55db1c8b
共有 2 個文件被更改,包括 231 次插入0 次删除
  1. 109 0
      piket_client/cli.py
  2. 122 0
      piket_client/model.py

+ 109 - 0
piket_client/cli.py

@@ -1,8 +1,10 @@
1 1
 import click
2 2
 from piket_client.model import (
3
+    AardbeiActivity,
3 4
     ServerStatus,
4 5
     NetworkError,
5 6
     Consumption,
7
+    AardbeiPeopleDiff,
6 8
     Person,
7 9
     Settlement,
8 10
     ConsumptionType,
@@ -217,5 +219,112 @@ def print_error(msg: str) -> None:
217 219
     click.echo(click.style(msg, fg="red", bold=True), err=True)
218 220
 
219 221
 
222
+@cli.group()
223
+@click.option("--token", required=True)
224
+@click.option("--endpoint", default="http://localhost:3000")
225
+@click.pass_context
226
+def aardbei(ctx, token: str, endpoint: str) -> None:
227
+    ctx.ensure_object(dict)
228
+    ctx.obj["AardbeiToken"] = token
229
+    ctx.obj["AardbeiEndpoint"] = endpoint
230
+
231
+
232
+@aardbei.group("activities")
233
+def aardbei_activities() -> None:
234
+    pass
235
+
236
+
237
+@aardbei_activities.command("list")
238
+@click.pass_context
239
+def aardbei_list_activities(ctx) -> None:
240
+    acts = AardbeiActivity.get_available(
241
+        token=ctx.obj["AardbeiToken"], endpoint=ctx.obj["AardbeiEndpoint"]
242
+    )
243
+
244
+    if isinstance(acts, NetworkError):
245
+        print_error(f"Could not get activities: {acts.value}")
246
+        return
247
+
248
+    table = PrettyTable()
249
+    table.field_names = ["ID", "Name"]
250
+    table.align = "l"
251
+
252
+    for a in acts:
253
+        table.add_row([a.aardbei_id, a.name])
254
+
255
+    print(table)
256
+
257
+
258
+@aardbei_activities.command("apply")
259
+@click.argument("activity_id", type=click.INT)
260
+@click.pass_context
261
+def aardbei_apply_activity(ctx, activity_id: int) -> None:
262
+    result = AardbeiActivity.apply_activity(
263
+        token=ctx.obj["AardbeiToken"],
264
+        endpoint=ctx.obj["AardbeiEndpoint"],
265
+        activity_id=activity_id,
266
+    )
267
+
268
+    if isinstance(result, NetworkError):
269
+        print_error("Failed to apply activity: {result.value}")
270
+        return
271
+
272
+    print_ok(f"Activity applied. There are now {result} active people.")
273
+
274
+
275
+@aardbei.group("people")
276
+def aardbei_people() -> None:
277
+    pass
278
+
279
+
280
+@aardbei_people.command("diff")
281
+@click.pass_context
282
+def aardbei_diff_people(ctx) -> None:
283
+    diff = AardbeiPeopleDiff.get_diff(
284
+        token=ctx.obj["AardbeiToken"], endpoint=ctx.obj["AardbeiEndpoint"]
285
+    )
286
+
287
+    if isinstance(diff, NetworkError):
288
+        print_error(f"Could not get differences: {diff.value}")
289
+        return
290
+
291
+    if diff.num_changes == 0:
292
+        print_ok("There are no changes to apply.")
293
+        return
294
+
295
+    click.echo(f"There are {diff.num_changes} pending changes:")
296
+    show_diff(diff)
297
+
298
+
299
+@aardbei_people.command("sync")
300
+@click.pass_context
301
+def aardbei_sync_people(ctx) -> None:
302
+    diff = AardbeiPeopleDiff.sync(
303
+        token=ctx.obj["AardbeiToken"], endpoint=ctx.obj["AardbeiEndpoint"]
304
+    )
305
+
306
+    if isinstance(diff, NetworkError):
307
+        print_error(f"Could not apply differences: {diff.value}")
308
+        return
309
+
310
+    if diff.num_changes == 0:
311
+        print_ok("There were no changes to apply.")
312
+        return
313
+
314
+    print_ok(f"Applied {diff.num_changes} pending changes:")
315
+    show_diff(diff)
316
+
317
+
318
+def show_diff(diff: AardbeiPeopleDiff) -> None:
319
+    for name in diff.new_people:
320
+        click.echo(f" - Create local Person for {name}")
321
+
322
+    for name in diff.link_existing:
323
+        click.echo(f" - Link local and remote people for {name}")
324
+
325
+    for name in diff.altered_name:
326
+        click.echo(f" - Process name change for {name}")
327
+
328
+
220 329
 if __name__ == "__main__":
221 330
     cli()

+ 122 - 0
piket_client/model.py

@@ -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