Package kenozooid :: Package cli :: Module logbook

Source Code for Module kenozooid.cli.logbook

  1  # 
  2  # Kenozooid - dive planning and analysis toolbox. 
  3  # 
  4  # Copyright (C) 2009-2019 by Artur Wroblewski <wrobell@riseup.net> 
  5  # 
  6  # This program is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 18  # 
 19   
 20  """ 
 21  Kenozooid's logbook command line user interface. 
 22  """ 
 23   
 24  import sys 
 25  import os.path 
 26  from functools import partial 
 27  import logging 
 28   
 29  from kenozooid.component import inject 
 30  from kenozooid.cli import CLICommand, add_master_command, add_uddf_input 
 31  from kenozooid.util import nformat 
 32   
 33  log = logging.getLogger('kenozooid.cli.logbook') 
 34   
 35   
 36  # for commands 'dive add', 'dive list', etc 
 37  add_master_command('dive', 
 38          'Kenozooid dive management commands', 
 39          'manage dives in UDDF file') 
 40   
 41  # for commands 'site add', 'site list', etc 
 42  add_master_command('site', 
 43          'Kenozooid dive site management commands', 
 44          'manage dive sites in UDDF file') 
 45   
 46  # for commands 'buddy add', 'buddy list', etc 
 47  add_master_command('buddy', 
 48          'Kenozooid dive buddy management commands', 
 49          'manage dive buddies in UDDF file') 
