Mercurial > p > roundup > code
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] |
