def saveButton(self): "OK button feedback." logger.debug('save button hit') # self.value = self.e.get() logger.debug(f'values: {self.filename}') if self.master.responses[self.filename].type == 'json': fullFile = os.path.join(self.master.responseFolder, self.filename) fp = open(fullFile, 'w') js = json.dumps(self.master.responses[self.filename].response, indent=4) json.dump(self.master.responses[self.filename].response, fp, indent=4) fp.close() elif self.master.responses[self.filename].type == 'xml': # js = xml .dumps(self.master.responses[self.filename].response) pass # save string in file else: # add error details logger.warn( f'file is not saved: {self.filename}, {self.master.responses[self.filename].type}' ) self.top.destroy() self.master.set(self.master.rowid, self.master.columnid, self.filename) pass
def __init__(self, parent, title, body, row, column, value, response_file): """ Creeate pop up to replace cell. :param parent: :param title: :param body: :param row: :param column: :param value: """ ttk.Frame.__init__(self, parent) self.top = tkinter.Toplevel(parent) self.top.title(title) self.label = ttk.Label(self.top, text=body, justify=tkinter.LEFT) self.label.grid(row=1, column=1, sticky='E', padx=10, pady=10) self.e = ttk.Entry(self.top) self.e.grid(row=2, column=1, pady=10) self.e.focus_set() self.row = row self.column = column self.response_file = response_file self.button = ttk.Button(self.top, text=_("OK"), command=self.setValueButton) self.button.grid(row=3, column=1) self.button_cancel = ttk.Button(self.top, text=_("Cancel"), command=self.cancelButton) self.button_cancel.grid(row=3, column=2) logger.debug(f'popup: {(row, column, value)}')
def setValueButton(self): logger.debug('ok button hit') self.value = self.e.get() logger.debug(f'values: {self.value}') self.top.destroy() values = self.master.item(self.row)['values'] values[int(self.column[1:]) - 1] = self.value self.master.responses[self.response_file].response.addValue( self.row, self.value) self.master.item(self.row, values=values) self.master.responses[self.master.column( self.column)['id']].response[self.row] = self.value pass
def callback(self, event): rowid = self.identify_row(event.y) column = self.identify_column(event.x) selItems = self.selection() # col = int(column[1]) - 1 if rowid == '': logger.debug('Click on header') if column == '#0': logger.debug('Cell data:', 'row heading: ', selItems) else: # logger.debug(f'Cell data: column heading: {self["column"][column]}') _description = _( f'promptd to edit or save response: {self["column"][int(column[1:])-1]}.' ) self.rowid = rowid self.columnid = column filename = self.column(int(column[1:]) - 1)['id'] self.PopupSaveFile(self, f'Disco Response Manager - Save: ', _description, rowid, column, filename) else: if self.item(rowid)["values"][int(column[1:]) - 1] == 'None': logger.debug(f'Click on row: {rowid}') _description = _(f'Current {rowid}: Can not edit.') filename = self.column(int(column[1:]) - 1)['id'] PopupDialog(self, f'Update {filename}:', _description) else: logger.debug(f'Click on row: {rowid}') _description = _( f'Current {rowid}: {self.item(rowid)["values"][int(column[1:]) - 1]}.' ) filename = self.column(int(column[1:]) - 1)['id'] self.PopupUpdateValue(self, f'Update {filename}:', _description, rowid, column, selItems, filename)
def renderTree(self, all_responses): """ Use create table for self: walk each response.response[]... to get value for column list :param self: :param responses: :return: """ logger.debug(f'all_responses: {all_responses}') responses = dict(filter(lambda x: x[1].show, all_responses.items())) # responses = dict(list(filter(lambda x: x[1].show, all_responses.items()))[0:2]) self.responses = responses def heading(column): logger.debug(f"click! {column}") responses['success.json.erb'] = all_responses['success.json.erb'] successResponse = list(responses.keys())[0] columns = tuple(responses.keys()) self["columns"] = columns # self["displaycolumns"] = columns # self.column(successResponse, width=200) for title in columns: self.heading(title, text=title) all_keys = set() for response in responses: all_keys = all_keys.union(responses[response].allKeys) all_keys = sorted(all_keys) tree_list = self.insertTree(all_keys, responses) pass # self.tag_bind('missing', '<1>', callback=self.tag_callback) for (iid, values) in tree_list.items(): logger.debug(f'add: {iid}, {values}') try: self.insert(values.parent, 'end', iid, text=values.text, tags=values.tags, values=values.values) except tkinter.TclError as e: pass
def __init__(self, parent, responseFolder): logger.debug(f'init: {responseFolder}') ttk.Treeview.__init__(self, parent) self.responseFolder = responseFolder # self.tag_configure('diff', background='aquamarine1') self.tag_configure('missing', background='aquamarine1') #, relief='raised') vsb = ttk.Scrollbar(parent, orient='vertical', command=self.yview) hsb = ttk.Scrollbar(parent, orient='horizontal', command=self.xview) self.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) self.grid(column=0, row=0, sticky='nsew', in_=parent) vsb.grid(column=1, row=0, sticky='ns', in_=parent) hsb.grid(column=0, row=1, sticky='ew', in_=parent) parent.grid_columnconfigure(0, weight=1) parent.grid_rowconfigure(0, weight=1) # self.bind('<Button-1>', callback) self.bind('<Double-Button-1>', self.callback) if self.responseFolder != '' and os.path.exists(self.responseFolder): responses = self.getResponses(self.responseFolder) self.renderTree(responses)
def addValue(self, key, value): """ Find last key in self from key string Can only update existing entries. Need support to add elements for new value Add support for key as list of keys :param key: string of keys with DELIMITER :param value: value to update last key :return: None """ if isinstance(key, int): keys = [key] else: keys = key.split(self.DELIMITER) prior_part_key = keys.pop(0) my_dict = self logger.debug(f'keys: {list(keys)}, value: {value}') for part_key in keys: logger.debug(f'\tpart_key: {part_key}') if prior_part_key not in my_dict or my_dict[prior_part_key] is None: # add [] or {} based on part_key isnumeric or letters if part_key.isnumeric(): part_key = not (part_key) my_dict[prior_part_key] = [None] * (part_key + 1) logger.debug(f'\t{tabs}\t\tpart_key: {part_key}, numeric') else: my_dict[prior_part_key] = FlatDict() logger.debug( f'\t{tabs}\t\tpart_key: {part_key}, my_dict[prior_part_key]' ) my_dict = my_dict[prior_part_key] prior_part_key = part_key logger.debug(f'prior_part_key: {prior_part_key}, value: {value}') if prior_part_key.isnumeric(): prior_part_key = int(prior_part_key) my_dict[prior_part_key] = value return
def getValue(self, key): """ Key contains individual dict and list keys separated by ":" Returns final value from complex key. None is returned when partial key is not found Add support for key as list of keys :param key: string of keys with DELIMITER :return: value of final key """ if isinstance(key, int): keys = [key] else: keys = key.split(self.DELIMITER) my_dict = self logger.debug(f'keys: {list(keys)}') for part_key in keys: logger.debug(f'\tpart_key: {part_key}') if part_key.isnumeric(): part_key = int(part_key) elif part_key == '': return '' my_dict = my_dict[part_key] logger.debug(f'my_dict: {my_dict}') return my_dict
def tag_callback(self, event): rowid = self.identify_row(event.y) column = self.identify_column(event.x) selItems = self.selection() # col = int(column[1]) - 1 logger.debug(f'Click on tag: {rowid}') if int(column[1:]) == 0: logger.debug(f'index is row label: {column}') filename = 'Row Heading' return else: filename = self.column(int(column[1:]) - 1)['id'] if self.item(rowid)["values"][int(column[1:]) - 1] == 'None': _description = _( f'Current {rowid}: {self.item(rowid)["values"][int(column[1:]) - 1]}.\nCan not edit field' ) PopupDialog(self, f'Tag {filename}:', _description) else: _description = _( f'Current {rowid}: {self.item(rowid)["values"][int(column[1:]) - 1]}.' ) self.PopupUpdateValue(self, f'Tag {filename}:', _description, rowid, column, selItems)
def getResponses(self, responseFolder): logger.debug(f'Get responses from {responseFolder}') responses = FlatDict() responseFileSet = set(os.listdir(responseFolder)) successFile = 'success.json.erb' responseFileSet.discard(successFile) responseFiles = [successFile] + sorted(list(responseFileSet)) #[0:6] errorFiles = [] for filename in responseFiles: logger.debug(f'\tfilename: {filename}') jsonInput = open(os.path.join(responseFolder, filename), 'r').read() if jsonInput.startswith('<?xml'): errorFiles.append( f'{filename}, reason:xml is not support, yet') else: try: response = FlatDict(json.loads(jsonInput)) responses[filename] = RESPONSE_ENTRY( response['status'] == 'SUCCESS', response['status'].upper() == 'SUCCESS', response, response.getKeys(), 'json') except (ValueError, json.JSONDecodeError) as e: logger.info(f'json fails: filename:{filename}, reason:{e}') errorFiles.append(f'{filename}, reason:{e}') except RuntimeError as e: logger.info( f'RuntimeError: filename:{filename}, reason:{e}') errorFiles.append(f'{filename}, reason:{e}') if len(errorFiles) > 0: description = f'Folder: {responseFolder} contains unsupported files:' for filename in errorFiles: description += f'\n\t{filename}' PopupDialog(self, 'Error Disco Response Manager', description) logger.debug(f'problem with file {filename}') return responses
def cancelButton(self): logger.debug('cancel button hit') self.top.destroy() pass
def heading(column): logger.debug(f"click! {column}")
def insertTree(self, flat_keys, responses): """ return new_tree with row for each partial key from flatKeys parent, 'end', next_parent, None, text, tag_list, value_list Bug: Extra columns are added for dict with sub elements :param flat_keys: sorted list of full keys for all responses :param responses: list of RESPONSE_ENTRY :param table_entries: TABLE_ENTRY, parent text values tags :return: """ table_entries = {} responses_keys = list(responses.keys()) value = {'': {}} for response_file in responses.keys(): value[''][response_file] = responses[response_file].response for flat_key in flat_keys: tags = collections.OrderedDict( list(map(lambda x: ('', None), responses.keys()))) parent = '' flat_key_parts = flat_key.split(FlatDict.DELIMITER) flat_key_parts_list = [False] * len(flat_key_parts) logger.debug(f'flat key: {flat_key}') flat_key_parts_index = 0 reset_index = False while flat_key_parts_index < len(flat_key_parts): flat_key_part = flat_key_parts[flat_key_parts_index] flat_key_part_done = True if parent == '': next_parent = flat_key_part else: next_parent = FlatDict.DELIMITER.join( [parent, str(flat_key_part)]) if isinstance(flat_key_part, str) and flat_key_part.isnumeric(): flat_key_part = int(flat_key_part) flat_key_parts[flat_key_parts_index] = flat_key_part logger.debug(f'\tflat_key_part: {flat_key_part}') num_none_entries = 0 for responses_keys_index in range(len(responses_keys)): response_file = responses_keys[responses_keys_index] logger.debug(f'\t\tprocess file: {response_file}') if next_parent not in table_entries: table_entries[next_parent] = TABLE_ENTRY( parent, flat_key_part, [], [None] * len(responses_keys)) value[next_parent] = {response_file: None} table_entries[next_parent].tags[ responses_keys_index] = None logger.debug(f'\t\t\tAdding table row: {next_parent}') if value[parent][response_file] is None: table_entries[next_parent].values.append(None) value[next_parent][response_file] = None table_entries[next_parent].tags[ responses_keys_index] = 'missing' if next_parent not in tags or tags[ next_parent] == 'missing': tags[next_parent] = 'missing' logger.debug( f'\t\t\tcontinue missing element: {flat_key_part}') else: if isinstance(value[parent][response_file], dict): if tags[parent] == 'missing' or flat_key_part not in value[ parent][response_file]: table_entries[next_parent].values.append(None) value[next_parent][response_file] = None if next_parent not in tags or tags[ next_parent] == 'missing': tags[next_parent] = 'missing' table_entries[next_parent].tags[ responses_keys_index] = 'missing' # num_none_entries += 1 logger.debug( f'\t\t\tcontinue missing dict: {flat_key_part}' ) else: if isinstance( value[parent][response_file] [flat_key_part], (dict, list)): table_entries[next_parent].values.append( None) value[next_parent][response_file] = value[ parent][response_file][flat_key_part] tags[next_parent] = None logger.debug( f'\t\t\tcontinue next dict/list: {flat_key_part}' ) else: table_entries[next_parent].values.append( value[parent][response_file] [flat_key_part]) # value[next_parent][response_file] = value[parent][response_file][flat_key_part] # table_entries[next_parent].tags tags[next_parent] = None logger.debug( f'\t\t\tcontinue got a value: {value[parent][response_file][flat_key_part]}' ) flat_key_part_done = True elif isinstance(value[parent][response_file], list): if tags[parent] == 'missing' or flat_key_part >= len( value[parent][response_file]): table_entries[next_parent].values.append(None) value[next_parent][response_file] = None table_entries[next_parent].tags[ responses_keys_index] = 'missing' if next_parent not in tags or tags[ next_parent] == 'missing': tags[next_parent] = 'missing' num_none_entries += 1 logger.debug( f'\t\t\tcontinue missing list: {flat_key_part}' ) else: flat_key_parts_list[ flat_key_parts_index] = True if isinstance( value[parent][response_file] [flat_key_part], (dict, list)): table_entries[next_parent].values.append( None) value[next_parent][response_file] = value[ parent][response_file][flat_key_part] tags[next_parent] = None logger.debug( f'\t\t\tcontinue next dict/list: {flat_key_part}' ) else: table_entries[next_parent].values.append( value[parent][response_file] [flat_key_part]) # value tags[next_parent] = None logger.debug( f'\t\t\tcontinue got a value: {value[parent][response_file][flat_key_part]}' ) flat_key_part_done = flat_key_part >= ( len(value[parent]) - 1) else: table_entries[next_parent].values.append( value[parent][response_file][flat_key_part]) # value table_entries[next_parent].tags[ responses_keys_index] = None tags[next_parent] = None logger.debug( f'\t\t\tcontinue value: {value[parent][response_file][flat_key_part]}' ) # table_entries[next_parent].tags = tags[responses_keys_index] if flat_key_part_done: logger.debug( f'Flat key done: {flat_key_parts} - {table_entries[next_parent].tags.count(None)}' ) if table_entries[next_parent].tags.count('missing') == len( table_entries[next_parent].tags): # if num_none_entries == len(responses.keys()): # clean up extra None in last table entry # table_entries.pop(parent) table_entries.pop(next_parent) flat_key_parts_index = len(flat_key_parts) logger.debug( f'Remove extra list entries: {flat_key_parts_index}' ) # else: logger.debug(f'Done with: {parent}/{next_parent}') parent = next_parent flat_key_parts_index += 1 if flat_key_parts_index >= len(flat_key_parts) and sum( flat_key_parts_list): for index in range(1, len(flat_key_parts_list) + 2): if flat_key_parts_list[-index]: break flat_key_parts_index = len(flat_key_parts_list) - index flat_key_parts_list[flat_key_parts_index] = False for index in range(flat_key_parts_index + 1, len(flat_key_parts)): if isinstance(flat_key_parts[index], int): flat_key_parts[index] -= 1 # extra_entry_key = FlatDict.DELIMITER.join((map(lambda x: str(x), flat_key_parts[0:index]))) # table_entries.pop(extra_entry_key) flat_key_parts[index] = 0 flat_key_parts[flat_key_parts_index] += 1 parent = FlatDict.DELIMITER.join( (map(lambda x: str(x), flat_key_parts[0:flat_key_parts_index]))) logger.debug(f'New parent: {parent}') else: flat_key_parts[flat_key_parts_index] = flat_key_part + 1 logger.debug(f'Try another List entry: {flat_key_parts}') pass return table_entries
def updateTree(self, responseFolder): logger.debug(f'update: {responseFolder}') self.responseFolder = responseFolder if self.responseFolder != '' and os.path.exists(self.responseFolder): responses = self.getResponses(self.responseFolder) self.renderTree(responses)
def cancelButton(self): "Concel button feedback." logger.debug('cancel button hit') self.top.destroy() pass
def getKeys(self): """ get unique string of keys to values in response dict list use 0 for entry Add support to return keys as list :return: list of all key string to access elements """ response = self notDone = True keys = iter(response.keys()) key = None jsonStack = collections.deque() fullKeys = [] fullKey = [] logger.debug(f'keys: {response.keys()}') while notDone: tabs = '\t' * len(jsonStack) if isinstance(response, dict): key = next(keys, None) elif isinstance(response, list): if key is not None: if len(response) == 0: response = [0] key = 0 else: key = None logger.debug(f'\t{tabs}key: {key}') if key is None: if len(jsonStack) > 0: (response, fullKey, keys) = jsonStack.pop() else: notDone = False else: logger.debug(f'\t{tabs}\tresponse[key]: {response[key]}') if isinstance(response[key], (list, dict)): logger.debug(f'\t\t\t{tabs}list/dict') jsonStack.append((response, fullKey, keys)) fullKey = fullKey.copy() response = response[key] key = key if isinstance(key, str) else str(key) fullKey.append(key) if isinstance(response, dict): # need another append, response and fullKey sortedKeys = sorted(response.keys()) keys = iter(sortedKeys) else: response = [0] if len(response) == 0 else response keys = iter(response) pass else: logger.debug(f'\t\t\t{tabs}value') if len(response) > 0 and isinstance( response[key], (dict, list)): key = response[key] else: key = key if isinstance(key, str) else str(key) fullKeys.append(self.DELIMITER.join(fullKey + [key])) key = None logger.debug( f'{tabs}*** last fullKey: {fullKeys[-1] if len(fullKeys) > 0 else "start"}' ) return fullKeys