def importTableTabbed(self): """Import a file with a tab-delimited table with header row. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) tableFormat = nodeformat.NodeFormat(_('TABLE'), structure.treeFormats) structure.treeFormats.addTypeIfMissing(tableFormat) with self.pathObj.open(encoding=globalref.localTextEncoding) as f: headings = [ self.correctFieldName(name) for name in f.readline().split('\t') ] tableFormat.addFieldList(headings, True, True) lineNum = 1 for line in f: lineNum += 1 if line.strip(): entries = line.split('\t') node = treenode.TreeNode(tableFormat) structure.childList[0].childList.append(node) structure.addNodeDictRef(node) try: for heading in headings: node.data[heading] = entries.pop(0) except IndexError: pass # fewer entries than headings is OK if entries: self.errorMessage = ( _('Too many entries on Line {0}').format(lineNum)) return None # abort if too few headings structure.generateSpots(None) return structure
def mimeData(self, indexList): """Return a mime data object for the given node index branches. Arguments: indexList -- a list of node indexes to convert """ spots = [index.internalPointer() for index in indexList] # remove selections from the same branch TreeModel.storedDragSpots = [ spot for spot in spots if spot.parentSpotSet().isdisjoint(set(spots)) ] nodes = [spot.nodeRef for spot in TreeModel.storedDragSpots] TreeModel.storedDragModel = self struct = treestructure.TreeStructure(topNodes=nodes, addSpots=False) generics = { formatRef.genericType for formatRef in struct.treeFormats.values() if formatRef.genericType } for generic in generics: genericRef = self.treeStructure.treeFormats[generic] struct.treeFormats.addTypeIfMissing(genericRef) for formatRef in genericRef.derivedTypes: struct.treeFormats.addTypeIfMissing(formatRef) data = struct.fileData() dataStr = json.dumps(data, indent=0, sort_keys=True) mime = QMimeData() mime.setData('application/json', bytes(dataStr, encoding='utf-8')) return mime
def cmd_export(params): src_file = os.path.abspath(params[0]) dst_file = os.path.abspath(params[1]) print(f"command:\texport") print(f"\nsource:\t\t{src_file}\ndest:\t\t{dst_file}") src_path = pathlib.Path(src_file) dst_path = pathlib.Path(dst_file) import treestructure with src_path.open('r', encoding='utf-8') as src_handler: src_json = json.load(src_handler) src_struct = treestructure.TreeStructure(src_json) root_spots = src_struct.rootSpots() lines = [] for spot in root_spots: lines.extend(spot.nodeRef.outputEx(False, False)) if spot.nodeRef.formatRef.spaceBetween: lines.append('') lines = [(line + '\n') for line in lines] try: with dst_path.open('w', encoding='utf-8') as dst_handler: dst_handler.writelines(lines) except: raise
def copySelectedNodes(self): """Copy these node branches to the clipboard. """ nodes = self.selectedBranches() if not nodes: return clip = QApplication.clipboard() if clip.supportsSelection(): titleList = [] for node in nodes: titleList.extend(node.exportTitleText()) clip.setText('\n'.join(titleList), QClipboard.Selection) struct = treestructure.TreeStructure(topNodes=nodes, addSpots=False) generics = { formatRef.genericType for formatRef in struct.treeFormats.values() if formatRef.genericType } for generic in generics: genericRef = self.modelRef.treeStructure.treeFormats[generic] struct.treeFormats.addTypeIfMissing(genericRef) for formatRef in genericRef.derivedTypes: struct.treeFormats.addTypeIfMissing(formatRef) data = struct.fileData() dataStr = json.dumps(data, indent=0, sort_keys=True) mime = QMimeData() mime.setData('application/json', bytes(dataStr, encoding='utf-8')) clip.setMimeData(mime)
def importOldTreeLine(self): """Import an old TreeLine File (1.x or 2.x). Return the structure if import is successful, otherwise None. """ tree = ElementTree.ElementTree() try: tree.parse(str(self.pathObj)) except ElementTree.ParseError: tree = None if not tree or not tree.getroot().get('item') == 'y': fileObj = self.pathObj.open('rb') # decompress before decrypt to support TreeLine 1.4 and earlier fileObj, compressed = globalref.mainControl.decompressFile(fileObj) fileObj, encrypted = globalref.mainControl.decryptFile(fileObj) if not fileObj: return None if encrypted and not compressed: fileObj, compressed = ( globalref.mainControl.decompressFile(fileObj)) if compressed or encrypted: tree = ElementTree.ElementTree() try: tree.parse(fileObj) except ElementTree.ParseError: tree = None fileObj.close() if not tree or not tree.getroot().get('item') == 'y': return None version = tree.getroot().get('tlversion', '').split('.') try: self.treeLineImportVersion = [int(i) for i in version] except ValueError: pass self.treeLineRootAttrib = self.convertPrintData(tree.getroot().attrib) structure = treestructure.TreeStructure() idRefDict = {} linkList = [] self.loadOldTreeLineNode(tree.getroot(), structure, idRefDict, linkList, None) self.convertOldNodes(structure) linkRe = re.compile(r'<a [^>]*href="#(.*?)"[^>]*>.*?</a>', re.I | re.S) for node, fieldName in linkList: text = node.data[fieldName] startPos = 0 while True: match = linkRe.search(text, startPos) if not match: break newId = idRefDict.get(match.group(1), '') if newId: text = text[:match.start(1)] + newId + text[match.end(1):] startPos = match.start(1) node.data[fieldName] = text structure.generateSpots(None) structure.treeFormats.updateDerivedRefs() for nodeFormat in structure.treeFormats.values(): nodeFormat.updateLineParsing() return structure
def importOdfText(self): """Import an ODF format text file outline. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) structure.removeNodeDictRef(structure.childList[0]) structure.childList = [] odfFormat = structure.treeFormats[treeformats.defaultTypeName] odfFormat.addField(textFieldName) odfFormat.changeOutputLines([ '<b>{{*{0}*}}</b>'.format(nodeformat.defaultFieldName), '{{*{0}*}}'.format(textFieldName) ]) odfFormat.formatHtml = True try: with zipfile.ZipFile(str(self.pathObj), 'r') as f: text = f.read('content.xml') except (zipfile.BadZipFile, KeyError): return None try: rootElement = ElementTree.fromstring(text) except ElementTree.ParseError: return None nameSpace = '{urn:oasis:names:tc:opendocument:xmlns:text:1.0}' headerTag = '{0}h'.format(nameSpace) paraTag = '{0}p'.format(nameSpace) numRegExp = re.compile(r'.*?(\d+)$') parents = [structure] prevLevel = 0 for elem in rootElement.iter(): if elem.tag == headerTag: style = elem.get('{0}style-name'.format(nameSpace), '') try: level = int(numRegExp.match(style).group(1)) except AttributeError: return None if level < 1 or level > prevLevel + 1: return None parents = parents[:level] node = treenode.TreeNode(odfFormat) structure.addNodeDictRef(node) parents[-1].childList.append(node) node.data[nodeformat.defaultFieldName] = ''.join( elem.itertext()) parents.append(node) prevLevel = level elif elem.tag == paraTag: text = ''.join(elem.itertext()) origText = node.data.get(textFieldName, '') if origText: text = '{0}<br />{1}'.format(origText, text) node.data[textFieldName] = text structure.generateSpots(None) return structure
def importTableCsvLevels(self): """Import a CSV-delimited table file with level column, header row. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addSpots=False) tableFormat = nodeformat.NodeFormat(_('TABLE'), structure.treeFormats) structure.treeFormats.addTypeIfMissing(tableFormat) nodeList = [] with self.pathObj.open(newline='', encoding=globalref.localTextEncoding) as f: reader = csv.reader(f) try: headings = [ self.correctFieldName(name) for name in next(reader) ][1:] tableFormat.addFieldList(headings, True, True) for entries in reader: if entries: node = treenode.TreeNode(tableFormat) structure.addNodeDictRef(node) try: level = int(entries.pop(0)) except ValueError: self.errorMessage = (_('Invalid level number on ' 'line {0}').format( reader.line_num)) return None # abort nodeList.append((node, level)) try: for heading in headings: node.data[heading] = entries.pop(0) except IndexError: pass # fewer entries than headings is OK if entries: self.errorMessage = (_('Too many entries on ' 'Line {0}').format( reader.line_num)) return None # abort if too few headings except csv.Error: self.errorMessage = (_('Bad CSV format on Line {0}').format( reader.line_num)) return None # abort if nodeList: if structure.loadChildNodeLevels(nodeList): structure.generateSpots(None) return structure self.errorMessage = (_('Invalid level structure')) return None
def importTreePad(self): """Import a Treepad file, text nodes only. Return the model if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) structure.removeNodeDictRef(structure.childList[0]) structure.childList = [] tpFormat = structure.treeFormats[treeformats.defaultTypeName] tpFormat.addFieldList([textFieldName], False, True) tpFormat.fieldDict[textFieldName].changeType('SpacedText') try: with self.pathObj.open(encoding=globalref.localTextEncoding) as f: textList = f.read().split('<end node> 5P9i0s8y19Z') except UnicodeDecodeError: with self.pathObj.open(encoding='latin-1') as f: textList = f.read().split('<end node> 5P9i0s8y19Z') except UnicodeDecodeError: return None nodeList = [] for text in textList: text = text.strip() if text: try: text = text.split('<node>', 1)[1].lstrip() lines = text.split('\n') title = lines[0] level = int(lines[1]) lines = lines[2:] except (ValueError, IndexError): return None node = treenode.TreeNode(tpFormat) node.data[nodeformat.defaultFieldName] = title node.data[textFieldName] = '\n'.join(lines) node.level = level nodeList.append(node) structure.addNodeDictRef(node) parentList = [] for node in nodeList: if node.level != 0: parentList = parentList[:node.level] node.parent = parentList[-1] parentList[-1].childList.append(node) parentList.append(node) structure.childList = [nodeList[0]] structure.generateSpots(None) return structure
def importXbel(self): """Import an XBEL format bookmark file. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure() structure.treeFormats = self.createBookmarkFormat() tree = ElementTree.ElementTree() try: tree.parse(str(self.pathObj)) except ElementTree.ParseError: return None self.loadXbelNode(tree.getroot(), structure, None) if structure.childList: structure.generateSpots(None) return structure return None
def importMozilla(self): """Import an HTML mozilla-format bookmark file. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure() structure.treeFormats = self.createBookmarkFormat() with self.pathObj.open(encoding='utf-8') as f: text = f.read() try: handler = HtmlBookmarkHandler(structure) handler.feed(text) handler.close() except ValueError: return None structure.generateSpots(None) return structure
def importTextLines(self): """Import a text file, creating one node per line. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) nodeFormat = structure.childList[0].formatRef structure.removeNodeDictRef(structure.childList[0]) structure.childList = [] with self.pathObj.open(encoding=globalref.localTextEncoding) as f: for line in f: line = line.strip() if line: node = treenode.TreeNode(nodeFormat) structure.childList.append(node) structure.addNodeDictRef(node) node.data[nodeformat.defaultFieldName] = line structure.generateSpots(None) return structure
def importTableCsv(self): """Import a file with a CSV-delimited table with header row. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) tableFormat = nodeformat.NodeFormat(_('TABLE'), structure.treeFormats) structure.treeFormats.addTypeIfMissing(tableFormat) with self.pathObj.open(newline='', encoding=globalref.localTextEncoding) as f: reader = csv.reader(f) try: headings = [ self.correctFieldName(name) for name in next(reader) ] tableFormat.addFieldList(headings, True, True) for entries in reader: if entries: node = treenode.TreeNode(tableFormat) structure.childList[0].childList.append(node) structure.addNodeDictRef(node) try: for heading in headings: node.data[heading] = entries.pop(0) except IndexError: pass # fewer entries than headings is OK if entries: self.errorMessage = (_('Too many entries on ' 'Line {0}').format( reader.line_num)) return None # abort if too few headings except csv.Error: self.errorMessage = (_('Bad CSV format on Line {0}').format( reader.line_num)) return None # abort structure.generateSpots(None) return structure
def importTextPara(self): """Import a text file, creating one node per paragraph. Blank line delimited. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) nodeFormat = structure.childList[0].formatRef structure.removeNodeDictRef(structure.childList[0]) structure.childList = [] with self.pathObj.open(encoding=globalref.localTextEncoding) as f: text = f.read() paraList = text.split('\n\n') for para in paraList: para = para.strip() if para: node = treenode.TreeNode(nodeFormat) structure.childList.append(node) structure.addNodeDictRef(node) node.data[nodeformat.defaultFieldName] = para structure.generateSpots(None) return structure
def importXml(self): """Import a non-treeline generic XML file. Return the structure if import is successful, otherwise None. """ structure = treestructure.TreeStructure() tree = ElementTree.ElementTree() try: tree.parse(str(self.pathObj)) self.loadXmlNode(tree.getroot(), structure, None) except ElementTree.ParseError: return None for elemFormat in structure.treeFormats.values(): if not elemFormat.getTitleLine(): # fix formats if required elemFormat.changeTitleLine(elemFormat.name) for fieldName in elemFormat.fieldNames(): elemFormat.addOutputLine('{0}="{{*{1}*}}"'.format( fieldName, fieldName)) if not elemFormat.fieldDict: elemFormat.addField(genericXmlTextFieldName) if structure.childList: structure.generateSpots(None) return structure return None
def importTabbedText(self): """Import a file with tabbed title structure. Return the structure if import is successful, otherwise None """ structure = treestructure.TreeStructure(addDefaults=True, addSpots=False) formatRef = structure.childList[0].formatRef structure.removeNodeDictRef(structure.childList[0]) structure.childList = [] nodeList = [] with self.pathObj.open(encoding=globalref.localTextEncoding) as f: for line in f: text = line.strip() if text: level = line.count('\t', 0, len(line) - len(line.lstrip())) node = treenode.TreeNode(formatRef) node.setTitle(text) structure.addNodeDictRef(node) nodeList.append((node, level)) if nodeList and structure.loadChildNodeLevels(nodeList): structure.generateSpots(None) return structure return None
def cmd_render(params): node_type = params[0] if not node_type in AVAILABLE_NODE_TYPES: raise ValueError( f"invalid node type: {node_type}. available types: {AVAILABLE_NODE_TYPES}" ) output_type = params[1] if not output_type in AVAILABLE_OUTPUT_TYPES: raise ValueError( f"invalid output type: {output_type}. available types: {AVAILABLE_OUTPUT_TYPES}" ) output_suffix = params[2] repo_file = os.path.abspath(params[3]) output_dir = params[4] template_formats = get_template_formats(node_type, output_type, output_suffix) print(f"command: [render] tree nodes via template") print(f"node type: {node_type}") print(f"output type: {output_type}") print(f"output suffix: {output_suffix}") print(f"repo: {repo_file}") print(f"template formats: {len(template_formats)}") print(f"output dir: {output_dir}") repo_path = Path(repo_file) with repo_path.open('r', encoding='utf-8') as repo_handler: # merge the repo with formats repo_json = json.load(repo_handler) merged_formats = merge_formats(repo_json["formats"], template_formats) repo_json["formats"] = merged_formats # locate the root_stpos of the node type import treestructure repo_struct = treestructure.TreeStructure(repo_json) root_spots = repo_struct.rootSpots() selected_root_spot = None for spot in root_spots: if spot.nodeRef.data['Name'] == node_type: selected_root_spot = spot break if not selected_root_spot: raise Exception(f"Can't find node type: {node_type}") # iterate each child and render for child_node in selected_root_spot.nodeRef.childList: dst_dir = output_dir.replace('uid', child_node.uId) Path(dst_dir).mkdir(parents=True, exist_ok=True) dst_file = f"{dst_dir}/index.{output_suffix}" # get output lines lines = format_output_ext(child_node, False, False) # write to file dst_path = Path(dst_file) with dst_path.open('w', encoding='utf-8') as dst_handler: dst_handler.writelines(lines) print(f"output done. file: {dst_file}")
def visualConfigStructure(self, fileName): """Export a TreeLine structure containing the config types and fields. Returns the structure. Arguments: fileName -- the name for the root node """ structure = treestructure.TreeStructure() structure.treeFormats = TreeFormats() rootFormat = nodeformat.NodeFormat(_showConfRootTypeName, structure.treeFormats, addDefaultField=True) structure.treeFormats[rootFormat.name] = rootFormat typeFormat = nodeformat.NodeFormat(_showConfTypeTypeName, structure.treeFormats, addDefaultField=True) typeFormat.addField(_showConfTypeTitleFieldName) typeFormat.addField(_showConfTypeOutputFieldName) typeFormat.addField(_showConfTypeSpaceFieldName, {'fieldtype': 'Boolean'}) typeFormat.addField(_showConfTypeHtmlFieldName, {'fieldtype': 'Boolean'}) typeFormat.addField(_showConfTypeBulletsFieldName, {'fieldtype': 'Boolean'}) typeFormat.addField(_showConfTypeTableFieldName, {'fieldtype': 'Boolean'}) typeFormat.addField(_showConfTypeChildFieldName) typeFormat.addField(_showConfTypeIconFieldName) typeFormat.addField(_showConfTypeGenericFieldName) typeFormat.addField(_showConfTypeConditionFieldName) typeFormat.addField(_showConfTypeSeparatorFieldName) typeFormat.addField(_showConfTypeChildLimitFieldName) structure.treeFormats[typeFormat.name] = typeFormat fieldFormat = nodeformat.NodeFormat(_showConfFieldTypeName, structure.treeFormats, addDefaultField=True) fieldFormat.addField(_showConfFieldTypeFieldName) fieldFormat.addField(_showConfFieldFormatFieldName) fieldFormat.addField(_showConfFieldPrefixFieldName) fieldFormat.addField(_showConfFieldSuffixFieldName) fieldFormat.addField(_showConfFieldInitFieldName) fieldFormat.addField(_showConfFieldLinesFieldName, {'fieldtype': 'Number'}) fieldFormat.addField(_showConfFieldSortKeyFieldName, {'fieldtype': 'Number'}) fieldFormat.addField(_showConfFieldSortDirFieldName, {'fieldtype': 'Boolean'}) fieldFormat.addField(_showConfFieldEvalHtmlFieldName, {'fieldtype': 'Boolean'}) line = '{{*{0}*}} ({{*{1}*}})'.format(nodeformat.defaultFieldName, _showConfFieldTypeFieldName) fieldFormat.changeTitleLine(line) fieldFormat.changeOutputLines([line]) structure.treeFormats[fieldFormat.name] = fieldFormat rootNode = treenode.TreeNode(rootFormat) structure.childList.append(rootNode) structure.addNodeDictRef(rootNode) rootNode.data[nodeformat.defaultFieldName] = fileName for typeName in self.typeNames(): typeNode = treenode.TreeNode(typeFormat) rootNode.childList.append(typeNode) structure.addNodeDictRef(typeNode) typeNode.data[nodeformat.defaultFieldName] = typeName titleLine = self[typeName].getTitleLine() outputList = self[typeName].getOutputLines() if self[typeName].formatHtml: titleLine = xml.sax.saxutils.escape(titleLine) outputList = [ xml.sax.saxutils.escape(line) for line in outputList ] outputLines = '<br>\n'.join(outputList) typeNode.data[_showConfTypeTitleFieldName] = titleLine typeNode.data[_showConfTypeOutputFieldName] = outputLines spaceBetween = repr(self[typeName].spaceBetween) typeNode.data[_showConfTypeSpaceFieldName] = spaceBetween formatHtml = repr(self[typeName].formatHtml) typeNode.data[_showConfTypeHtmlFieldName] = formatHtml useBullets = repr(self[typeName].useBullets) typeNode.data[_showConfTypeBulletsFieldName] = useBullets useTables = repr(self[typeName].useTables) typeNode.data[_showConfTypeTableFieldName] = useTables typeNode.data[_showConfTypeChildFieldName] = ( self[typeName].childType) typeNode.data[_showConfTypeIconFieldName] = ( self[typeName].iconName) typeNode.data[_showConfTypeGenericFieldName] = ( self[typeName].genericType) if self[typeName].conditional: condition = self[typeName].conditional.conditionStr() typeNode.data[_showConfTypeConditionFieldName] = condition separator = self[typeName].outputSeparator typeNode.data[_showConfTypeSeparatorFieldName] = separator childLimit = ','.join(sorted(list(self[typeName].childTypeLimit))) typeNode.data[_showConfTypeChildLimitFieldName] = childLimit fieldSortKeyDict = {} fieldSortSet = False for field in self[typeName].fields(): fieldSortKeyDict[field.name] = repr(field.sortKeyNum) if field.sortKeyNum != 0: fieldSortSet = True if not fieldSortSet: sortField = list(self[typeName].fields())[0] fieldSortKeyDict[sortField.name] = repr(1) for field in self[typeName].fields(): fieldNode = treenode.TreeNode(fieldFormat) typeNode.childList.append(fieldNode) structure.addNodeDictRef(fieldNode) fieldNode.data[nodeformat.defaultFieldName] = field.name fieldNode.data[_showConfFieldTypeFieldName] = field.typeName fieldNode.data[_showConfFieldFormatFieldName] = field.format fieldNode.data[_showConfFieldPrefixFieldName] = field.prefix fieldNode.data[_showConfFieldSuffixFieldName] = field.suffix fieldNode.data[_showConfFieldInitFieldName] = field.initDefault numLines = repr(field.numLines) fieldNode.data[_showConfFieldLinesFieldName] = numLines sortKeyNum = fieldSortKeyDict[field.name] fieldNode.data[_showConfFieldSortKeyFieldName] = sortKeyNum sortKeyFwd = repr(field.sortKeyForward) fieldNode.data[_showConfFieldSortDirFieldName] = sortKeyFwd evalHtml = repr(field.evalHtml) fieldNode.data[_showConfFieldEvalHtmlFieldName] = evalHtml structure.generateSpots(None) return structure