comparison roundup/admin.py @ 7692:8fb42f41ef10 issue2550923_computed_property

merge in default branch to see if ti clears a travis-ci build error on 2.7 python; default branch builds fine
author John Rouillard <rouilj@ieee.org>
date Mon, 11 Sep 2023 00:10:29 -0400
parents 8329b2227adb
children 4de48eadf5f4
comparison
equal deleted inserted replaced
7528:14a8e11f3a87 7692:8fb42f41ef10
102 self.tracker_home = '' 102 self.tracker_home = ''
103 self.db = None 103 self.db = None
104 self.db_uncommitted = False 104 self.db_uncommitted = False
105 self.force = None 105 self.force = None
106 self.settings = { 106 self.settings = {
107 'display_header': False,
107 'display_protected': False, 108 'display_protected': False,
108 'indexer_backend': "as set in config.ini", 109 'indexer_backend': "as set in config.ini",
109 '_reopen_tracker': False, 110 '_reopen_tracker': False,
110 'show_retired': False, 111 'show_retired': "no",
112 '_retired_val': False,
111 'verbose': False, 113 'verbose': False,
112 '_inttest': 3, 114 '_inttest': 3,
113 '_floattest': 3.5, 115 '_floattest': 3.5,
114 } 116 }
115 self.settings_help = { 117 self.settings_help = {
118 'display_header':
119 _("Have 'display designator[,designator*]' show header inside "
120 " []'s before items. Includes retired/active status."),
121
116 'display_protected': 122 'display_protected':
117 _("Have 'display designator' show protected fields: creator. NYI"), 123 _("Have 'display designator' and 'specification class' show "
124 "protected fields: creator, id etc."),
118 125
119 'indexer_backend': 126 'indexer_backend':
120 _("Set indexer to use when running 'reindex' NYI"), 127 _("Set indexer to use when running 'reindex' NYI"),
121 128
122 '_reopen_tracker': 129 '_reopen_tracker':
123 _("Force reopening of tracker when running each command."), 130 _("Force reopening of tracker when running each command."),
124 131
125 'show_retired': _("Show retired items in table, list etc. NYI"), 132 'show_retired': _("Show retired items in table, list etc. One of 'no', 'only', 'both'"),
133 '_retired_val': _("internal mapping for show_retired."),
126 'verbose': _("Enable verbose output: tracing, descriptions..."), 134 'verbose': _("Enable verbose output: tracing, descriptions..."),
127 135
128 '_inttest': "Integer valued setting. For testing only.", 136 '_inttest': "Integer valued setting. For testing only.",
129 '_floattest': "Float valued setting. For testing only.", 137 '_floattest': "Float valued setting. For testing only.",
130 } 138 }
170 -c -- when outputting lists of data, comma-separate them. 178 -c -- when outputting lists of data, comma-separate them.
171 Same as '-S ","'. 179 Same as '-S ","'.
172 -S <string> -- when outputting lists of data, string-separate them 180 -S <string> -- when outputting lists of data, string-separate them
173 -s -- when outputting lists of data, space-separate them. 181 -s -- when outputting lists of data, space-separate them.
174 Same as '-S " "'. 182 Same as '-S " "'.
183 -P pragma=value -- Set a pragma on command line rather than interactively.
184 Can be used multiple times.
175 -V -- be verbose when importing 185 -V -- be verbose when importing
176 -v -- report Roundup and Python versions (and quit) 186 -v -- report Roundup and Python versions (and quit)
177 187
178 Only one of -s, -c or -S can be specified. 188 Only one of -s, -c or -S can be specified.
179 189
509 for the given node. 519 for the given node.
510 """ 520 """
511 if len(args) < 1: 521 if len(args) < 1:
512 raise UsageError(_('Not enough arguments supplied')) 522 raise UsageError(_('Not enough arguments supplied'))
513 523
524 display_protected = self.settings['display_protected']
525 display_header = self.settings['display_header']
526
514 # decode the node designator 527 # decode the node designator
515 for designator in args[0].split(','): 528 for designator in args[0].split(','):
516 try: 529 try:
517 classname, nodeid = hyperdb.splitDesignator(designator) 530 classname, nodeid = hyperdb.splitDesignator(designator)
518 except hyperdb.DesignatorError as message: 531 except hyperdb.DesignatorError as message:
520 533
521 # get the class 534 # get the class
522 cl = self.get_class(classname) 535 cl = self.get_class(classname)
523 536
524 # display the values 537 # display the values
525 keys = sorted(cl.properties) 538 normal_props = sorted(cl.properties)
539 if display_protected:
540 keys = sorted(cl.getprops())
541 else:
542 keys = normal_props
543
544 if display_header:
545 status = "retired" if cl.is_retired(nodeid) else "active"
546 print('\n[%s (%s)]' % (designator, status))
526 for key in keys: 547 for key in keys:
527 value = cl.get(nodeid, key) 548 value = cl.get(nodeid, key)
528 print(_('%(key)s: %(value)s') % locals()) 549 # prepend * for protected properties else just indent
550 # with space.
551 if display_protected or display_header:
552 protected = "*" if key not in normal_props else ' '
553 else:
554 protected = ""
555 print(_('%(protected)s%(key)s: %(value)s') % locals())
529 556
530 def do_export(self, args, export_files=True): 557 def do_export(self, args, export_files=True):
531 ''"""Usage: export [[-]class[,class]] export_dir 558 ''"""Usage: export [[-]class[,class]] export_dir
532 Export the database to colon-separated-value files. 559 Export the database to colon-separated-value files.
533 To exclude the files (e.g. for the msg or file class), 560 To exclude the files (e.g. for the msg or file class),
1283 """ 1310 """
1284 if len(args) > 2: 1311 if len(args) > 2:
1285 raise UsageError(_('Too many arguments supplied')) 1312 raise UsageError(_('Too many arguments supplied'))
1286 if len(args) < 1: 1313 if len(args) < 1:
1287 raise UsageError(_('Not enough arguments supplied')) 1314 raise UsageError(_('Not enough arguments supplied'))
1315
1316 retired = self.settings['_retired_val']
1317
1288 classname = args[0] 1318 classname = args[0]
1289 1319
1290 # get the class 1320 # get the class
1291 cl = self.get_class(classname) 1321 cl = self.get_class(classname)
1292 1322
1298 1328
1299 if self.separator: 1329 if self.separator:
1300 if len(args) == 2: 1330 if len(args) == 2:
1301 # create a list of propnames since user specified propname 1331 # create a list of propnames since user specified propname
1302 proplist = [] 1332 proplist = []
1303 for nodeid in cl.list(): 1333 for nodeid in cl.getnodeids(retired=retired):
1304 try: 1334 try:
1305 proplist.append(cl.get(nodeid, propname)) 1335 proplist.append(cl.get(nodeid, propname))
1306 except KeyError: 1336 except KeyError:
1307 raise UsageError(_('%(classname)s has no property ' 1337 raise UsageError(_('%(classname)s has no property '
1308 '"%(propname)s"') % locals()) 1338 '"%(propname)s"') % locals())
1309 print(self.separator.join(proplist)) 1339 print(self.separator.join(proplist))
1310 else: 1340 else:
1311 # create a list of index id's since user didn't specify 1341 # create a list of index id's since user didn't specify
1312 # otherwise 1342 # otherwise
1313 print(self.separator.join(cl.list())) 1343 print(self.separator.join(cl.getnodeids(retired=retired)))
1314 else: 1344 else:
1315 for nodeid in cl.list(): 1345 for nodeid in cl.getnodeids(retired=retired):
1316 try: 1346 try:
1317 value = cl.get(nodeid, propname) 1347 value = cl.get(nodeid, propname)
1318 except KeyError: 1348 except KeyError:
1319 raise UsageError(_('%(classname)s has no property ' 1349 raise UsageError(_('%(classname)s has no property '
1320 '"%(propname)s"') % locals()) 1350 '"%(propname)s"') % locals())
1476 1506
1477 pragma list 1507 pragma list
1478 1508
1479 will show all settings and their current values. If verbose 1509 will show all settings and their current values. If verbose
1480 is enabled hidden settings and descriptions will be shown. 1510 is enabled hidden settings and descriptions will be shown.
1511 """
1512 """
1513 The following are to be implemented:
1514 exportfiles={true|false} - Not Implemented - If true
1515 (default) export/import db tables and files. If
1516 False, export/import just database tables, not
1517 files. Use for faster database migration.
1518 Replaces exporttables/importtables with
1519 exportfiles=false then export/import
1481 """ 1520 """
1482 1521
1483 if len(args) < 1: 1522 if len(args) < 1:
1484 raise UsageError(_('Not enough arguments supplied')) 1523 raise UsageError(_('Not enough arguments supplied'))
1485 1524
1522 raise UsageError(_( 1561 raise UsageError(_(
1523 'Incorrect value for integer setting %(setting)s: ' 1562 'Incorrect value for integer setting %(setting)s: '
1524 '%(value)s.') % {"setting": setting, "value": value}) 1563 '%(value)s.') % {"setting": setting, "value": value})
1525 value = _val 1564 value = _val
1526 elif type(self.settings[setting]) is str: 1565 elif type(self.settings[setting]) is str:
1527 pass 1566 if setting == "show_retired":
1567 if value not in ["no", "only", "both"]:
1568 raise UsageError(_(
1569 'Incorrect value for setting %(setting)s: '
1570 '%(value)s. Should be no, both, or only.') % {
1571 "setting": setting, "value": value})
1572 if value == "both":
1573 self.settings['_retired_val'] = None
1574 elif value == "only": # numerical value not boolean
1575 self.settings['_retired_val'] = True
1576 else: # numerical value not boolean
1577 self.settings['_retired_val'] = False
1528 else: 1578 else:
1529 raise UsageError(_('Internal error: pragma can not handle ' 1579 raise UsageError(_('Internal error: pragma can not handle '
1530 'values of type: %s') % 1580 'values of type: %s') %
1531 type(self.settings[setting]).__name__) 1581 type(self.settings[setting]).__name__)
1532 1582
1568 range(int(r.group(2)), int(r.group(3)))): 1618 range(int(r.group(2)), int(r.group(3)))):
1569 try: 1619 try:
1570 cl.index(str(item)) 1620 cl.index(str(item))
1571 except IndexError: 1621 except IndexError:
1572 print(_('no such item "%(class)s%(id)s"') % { 1622 print(_('no such item "%(class)s%(id)s"') % {
1573 'class': r.group(1), 1623 'class': r.group(1),
1574 'id': item}) 1624 'id': item})
1575 1625
1576 else: 1626 else:
1577 cl = self.get_class(arg) # Bad class raises UsageError 1627 cl = self.get_class(arg) # Bad class raises UsageError
1578 self.db.reindex(arg, show_progress=True) 1628 self.db.reindex(arg, show_progress=True)
1579 else: 1629 else:
1580 self.db.reindex(show_progress=True) 1630 self.db.reindex(show_progress=True)
1796 # get the class 1846 # get the class
1797 cl = self.get_class(classname) 1847 cl = self.get_class(classname)
1798 1848
1799 # get the key property 1849 # get the key property
1800 keyprop = cl.getkey() 1850 keyprop = cl.getkey()
1801 for key in cl.properties: 1851 if self.settings['display_protected']:
1802 value = cl.properties[key] 1852 properties = cl.getprops()
1853 else:
1854 properties = cl.properties
1855 for key in properties:
1856 value = properties[key]
1803 if keyprop == key: 1857 if keyprop == key:
1804 sys.stdout.write(_('%(key)s: %(value)s (key property)\n') % 1858 sys.stdout.write(_('%(key)s: %(value)s (key property)\n') %
1805 locals()) 1859 locals())
1806 else: 1860 else:
1807 sys.stdout.write(_('%(key)s: %(value)s\n') % locals()) 1861 sys.stdout.write(_('%(key)s: %(value)s\n') % locals())
1836 will result in a the 4 character wide "Name" column. 1890 will result in a the 4 character wide "Name" column.
1837 """ 1891 """
1838 if len(args) < 1: 1892 if len(args) < 1:
1839 raise UsageError(_('Not enough arguments supplied')) 1893 raise UsageError(_('Not enough arguments supplied'))
1840 classname = args[0] 1894 classname = args[0]
1895
1896 retired = self.settings['_retired_val']
1841 1897
1842 # get the class 1898 # get the class
1843 cl = self.get_class(classname) 1899 cl = self.get_class(classname)
1844 1900
1845 # figure the property names to display 1901 # figure the property names to display
1877 'integer width: "%(width)s"') % 1933 'integer width: "%(width)s"') %
1878 locals()) 1934 locals())
1879 else: 1935 else:
1880 # this is going to be slow 1936 # this is going to be slow
1881 maxlen = len(spec) 1937 maxlen = len(spec)
1882 for nodeid in cl.list(): 1938 for nodeid in cl.getnodeids(retired=retired):
1883 curlen = len(str(cl.get(nodeid, spec))) 1939 curlen = len(str(cl.get(nodeid, spec)))
1884 if curlen > maxlen: 1940 if curlen > maxlen:
1885 maxlen = curlen 1941 maxlen = curlen
1886 props.append((spec, maxlen)) 1942 props.append((spec, maxlen))
1887 1943
1888 # now display the heading 1944 # now display the heading
1889 print(' '.join([name.capitalize().ljust(width) 1945 print(' '.join([name.capitalize().ljust(width)
1890 for name, width in props])) 1946 for name, width in props]))
1891 1947
1892 # and the table data 1948 # and the table data
1893 for nodeid in cl.list(): 1949 for nodeid in cl.getnodeids(retired=retired):
1894 table_columns = [] 1950 table_columns = []
1895 for name, width in props: 1951 for name, width in props:
1896 if name != 'id': 1952 if name != 'id':
1897 try: 1953 try:
1898 value = str(cl.get(nodeid, name)) 1954 value = str(cl.get(nodeid, name))
1988 if command in ['genconfig', 'templates']: 2044 if command in ['genconfig', 'templates']:
1989 try: 2045 try:
1990 ret = function(args[1:]) 2046 ret = function(args[1:])
1991 return ret 2047 return ret
1992 except UsageError as message: # noqa F841 2048 except UsageError as message: # noqa F841
1993 return self.usageError_feedback(message, function) 2049 return self.usageError_feedback(message, function)
1994 2050
1995 # make sure we have a tracker_home 2051 # make sure we have a tracker_home
1996 while not self.tracker_home: 2052 while not self.tracker_home:
1997 if not self.force: 2053 if not self.force:
1998 self.tracker_home = self.my_input(_('Enter tracker home: ')).strip() 2054 self.tracker_home = self.my_input(_('Enter tracker home: ')).strip()
2002 # before we open the db, we may be doing an install or init 2058 # before we open the db, we may be doing an install or init
2003 if command == 'initialise': 2059 if command == 'initialise':
2004 try: 2060 try:
2005 return self.do_initialise(self.tracker_home, args) 2061 return self.do_initialise(self.tracker_home, args)
2006 except UsageError as message: # noqa: F841 2062 except UsageError as message: # noqa: F841
2007 return self.usageError_feedback(message, function) 2063 return self.usageError_feedback(message, function)
2008 elif command == 'install': 2064 elif command == 'install':
2009 try: 2065 try:
2010 return self.do_install(self.tracker_home, args) 2066 return self.do_install(self.tracker_home, args)
2011 except UsageError as message: # noqa: F841 2067 except UsageError as message: # noqa: F841
2012 return self.usageError_feedback(message, function) 2068 return self.usageError_feedback(message, function)
2013 2069
2014 # get the tracker 2070 # get the tracker
2015 try: 2071 try:
2016 if self.tracker and not self.settings['_reopen_tracker']: 2072 if self.tracker and not self.settings['_reopen_tracker']:
2017 tracker = self.tracker 2073 tracker = self.tracker
2018 else: 2074 else:
2019 if self.settings["verbose"]: 2075 if self.settings["verbose"]:
2020 print("Reopening tracker") 2076 print("Reopening tracker")
2021 tracker = roundup.instance.open(self.tracker_home) 2077 tracker = roundup.instance.open(self.tracker_home)
2022 self.tracker = tracker 2078 self.tracker = tracker
2079 self.settings['indexer_backend'] = self.tracker.config['INDEXER']
2080
2023 except ValueError as message: # noqa: F841 2081 except ValueError as message: # noqa: F841
2024 self.tracker_home = '' 2082 self.tracker_home = ''
2025 print(_("Error: Couldn't open tracker: %(message)s") % locals()) 2083 print(_("Error: Couldn't open tracker: %(message)s") % locals())
2026 return 1 2084 return 1
2027 except NoConfigError as message: # noqa: F841 2085 except NoConfigError as message: # noqa: F841
2088 self.db.commit() 2146 self.db.commit()
2089 return 0 2147 return 0
2090 2148
2091 def main(self): 2149 def main(self):
2092 try: 2150 try:
2093 opts, args = getopt.getopt(sys.argv[1:], 'i:u:hcdsS:vV') 2151 opts, args = getopt.getopt(sys.argv[1:], 'i:u:hcdP:sS:vV')
2094 except getopt.GetoptError as e: 2152 except getopt.GetoptError as e:
2095 self.usage(str(e)) 2153 self.usage(str(e))
2096 return 1 2154 return 1
2097 2155
2098 # handle command-line args 2156 # handle command-line args
2134 self.usage('Only one of -c, -S and -s may be specified') 2192 self.usage('Only one of -c, -S and -s may be specified')
2135 return 1 2193 return 1
2136 self.separator = ' ' 2194 self.separator = ' '
2137 elif opt == '-d': 2195 elif opt == '-d':
2138 self.print_designator = 1 2196 self.print_designator = 1
2197 elif opt == '-P':
2198 self.do_pragma([arg])
2139 elif opt == '-u': 2199 elif opt == '-u':
2140 login_opt = arg.split(':') 2200 login_opt = arg.split(':')
2141 self.name = login_opt[0] 2201 self.name = login_opt[0]
2142 if len(login_opt) > 1: 2202 if len(login_opt) > 1:
2143 self.password = login_opt[1] 2203 self.password = login_opt[1]

Roundup Issue Tracker: http://roundup-tracker.org/