Mercurial > libervia-backend
comparison libervia/cli/cmd_event.py @ 4075:47401850dec6
refactoring: rename `libervia.frontends.jp` to `libervia.cli`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 14:54:26 +0200 |
parents | libervia/frontends/jp/cmd_event.py@26b7ed2817da |
children | e9d800b105c1 |
comparison
equal
deleted
inserted
replaced
4074:26b7ed2817da | 4075:47401850dec6 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 | |
4 # libervia-cli: Libervia CLI frontend | |
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 import argparse | |
22 import sys | |
23 | |
24 from sqlalchemy import desc | |
25 | |
26 from libervia.backend.core.i18n import _ | |
27 from libervia.backend.core.i18n import _ | |
28 from libervia.backend.tools.common import data_format | |
29 from libervia.backend.tools.common import data_format | |
30 from libervia.backend.tools.common import date_utils | |
31 from libervia.backend.tools.common.ansi import ANSI as A | |
32 from libervia.backend.tools.common.ansi import ANSI as A | |
33 from libervia.cli import common | |
34 from libervia.cli.constants import Const as C | |
35 from libervia.cli.constants import Const as C | |
36 | |
37 from . import base | |
38 | |
39 __commands__ = ["Event"] | |
40 | |
41 OUTPUT_OPT_TABLE = "table" | |
42 | |
43 | |
44 class Get(base.CommandBase): | |
45 def __init__(self, host): | |
46 base.CommandBase.__init__( | |
47 self, | |
48 host, | |
49 "get", | |
50 use_output=C.OUTPUT_LIST_DICT, | |
51 use_pubsub=True, | |
52 pubsub_flags={C.MULTI_ITEMS, C.CACHE}, | |
53 use_verbose=True, | |
54 extra_outputs={ | |
55 "default": self.default_output, | |
56 }, | |
57 help=_("get event(s) data"), | |
58 ) | |
59 | |
60 def add_parser_options(self): | |
61 pass | |
62 | |
63 async def start(self): | |
64 try: | |
65 events_data_s = await self.host.bridge.events_get( | |
66 self.args.service, | |
67 self.args.node, | |
68 self.args.items, | |
69 self.get_pubsub_extra(), | |
70 self.profile, | |
71 ) | |
72 except Exception as e: | |
73 self.disp(f"can't get events data: {e}", error=True) | |
74 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
75 else: | |
76 events_data = data_format.deserialise(events_data_s, type_check=list) | |
77 await self.output(events_data) | |
78 self.host.quit() | |
79 | |
80 def default_output(self, events): | |
81 nb_events = len(events) | |
82 for idx, event in enumerate(events): | |
83 names = event["name"] | |
84 name = names.get("") or next(iter(names.values())) | |
85 start = event["start"] | |
86 start_human = date_utils.date_fmt( | |
87 start, "medium", tz_info=date_utils.TZ_LOCAL | |
88 ) | |
89 end = event["end"] | |
90 self.disp(A.color( | |
91 A.BOLD, start_human, A.RESET, " ", | |
92 f"({date_utils.delta2human(start, end)}) ", | |
93 C.A_HEADER, name | |
94 )) | |
95 if self.verbosity > 0: | |
96 descriptions = event.get("descriptions", []) | |
97 if descriptions: | |
98 self.disp(descriptions[0]["description"]) | |
99 if idx < (nb_events-1): | |
100 self.disp("") | |
101 | |
102 | |
103 class CategoryAction(argparse.Action): | |
104 | |
105 def __init__(self, option_strings, dest, nargs=None, metavar=None, **kwargs): | |
106 if nargs is not None or metavar is not None: | |
107 raise ValueError("nargs and metavar must not be used") | |
108 if metavar is not None: | |
109 metavar="TERM WIKIDATA_ID LANG" | |
110 if "--help" in sys.argv: | |
111 # FIXME: dirty workaround to have correct --help message | |
112 # argparse doesn't normally allow variable number of arguments beside "+" | |
113 # and "*", this workaround show METAVAR as 3 arguments were expected, while | |
114 # we can actuall use 1, 2 or 3. | |
115 nargs = 3 | |
116 metavar = ("TERM", "[WIKIDATA_ID]", "[LANG]") | |
117 else: | |
118 nargs = "+" | |
119 | |
120 super().__init__(option_strings, dest, metavar=metavar, nargs=nargs, **kwargs) | |
121 | |
122 def __call__(self, parser, namespace, values, option_string=None): | |
123 categories = getattr(namespace, self.dest) | |
124 if categories is None: | |
125 categories = [] | |
126 setattr(namespace, self.dest, categories) | |
127 | |
128 if not values: | |
129 parser.error("category values must be set") | |
130 | |
131 category = { | |
132 "term": values[0] | |
133 } | |
134 | |
135 if len(values) == 1: | |
136 pass | |
137 elif len(values) == 2: | |
138 value = values[1] | |
139 if value.startswith("Q"): | |
140 category["wikidata_id"] = value | |
141 else: | |
142 category["language"] = value | |
143 elif len(values) == 3: | |
144 __, wd, lang = values | |
145 category["wikidata_id"] = wd | |
146 category["language"] = lang | |
147 else: | |
148 parser.error("Category can't have more than 3 arguments") | |
149 | |
150 categories.append(category) | |
151 | |
152 | |
153 class EventBase: | |
154 def add_parser_options(self): | |
155 self.parser.add_argument( | |
156 "-S", "--start", type=base.date_decoder, metavar="TIME_PATTERN", | |
157 help=_("the start time of the event")) | |
158 end_group = self.parser.add_mutually_exclusive_group() | |
159 end_group.add_argument( | |
160 "-E", "--end", type=base.date_decoder, metavar="TIME_PATTERN", | |
161 help=_("the time of the end of the event")) | |
162 end_group.add_argument( | |
163 "-D", "--duration", help=_("duration of the event")) | |
164 self.parser.add_argument( | |
165 "-H", "--head-picture", help="URL to a picture to use as head-picture" | |
166 ) | |
167 self.parser.add_argument( | |
168 "-d", "--description", help="plain text description the event" | |
169 ) | |
170 self.parser.add_argument( | |
171 "-C", "--category", action=CategoryAction, dest="categories", | |
172 help="Category of the event" | |
173 ) | |
174 self.parser.add_argument( | |
175 "-l", "--location", action="append", nargs="+", metavar="[KEY] VALUE", | |
176 help="Location metadata" | |
177 ) | |
178 rsvp_group = self.parser.add_mutually_exclusive_group() | |
179 rsvp_group.add_argument( | |
180 "--rsvp", action="store_true", help=_("RSVP is requested")) | |
181 rsvp_group.add_argument( | |
182 "--rsvp_json", metavar="JSON", help=_("JSON description of RSVP form")) | |
183 for node_type in ("invitees", "comments", "blog", "schedule"): | |
184 self.parser.add_argument( | |
185 f"--{node_type}", | |
186 nargs=2, | |
187 metavar=("JID", "NODE"), | |
188 help=_("link {node_type} pubsub node").format(node_type=node_type) | |
189 ) | |
190 self.parser.add_argument( | |
191 "-a", "--attachment", action="append", dest="attachments", | |
192 help=_("attach a file") | |
193 ) | |
194 self.parser.add_argument("--website", help=_("website of the event")) | |
195 self.parser.add_argument( | |
196 "--status", choices=["confirmed", "tentative", "cancelled"], | |
197 help=_("status of the event") | |
198 ) | |
199 self.parser.add_argument( | |
200 "-T", "--language", metavar="LANG", action="append", dest="languages", | |
201 help=_("main languages spoken at the event") | |
202 ) | |
203 self.parser.add_argument( | |
204 "--wheelchair", choices=["full", "partial", "no"], | |
205 help=_("is the location accessible by wheelchair") | |
206 ) | |
207 self.parser.add_argument( | |
208 "--external", | |
209 nargs=3, | |
210 metavar=("JID", "NODE", "ITEM"), | |
211 help=_("link to an external event") | |
212 ) | |
213 | |
214 def get_event_data(self): | |
215 if self.args.duration is not None: | |
216 if self.args.start is None: | |
217 self.parser.error("--start must be send if --duration is used") | |
218 # if duration is used, we simply add it to start time to get end time | |
219 self.args.end = base.date_decoder(f"{self.args.start} + {self.args.duration}") | |
220 | |
221 event = {} | |
222 if self.args.name is not None: | |
223 event["name"] = {"": self.args.name} | |
224 | |
225 if self.args.start is not None: | |
226 event["start"] = self.args.start | |
227 | |
228 if self.args.end is not None: | |
229 event["end"] = self.args.end | |
230 | |
231 if self.args.head_picture: | |
232 event["head-picture"] = { | |
233 "sources": [{ | |
234 "url": self.args.head_picture | |
235 }] | |
236 } | |
237 if self.args.description: | |
238 event["descriptions"] = [ | |
239 { | |
240 "type": "text", | |
241 "description": self.args.description | |
242 } | |
243 ] | |
244 if self.args.categories: | |
245 event["categories"] = self.args.categories | |
246 if self.args.location is not None: | |
247 location = {} | |
248 for location_data in self.args.location: | |
249 if len(location_data) == 1: | |
250 location["description"] = location_data[0] | |
251 else: | |
252 key, *values = location_data | |
253 location[key] = " ".join(values) | |
254 event["locations"] = [location] | |
255 | |
256 if self.args.rsvp: | |
257 event["rsvp"] = [{}] | |
258 elif self.args.rsvp_json: | |
259 if isinstance(self.args.rsvp_elt, dict): | |
260 event["rsvp"] = [self.args.rsvp_json] | |
261 else: | |
262 event["rsvp"] = self.args.rsvp_json | |
263 | |
264 for node_type in ("invitees", "comments", "blog", "schedule"): | |
265 value = getattr(self.args, node_type) | |
266 if value: | |
267 service, node = value | |
268 event[node_type] = {"service": service, "node": node} | |
269 | |
270 if self.args.attachments: | |
271 attachments = event["attachments"] = [] | |
272 for attachment in self.args.attachments: | |
273 attachments.append({ | |
274 "sources": [{"url": attachment}] | |
275 }) | |
276 | |
277 extra = {} | |
278 | |
279 for arg in ("website", "status", "languages"): | |
280 value = getattr(self.args, arg) | |
281 if value is not None: | |
282 extra[arg] = value | |
283 if self.args.wheelchair is not None: | |
284 extra["accessibility"] = {"wheelchair": self.args.wheelchair} | |
285 | |
286 if extra: | |
287 event["extra"] = extra | |
288 | |
289 if self.args.external: | |
290 ext_jid, ext_node, ext_item = self.args.external | |
291 event["external"] = { | |
292 "jid": ext_jid, | |
293 "node": ext_node, | |
294 "item": ext_item | |
295 } | |
296 return event | |
297 | |
298 | |
299 class Create(EventBase, base.CommandBase): | |
300 def __init__(self, host): | |
301 super().__init__( | |
302 host, | |
303 "create", | |
304 use_pubsub=True, | |
305 help=_("create or replace event"), | |
306 ) | |
307 | |
308 def add_parser_options(self): | |
309 super().add_parser_options() | |
310 self.parser.add_argument( | |
311 "-i", | |
312 "--id", | |
313 default="", | |
314 help=_("ID of the PubSub Item"), | |
315 ) | |
316 # name is mandatory here | |
317 self.parser.add_argument("name", help=_("name of the event")) | |
318 | |
319 async def start(self): | |
320 if self.args.start is None: | |
321 self.parser.error("--start must be set") | |
322 event_data = self.get_event_data() | |
323 # we check self.args.end after get_event_data because it may be set there id | |
324 # --duration is used | |
325 if self.args.end is None: | |
326 self.parser.error("--end or --duration must be set") | |
327 try: | |
328 await self.host.bridge.event_create( | |
329 data_format.serialise(event_data), | |
330 self.args.id, | |
331 self.args.node, | |
332 self.args.service, | |
333 self.profile, | |
334 ) | |
335 except Exception as e: | |
336 self.disp(f"can't create event: {e}", error=True) | |
337 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
338 else: | |
339 self.disp(_("Event created successfuly)")) | |
340 self.host.quit() | |
341 | |
342 | |
343 class Modify(EventBase, base.CommandBase): | |
344 def __init__(self, host): | |
345 super(Modify, self).__init__( | |
346 host, | |
347 "modify", | |
348 use_pubsub=True, | |
349 pubsub_flags={C.SINGLE_ITEM}, | |
350 help=_("modify an existing event"), | |
351 ) | |
352 EventBase.__init__(self) | |
353 | |
354 def add_parser_options(self): | |
355 super().add_parser_options() | |
356 # name is optional here | |
357 self.parser.add_argument("-N", "--name", help=_("name of the event")) | |
358 | |
359 async def start(self): | |
360 event_data = self.get_event_data() | |
361 try: | |
362 await self.host.bridge.event_modify( | |
363 data_format.serialise(event_data), | |
364 self.args.item, | |
365 self.args.service, | |
366 self.args.node, | |
367 self.profile, | |
368 ) | |
369 except Exception as e: | |
370 self.disp(f"can't update event data: {e}", error=True) | |
371 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
372 else: | |
373 self.host.quit() | |
374 | |
375 | |
376 class InviteeGet(base.CommandBase): | |
377 def __init__(self, host): | |
378 base.CommandBase.__init__( | |
379 self, | |
380 host, | |
381 "get", | |
382 use_output=C.OUTPUT_DICT, | |
383 use_pubsub=True, | |
384 pubsub_flags={C.SINGLE_ITEM}, | |
385 use_verbose=True, | |
386 help=_("get event attendance"), | |
387 ) | |
388 | |
389 def add_parser_options(self): | |
390 self.parser.add_argument( | |
391 "-j", "--jid", action="append", dest="jids", default=[], | |
392 help=_("only retrieve RSVP from those JIDs") | |
393 ) | |
394 | |
395 async def start(self): | |
396 try: | |
397 event_data_s = await self.host.bridge.event_invitee_get( | |
398 self.args.service, | |
399 self.args.node, | |
400 self.args.item, | |
401 self.args.jids, | |
402 "", | |
403 self.profile, | |
404 ) | |
405 except Exception as e: | |
406 self.disp(f"can't get event data: {e}", error=True) | |
407 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
408 else: | |
409 event_data = data_format.deserialise(event_data_s) | |
410 await self.output(event_data) | |
411 self.host.quit() | |
412 | |
413 | |
414 class InviteeSet(base.CommandBase): | |
415 def __init__(self, host): | |
416 super(InviteeSet, self).__init__( | |
417 host, | |
418 "set", | |
419 use_pubsub=True, | |
420 pubsub_flags={C.SINGLE_ITEM}, | |
421 help=_("set event attendance"), | |
422 ) | |
423 | |
424 def add_parser_options(self): | |
425 self.parser.add_argument( | |
426 "-f", | |
427 "--field", | |
428 action="append", | |
429 nargs=2, | |
430 dest="fields", | |
431 metavar=("KEY", "VALUE"), | |
432 help=_("configuration field to set"), | |
433 ) | |
434 | |
435 async def start(self): | |
436 # TODO: handle RSVP with XMLUI in a similar way as for `ad-hoc run` | |
437 fields = dict(self.args.fields) if self.args.fields else {} | |
438 try: | |
439 self.host.bridge.event_invitee_set( | |
440 self.args.service, | |
441 self.args.node, | |
442 self.args.item, | |
443 data_format.serialise(fields), | |
444 self.profile, | |
445 ) | |
446 except Exception as e: | |
447 self.disp(f"can't set event data: {e}", error=True) | |
448 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
449 else: | |
450 self.host.quit() | |
451 | |
452 | |
453 class InviteesList(base.CommandBase): | |
454 def __init__(self, host): | |
455 extra_outputs = {"default": self.default_output} | |
456 base.CommandBase.__init__( | |
457 self, | |
458 host, | |
459 "list", | |
460 use_output=C.OUTPUT_DICT_DICT, | |
461 extra_outputs=extra_outputs, | |
462 use_pubsub=True, | |
463 pubsub_flags={C.NODE}, | |
464 use_verbose=True, | |
465 help=_("get event attendance"), | |
466 ) | |
467 | |
468 def add_parser_options(self): | |
469 self.parser.add_argument( | |
470 "-m", | |
471 "--missing", | |
472 action="store_true", | |
473 help=_("show missing people (invited but no R.S.V.P. so far)"), | |
474 ) | |
475 self.parser.add_argument( | |
476 "-R", | |
477 "--no-rsvp", | |
478 action="store_true", | |
479 help=_("don't show people which gave R.S.V.P."), | |
480 ) | |
481 | |
482 def _attend_filter(self, attend, row): | |
483 if attend == "yes": | |
484 attend_color = C.A_SUCCESS | |
485 elif attend == "no": | |
486 attend_color = C.A_FAILURE | |
487 else: | |
488 attend_color = A.FG_WHITE | |
489 return A.color(attend_color, attend) | |
490 | |
491 def _guests_filter(self, guests): | |
492 return "(" + str(guests) + ")" if guests else "" | |
493 | |
494 def default_output(self, event_data): | |
495 data = [] | |
496 attendees_yes = 0 | |
497 attendees_maybe = 0 | |
498 attendees_no = 0 | |
499 attendees_missing = 0 | |
500 guests = 0 | |
501 guests_maybe = 0 | |
502 for jid_, jid_data in event_data.items(): | |
503 jid_data["jid"] = jid_ | |
504 try: | |
505 guests_int = int(jid_data["guests"]) | |
506 except (ValueError, KeyError): | |
507 pass | |
508 attend = jid_data.get("attend", "") | |
509 if attend == "yes": | |
510 attendees_yes += 1 | |
511 guests += guests_int | |
512 elif attend == "maybe": | |
513 attendees_maybe += 1 | |
514 guests_maybe += guests_int | |
515 elif attend == "no": | |
516 attendees_no += 1 | |
517 jid_data["guests"] = "" | |
518 else: | |
519 attendees_missing += 1 | |
520 jid_data["guests"] = "" | |
521 data.append(jid_data) | |
522 | |
523 show_table = OUTPUT_OPT_TABLE in self.args.output_opts | |
524 | |
525 table = common.Table.from_list_dict( | |
526 self.host, | |
527 data, | |
528 ("nick",) + (("jid",) if self.host.verbosity else ()) + ("attend", "guests"), | |
529 headers=None, | |
530 filters={ | |
531 "nick": A.color(C.A_HEADER, "{}" if show_table else "{} "), | |
532 "jid": "{}" if show_table else "{} ", | |
533 "attend": self._attend_filter, | |
534 "guests": "{}" if show_table else self._guests_filter, | |
535 }, | |
536 defaults={"nick": "", "attend": "", "guests": 1}, | |
537 ) | |
538 if show_table: | |
539 table.display() | |
540 else: | |
541 table.display_blank(show_header=False, col_sep="") | |
542 | |
543 if not self.args.no_rsvp: | |
544 self.disp("") | |
545 self.disp( | |
546 A.color( | |
547 C.A_SUBHEADER, | |
548 _("Attendees: "), | |
549 A.RESET, | |
550 str(len(data)), | |
551 _(" ("), | |
552 C.A_SUCCESS, | |
553 _("yes: "), | |
554 str(attendees_yes), | |
555 A.FG_WHITE, | |
556 _(", maybe: "), | |
557 str(attendees_maybe), | |
558 ", ", | |
559 C.A_FAILURE, | |
560 _("no: "), | |
561 str(attendees_no), | |
562 A.RESET, | |
563 ")", | |
564 ) | |
565 ) | |
566 self.disp( | |
567 A.color(C.A_SUBHEADER, _("confirmed guests: "), A.RESET, str(guests)) | |
568 ) | |
569 self.disp( | |
570 A.color( | |
571 C.A_SUBHEADER, | |
572 _("unconfirmed guests: "), | |
573 A.RESET, | |
574 str(guests_maybe), | |
575 ) | |
576 ) | |
577 self.disp( | |
578 A.color(C.A_SUBHEADER, _("total: "), A.RESET, str(guests + guests_maybe)) | |
579 ) | |
580 if attendees_missing: | |
581 self.disp("") | |
582 self.disp( | |
583 A.color( | |
584 C.A_SUBHEADER, | |
585 _("missing people (no reply): "), | |
586 A.RESET, | |
587 str(attendees_missing), | |
588 ) | |
589 ) | |
590 | |
591 async def start(self): | |
592 if self.args.no_rsvp and not self.args.missing: | |
593 self.parser.error(_("you need to use --missing if you use --no-rsvp")) | |
594 if not self.args.missing: | |
595 prefilled = {} | |
596 else: | |
597 # we get prefilled data with all people | |
598 try: | |
599 affiliations = await self.host.bridge.ps_node_affiliations_get( | |
600 self.args.service, | |
601 self.args.node, | |
602 self.profile, | |
603 ) | |
604 except Exception as e: | |
605 self.disp(f"can't get node affiliations: {e}", error=True) | |
606 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
607 else: | |
608 # we fill all affiliations with empty data, answered one will be filled | |
609 # below. We only consider people with "publisher" affiliation as invited, | |
610 # creators are not, and members can just observe | |
611 prefilled = { | |
612 jid_: {} | |
613 for jid_, affiliation in affiliations.items() | |
614 if affiliation in ("publisher",) | |
615 } | |
616 | |
617 try: | |
618 event_data = await self.host.bridge.event_invitees_list( | |
619 self.args.service, | |
620 self.args.node, | |
621 self.profile, | |
622 ) | |
623 except Exception as e: | |
624 self.disp(f"can't get event data: {e}", error=True) | |
625 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
626 | |
627 # we fill nicknames and keep only requested people | |
628 | |
629 if self.args.no_rsvp: | |
630 for jid_ in event_data: | |
631 # if there is a jid in event_data it must be there in prefilled too | |
632 # otherwie somebody is not on the invitees list | |
633 try: | |
634 del prefilled[jid_] | |
635 except KeyError: | |
636 self.disp( | |
637 A.color( | |
638 C.A_WARNING, | |
639 f"We got a RSVP from somebody who was not in invitees " | |
640 f"list: {jid_}", | |
641 ), | |
642 error=True, | |
643 ) | |
644 else: | |
645 # we replace empty dicts for existing people with R.S.V.P. data | |
646 prefilled.update(event_data) | |
647 | |
648 # we get nicknames for everybody, make it easier for organisers | |
649 for jid_, data in prefilled.items(): | |
650 id_data = await self.host.bridge.identity_get(jid_, [], True, self.profile) | |
651 id_data = data_format.deserialise(id_data) | |
652 data["nick"] = id_data["nicknames"][0] | |
653 | |
654 await self.output(prefilled) | |
655 self.host.quit() | |
656 | |
657 | |
658 class InviteeInvite(base.CommandBase): | |
659 def __init__(self, host): | |
660 base.CommandBase.__init__( | |
661 self, | |
662 host, | |
663 "invite", | |
664 use_pubsub=True, | |
665 pubsub_flags={C.NODE, C.SINGLE_ITEM}, | |
666 help=_("invite someone to the event through email"), | |
667 ) | |
668 | |
669 def add_parser_options(self): | |
670 self.parser.add_argument( | |
671 "-e", | |
672 "--email", | |
673 action="append", | |
674 default=[], | |
675 help="email(s) to send the invitation to", | |
676 ) | |
677 self.parser.add_argument( | |
678 "-N", | |
679 "--name", | |
680 default="", | |
681 help="name of the invitee", | |
682 ) | |
683 self.parser.add_argument( | |
684 "-H", | |
685 "--host-name", | |
686 default="", | |
687 help="name of the host", | |
688 ) | |
689 self.parser.add_argument( | |
690 "-l", | |
691 "--lang", | |
692 default="", | |
693 help="main language spoken by the invitee", | |
694 ) | |
695 self.parser.add_argument( | |
696 "-U", | |
697 "--url-template", | |
698 default="", | |
699 help="template to construct the URL", | |
700 ) | |
701 self.parser.add_argument( | |
702 "-S", | |
703 "--subject", | |
704 default="", | |
705 help="subject of the invitation email (default: generic subject)", | |
706 ) | |
707 self.parser.add_argument( | |
708 "-b", | |
709 "--body", | |
710 default="", | |
711 help="body of the invitation email (default: generic body)", | |
712 ) | |
713 | |
714 async def start(self): | |
715 email = self.args.email[0] if self.args.email else None | |
716 emails_extra = self.args.email[1:] | |
717 | |
718 try: | |
719 await self.host.bridge.event_invite_by_email( | |
720 self.args.service, | |
721 self.args.node, | |
722 self.args.item, | |
723 email, | |
724 emails_extra, | |
725 self.args.name, | |
726 self.args.host_name, | |
727 self.args.lang, | |
728 self.args.url_template, | |
729 self.args.subject, | |
730 self.args.body, | |
731 self.args.profile, | |
732 ) | |
733 except Exception as e: | |
734 self.disp(f"can't create invitation: {e}", error=True) | |
735 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
736 else: | |
737 self.host.quit() | |
738 | |
739 | |
740 class Invitee(base.CommandBase): | |
741 subcommands = (InviteeGet, InviteeSet, InviteesList, InviteeInvite) | |
742 | |
743 def __init__(self, host): | |
744 super(Invitee, self).__init__( | |
745 host, "invitee", use_profile=False, help=_("manage invities") | |
746 ) | |
747 | |
748 | |
749 class Event(base.CommandBase): | |
750 subcommands = (Get, Create, Modify, Invitee) | |
751 | |
752 def __init__(self, host): | |
753 super(Event, self).__init__( | |
754 host, "event", use_profile=False, help=_("event management") | |
755 ) |