50 51 52 @inject(CLICommand, name='dive list') 53 -class ListDives(object):
54 """ 55 List dives from UDDF file. 56 """ 57 description = 'list dives stored in UDDF file' 58 59 @classmethod
60 - def add_arguments(self, parser):
61 """ 62 Add options for dive list fetched from UDDF file. 63 """ 64 parser.add_argument('input', 65 nargs='+', 66 help='UDDF files with dive data')
67 68
69 - def __call__(self, args):
70 """ 71 Execute command for list of dives in UDDF file. 72 """ 73 import kenozooid.logbook as kl 74 75 for fin in args.input: 76 dives = kl.find_dives([fin]) 77 print('{}:'.format(fin)) 78 for i, d in enumerate(kl.list_dives(dives), 1): 79 print('{:5}: {:>4} {:>9} {:>9} ({:>5}) {:>9} {:>9}'.format(i, 80 d[0] or ' -- ', d[1], d[2], d[3] or ' --- ', d[4], d[5]))
81
82 83 84 @inject(CLICommand, name='dive add') 85 -class AddDive(object):
86 """ 87 Add a dive to logbook file. 88 """ 89 description = 'add dive to logbook file' 90 91 @classmethod
92 - def add_arguments(self, parser):
93 """ 94 Add options for dive adding to logbook file. 95 """ 96 parser.add_argument('datetime', help='date and optional time of a dive') 97 parser.add_argument('depth', help='maximum depth of a dive') 98 parser.add_argument('duration', help='duration of a dive') 99 parser.add_argument('-s', '--site', metavar='site', help='dive site') 100 parser.add_argument('-b', '--buddy', 101 nargs='+', 102 metavar='buddy', 103 help='dive buddies') 104 parser.add_argument('logbook', help='logbook file')
105 106
107 - def __call__(self, args):
108 """ 109 Execute command for adding dives into logbook file. 110 """ 111 import kenozooid.logbook as kl 112 import kenozooid.data as kd 113 from dateutil.parser import parse as dparse 114 115 lfile = args.logbook 116 117 datetime = dparse(args.datetime) 118 depth = float(args.depth) 119 duration = float(args.duration) * 60 120 121 site = args.site 122 buddy = args.buddy 123 124 dive = kd.Dive(datetime=datetime, depth=depth, duration=duration) 125 kl.add_dive(dive, lfile, qsite=site, qbuddies=buddy)
126
127 128 129 @inject(CLICommand, name='dive copy') 130 -class CopyDive(object):
131 """ 132 Copy dives to logbook file. 133 """ 134 description = 'copy dives to logbook file' 135 136 @classmethod
137 - def add_arguments(self, parser):
138 """ 139 Add options for dive copying to logbook file. 140 """ 141 add_uddf_input(parser) 142 parser.add_argument('logbook', help='logbook file')
143 144
145 - def __call__(self, args):
146 """ 147 Execute command for copying dives into logbook file. 148 """ 149 import kenozooid.logbook as kl 150 151 r, f = args.input 152 kl.copy_dives(f, r, args.dives, args.logbook)
153
154 155 156 @inject(CLICommand, name='site add') 157 -class AddDiveSite(object):
158 """ 159 Add dive site to UDDF file. 160 """ 161 description = 'add dive site to UDDF file' 162 163 @classmethod
164 - def add_arguments(self, parser):
165 """ 166 Add options for dive site adding to UDDF file. 167 """ 168 parser.add_argument('-c', '--coords', 169 nargs=2, 170 metavar=('x', 'y'), 171 type=float, 172 help='coordinates (longitude and latitude) of dive site') 173 #parser.add_argument('-c', '--country', 174 # nargs=1, 175 # metavar='country', 176 # help='dive site country, i.e. Ireland') 177 #parser.add_argument('-p', '--province', 178 # nargs=1, 179 # metavar='province', 180 # help='dive site province, i.e. Howth') 181 parser.add_argument('id', 182 nargs='?', 183 help='id of dive site') 184 parser.add_argument('location', 185 nargs=1, 186 help='location of dive site, i.e. Scapa Flow, Red Sea') 187 parser.add_argument('name', 188 nargs=1, 189 help='name of dive site, i.e. SMS Markgraf, SS Thistlegorm') 190 parser.add_argument('output', 191 nargs=1, 192 help='UDDF output file')
193 194
195 - def __call__(self, args):
196 """ 197 Execute command for adding dive site into UDDF file. 198 """ 199 import kenozooid.uddf as ku 200 201 id = args.id 202 if args.coords: 203 x, y = args.coords 204 else: 205 x, y = None, None 206 location = args.location[0] 207 name = args.name[0] 208 fout = args.output[0] 209 210 if os.path.exists(fout): 211 doc = ku.parse(fout).getroot() 212 else: 213 doc = ku.create() 214 215 ku.create_site_data(doc, id=id, location=location, name=name, x=x, y=y) 216 ku.save(doc, fout)
217
218 219 220 @inject(CLICommand, name='site list') 221 -class ListDiveSites(object):
222 """ 223 List dive sites from UDDF file. 224 """ 225 description = 'list dive sites stored in UDDF file' 226 227 @classmethod
228 - def add_arguments(self, parser):
229 """ 230 Add options for dive site list fetched from UDDF file. 231 """ 232 parser.add_argument('site', 233 nargs='?', 234 help='dive site search string; matches id or partially dive' \ 235 ' site name') 236 parser.add_argument('input', 237 nargs='+', 238 help='UDDF files with dive sites')
239 240
241 - def __call__(self, args):
242 """ 243 Execute command for list of dive sites in UDDF file. 244 """ 245 import kenozooid.uddf as ku 246 247 fmt = '{0:4}: {1.id:10} {1.location:20} {1.name:20}' 248 249 if args.site: 250 query = ku.XP_FIND_SITE 251 else: 252 query = '//uddf:site' 253 files = args.input 254 255 for fin in files: 256 nodes = ku.find(fin, query, site=args.site) 257 print('{}:'.format(fin)) 258 for i, n in enumerate(nodes): 259 n = ku.site_data(n) 260 261 coords = '' 262 if n.x is not None: 263 coords = ' {0.x: #.9},{0.y: #.9}'.format(n) 264 265 print(nformat(fmt, i + 1, n) + coords)
266
267 268 269 @inject(CLICommand, name='site del') 270 -class DelDiveSite(object):
271 """ 272 Remove dive site from UDDF file. 273 """ 274 description = 'remove dive site stored in UDDF file' 275 276 @classmethod
277 - def add_arguments(self, parser):
278 """ 279 Add options for removal of dive site from UDDF file. 280 """ 281 parser.add_argument('site', 282 nargs=1, 283 help='dive site search string; matches id or partially dive' \ 284 ' site name') 285 parser.add_argument('input', 286 nargs=1, 287 help='UDDF file with dive sites')
288 289
290 - def __call__(self, args):
291 """ 292 Execute command for removal of dive sites from UDDF file. 293 """ 294 import kenozooid.uddf as ku 295 296 query = ku.XP_FIND_SITE 297 fin = args.input[0] 298 299 doc = ku.parse(fin) 300 ku.remove_nodes(doc, query, site=args.site[0]) 301 ku.save(doc.getroot(), fin)
302
303 304 305 @inject(CLICommand, name='buddy add') 306 -class AddBuddy(object):
307 """ 308 Add dive buddy to UDDF file. 309 """ 310 description = 'add dive buddy to UDDF file' 311 312 @classmethod
313 - def add_arguments(self, parser):
314 """ 315 Add options for dive buddy adding to UDDF file. 316 """ 317 parser.add_argument('-m', '--member', 318 nargs=2, 319 metavar=('org', 'number'), 320 help='organization and its member id number i.e. CFT 123') 321 parser.add_argument('id', 322 nargs='?', 323 help='id of a buddy, i.e. tcora') 324 parser.add_argument('name', 325 nargs=1, 326 help='buddy name, i.e. "Tom Cora", "Thomas Henry Corra"' 327 ' or "Corra, Thomas Henry"') 328 parser.add_argument('output', 329 nargs=1, 330 help='UDDF output file')
331 332
333 - def __call__(self, args):
334 """ 335 Execute command for adding dive buddy into UDDF file. 336 """ 337 import kenozooid.uddf as ku 338 339 if args.member: 340 org, number = args.member 341 else: 342 org, number = None, None 343 344 id = args.id 345 fn, mn, ln = _name_parse(args.name[0]) 346 fout = args.output[0] 347 348 if os.path.exists(fout): 349 doc = ku.parse(fout).getroot() 350 else: 351 doc = ku.create() 352 353 ku.create_buddy_data(doc, id=id, fname=fn, mname=mn, 354 lname=ln, org=org, number=number) 355 356 ku.save(doc, fout)
357
358 359 @inject(CLICommand, name='buddy list') 360 -class ListBuddies(object):
361 """ 362 List dive buddies from UDDF file. 363 """ 364 description = 'list dive buddies stored in UDDF file' 365 366 @classmethod
367 - def add_arguments(self, parser):
368 """ 369 Add options for dive buddy list fetched from UDDF file. 370 """ 371 parser.add_argument('buddy', 372 nargs='?', 373 help='buddy search string; matches id, member number or' 374 ' partially firstname or lastname') 375 parser.add_argument('input', 376 nargs='+', 377 help='UDDF file with dive buddies')
378 379
380 - def __call__(self, args):
381 """ 382 Execute command for list of dive buddies in UDDF file. 383 """ 384 import kenozooid.uddf as ku 385 386 fmt = '{0:4}: {1.id:10} {1.fname:10} {1.lname:20}' \ 387 ' {1.org:5} {1.number:11}' 388 389 if args.buddy: 390 query = ku.XP_FIND_BUDDY 391 else: 392 query = '//uddf:buddy' 393 files = args.input 394 395 for fin in files: 396 nodes = ku.find(fin, query, buddy=args.buddy) 397 print('{}:'.format(fin)) 398 for i, n in enumerate(nodes): 399 b = ku.buddy_data(n) 400 print(nformat(fmt, i + 1, b))
401
402 403 @inject(CLICommand, name='buddy del') 404 -class DelBuddy(object):
405 """ 406 Remove dive buddies from UDDF file. 407 """ 408 description = 'remove dive buddies stored in UDDF file' 409 410 @classmethod
411 - def add_arguments(self, parser):
412 """ 413 Add options for removal of dive buddy from UDDF file. 414 """ 415 parser.add_argument('buddy', 416 nargs=1, 417 help='buddy search string; matches id, member number or' 418 ' partially firstname or lastname') 419 parser.add_argument('input', 420 nargs=1, 421 help='UDDF file with dive buddies')
422 423
424 - def __call__(self, args):
425 """ 426 Execute command for removal of dive buddies from UDDF file. 427 """ 428 import kenozooid.uddf as ku 429 430 query = ku.XP_FIND_BUDDY 431 fin = args.input[0] 432 433 doc = ku.parse(fin) 434 ku.remove_nodes(doc, query, buddy=args.buddy[0]) 435 ku.save(doc.getroot(), fin)
436
437 438 @inject(CLICommand, name='upgrade') 439 -class UpgradeFile(object):
440 """ 441 Upgrade a file to newer version of UDDF. 442 """ 443 description = 'upgrade UDDF file to newer version' 444 445 @classmethod
446 - def add_arguments(self, parser):
447 """ 448 Add options for UDDF file format upgrade. 449 """ 450 parser.add_argument('input', 451 nargs='+', 452 help='input UDDF file')
453 454
455 - def __call__(self, args):
456 """ 457 Execute command UDDF file upgrade. 458 """ 459 import kenozooid.uddf as ku 460 import kenozooid.logbook as kl 461 462 for fin in args.input: 463 try: 464 print('Upgrading {}'.format(fin)) 465 doc = kl.upgrade_file(fin) 466 ku.save(doc.getroot(), fin) 467 except Exception as ex: 468 print('Cannot upgrade file {}'.format(fin), file=sys.stderr) 469 print('Error: {}'.format(ex))
470
471 472 473 @inject(CLICommand, name='dive enum') 474 -class EnumDives(object):
475 """ 476 Enumerate dives. 477 """ 478 description = 'enumerate dives' 479 480 @classmethod
481 - def add_arguments(self, parser):
482 """ 483 Add options for dive enumeration. 484 """ 485 parser.add_argument('-ns', '---dive-total-number', 486 metavar='dive_total_number', 487 nargs='?', 488 type=int, 489 default=1, 490 help='start of total dive number') 491 parser.add_argument('input', 492 nargs='+', 493 help='UDDF files with dive data')
494 495
496 - def __call__(self, args):
497 """ 498 Execute command to enumerate dives. 499 """ 500 import kenozooid.logbook as kl 501 kl.enum_dives(args.input, args.dive_total_number)
502
503 504 505 -def _name_parse(name):
506 """ 507 Parse name data from a string. The name string is in simplified BibTeX 508 format, i.e. 509 510 - Tom 511 - Tom Cora 512 - Thomas Henry Corra 513 - Corra, Thomas Henry 514 515 Parsed name is tuple consisting of first name, middle name and last 516 name. 517 """ 518 f, m, l = None, None, None 519 520 t = name.split(',') 521 if len(t) == 1: 522 nd = t[0].strip().split(' ', 2) 523 if len(nd) == 3: 524 f, m, l = nd 525 elif len(nd) == 2: 526 f, l = nd 527 elif len(nd) == 1: 528 f = nd[0] 529 elif len(t) == 2: 530 l = t[0].strip() 531 nd = t[1].strip().split(' ', 1) 532 f = nd[0] 533 if len(nd) == 2: 534 m = nd[1] 535 else: 536 raise ValueError('Cannot parse name') 537 538 if f: 539 f = f.strip() 540 if m: 541 m = m.strip() 542 if l: 543 l = l.strip() 544 return f, m, l
545 546 547 # vim: sw=4:et:ai 548