diff sat_frontends/jp/cmd_event.py @ 3040:fee60f17ebac

jp: jp asyncio port: /!\ this commit is huge. Jp is temporarily not working with `dbus` bridge /!\ This patch implements the port of jp to asyncio, so it is now correctly using the bridge asynchronously, and it can be used with bridges like `pb`. This also simplify the code, notably for things which were previously implemented with many callbacks (like pagination with RSM). During the process, some behaviours have been modified/fixed, in jp and backends, check diff for details.
author Goffi <goffi@goffi.org>
date Wed, 25 Sep 2019 08:56:41 +0200
parents ab2696e34d29
children 040ca99e25fe
line wrap: on
line diff
--- a/sat_frontends/jp/cmd_event.py	Wed Sep 25 08:53:38 2019 +0200
+++ b/sat_frontends/jp/cmd_event.py	Wed Sep 25 08:56:41 2019 +0200
@@ -23,7 +23,6 @@
 from sat.tools.common.ansi import ANSI as A
 from sat_frontends.jp.constants import Const as C
 from sat_frontends.jp import common
-from functools import partial
 from dateutil import parser as du_parser
 import calendar
 import time
@@ -47,30 +46,26 @@
             use_verbose=True,
             help=_("get event data"),
         )
-        self.need_loop = True
 
     def add_parser_options(self):
         pass
 
