query: Let the user interrupt interactive queries.

This makes it easier to iterate on a query because you don't have to restart
the program and reload the books if something goes sideways.
This commit is contained in:
Brett Smith 2021-04-01 09:57:42 -04:00
parent 6109187286
commit e2dda7ae0c
2 changed files with 22 additions and 6 deletions

View file

@ -548,8 +548,7 @@ class BQLShell(bc_query_shell.BQLShell):
self.last_line_parsed = line self.last_line_parsed = line
super().run_parser(line, default_close_date) super().run_parser(line, default_close_date)
@functools.wraps(bc_query_shell.BQLShell.on_Select, ('__doc__',)) def _select(self, statement: QueryStatement) -> None:
def on_Select(self, statement: QueryStatement) -> None:
output_format: str = self.vars['format'] output_format: str = self.vars['format']
try: try:
render_func = getattr(self, f'_render_{output_format}') render_func = getattr(self, f'_render_{output_format}')
@ -587,6 +586,16 @@ class BQLShell(bc_query_shell.BQLShell):
logger.debug("rendering query as %s", output_format) logger.debug("rendering query as %s", output_format)
render_func(statement, row_types, rows) render_func(statement, row_types, rows)
@functools.wraps(bc_query_shell.BQLShell.on_Select, ('__doc__',))
def on_Select(self, statement: QueryStatement) -> None:
try:
self._select(statement)
except KeyboardInterrupt:
if self.is_interactive:
logger.info("interrupted")
else:
raise
def _hint_TypeError(self, error: TypeError, statement: QueryStatement) -> None: def _hint_TypeError(self, error: TypeError, statement: QueryStatement) -> None:
try: try:
errmsg = str(error.args[0]) errmsg = str(error.args[0])
@ -653,7 +662,7 @@ class QueryODS(core.BaseODS[NamedTuple, None]):
]) ])
def is_empty(self) -> bool: def is_empty(self) -> bool:
return not self.sheet.childNodes return not self.document.spreadsheet.firstChild.getAttribute('name').startswith('Query ')
def section_key(self, row: NamedTuple) -> None: def section_key(self, row: NamedTuple) -> None:
return None return None
@ -760,9 +769,12 @@ class QueryODS(core.BaseODS[NamedTuple, None]):
query_string: Optional[str]=None, query_string: Optional[str]=None,
) -> None: ) -> None:
if self.is_empty(): if self.is_empty():
self.sheet.setAttribute('name', "Query 1") query_count = 1
else: else:
self.use_sheet(f"Query {len(self.document.spreadsheet.childNodes) + 1}") query_count = len(self.document.spreadsheet.childNodes) + 1
# We avoid using self.use_sheet() because fully building the sheet
# before adding it to the doc makes the query safer to interrupt.
self.sheet = odf.table.Table(name=f"Query {query_count}")
for name, row_type in row_types: for name, row_type in row_types:
if issubclass(row_type, datetime.date): if issubclass(row_type, datetime.date):
col_width = 1.0 col_width = 1.0
@ -785,6 +797,10 @@ class QueryODS(core.BaseODS[NamedTuple, None]):
cell_func(value) cell_func(value)
for cell_func, value in zip(cell_funcs, row) for cell_func, value in zip(cell_funcs, row)
)) ))
if query_count == 1:
self.document.spreadsheet.childNodes[-1] = self.sheet
else:
self.document.spreadsheet.appendChild(self.sheet)
class ReportFormat(enum.Enum): class ReportFormat(enum.Enum):

View file

@ -1,6 +1,6 @@
[metadata] [metadata]
name = conservancy_beancount name = conservancy_beancount
version = 1.19.7 version = 1.19.8
author = Software Freedom Conservancy author = Software Freedom Conservancy
author_email = info@sfconservancy.org author_email = info@sfconservancy.org
description = Plugin, library, and reports for reading Conservancys books description = Plugin, library, and reports for reading Conservancys books