comparison sat_frontends/jp/cmd_ticket.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 9d0df638c8b4
comparison
equal deleted inserted replaced
3039:a1bc34f90fa5 3040:fee60f17ebac
20 20
21 from . import base 21 from . import base
22 from sat.core.i18n import _ 22 from sat.core.i18n import _
23 from sat_frontends.jp import common 23 from sat_frontends.jp import common
24 from sat_frontends.jp.constants import Const as C 24 from sat_frontends.jp.constants import Const as C
25 from functools import partial
26 import json 25 import json
27 import os 26 import os
28 27
29 __commands__ = ["Ticket"] 28 __commands__ = ["Ticket"]
30 29
42 pubsub_flags={C.MULTI_ITEMS}, 41 pubsub_flags={C.MULTI_ITEMS},
43 pubsub_defaults={"service": _("auto"), "node": _("auto")}, 42 pubsub_defaults={"service": _("auto"), "node": _("auto")},
44 use_output=C.OUTPUT_LIST_XMLUI, 43 use_output=C.OUTPUT_LIST_XMLUI,
45 help=_("get tickets"), 44 help=_("get tickets"),
46 ) 45 )
47 self.need_loop = True
48 46
49 def add_parser_options(self): 47 def add_parser_options(self):
50 pass 48 pass
51 49
52 def ticketsGetCb(self, tickets_data): 50 async def start(self):
53 self.output(tickets_data[0]) 51 await common.fill_well_known_uri(self, os.getcwd(), "tickets", meta_map={})
54 self.host.quit(C.EXIT_OK) 52 try:
55 53 tickets_data = await self.host.bridge.ticketsGet(
56 def getTickets(self): 54 self.args.service,
57 self.host.bridge.ticketsGet( 55 self.args.node,
58 self.args.service, 56 self.args.max,
59 self.args.node, 57 self.args.items,
60 self.args.max, 58 "",
61 self.args.items, 59 self.getPubsubExtra(),
62 "", 60 self.profile,
63 self.getPubsubExtra(), 61 )
64 self.profile, 62 except Exception as e:
65 callback=self.ticketsGetCb, 63 self.disp(f"can't get tickets: {e}", error=True)
66 errback=partial( 64 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
67 self.errback, 65 else:
68 msg=_("can't get tickets: {}"), 66 await self.output(tickets_data[0])
69 exit_code=C.EXIT_BRIDGE_ERRBACK, 67 self.host.quit(C.EXIT_OK)
70 ), 68
71 ) 69
72 70 class Import(base.CommandBase):
73 def start(self):
74 common.URIFinder(self, os.getcwd(), "tickets", self.getTickets, meta_map={})
75
76
77 class Import(base.CommandAnswering):
78 # TODO: factorize with blog/import 71 # TODO: factorize with blog/import
79 72
80 def __init__(self, host): 73 def __init__(self, host):
81 super(Import, self).__init__( 74 super(Import, self).__init__(
82 host, 75 host,
83 "import", 76 "import",
84 use_progress=True, 77 use_progress=True,
78 use_verbose=True,
85 help=_("import tickets from external software/dataset"), 79 help=_("import tickets from external software/dataset"),
86 ) 80 )
87 self.need_loop = True
88 81
89 def add_parser_options(self): 82 def add_parser_options(self):
90 self.parser.add_argument( 83 self.parser.add_argument(
91 "importer", 84 "importer",
92 nargs="?", 85 nargs="?",
107 action="append", 100 action="append",
108 nargs=2, 101 nargs=2,
109 default=[], 102 default=[],
110 metavar=("IMPORTED_FIELD", "DEST_FIELD"), 103 metavar=("IMPORTED_FIELD", "DEST_FIELD"),
111 help=_( 104 help=_(
112 "specified field in import data will be put in dest field (default: use same field name, or ignore if it doesn't exist)" 105 "specified field in import data will be put in dest field (default: use "
106 "same field name, or ignore if it doesn't exist)"
113 ), 107 ),
114 ) 108 )
115 self.parser.add_argument( 109 self.parser.add_argument(
116 "-s", 110 "-s",
117 "--service", 111 "--service",
123 "-n", 117 "-n",
124 "--node", 118 "--node",
125 default="", 119 default="",
126 metavar="PUBSUB_NODE", 120 metavar="PUBSUB_NODE",
127 help=_( 121 help=_(
128 "PubSub node where the items must be uploaded (default: tickets' defaults)" 122 "PubSub node where the items must be uploaded (default: tickets' "
123 "defaults)"
129 ), 124 ),
130 ) 125 )
131 self.parser.add_argument( 126 self.parser.add_argument(
132 "location", 127 "location",
133 nargs="?", 128 nargs="?",
134 help=_( 129 help=_(
135 "importer data location (see importer description), nothing to show importer description" 130 "importer data location (see importer description), nothing to show "
131 "importer description"
136 ), 132 ),
137 ) 133 )
138 134
139 def onProgressStarted(self, metadata): 135 async def onProgressStarted(self, metadata):
140 self.disp(_("Tickets upload started"), 2) 136 self.disp(_("Tickets upload started"), 2)
141 137
142 def onProgressFinished(self, metadata): 138 async def onProgressFinished(self, metadata):
143 self.disp(_("Tickets uploaded successfully"), 2) 139 self.disp(_("Tickets uploaded successfully"), 2)
144 140
145 def onProgressError(self, error_msg): 141 async def onProgressError(self, error_msg):
146 self.disp(_("Error while uploading tickets: {}").format(error_msg), error=True) 142 self.disp(_(f"Error while uploading tickets: {error_msg}"), error=True)
147 143
148 def error(self, failure): 144 async def start(self):
149 self.disp(
150 _("Error while trying to upload tickets: {reason}").format(reason=failure),
151 error=True,
152 )
153 self.host.quit(1)
154
155 def start(self):
156 if self.args.location is None: 145 if self.args.location is None:
146 # no location, the list of importer or description is requested
157 for name in ("option", "service", "node"): 147 for name in ("option", "service", "node"):
158 if getattr(self.args, name): 148 if getattr(self.args, name):
159 self.parser.error( 149 self.parser.error(
160 _( 150 _(f"{name} argument can't be used without location argument"))
161 "{name} argument can't be used without location argument"
162 ).format(name=name)
163 )
164 if self.args.importer is None: 151 if self.args.importer is None:
165 self.disp( 152 self.disp(
166 "\n".join( 153 "\n".join(
167 [ 154 [
168 "{}: {}".format(name, desc) 155 f"{name}: {desc}"
169 for name, desc in self.host.bridge.ticketsImportList() 156 for name, desc in await self.host.bridge.ticketsImportList()
170 ] 157 ]
171 ) 158 )
172 ) 159 )
173 else: 160 else:
174 try: 161 try:
175 short_desc, long_desc = self.host.bridge.ticketsImportDesc( 162 short_desc, long_desc = await self.host.bridge.ticketsImportDesc(
176 self.args.importer 163 self.args.importer
177 ) 164 )
178 except Exception as e: 165 except Exception as e:
179 msg = [l for l in str(e).split("\n") if l][ 166 self.disp(f"can't get importer description: {e}", error=True)
180 -1 167 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
181 ] # we only keep the last line
182 self.disp(msg)
183 self.host.quit(1)
184 else: 168 else:
185 self.disp( 169 self.disp(f"{name}: {short_desc}\n\n{long_desc}")
186 "{name}: {short_desc}\n\n{long_desc}".format(
187 name=self.args.importer,
188 short_desc=short_desc,
189 long_desc=long_desc,
190 )
191 )
192 self.host.quit() 170 self.host.quit()
193 else: 171 else:
194 # we have a location, an import is requested 172 # we have a location, an import is requested
173
174 if self.args.progress:
175 # we use a custom progress bar template as we want a counter
176 self.pbar_template = [
177 _("Progress: "), ["Percentage"], " ", ["Bar"], " ",
178 ["Counter"], " ", ["ETA"]
179 ]
180
195 options = {key: value for key, value in self.args.option} 181 options = {key: value for key, value in self.args.option}
196 fields_map = dict(self.args.map) 182 fields_map = dict(self.args.map)
197 if fields_map: 183 if fields_map:
198 if FIELDS_MAP in options: 184 if FIELDS_MAP in options:
199 self.parser.error( 185 self.parser.error(
200 _( 186 _("fields_map must be specified either preencoded in --option or "
201 "fields_map must be specified either preencoded in --option or using --map, but not both at the same time" 187 "using --map, but not both at the same time")
202 )
203 ) 188 )
204 options[FIELDS_MAP] = json.dumps(fields_map) 189 options[FIELDS_MAP] = json.dumps(fields_map)
205 190
206 def gotId(id_): 191 try:
207 self.progress_id = id_ 192 progress_id = await self.host.bridge.ticketsImport(
208 193 self.args.importer,
209 self.host.bridge.ticketsImport( 194 self.args.location,
210 self.args.importer, 195 options,
211 self.args.location, 196 self.args.service,
212 options, 197 self.args.node,
213 self.args.service, 198 self.profile,
214 self.args.node, 199 )
215 self.profile, 200 except Exception as e:
216 callback=gotId, 201 self.disp(
217 errback=self.error, 202 _(f"Error while trying to import tickets: {e}"),
218 ) 203 error=True,
204 )
205 self.host.quit(C.EXIT_BRIDGE_ERRBACK)
206 else:
207 await self.set_progress_id(progress_id)
219 208
220 209
221 class Ticket(base.CommandBase): 210 class Ticket(base.CommandBase):
222 subcommands = (Get, Import) 211 subcommands = (Get, Import)
223 212