-    def eventInviteeGetCb(self, result):
-        event_date, event_data = result
-        event_data["date"] = event_date
-        self.output(event_data)
-        self.host.quit()
-
-    def start(self):
-        self.host.bridge.eventGet(
-            self.args.service,
-            self.args.node,
-            self.args.item,
-            self.profile,
-            callback=self.eventInviteeGetCb,
-            errback=partial(
-                self.errback,
-                msg=_("can't get event data: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+    async def start(self):
+        try:
+            event_tuple = await self.host.bridge.eventGet(
+                self.args.service,
+                self.args.node,
+                self.args.item,
+                self.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't get event data: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            event_date, event_data = event_tuple
+            event_data["date"] = event_date
+            await self.output(event_data)
+            self.host.quit()
 
 
 class EventBase(object):
@@ -126,29 +121,25 @@
             help=_("create or replace event"),
         )
         EventBase.__init__(self)
-        self.need_loop = True
 
-    def eventCreateCb(self, node):
-        self.disp(_("Event created successfuly on node {node}").format(node=node))
-        self.host.quit()
-
-    def start(self):
+    async def start(self):
         fields = self.parseFields()
         date = self.parseDate()
-        self.host.bridge.eventCreate(
-            date,
-            fields,
-            self.args.service,
-            self.args.node,
-            self.args.id,
-            self.profile,
-            callback=self.eventCreateCb,
-            errback=partial(
-                self.errback,
-                msg=_("can't create event: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+        try:
+            node = await self.host.bridge.eventCreate(
+                date,
+                fields,
+                self.args.service,
+                self.args.node,
+                self.args.id,
+                self.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't create event: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            self.disp(_(f"Event created successfuly on node {node}"))
+            self.host.quit()
 
 
 class Modify(EventBase, base.CommandBase):
@@ -161,25 +152,24 @@
             help=_("modify an existing event"),
         )
         EventBase.__init__(self)
-        self.need_loop = True
 
-    def start(self):
+    async def start(self):
         fields = self.parseFields()
         date = 0 if not self.args.date else self.parseDate()
-        self.host.bridge.eventModify(
-            self.args.service,
-            self.args.node,
-            self.args.id,
-            date,
-            fields,
-            self.profile,
-            callback=self.host.quit,
-            errback=partial(
-                self.errback,
-                msg=_("can't update event data: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+        try:
+            self.host.bridge.eventModify(
+                self.args.service,
+                self.args.node,
+                self.args.id,
+                date,
+                fields,
+                self.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't update event data: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            self.host.quit()
 
 
 class InviteeGet(base.CommandBase):
@@ -190,31 +180,30 @@
             "get",
             use_output=C.OUTPUT_DICT,
             use_pubsub=True,
-            pubsub_flags={C.NODE, C.ITEM, C.SINGLE_ITEM},
+            pubsub_flags={C.NODE},
             use_verbose=True,
             help=_("get event attendance"),
         )
-        self.need_loop = True
 
     def add_parser_options(self):
-        pass
-
-    def eventInviteeGetCb(self, event_data):
-        self.output(event_data)
-        self.host.quit()
+        self.parser.add_argument(
+            "-j", "--jid", default="", help=_("bare jid of the invitee")
+        )
 
-    def start(self):
-        self.host.bridge.eventInviteeGet(
-            self.args.service,
-            self.args.node,
-            self.profile,
-            callback=self.eventInviteeGetCb,
-            errback=partial(
-                self.errback,
-                msg=_("can't get event data: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+    async def start(self):
+        try:
+            event_data = await self.host.bridge.eventInviteeGet(
+                self.args.service,
+                self.args.node,
+                self.args.jid,
+                self.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't get event data: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            await self.output(event_data)
+            self.host.quit()
 
 
 class InviteeSet(base.CommandBase):
@@ -227,7 +216,6 @@
             pubsub_flags={C.NODE},
             help=_("set event attendance"),
         )
-        self.need_loop = True
 
     def add_parser_options(self):
         self.parser.add_argument(
@@ -240,20 +228,20 @@
             help=_("configuration field to set"),
         )
 
-    def start(self):
+    async def start(self):
         fields = dict(self.args.fields) if self.args.fields else {}
-        self.host.bridge.eventInviteeSet(
-            self.args.service,
-            self.args.node,
-            fields,
-            self.profile,
-            callback=self.host.quit,
-            errback=partial(
-                self.errback,
-                msg=_("can't set event data: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+        try:
+            self.host.bridge.eventInviteeSet(
+                self.args.service,
+                self.args.node,
+                fields,
+                self.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't set event data: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            self.host.quit()
 
 
 class InviteesList(base.CommandBase):
@@ -270,7 +258,6 @@
             use_verbose=True,
             help=_("get event attendance"),
         )
-        self.need_loop = True
 
     def add_parser_options(self):
         self.parser.add_argument(
@@ -399,72 +386,68 @@
                 )
             )
 
-    def eventInviteesListCb(self, event_data, prefilled_data):
-        """fill nicknames and keep only requested people
-
-        @param event_data(dict): R.S.V.P. answers
-        @param prefilled_data(dict): prefilled data with all people
-            only filled if --missing is used
-        """
-        if self.args.no_rsvp:
-            for jid_ in event_data:
-                # if there is a jid in event_data
-                # it must be there in prefilled_data too
-                # so no need to check for KeyError
-                del prefilled_data[jid_]
-        else:
-            # we replace empty dicts for existing people with R.S.V.P. data
-            prefilled_data.update(event_data)
-
-        # we get nicknames for everybody, make it easier for organisers
-        for jid_, data in prefilled_data.items():
-            id_data = self.host.bridge.identityGet(jid_, self.profile)
-            data["nick"] = id_data.get("nick", "")
-
-        self.output(prefilled_data)
-        self.host.quit()
-
-    def getList(self, prefilled_data={}):
-        self.host.bridge.eventInviteesList(
-            self.args.service,
-            self.args.node,
-            self.profile,
-            callback=partial(self.eventInviteesListCb, prefilled_data=prefilled_data),
-            errback=partial(
-                self.errback,
-                msg=_("can't get event data: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
-
-    def psNodeAffiliationsGetCb(self, affiliations):
-        # we fill all affiliations with empty data
-        # answered one will be filled in eventInviteesListCb
-        # we only consider people with "publisher" affiliation as invited, creators are not, and members can just observe
-        prefilled = {
-            jid_: {}
-            for jid_, affiliation in affiliations.items()
-            if affiliation in ("publisher",)
-        }
-        self.getList(prefilled)
-
-    def start(self):
+    async def start(self):
         if self.args.no_rsvp and not self.args.missing:
             self.parser.error(_("you need to use --missing if you use --no-rsvp"))
-        if self.args.missing:
-            self.host.bridge.psNodeAffiliationsGet(
+        if not self.args.missing:
+            prefilled = {}
+        else:
+            # we get prefilled data with all people
+            try:
+                affiliations = await self.host.bridge.psNodeAffiliationsGet(
+                    self.args.service,
+                    self.args.node,
+                    self.profile,
+                )
+            except Exception as e:
+                self.disp(f"can't get node affiliations: {e}", error=True)
+                self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+            else:
+                # we fill all affiliations with empty data, answered one will be filled
+                # below. We only consider people with "publisher" affiliation as invited,
+                # creators are not, and members can just observe
+                prefilled = {
+                    jid_: {}
+                    for jid_, affiliation in affiliations.items()
+                    if affiliation in ("publisher",)
+                }
+
+        try:
+            event_data = await self.host.bridge.eventInviteesList(
                 self.args.service,
                 self.args.node,
                 self.profile,
-                callback=self.psNodeAffiliationsGetCb,
-                errback=partial(
-                    self.errback,
-                    msg=_("can't get event data: {}"),
-                    exit_code=C.EXIT_BRIDGE_ERRBACK,
-                ),
             )
+        except Exception as e:
+            self.disp(f"can't get event data: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+
+        # we fill nicknames and keep only requested people
+
+        if self.args.no_rsvp:
+            for jid_ in event_data:
+                # if there is a jid in event_data it must be there in prefilled too
+                # otherwie somebody is not on the invitees list
+                try:
+                    del prefilled[jid_]
+                except KeyError:
+                    self.disp(A.color(
+                        C.A_WARNING,
+                        f"We got a RSVP from somebody who was not in invitees "
+                        f"list: {jid_}"
+                        ),
+                        error=True)
         else:
-            self.getList()
+            # we replace empty dicts for existing people with R.S.V.P. data
+            prefilled.update(event_data)
+
+        # we get nicknames for everybody, make it easier for organisers
+        for jid_, data in prefilled.items():
+            id_data = await self.host.bridge.identityGet(jid_, self.profile)
+            data["nick"] = id_data.get("nick", "")
+
+        await self.output(prefilled)
+        self.host.quit()
 
 
 class InviteeInvite(base.CommandBase):
@@ -477,7 +460,6 @@
             pubsub_flags={C.NODE, C.SINGLE_ITEM},
             help=_("invite someone to the event through email"),
         )
-        self.need_loop = True
 
     def add_parser_options(self):
         self.parser.add_argument(
@@ -524,30 +506,30 @@
             help="body of the invitation email (default: generic body)",
         )
 
-    def start(self):
+    async def start(self):
         email = self.args.email[0] if self.args.email else None
         emails_extra = self.args.email[1:]
 
-        self.host.bridge.eventInviteByEmail(
-            self.args.service,
-            self.args.node,
-            self.args.item,
-            email,
-            emails_extra,
-            self.args.name,
-            self.args.host_name,
-            self.args.lang,
-            self.args.url_template,
-            self.args.subject,
-            self.args.body,
-            self.args.profile,
-            callback=self.host.quit,
-            errback=partial(
-                self.errback,
-                msg=_("can't create invitation: {}"),
-                exit_code=C.EXIT_BRIDGE_ERRBACK,
-            ),
-        )
+        try:
+            await self.host.bridge.eventInviteByEmail(
+                self.args.service,
+                self.args.node,
+                self.args.item,
+                email,
+                emails_extra,
+                self.args.name,
+                self.args.host_name,
+                self.args.lang,
+                self.args.url_template,
+                self.args.subject,
+                self.args.body,
+                self.args.profile,
+            )
+        except Exception as e:
+            self.disp(f"can't create invitation: {e}", error=True)
+            self.host.quit(C.EXIT_BRIDGE_ERRBACK)
+        else:
+            self.host.quit()
 
 
 class Invitee(base.CommandBase):