Mercurial > libervia-backend
comparison 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 |
comparison
equal
deleted
inserted
replaced
3039:a1bc34f90fa5 | 3040:fee60f17ebac |
---|---|
21 from . import base | 21 from . import base |
22 from sat.core.i18n import _ | 22 from sat.core.i18n import _ |
23 from sat.tools.common.ansi import ANSI as A | 23 from sat.tools.common.ansi import ANSI as A |
24 from sat_frontends.jp.constants import Const as C | 24 from sat_frontends.jp.constants import Const as C |
25 from sat_frontends.jp import common | 25 from sat_frontends.jp import common |
26 from functools import partial | |
27 from dateutil import parser as du_parser | 26 from dateutil import parser as du_parser |
28 import calendar | 27 import calendar |
29 import time | 28 import time |
30 | 29 |
31 __commands__ = ["Event"] | 30 __commands__ = ["Event"] |
45 use_pubsub=True, | 44 use_pubsub=True, |
46 pubsub_flags={C.SINGLE_ITEM}, | 45 pubsub_flags={C.SINGLE_ITEM}, |
47 use_verbose=True, | 46 use_verbose=True, |
48 help=_("get event data"), | 47 help=_("get event data"), |
49 ) | 48 ) |
50 self.need_loop = True | |
51 | 49 |
52 def add_parser_options(self): | 50 def add_parser_options(self): |
53 pass | 51 pass |
54 | 52 |
55 def eventInviteeGetCb(self, result): | 53 async def start(self): |
56 event_date, event_data = result | 54 try: |
57 event_data["date"] = event_date | 55 event_tuple = await self.host.bridge.eventGet( |
58 self.output(event_data) | 56 self.args.service, |
59 self.host.quit() | 57 self.args.node, |
60 | 58 self.args.item, |
61 def start(self): | 59 self.profile, |
62 self.host.bridge.eventGet( | 60 ) |
63 self.args.service, | 61 except Exception as e: |
64 self.args.node, | 62 self.disp(f"can't get event data: {e}", error=True) |
65 self.args.item, | 63 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
66 self.profile, | 64 else: |
67 callback=self.eventInviteeGetCb, | 65 event_date, event_data = event_tuple |
68 errback=partial( | 66 event_data["date"] = event_date |
69 self.errback, | 67 await self.output(event_data) |
70 msg=_("can't get event data: {}"), | 68 self.host.quit() |
71 exit_code=C.EXIT_BRIDGE_ERRBACK, | |
72 ), | |
73 ) | |
74 | 69 |
75 | 70 |
76 class EventBase(object): | 71 class EventBase(object): |
77 def add_parser_options(self): | 72 def add_parser_options(self): |
78 self.parser.add_argument( | 73 self.parser.add_argument( |
124 "create", | 119 "create", |
125 use_pubsub=True, | 120 use_pubsub=True, |
126 help=_("create or replace event"), | 121 help=_("create or replace event"), |
127 ) | 122 ) |
128 EventBase.__init__(self) | 123 EventBase.__init__(self) |
129 self.need_loop = True | 124 |
130 | 125 async def start(self): |
131 def eventCreateCb(self, node): | |
132 self.disp(_("Event created successfuly on node {node}").format(node=node)) | |
133 self.host.quit() | |
134 | |
135 def start(self): | |
136 fields = self.parseFields() | 126 fields = self.parseFields() |
137 date = self.parseDate() | 127 date = self.parseDate() |
138 self.host.bridge.eventCreate( | 128 try: |
139 date, | 129 node = await self.host.bridge.eventCreate( |
140 fields, | 130 date, |
141 self.args.service, | 131 fields, |
142 self.args.node, | 132 self.args.service, |
143 self.args.id, | 133 self.args.node, |
144 self.profile, | 134 self.args.id, |
145 callback=self.eventCreateCb, | 135 self.profile, |
146 errback=partial( | 136 ) |
147 self.errback, | 137 except Exception as e: |
148 msg=_("can't create event: {}"), | 138 self.disp(f"can't create event: {e}", error=True) |
149 exit_code=C.EXIT_BRIDGE_ERRBACK, | 139 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
150 ), | 140 else: |
151 ) | 141 self.disp(_(f"Event created successfuly on node {node}")) |
142 self.host.quit() | |
152 | 143 |
153 | 144 |
154 class Modify(EventBase, base.CommandBase): | 145 class Modify(EventBase, base.CommandBase): |
155 def __init__(self, host): | 146 def __init__(self, host): |
156 super(Modify, self).__init__( | 147 super(Modify, self).__init__( |
159 use_pubsub=True, | 150 use_pubsub=True, |
160 pubsub_flags={C.NODE}, | 151 pubsub_flags={C.NODE}, |
161 help=_("modify an existing event"), | 152 help=_("modify an existing event"), |
162 ) | 153 ) |
163 EventBase.__init__(self) | 154 EventBase.__init__(self) |
164 self.need_loop = True | 155 |
165 | 156 async def start(self): |
166 def start(self): | |
167 fields = self.parseFields() | 157 fields = self.parseFields() |
168 date = 0 if not self.args.date else self.parseDate() | 158 date = 0 if not self.args.date else self.parseDate() |
169 self.host.bridge.eventModify( | 159 try: |
170 self.args.service, | 160 self.host.bridge.eventModify( |
171 self.args.node, | 161 self.args.service, |
172 self.args.id, | 162 self.args.node, |
173 date, | 163 self.args.id, |
174 fields, | 164 date, |
175 self.profile, | 165 fields, |
176 callback=self.host.quit, | 166 self.profile, |
177 errback=partial( | 167 ) |
178 self.errback, | 168 except Exception as e: |
179 msg=_("can't update event data: {}"), | 169 self.disp(f"can't update event data: {e}", error=True) |
180 exit_code=C.EXIT_BRIDGE_ERRBACK, | 170 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
181 ), | 171 else: |
182 ) | 172 self.host.quit() |
183 | 173 |
184 | 174 |
185 class InviteeGet(base.CommandBase): | 175 class InviteeGet(base.CommandBase): |
186 def __init__(self, host): | 176 def __init__(self, host): |
187 base.CommandBase.__init__( | 177 base.CommandBase.__init__( |
188 self, | 178 self, |
189 host, | 179 host, |
190 "get", | 180 "get", |
191 use_output=C.OUTPUT_DICT, | 181 use_output=C.OUTPUT_DICT, |
192 use_pubsub=True, | 182 use_pubsub=True, |
193 pubsub_flags={C.NODE, C.ITEM, C.SINGLE_ITEM}, | 183 pubsub_flags={C.NODE}, |
194 use_verbose=True, | 184 use_verbose=True, |
195 help=_("get event attendance"), | 185 help=_("get event attendance"), |
196 ) | 186 ) |
197 self.need_loop = True | |
198 | 187 |
199 def add_parser_options(self): | 188 def add_parser_options(self): |
200 pass | 189 self.parser.add_argument( |
201 | 190 "-j", "--jid", default="", help=_("bare jid of the invitee") |
202 def eventInviteeGetCb(self, event_data): | 191 ) |
203 self.output(event_data) | 192 |
204 self.host.quit() | 193 async def start(self): |
205 | 194 try: |
206 def start(self): | 195 event_data = await self.host.bridge.eventInviteeGet( |
207 self.host.bridge.eventInviteeGet( | 196 self.args.service, |
208 self.args.service, | 197 self.args.node, |
209 self.args.node, | 198 self.args.jid, |
210 self.profile, | 199 self.profile, |
211 callback=self.eventInviteeGetCb, | 200 ) |
212 errback=partial( | 201 except Exception as e: |
213 self.errback, | 202 self.disp(f"can't get event data: {e}", error=True) |
214 msg=_("can't get event data: {}"), | 203 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
215 exit_code=C.EXIT_BRIDGE_ERRBACK, | 204 else: |
216 ), | 205 await self.output(event_data) |
217 ) | 206 self.host.quit() |
218 | 207 |
219 | 208 |
220 class InviteeSet(base.CommandBase): | 209 class InviteeSet(base.CommandBase): |
221 def __init__(self, host): | 210 def __init__(self, host): |
222 super(InviteeSet, self).__init__( | 211 super(InviteeSet, self).__init__( |
225 use_output=C.OUTPUT_DICT, | 214 use_output=C.OUTPUT_DICT, |
226 use_pubsub=True, | 215 use_pubsub=True, |
227 pubsub_flags={C.NODE}, | 216 pubsub_flags={C.NODE}, |
228 help=_("set event attendance"), | 217 help=_("set event attendance"), |
229 ) | 218 ) |
230 self.need_loop = True | |
231 | 219 |
232 def add_parser_options(self): | 220 def add_parser_options(self): |
233 self.parser.add_argument( | 221 self.parser.add_argument( |
234 "-f", | 222 "-f", |
235 "--field", | 223 "--field", |
238 dest="fields", | 226 dest="fields", |
239 metavar=("KEY", "VALUE"), | 227 metavar=("KEY", "VALUE"), |
240 help=_("configuration field to set"), | 228 help=_("configuration field to set"), |
241 ) | 229 ) |
242 | 230 |
243 def start(self): | 231 async def start(self): |
244 fields = dict(self.args.fields) if self.args.fields else {} | 232 fields = dict(self.args.fields) if self.args.fields else {} |
245 self.host.bridge.eventInviteeSet( | 233 try: |
246 self.args.service, | 234 self.host.bridge.eventInviteeSet( |
247 self.args.node, | 235 self.args.service, |
248 fields, | 236 self.args.node, |
249 self.profile, | 237 fields, |
250 callback=self.host.quit, | 238 self.profile, |
251 errback=partial( | 239 ) |
252 self.errback, | 240 except Exception as e: |
253 msg=_("can't set event data: {}"), | 241 self.disp(f"can't set event data: {e}", error=True) |
254 exit_code=C.EXIT_BRIDGE_ERRBACK, | 242 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
255 ), | 243 else: |
256 ) | 244 self.host.quit() |
257 | 245 |
258 | 246 |
259 class InviteesList(base.CommandBase): | 247 class InviteesList(base.CommandBase): |
260 def __init__(self, host): | 248 def __init__(self, host): |
261 extra_outputs = {"default": self.default_output} | 249 extra_outputs = {"default": self.default_output} |
268 use_pubsub=True, | 256 use_pubsub=True, |
269 pubsub_flags={C.NODE}, | 257 pubsub_flags={C.NODE}, |
270 use_verbose=True, | 258 use_verbose=True, |
271 help=_("get event attendance"), | 259 help=_("get event attendance"), |
272 ) | 260 ) |
273 self.need_loop = True | |
274 | 261 |
275 def add_parser_options(self): | 262 def add_parser_options(self): |
276 self.parser.add_argument( | 263 self.parser.add_argument( |
277 "-m", | 264 "-m", |
278 "--missing", | 265 "--missing", |
397 A.RESET, | 384 A.RESET, |
398 str(attendees_missing), | 385 str(attendees_missing), |
399 ) | 386 ) |
400 ) | 387 ) |
401 | 388 |
402 def eventInviteesListCb(self, event_data, prefilled_data): | 389 async def start(self): |
403 """fill nicknames and keep only requested people | 390 if self.args.no_rsvp and not self.args.missing: |
404 | 391 self.parser.error(_("you need to use --missing if you use --no-rsvp")) |
405 @param event_data(dict): R.S.V.P. answers | 392 if not self.args.missing: |
406 @param prefilled_data(dict): prefilled data with all people | 393 prefilled = {} |
407 only filled if --missing is used | 394 else: |
408 """ | 395 # we get prefilled data with all people |
396 try: | |
397 affiliations = await self.host.bridge.psNodeAffiliationsGet( | |
398 self.args.service, | |
399 self.args.node, | |
400 self.profile, | |
401 ) | |
402 except Exception as e: | |
403 self.disp(f"can't get node affiliations: {e}", error=True) | |
404 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
405 else: | |
406 # we fill all affiliations with empty data, answered one will be filled | |
407 # below. We only consider people with "publisher" affiliation as invited, | |
408 # creators are not, and members can just observe | |
409 prefilled = { | |
410 jid_: {} | |
411 for jid_, affiliation in affiliations.items() | |
412 if affiliation in ("publisher",) | |
413 } | |
414 | |
415 try: | |
416 event_data = await self.host.bridge.eventInviteesList( | |
417 self.args.service, | |
418 self.args.node, | |
419 self.profile, | |
420 ) | |
421 except Exception as e: | |
422 self.disp(f"can't get event data: {e}", error=True) | |
423 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
424 | |
425 # we fill nicknames and keep only requested people | |
426 | |
409 if self.args.no_rsvp: | 427 if self.args.no_rsvp: |
410 for jid_ in event_data: | 428 for jid_ in event_data: |
411 # if there is a jid in event_data | 429 # if there is a jid in event_data it must be there in prefilled too |
412 # it must be there in prefilled_data too | 430 # otherwie somebody is not on the invitees list |
413 # so no need to check for KeyError | 431 try: |
414 del prefilled_data[jid_] | 432 del prefilled[jid_] |
433 except KeyError: | |
434 self.disp(A.color( | |
435 C.A_WARNING, | |
436 f"We got a RSVP from somebody who was not in invitees " | |
437 f"list: {jid_}" | |
438 ), | |
439 error=True) | |
415 else: | 440 else: |
416 # we replace empty dicts for existing people with R.S.V.P. data | 441 # we replace empty dicts for existing people with R.S.V.P. data |
417 prefilled_data.update(event_data) | 442 prefilled.update(event_data) |
418 | 443 |
419 # we get nicknames for everybody, make it easier for organisers | 444 # we get nicknames for everybody, make it easier for organisers |
420 for jid_, data in prefilled_data.items(): | 445 for jid_, data in prefilled.items(): |
421 id_data = self.host.bridge.identityGet(jid_, self.profile) | 446 id_data = await self.host.bridge.identityGet(jid_, self.profile) |
422 data["nick"] = id_data.get("nick", "") | 447 data["nick"] = id_data.get("nick", "") |
423 | 448 |
424 self.output(prefilled_data) | 449 await self.output(prefilled) |
425 self.host.quit() | 450 self.host.quit() |
426 | |
427 def getList(self, prefilled_data={}): | |
428 self.host.bridge.eventInviteesList( | |
429 self.args.service, | |
430 self.args.node, | |
431 self.profile, | |
432 callback=partial(self.eventInviteesListCb, prefilled_data=prefilled_data), | |
433 errback=partial( | |
434 self.errback, | |
435 msg=_("can't get event data: {}"), | |
436 exit_code=C.EXIT_BRIDGE_ERRBACK, | |
437 ), | |
438 ) | |
439 | |
440 def psNodeAffiliationsGetCb(self, affiliations): | |
441 # we fill all affiliations with empty data | |
442 # answered one will be filled in eventInviteesListCb | |
443 # we only consider people with "publisher" affiliation as invited, creators are not, and members can just observe | |
444 prefilled = { | |
445 jid_: {} | |
446 for jid_, affiliation in affiliations.items() | |
447 if affiliation in ("publisher",) | |
448 } | |
449 self.getList(prefilled) | |
450 | |
451 def start(self): | |
452 if self.args.no_rsvp and not self.args.missing: | |
453 self.parser.error(_("you need to use --missing if you use --no-rsvp")) | |
454 if self.args.missing: | |
455 self.host.bridge.psNodeAffiliationsGet( | |
456 self.args.service, | |
457 self.args.node, | |
458 self.profile, | |
459 callback=self.psNodeAffiliationsGetCb, | |
460 errback=partial( | |
461 self.errback, | |
462 msg=_("can't get event data: {}"), | |
463 exit_code=C.EXIT_BRIDGE_ERRBACK, | |
464 ), | |
465 ) | |
466 else: | |
467 self.getList() | |
468 | 451 |
469 | 452 |
470 class InviteeInvite(base.CommandBase): | 453 class InviteeInvite(base.CommandBase): |
471 def __init__(self, host): | 454 def __init__(self, host): |
472 base.CommandBase.__init__( | 455 base.CommandBase.__init__( |
475 "invite", | 458 "invite", |
476 use_pubsub=True, | 459 use_pubsub=True, |
477 pubsub_flags={C.NODE, C.SINGLE_ITEM}, | 460 pubsub_flags={C.NODE, C.SINGLE_ITEM}, |
478 help=_("invite someone to the event through email"), | 461 help=_("invite someone to the event through email"), |
479 ) | 462 ) |
480 self.need_loop = True | |
481 | 463 |
482 def add_parser_options(self): | 464 def add_parser_options(self): |
483 self.parser.add_argument( | 465 self.parser.add_argument( |
484 "-e", | 466 "-e", |
485 "--email", | 467 "--email", |
522 "--body", | 504 "--body", |
523 default="", | 505 default="", |
524 help="body of the invitation email (default: generic body)", | 506 help="body of the invitation email (default: generic body)", |
525 ) | 507 ) |
526 | 508 |
527 def start(self): | 509 async def start(self): |
528 email = self.args.email[0] if self.args.email else None | 510 email = self.args.email[0] if self.args.email else None |
529 emails_extra = self.args.email[1:] | 511 emails_extra = self.args.email[1:] |
530 | 512 |
531 self.host.bridge.eventInviteByEmail( | 513 try: |
532 self.args.service, | 514 await self.host.bridge.eventInviteByEmail( |
533 self.args.node, | 515 self.args.service, |
534 self.args.item, | 516 self.args.node, |
535 email, | 517 self.args.item, |
536 emails_extra, | 518 email, |
537 self.args.name, | 519 emails_extra, |
538 self.args.host_name, | 520 self.args.name, |
539 self.args.lang, | 521 self.args.host_name, |
540 self.args.url_template, | 522 self.args.lang, |
541 self.args.subject, | 523 self.args.url_template, |
542 self.args.body, | 524 self.args.subject, |
543 self.args.profile, | 525 self.args.body, |
544 callback=self.host.quit, | 526 self.args.profile, |
545 errback=partial( | 527 ) |
546 self.errback, | 528 except Exception as e: |
547 msg=_("can't create invitation: {}"), | 529 self.disp(f"can't create invitation: {e}", error=True) |
548 exit_code=C.EXIT_BRIDGE_ERRBACK, | 530 self.host.quit(C.EXIT_BRIDGE_ERRBACK) |
549 ), | 531 else: |
550 ) | 532 self.host.quit() |
551 | 533 |
552 | 534 |
553 class Invitee(base.CommandBase): | 535 class Invitee(base.CommandBase): |
554 subcommands = (InviteeGet, InviteeSet, InviteesList, InviteeInvite) | 536 subcommands = (InviteeGet, InviteeSet, InviteesList, InviteeInvite) |
555 | 537 |