def Main(): cgiEnv = lib_common.CgiEnv() grph = cgiEnv.GetGraph() # Not really useful. grph.add((lib_common.nodeMachine, pc.property_hostname, lib_common.NodeLiteral(lib_util.currentHostname))) mapToProc = {} for proc in CIM_Process.ProcessIter(): # TODO: Instead, should test psutil version !!! try: FunctionProcess(mapToProc, proc) except CIM_Process.AccessDenied: pass except Exception: lib_common.ErrorMessageHtml("Unexpected error:" + str(sys.exc_info()[0])) # sys.stderr.write( "Leaving processes enumeration\n" ) addedProcs = {} # Now display only memory maps with more than one process linked to it. for mapPath, procLst in lib_util.six_iteritems(mapToProc): if len(procLst) <= 0: continue uriMemMap = lib_common.gUriGen.MemMapUri(mapPath) for pid in procLst: try: nodeProcess = addedProcs[pid] except KeyError: nodeProcess = lib_common.gUriGen.PidUri(pid) addedProcs[pid] = nodeProcess grph.add((nodeProcess, pc.property_memmap, uriMemMap)) # sys.stderr.write( "Leaving second maps enumeration\n" ) # TODO: They could also be displayed based on the hierarchy of their # associated file in the directory tree. for pid, nodeProcess in lib_util.six_iteritems(addedProcs): grph.add((nodeProcess, pc.property_pid, lib_common.NodeLiteral(pid))) # TODO: Petit bug: Ca duplique les memmap. Forcement, l'affichage en tables # suppose que c'est un arbre. Mais c'est plus rapide et plus clair. # cgiEnv.OutCgiRdf("",[pc.property_memmap]) cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def CreateObjs(grph,rootNode,directoryName,objectsByLocation,paramExplodeClasses): DEBUG("directoryName=%s num=%d", directoryName, len(objectsByLocation)) for (locationFile, v1) in lib_util.six_iteritems(objectsByLocation): DEBUG("locationFile=%s",locationFile) # TODO: Eventuellement exploser selon les sous-directorys nodeFile = lib_common.gUriGen.FileUri( locationFile ) grph.add( ( rootNode, pc.property_directory, nodeFile ) ) for (compounddefKind, v2) in v1.items(): #sys.stderr.write("compounddefKind=%s\n"%compounddefKind) for (compoundName, v3) in v2.items(): #sys.stderr.write("compoundName=%s\n"%compoundName) for (memberKind, v4) in v3.items(): #sys.stderr.write("memberKind=%s\n"%memberKind) for (memberDefinition, listTypes) in v4.items(): if memberKind == "function": if( len(listTypes) > 1 ): concatTypes = ",".join(listTypes[1:]) else: concatTypes = "" # funcName = listTypes[0] + " " + memberName + "(" + concatTypes + ")" funcName = memberDefinition + "(" + concatTypes + ")" #nodeFunction = lib_common.gUriGen.SymbolUri( funcName, locationFile ) #if nodeFile: # grph.add( ( nodeFile, pc.property_member, nodeFunction ) ) DisplayDefinition(grph,nodeFile,locationFile,funcName,paramExplodeClasses) elif memberKind == "variable": DisplayDefinition(grph,nodeFile,locationFile,memberDefinition,paramExplodeClasses) # nodeVariable = lib_common.gUriGen.SymbolUri( memberName, locationFile ) #nodeVariable = lib_common.gUriGen.SymbolUri( memberDefinition, locationFile ) #if nodeFile: # grph.add( ( nodeFile, pc.property_member, nodeVariable ) ) return
def AddCIMClasses(grph, rootNode, entity_host, nameSpace, className, entity_id): # Maybe some of these servers are not able to display anything about this object. pairNameNodeWbem = None if wbemOk: if lib_wbem.ValidClassWbem(entity_host, className): pairNameNodeWbem = CreateWbemNode(grph, rootNode, entity_host, nameSpace, className, entity_id) pairNameNodeWmi = None if lib_wmi.ValidClassWmi(className): pairNameNodeWmi = CreateWmiNode(grph, rootNode, entity_host, nameSpace, className, entity_id) # Match the two inheritance trees. if pairNameNodeWbem and pairNameNodeWmi: for (baseClsNam, nodeWbem) in lib_util.six_iteritems(pairNameNodeWbem): try: nodeWmi = pairNameNodeWmi[baseClsNam] except KeyError: continue nodeClsAll = lib_util.EntityClassNode(baseClsNam, nameSpace, entity_host, "WBEM ou WMI") grph.add((nodeClsAll, pc.property_wbem_data, nodeWbem)) grph.add((nodeClsAll, pc.property_wmi_data, nodeWmi))
def AddCIMClasses(grph, root_node, entity_host, name_space, class_name, entity_id): """entity_type = "CIM_Process", "Win32_Service" etc... This might happen at an intermediary level, with inheritance (To be implemented). Maybe some of these servers are not able to display anything about this object.""" pair_name_node_wbem = None if wbem_ok: if lib_wbem.ValidClassWbem(class_name): pair_name_node_wbem = _create_wbem_node(grph, root_node, entity_host, name_space, class_name, entity_id) pair_name_node_wmi = None if lib_wmi.ValidClassWmi(class_name): pair_name_node_wmi = _create_wmi_node(grph, root_node, entity_host, name_space, class_name, entity_id) # Match the two inheritance trees. if pair_name_node_wbem and pair_name_node_wmi: for base_cls_nam, node_wbem in lib_util.six_iteritems( pair_name_node_wbem): try: nodeWmi = pair_name_node_wmi[base_cls_nam] except KeyError: continue node_cls_all = lib_util.EntityClassNode(base_cls_nam, name_space, entity_host, "WBEM ou WMI") grph.add((node_cls_all, pc.property_wbem_data, node_wbem)) grph.add((node_cls_all, pc.property_wmi_data, nodeWmi))
def Main(): cgiEnv = lib_common.ScriptEnvironment() grph = cgiEnv.GetGraph() grph.add((lib_common.nodeMachine, pc.property_hostname, lib_util.NodeLiteral(lib_util.currentHostname))) # This is a dictionary of memory-mapped files to the processes using them. map_to_proc = {} for proc in psutil.process_iter(): try: # The pid is added to the list of each map used by this process. # New maps are added, and also the pid is added to existing of new maps. function_process(map_to_proc, proc) except CIM_Process.AccessDenied: # Most memory maps and processes cannot be access for security reasons. pass except Exception as exc: lib_common.ErrorMessageHtml("Unexpected error:" + exc) # This maps the pid to its rdf node. added_procs = {} # Now display only memory maps with more than one process linked to it. for map_path, proc_lst in lib_util.six_iteritems(map_to_proc): if len(proc_lst) <= 0: continue uri_mem_map = lib_uris.gUriGen.MemMapUri(map_path) for pid in proc_lst: try: node_process = added_procs[pid] except KeyError: node_process = lib_uris.gUriGen.PidUri(pid) added_procs[pid] = node_process grph.add((node_process, pc.property_memmap, uri_mem_map)) # TODO: They could also be displayed based on the hierarchy of their associated file in the directory tree. for pid, node_process in lib_util.six_iteritems(added_procs): grph.add((node_process, pc.property_pid, lib_util.NodeLiteral(pid))) cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
def Rdf2Dot(grph, logfil, stream, CollapsedProperties): fieldsSet = collections.defaultdict(list) # This maps RDFLIB nodes to DOT label names. dictRdf2Dot = {} # This returns the DOT label of a RDFLIB, and creates a new one if necessary. def RdfNodeToDotLabel(x): try: return dictRdf2Dot[x] except KeyError: nodelabel = "nd_%d" % len(dictRdf2Dot) dictRdf2Dot[x] = nodelabel return nodelabel # The QName is an abbreviation of URI reference with the namespace function for XML. # Edge label. # Transforms "http://primhillcomputers.com/ontologies/ppid" into "ppid" # TODO: Beware, a CGI parameter might be there. CGIPROP def qname(x, grph): try: q = grph.compute_qname(x) # q[0] is the shortened namespace "ns" # Could return q[0] + ":" + q[2] return q[2] except: return x # Nothing really interesting at the moment, just hardcodes. return lib_properties.prop_color(prop) # The input is any Python object. # This returns a simple object which can be transformed into a string. # If the input is a container, it returns a HTML table. def FormatElementAux(val, depth=0): if val is None: return "" try: int(val) return val except: pass try: float(val) return val except: pass if isinstance(val, dict): subTable = "" # TODO: Consider using six.iteritems. for subKey, subVal in val.items(): subTd = FormatPair(subKey, subVal, depth + 1) if subTd: subTable += "<tr>%s</tr>" % subTd return "<table border='0'>%s</table>" % subTable # Note: Recursive list are not very visible. if isinstance(val, (list, tuple)): # If this is an empty list or tuple. if not val: # return "(Empty)" # Empty set character in UTF8 return "{" + "∅" + "}" if depth % 2 == 0: subTable = "" for subElement in val: subTd = FormatElement(subElement, depth + 1) subTable += "<tr>%s</tr>" % subTd return "<table border='0'>%s</table>" % subTable else: subTable = "" for subElement in val: subTd = FormatElement(subElement, depth + 1) subTable += subTd return "<table border='0'><tr>%s</tr></table>" % subTable try: decodVal = json.loads(val) return FormatElementAux(decodVal, depth + 1) except ValueError: # It is a string which cannot be converted to json. val = cgi.escape(val) return lib_exports.StrWithBr(val) except TypeError: # "Expected a string or buffer" # It is not a string, so it could be a datetime.datetime val = cgi.escape(str(val)) return lib_exports.StrWithBr(val) return "FormatElement failure" def FormatElement(val, depth=0): if lib_kbase.IsLink(val): valTitle = "FormatElement " + ExternalToTitle(val) valTitleUL = lib_exports.DotUL(valTitle) return "<td align='left' balign='left' border='0' href='%s'>%s</td>" % ( val, valTitleUL) resStr = FormatElementAux(val, depth) return "<td align='left' balign='left' border='0'>%s</td>" % resStr # Prints a key-value pair as two TD tags, to go in an HTML table. def FormatPair(key, val, depth=0): colFirst = "<td align='left' valign='top' border='0'>%s</td>" % lib_exports.DotBold( key) colSecond = FormatElement(val, depth + 1) return colFirst + colSecond # Display in the DOT node the list of its literal properties. def FieldsToHtmlVertical(grph, the_fields): props = {} idx = 0 # TODO: The sort must put at first, some specific keys. # For example, sources_top/nmap_run.py, the port number as an int (Not a string) # Also, filenames, case-sensitive or not. for (key, val) in sorted(the_fields): # This should come first, but it does not so we prefix with "----". Hack ! if key == pc.property_information: # Completely left-aligned. Col span is 2, approximate ratio. val = lib_exports.StrWithBr(val, 2) currTd = "<td align='left' balign='left' colspan='2'>%s</td>" % val elif IsFlatProperty(key): urlTxt = lib_naming.ParseEntityUri(val)[0] splitTxt = lib_exports.StrWithBr(urlTxt, 2) # The text of the link must be underlined. currTd = '<td href="%s" align="left" colspan="2">%s</td>' % ( val, lib_exports.DotUL(splitTxt)) else: key_qname = qname(key, grph) # This assumes: type(val) == 'rdflib.term.Literal' # sys.stderr.write("FORMAT ELEMENT: %s\n" %(dir(val))) if lib_kbase.IsLiteral(val): currTd = FormatPair(key_qname, val.value) else: currTd = FormatPair(key_qname, val) props[idx] = currTd idx += 1 return props # Ca liste les labels des objects qui apparaissent dans les blocs, # et pointent vers le nom du record. dictCollapsedObjectLabelsToSubjectLabels = {} # This contains, for each node (subject), the related node (object) linked # to it with a property to be displayed in tables instead of individual nodes. dictPropsCollapsedSubjectsToObjectLists = {} for collapsPropObj in CollapsedProperties: collapsPropNam = lib_exports.PropToShortPropNam(collapsPropObj) dictPropsCollapsedSubjectsToObjectLists[ collapsPropNam] = collections.defaultdict(list) # TODO: (TRANSLATE THIS) Une premiere passe pour batir l'arbre d'une certaine propriete. # Si pas un DAG, tant pis, ca fera un lien en plus. # ON voulait batir des records, mais les nodes dans un record ne peuvent pas # avoir un URL: Donc ca va pas evidemment. # HTML-LIKE Labels avec PORT et PORTPOS. # CA VA AUSSI SIMPLIFIER L'AFFICHAGE DES TRUCS ENORMES: Modules, Fichiers etc... # Et on pourra trier car il y a un ordre. # Donc ca doit etre facile d'ajouter des proprietes affichees comme ca. logfil.write(TimeStamp() + " Rdf2Dot: First pass\n") # New intermediary node created. def CollapsedLabel(collapsPropNam, subjNam): return "R_" + collapsPropNam + "_" + subjNam # Called mainly from entity.py. If S points vers O, transforms "O" => "R_S:O" # Accordingly we create an edge: "S" => "R_S" def SubjNamFromCollapsed(collapsPropNam, subjNam): #sys.stderr.write("ADDING1 subjNam=%s collapsPropNam=%s\n" % (subjNam,collapsPropNam)) collapsedSubjNam = dictCollapsedObjectLabelsToSubjectLabels[subjNam][ collapsPropNam] #sys.stderr.write("ADDING2 subjNam=%s collapsPropNam=%s\n" % (subjNam,collapsPropNam)) newSubjNam = CollapsedLabel(collapsPropNam, collapsedSubjNam) + ":" + subjNam #sys.stderr.write("ADDED collapsedSubjNam=%s newSubjNam=%s collapsPropNam=%s\n" % (collapsedSubjNam,newSubjNam,collapsPropNam)) return newSubjNam # This is sorted so the result is deterministic. Very small performance impact. # Any order will do as long as the result is always the same. sortedGrph = sorted(grph) # TODO: Loop only on the "collapsed" properties, the ones whose objects must be displayed # in tables, instead of links - if only they have a single subject. Otherwise it cannot work. for subj, prop, obj in sortedGrph: # Objects linked with these properties, are listed in a table, instead of distinct nodes in a graph. if prop in CollapsedProperties: # TODO: We lose the property, unfortunately. Should make a map: subject => prop => object ? subjNam = RdfNodeToDotLabel(subj) propNam = lib_exports.PropToShortPropNam(prop) dictPropsCollapsedSubjectsToObjectLists[propNam][subj].append(obj) # Maybe we already entered it: Not a problem. objNam = RdfNodeToDotLabel(obj) # CollapsedProperties can contain only properties which define a tree, # as visibly the "object" nodes can have one ancestor only. try: # TODO: We should check if a node appears in two tables, # associated to two properties and/or two parent node. dictCollapsedObjectLabelsToSubjectLabels[objNam][ propNam] = subjNam except KeyError: dictCollapsedObjectLabelsToSubjectLabels[objNam] = dict() dictCollapsedObjectLabelsToSubjectLabels[objNam][ propNam] = subjNam # For getting the node of an object, as it might be in a table. def RdfNodeToDotLabelExtended(obj, prop): objNam = RdfNodeToDotLabel(obj) try: dictOfProps = dictCollapsedObjectLabelsToSubjectLabels[objNam] except KeyError: # sys.stderr.write("RdfNodeToDotLabelExtended propNam=%s objNam=%s\n"%(propNam,objNam) ) return objNam # Let's hope there is only one collapsed property for this node. Otherwise, it means # that this node would be displayed in two different tables. It happened... if not prop is None: propNam = lib_exports.PropToShortPropNam(prop) try: # Maybe this property is not collapsed. subjNam = dictOfProps[propNam] except KeyError: prop = None # Maybe the property is not known, if the node is the subject. # Or the property is not collapsed. if prop is None: # In Python3, keys() is an iterable. No need to create a list. #propNam = list(dictOfProps.keys())[0] #propNam = dictOfProps.keys()[0] for propNam in dictOfProps.keys(): break # First property available. subjNam = dictOfProps[propNam] newObjNam = CollapsedLabel(propNam, subjNam) + ":" + objNam return newObjNam # Now we know that we have seen all nodes in a collapsed property. for subj, prop, obj in sortedGrph: if prop in CollapsedProperties: continue # Maybe the subject node belongs to a table, but the property is not known. subjNam = RdfNodeToDotLabelExtended(subj, None) if lib_kbase.IsLink(obj): prp_col = lib_properties.prop_color(prop) # TODO: GENERALIZE THIS TO ALL COMMUTATIVE PROPERTIES. # THAT IS: PROPERTIES WHOSE TRIPLES ARE MERGED WHEN # WE HAVE AT THE SAME TIME: (Subj,Prop,Obj) and (Obj,Prop,Subj). # WHEN THIS HAPPENS, THE ARROW MUST BE BIDIRECTIONAL. # TODO: All commutative relation have bidirectional arrows. # At the moment, only one property can be bidirectional. if prop == pc.property_socket_end: # BEWARE, MAYBE THIS IS A PORT INTO A TABLE. SO IT HAS TO BE PREFIXED BY THE RECORD NAME. objNam = RdfNodeToDotLabelExtended(obj, prop) if (obj, prop, subj) in grph: if subjNam < objNam: stream.write( pattEdgeBiDir % (subjNam, objNam, prp_col, qname(prop, grph))) else: # One connection only: We cannot see the other. stream.write(pattEdgeOrien % (subjNam, objNam, prp_col, qname(prop, grph))) elif prop in [ pc.property_rdf_data_nolist1, pc.property_rdf_data_nolist2, pc.property_rdf_data_nolist3 ]: # TODO: Il suffit de tester si obj est un url de la forme "entity.py" ??? # HTML and images urls can be "flattened" because the nodes have no descendants. # Do not create a node for this. # MIME links displayed in the same column as sub-directory. # Also, it might be enough to test if the object has the form "entity.py" because it has no descendant. # TODO: CGIPROP: Can it have several html or sub-rdf ?? It is necessary ! fieldsSet[subj].append((prop, obj)) else: objNam = RdfNodeToDotLabelExtended(obj, prop) # C est la que si subjNam est dans une liste de dictCollapsedSubjectsToObjectLists, # il faut rajouter devant, le nom du record, c est a dire SON subjNam + "_table_rdf_data:". try: # Syntax with colon required by DOT. propNam = lib_exports.PropToShortPropNam(prop) subjNam = SubjNamFromCollapsed(propNam, subjNam) except KeyError: # sys.stderr.write("PASS subjNam=%s objNam=%s\n"%(subjNam,objNam)) pass stream.write(pattEdgeOrien % (subjNam, objNam, prp_col, qname(prop, grph))) elif obj == None: # No element created in nodes[] fieldsSet[subj].append((prop, "Null")) else: # For Literals. No element created in nodes[] # Literals can be processed according to their type. # Some specific properties cannot have children so they can be stored as literals? # Les proprietes comme "pid", on devrait plutot afficher le lien vers le process, dans la table ??? # Les URLs de certaines proprietes sont affichees en colonnes. # Ou bien pour ces proprietes, on recree un entity.py ?? fieldsSet[subj].append((prop, obj)) logfil.write(TimeStamp() + " Rdf2Dot: Replacing vectors: CollapsedProperties=%d.\n" % (len(CollapsedProperties))) # Now, replaces each vector by a single object containg a HTML table. # TODO: Unfortunately, the prop is lost, which implies that all children are mixed together. def ProcessCollapsedProperties(propNam): dictCollapsedSubjectsToObjectLists = dictPropsCollapsedSubjectsToObjectLists[ propNam] logfil.write(TimeStamp() + " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" % (len(dictCollapsedSubjectsToObjectLists))) for subjUrl, nodLst in lib_util.six_iteritems( dictCollapsedSubjectsToObjectLists): subjNam = RdfNodeToDotLabel(subjUrl) subjNamTab = CollapsedLabel(propNam, subjNam) try: # TODO: Cette logique ajoute parfois un niveau de noeud inutile. Plus exactement, ca duplique un noeud. # Ou plus exactement, le noed est represente par deux objects graphiques: # * Un qui a les scripts. # * Un autre qui a la liste HTML qu on fabrique. # => Peut-on imaginer de melanger les deux ?? # Dans WritePatterns: Ajouter le nom du noeud au label. subjNam = SubjNamFromCollapsed(propNam, subjNam) except KeyError: pass # Point from the subject to the table containing the objects. stream.write(pattEdgeOrien % (subjNam, subjNamTab, "GREEN", propNam)) (labText, subjEntityGraphicClass, entity_id) = lib_naming.ParseEntityUri(subjUrl) # Probleme avec les champs: # Faire une premiere passe et reperer les fields, detecter les noms des colonnes, leur attribuer ordre et indice. # Seconde passe pour batir les lignes. # Donc on ordonne toutes les colonnes. # Pour chaque field: les prendre dans le sens du header et quand il y a un trou, colonne vide. # Inutile de trier les field, mais il d'abord avoir une liste complete des champs, dans le bon sens. # CA SUPPOSE QUE DANS FIELDSSET LES KEYS SONT UNIQUES. # SI ON NE PEUT PAS, ALORS ON METTRA DES LISTES. MAIS CETTE CONTRAINTE SIMPLIFIE L'AFFICHAGE. # DOMMAGE QU ON SCANNE LES OBJETS DEUX FOIS UNIQUEMENT POUR AVOIR LES NOMS DES CHAMPS !!!!!!!!!!!!! # TODO: HEURISTIQUE: ON pourrait s'arreter aux dix premiers. Ou bien faire le tri avant ? # On bien prendre les colonnes de la premiere ligne, et recommencer si ca ne marche pas. # Unique columns of the descendant of this subject. rawFieldsKeys = set() for obj in nodLst: # One table per node. rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj]) # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) ) # Mandatory properties must come at the beginning of the columns of the header, with first indices. # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes. # S il y a du html ou du RDF, on veut que ca vienne en premier. fieldsKeysOrdered = [] for fldPriority in FlatPropertertiesList: try: # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ? # TODO: Remove if not HTML and no sub-rdf. CGIPROP # If the property is never used, exception then next property. rawFieldsKeys.remove(fldPriority) fieldsKeysOrdered.append(fldPriority) except KeyError: pass # This one is always removed because its content is concatenated at the first column. for fldToRemove in [pc.property_information]: try: rawFieldsKeys.remove(fldToRemove) except KeyError: pass # Appends rest of properties, sorted. fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys) # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) ) # This assumes that the header columns are sorted. keyIndices = { nameKey: indexKey for (indexKey, nameKey) in enumerate(fieldsKeys, 1) } numberKeys = len(keyIndices) + 1 # Apparently, no embedded tables. dictHtmlLines = dict() for objUri in nodLst: # One table per node. subObjId = RdfNodeToDotLabel(objUri) # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place. subNodUri = objUri.replace('&', '&') try: (subObjNam, subEntityGraphicClass, subEntityId) = lib_naming.ParseEntityUriShort(objUri) except UnicodeEncodeError: sys.stderr.write("UnicodeEncodeError error:%s\n" % (objUri)) (subObjNam, subEntityGraphicClass, subEntityId) = ("Utf problem1", "Utf problem2", "Utf problem3") # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass) # If this is a script, always displayed on white, even if reletd to a specific entity. # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY. if objUri.find("entity.py") < 0: objColor = "#FFFFFF" else: objColor = lib_patterns.EntityClassToColor( subEntityGraphicClass) # This lighter cololor for the first column. objColorLight = lib_patterns.ColorLighter(objColor) # Some colors a bit clearer ? Or take the original color of the class ? td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight td_bgcolor = td_bgcolor_plain # Some columns might not have a value. The first column is for the key. columns = [td_bgcolor + " ></td>"] * numberKeys # Just used for the vertical order of lines, one line per object. title = "" # TODO: CGIPROP. This is not a dict, the same key can appear several times ? for (key, val) in fieldsSet[objUri]: if key == pc.property_information: # This can be a short string only. title += val continue # TODO: This is hard-coded. if IsFlatProperty(key): # In fact, it might also be an internal URL with "entity.py" if lib_kbase.IsLiteral(val): if isinstance(val.value, (list, tuple)): strHtml = FormatElementAux(val.value) sys.stderr.write("val.value=%s\n" % strHtml) tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml else: tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value else: valTitle = lib_naming.ParseEntityUri(val)[0] valTitleUL = lib_exports.DotUL(valTitle) tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % ( val, valTitleUL) else: try: float(val) tmpCell = td_bgcolor + 'align="right">%s</td>' % val except: # Wraps the string if too long. Can happen only with a literal. tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr( val) idxKey = keyIndices[key] columns[idxKey] = tmpCell if title: title_key = title else: title_key = subObjNam # Maybe the first column is a literal ? if subEntityId != "PLAINTEXTONLY": # WE SHOULD PROBABLY ESCAPE HERE TOO. columns[ 0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri, title_key) else: subNodUri = cgi.escape(subNodUri) columns[ 0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri) # Several scripts might have the same help text, so add a number. # "Title" => "Title" # "Title" => "Title/2" # "Title" => "Title/3" etc... # Beware that it is quadratic with the number of scripts with identical info. title_idx = 2 title_uniq = title_key while title_uniq in dictHtmlLines: title_uniq = "%s/%d" % (title_key, title_idx) title_idx += 1 # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple: # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2" # TODO: title_uniq devrait etre plutot la liste des proprietes. # TODO: By clicking on the column names, we could change the order. dictHtmlLines[title_uniq] = "".join(columns) # Replace the first column by more useful information. numNodLst = len(nodLst) # TODO: Compute this once for all. eltNam = subEntityGraphicClass.split("/")[-1] if not eltNam: # TODO: This is not the right criteria. Must select if we are listing scripts. eltNam = "script" eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst) txtElements = "%d %s" % (numNodLst, eltNamPlural) header = '<td border="1">' + lib_exports.DotBold( txtElements) + "</td>" # TODO: Replace each column name with a link which sorts the line based on this column. # The order of columns could be specified with an extra cgi argument with the columns names. for key in fieldsKeys: columnTitle = qname(key, grph) columnTitle = columnTitle.replace("_", " ").capitalize() header += "<td border='1'>" + lib_exports.DotBold( columnTitle) + "</td>" # With an empty key, it comes first when sorting. dictHtmlLines[""] = header # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!! subjUrlClean = subjUrl.replace('&', '&') # ATTENTION: La forme du record est celle du sujet. # ca veut donc dire qu'on va avoir la meme couleur pour des objets de types # differents s'ils sont dans la meme relation avec un sujet identique ? numFields = len(fieldsKeys) + 1 # The label might be truncated if subjEntityGraphicClass: helpText = "List of " + subjEntityGraphicClass + " objects in " + labText else: helpText = "List of scripts in " + labText # TODO: Le title and the content are not necessarily of the same class. # labTextWithBr is the first line of the table containing nodes linked with the # same property. Unfortunately we have lost this property. labText = lib_exports.TruncateInSpace(labText, 30) labTextWithBr = lib_exports.StrWithBr(labText) labTextWithBr += ": " + propNam if entity_id == "PLAINTEXTONLY": subjUrlClean = "" # This color is the table's contour. lib_patterns.WritePatterned(stream, subjEntityGraphicClass, subjNamTab, helpText, '"#000000"', subjUrlClean, numFields, labTextWithBr, dictHtmlLines) # TODO: Eviter les repetitions de la meme valeur dans une colonne en comparant d une ligne a l autre. # TODO: Si une cellule est identique jusqu a un delimiteur, idem, remplacer par '"'. if CollapsedProperties: for collapsedProp in CollapsedProperties: collapsedPropNam = lib_exports.PropToShortPropNam(collapsedProp) ProcessCollapsedProperties(collapsedPropNam) logfil.write(TimeStamp() + " Rdf2Dot: Display remaining nodes. dictRdf2Dot=%d\n" % len(dictRdf2Dot)) # Now, display the normal nodes, which are not displayed in tables. for objRdfNode, objLabel in lib_util.six_iteritems(dictRdf2Dot): # TODO: Avoids this lookup. if objLabel in dictCollapsedObjectLabelsToSubjectLabels: continue objPropsAsHtml = FieldsToHtmlVertical(grph, fieldsSet[objRdfNode]) labHRef = objRdfNode.replace('&', '&') try: # TODO: Probleme ici: La chaine est deja codee pour HTML ce qui en rend le parsing different # TODO: ... de celui d'un URL deja decode. DOMMAGE: On quote puis unquote !!! (labText, objEntityGraphClass, entity_id) = lib_naming.ParseEntityUri( lib_util.urllib_unquote(objRdfNode)) except UnicodeEncodeError: sys.stderr.write("UnicodeEncodeError error:%s\n" % (objRdfNode)) # WritePatterned receives an list of strings similar to "<td>jhh</td><td>jhh</td><td>jhh</td>" # This function adds <tr> and </tr> on both sides. # This avoids concatenations. # Ampersand are intentionnally doubled, because later on they are replaced twice. # That is, interpreted twice as HTML entities. # This might be temporary until we replace CGI arguments by genuine WMI Monikers. labTextNoAmp = labText.replace("&amp;", " ") labTextNoAmp = labTextNoAmp.strip() labTextClean = lib_exports.StrWithBr(labTextNoAmp) # Two columns because it encompasses the key and the value. if objEntityGraphClass: helpText = labTextNoAmp + " is a " + objEntityGraphClass else: if labTextClean.startswith("http"): helpText = "External URL " + labTextNoAmp else: helpText = "Script " + labTextNoAmp # This color is the object's contour. lib_patterns.WritePatterned(stream, objEntityGraphClass, objLabel, helpText, '"#000000"', labHRef, 2, labTextClean, objPropsAsHtml) logfil.write(TimeStamp() + " Rdf2Dot: Leaving\n") stream.write("}\n")
def ProcessCollapsedProperties(propNam): dictCollapsedSubjectsToObjectLists = dictPropsCollapsedSubjectsToObjectLists[ propNam] logfil.write(TimeStamp() + " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" % (len(dictCollapsedSubjectsToObjectLists))) for subjUrl, nodLst in lib_util.six_iteritems( dictCollapsedSubjectsToObjectLists): subjNam = RdfNodeToDotLabel(subjUrl) subjNamTab = CollapsedLabel(propNam, subjNam) try: # TODO: Cette logique ajoute parfois un niveau de noeud inutile. Plus exactement, ca duplique un noeud. # Ou plus exactement, le noed est represente par deux objects graphiques: # * Un qui a les scripts. # * Un autre qui a la liste HTML qu on fabrique. # => Peut-on imaginer de melanger les deux ?? # Dans WritePatterns: Ajouter le nom du noeud au label. subjNam = SubjNamFromCollapsed(propNam, subjNam) except KeyError: pass # Point from the subject to the table containing the objects. stream.write(pattEdgeOrien % (subjNam, subjNamTab, "GREEN", propNam)) (labText, subjEntityGraphicClass, entity_id) = lib_naming.ParseEntityUri(subjUrl) # Probleme avec les champs: # Faire une premiere passe et reperer les fields, detecter les noms des colonnes, leur attribuer ordre et indice. # Seconde passe pour batir les lignes. # Donc on ordonne toutes les colonnes. # Pour chaque field: les prendre dans le sens du header et quand il y a un trou, colonne vide. # Inutile de trier les field, mais il d'abord avoir une liste complete des champs, dans le bon sens. # CA SUPPOSE QUE DANS FIELDSSET LES KEYS SONT UNIQUES. # SI ON NE PEUT PAS, ALORS ON METTRA DES LISTES. MAIS CETTE CONTRAINTE SIMPLIFIE L'AFFICHAGE. # DOMMAGE QU ON SCANNE LES OBJETS DEUX FOIS UNIQUEMENT POUR AVOIR LES NOMS DES CHAMPS !!!!!!!!!!!!! # TODO: HEURISTIQUE: ON pourrait s'arreter aux dix premiers. Ou bien faire le tri avant ? # On bien prendre les colonnes de la premiere ligne, et recommencer si ca ne marche pas. # Unique columns of the descendant of this subject. rawFieldsKeys = set() for obj in nodLst: # One table per node. rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj]) # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) ) # Mandatory properties must come at the beginning of the columns of the header, with first indices. # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes. # S il y a du html ou du RDF, on veut que ca vienne en premier. fieldsKeysOrdered = [] for fldPriority in FlatPropertertiesList: try: # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ? # TODO: Remove if not HTML and no sub-rdf. CGIPROP # If the property is never used, exception then next property. rawFieldsKeys.remove(fldPriority) fieldsKeysOrdered.append(fldPriority) except KeyError: pass # This one is always removed because its content is concatenated at the first column. for fldToRemove in [pc.property_information]: try: rawFieldsKeys.remove(fldToRemove) except KeyError: pass # Appends rest of properties, sorted. fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys) # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) ) # This assumes that the header columns are sorted. keyIndices = { nameKey: indexKey for (indexKey, nameKey) in enumerate(fieldsKeys, 1) } numberKeys = len(keyIndices) + 1 # Apparently, no embedded tables. dictHtmlLines = dict() for objUri in nodLst: # One table per node. subObjId = RdfNodeToDotLabel(objUri) # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place. subNodUri = objUri.replace('&', '&') try: (subObjNam, subEntityGraphicClass, subEntityId) = lib_naming.ParseEntityUriShort(objUri) except UnicodeEncodeError: sys.stderr.write("UnicodeEncodeError error:%s\n" % (objUri)) (subObjNam, subEntityGraphicClass, subEntityId) = ("Utf problem1", "Utf problem2", "Utf problem3") # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass) # If this is a script, always displayed on white, even if reletd to a specific entity. # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY. if objUri.find("entity.py") < 0: objColor = "#FFFFFF" else: objColor = lib_patterns.EntityClassToColor( subEntityGraphicClass) # This lighter cololor for the first column. objColorLight = lib_patterns.ColorLighter(objColor) # Some colors a bit clearer ? Or take the original color of the class ? td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight td_bgcolor = td_bgcolor_plain # Some columns might not have a value. The first column is for the key. columns = [td_bgcolor + " ></td>"] * numberKeys # Just used for the vertical order of lines, one line per object. title = "" # TODO: CGIPROP. This is not a dict, the same key can appear several times ? for (key, val) in fieldsSet[objUri]: if key == pc.property_information: # This can be a short string only. title += val continue # TODO: This is hard-coded. if IsFlatProperty(key): # In fact, it might also be an internal URL with "entity.py" if lib_kbase.IsLiteral(val): if isinstance(val.value, (list, tuple)): strHtml = FormatElementAux(val.value) sys.stderr.write("val.value=%s\n" % strHtml) tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml else: tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value else: valTitle = lib_naming.ParseEntityUri(val)[0] valTitleUL = lib_exports.DotUL(valTitle) tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % ( val, valTitleUL) else: try: float(val) tmpCell = td_bgcolor + 'align="right">%s</td>' % val except: # Wraps the string if too long. Can happen only with a literal. tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr( val) idxKey = keyIndices[key] columns[idxKey] = tmpCell if title: title_key = title else: title_key = subObjNam # Maybe the first column is a literal ? if subEntityId != "PLAINTEXTONLY": # WE SHOULD PROBABLY ESCAPE HERE TOO. columns[ 0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri, title_key) else: subNodUri = cgi.escape(subNodUri) columns[ 0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri) # Several scripts might have the same help text, so add a number. # "Title" => "Title" # "Title" => "Title/2" # "Title" => "Title/3" etc... # Beware that it is quadratic with the number of scripts with identical info. title_idx = 2 title_uniq = title_key while title_uniq in dictHtmlLines: title_uniq = "%s/%d" % (title_key, title_idx) title_idx += 1 # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple: # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2" # TODO: title_uniq devrait etre plutot la liste des proprietes. # TODO: By clicking on the column names, we could change the order. dictHtmlLines[title_uniq] = "".join(columns) # Replace the first column by more useful information. numNodLst = len(nodLst) # TODO: Compute this once for all. eltNam = subEntityGraphicClass.split("/")[-1] if not eltNam: # TODO: This is not the right criteria. Must select if we are listing scripts. eltNam = "script" eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst) txtElements = "%d %s" % (numNodLst, eltNamPlural) header = '<td border="1">' + lib_exports.DotBold( txtElements) + "</td>" # TODO: Replace each column name with a link which sorts the line based on this column. # The order of columns could be specified with an extra cgi argument with the columns names. for key in fieldsKeys: columnTitle = qname(key, grph) columnTitle = columnTitle.replace("_", " ").capitalize() header += "<td border='1'>" + lib_exports.DotBold( columnTitle) + "</td>" # With an empty key, it comes first when sorting. dictHtmlLines[""] = header # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!! subjUrlClean = subjUrl.replace('&', '&') # ATTENTION: La forme du record est celle du sujet. # ca veut donc dire qu'on va avoir la meme couleur pour des objets de types # differents s'ils sont dans la meme relation avec un sujet identique ? numFields = len(fieldsKeys) + 1 # The label might be truncated if subjEntityGraphicClass: helpText = "List of " + subjEntityGraphicClass + " objects in " + labText else: helpText = "List of scripts in " + labText # TODO: Le title and the content are not necessarily of the same class. # labTextWithBr is the first line of the table containing nodes linked with the # same property. Unfortunately we have lost this property. labText = lib_exports.TruncateInSpace(labText, 30) labTextWithBr = lib_exports.StrWithBr(labText) labTextWithBr += ": " + propNam if entity_id == "PLAINTEXTONLY": subjUrlClean = "" # This color is the table's contour. lib_patterns.WritePatterned(stream, subjEntityGraphicClass, subjNamTab, helpText, '"#000000"', subjUrlClean, numFields, labTextWithBr, dictHtmlLines)
def AddImportedModules(grph, node, filNam, maxDepth, dispPackages, dispFiles): sys.stderr.write( "AddImportedModules filNam=%s dispPackages=%d dispFiles=%d\n" % (filNam, dispPackages, dispFiles)) filename, file_extension = os.path.splitext(filNam) filextlo = file_extension.lower() if filextlo not in [".py", ".pyw"]: return finder = modulefinder.ModuleFinder() try: finder.run_script(filNam) except TypeError: exc = sys.exc_info()[0] lib_common.ErrorMessageHtml("Error loading Python script %s:%s" % (filNam, str(exc))) AddImportedModules.dictModules = dict() # A cache which associates a node to a Python module name. def GetModuNode(moduNam): try: moduNode = AddImportedModules.dictModules[moduNam] except KeyError: moduNode = MakeUri(moduNam) AddImportedModules.dictModules[moduNam] = moduNode return moduNode AddImportedModules.dictFiles = dict() # A cache which associates a node to a file name. def GetFileNode(moduFil): try: fileNode = AddImportedModules.dictModules[moduFil] except KeyError: fileNode = lib_uris.gUriGen.FileUri(moduFil) AddImportedModules.dictModules[moduFil] = fileNode return fileNode for moduNam, mod in lib_util.six_iteritems(finder.modules): splitNam = moduNam.split(".") # sys.stderr.write("splitNam=%s\n"%str(splitNam)) # sys.stderr.write("mod=%s\n"%str(mod)) moduFil = mod.__file__ # sys.stderr.write("moduFil=%s\n"%moduFil) if len(splitNam) > maxDepth: continue if dispPackages: moduNod = GetModuNode(moduNam) if dispFiles and moduFil: nodeFile = GetFileNode(moduFil) # nodeFile is the result of lib_common.NodeUrl grph.add((moduNod, pc.property_rdf_data_nolist2, nodeFile)) if len(splitNam) == 1: grph.add((node, propPythonPackage, moduNod)) sys.stderr.write("No parent: moduNam=%s\n" % (moduNam)) else: parentModuNam = ".".join(splitNam[:-1]) parentModuNod = GetModuNode(parentModuNam) grph.add((parentModuNod, propPythonRequires, moduNod)) sys.stderr.write("parentModuNam=%s moduNam=%s\n" % (parentModuNam, moduNam)) if dispFiles and not dispPackages: if moduFil: nodeFile = GetFileNode(moduFil) if len(splitNam) == 1: # TODO: Should be connected to the module. grph.add((node, propPythonPackage, nodeFile)) # TODO: LE RAJOUTER QUAND MEME SINON ON NE VOIT RIEN ! pass else: parentModuNam = ".".join(splitNam[:-1]) parentModuNod = GetModuNode(parentModuNam) grph.add((parentModuNod, propPythonRequires, nodeFile))
def ProcessCollapsedProperties(propNam): dictCollapsedSubjectsToObjectLists = dict_props_collapsed_subjects_to_object_lists[ propNam] logfil.write(TimeStamp() + " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" % (len(dictCollapsedSubjectsToObjectLists))) for subjUrl, nodLst in lib_util.six_iteritems( dictCollapsedSubjectsToObjectLists): subjNam = _rdf_node_to_dot_label(subjUrl) subjNamTab = CollapsedLabel(propNam, subjNam) try: # TODO: This logic adds an extra level of node: Try to flatten the tree. subjNam = SubjNamFromCollapsed(propNam, subjNam) except KeyError: pass # This points from the subject to the table containing the objects. # TODO: This color should be a parameter. stream.write(_pattern_edge_oriented % (subjNam, subjNamTab, "GREEN", propNam)) (labText, subjEntityGraphicClass, entity_id) = lib_naming.ParseEntityUri(subjUrl) # At the moment, two passes are necessary: # * A first pass to create the compte list of fields, because they might be a bit different # from one record to the other. The column names pf these fields get an unique index number # and can therefore be sorted. # * A second pass uses these result, to display the lines. # # This could be faster by assuming that the first ten columns have all the fields. # We could then start the second pass, and if an undetected column is found, # then restart from scratch. # Unique columns of the descendant of this subject. rawFieldsKeys = set() for obj in nodLst: # One table per node. rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj]) # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) ) # Mandatory properties must come at the beginning of the columns of the header, with first indices. # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes. # S il y a du html ou du RDF, on veut que ca vienne en premier. fieldsKeysOrdered = [] for fldPriority in _flat_properties_list: try: # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ? # TODO: Remove if not HTML and no sub-rdf. CGIPROP # If the property is never used, exception then next property. rawFieldsKeys.remove(fldPriority) fieldsKeysOrdered.append(fldPriority) except KeyError: pass # This one is always removed because its content is concatenated at the first column. for fldToRemove in [pc.property_information]: try: rawFieldsKeys.remove(fldToRemove) except KeyError: pass # Appends rest of properties, sorted. fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys) # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) ) # This assumes that the header columns are sorted. keyIndices = { nameKey: indexKey for (indexKey, nameKey) in enumerate(fieldsKeys, 1) } numberKeys = len(keyIndices) + 1 # Apparently, no embedded tables. dictHtmlLines = dict() for objUri in nodLst: # One table per node. subObjId = _rdf_node_to_dot_label(objUri) # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place. subNodUri = objUri.replace('&', '&') try: (subObjNam, subEntityGraphicClass, subEntityId) = lib_naming.ParseEntityUriShort(objUri) except UnicodeEncodeError: WARNING("UnicodeEncodeError error:%s", objUri) (subObjNam, subEntityGraphicClass, subEntityId) = ("Utf problem1", "Utf problem2", "Utf problem3") # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass) # If this is a script, always displayed on white, even if related to a specific entity. # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY. if objUri.find("entity.py") < 0: objColor = "#FFFFFF" else: objColor = lib_patterns.EntityClassToColor( subEntityGraphicClass) # This lighter cololor for the first column. objColorLight = lib_patterns.ColorLighter(objColor) # Some colors a bit clearer ? Or take the original color of the class ? td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight td_bgcolor = td_bgcolor_plain # Some columns might not have a value. The first column is for the key. columns = [td_bgcolor + " ></td>"] * numberKeys # Just used for the vertical order of lines, one line per object. title = "" # TODO: CGIPROP. This is not a dict, the same key can appear several times ? for (key, val) in fieldsSet[objUri]: if key == pc.property_information: # This can be a short string only. title += val continue # TODO: This is hard-coded. if is_flat_property(key): # In fact, it might also be an internal URL with "entity.py" if lib_kbase.IsLiteral(val): if isinstance(val.value, (list, tuple)): strHtml = _format_element_aux(val.value) DEBUG("val.value=%s", strHtml) tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml else: tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value else: # This displays objects in a table: The top-level object must be # in the same host, so there is no need to display a long label. valTitle = lib_naming.ParseEntityUriShort(val)[0] assert isinstance(valTitle, lib_util.six_text_type) # There might be non-ascii characters such as accents etc... try: valTitle.encode('ascii') except UnicodeEncodeError: valTitle = "Not ascii" valTitleUL = lib_exports.DotUL(valTitle) tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % ( val, valTitleUL) else: try: float(val) tmpCell = td_bgcolor + 'align="right">%s</td>' % val except: # Wraps the string if too long. Can happen only with a literal. tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr( val) idxKey = keyIndices[key] columns[idxKey] = tmpCell if title: title_key = title else: title_key = subObjNam # Maybe the first column is a literal ? if subEntityId != "PLAINTEXTONLY": # WE SHOULD PROBABLY ESCAPE HERE TOO. columns[ 0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri, title_key) else: subNodUri = lib_util.html_escape(subNodUri) columns[ 0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % ( subObjId, subNodUri) # Several scripts might have the same help text, so add a number. # "Title" => "Title" # "Title" => "Title/2" # "Title" => "Title/3" etc... # Beware that it is quadratic with the number of scripts with identical info. title_idx = 2 title_uniq = title_key while title_uniq in dictHtmlLines: title_uniq = "%s/%d" % (title_key, title_idx) title_idx += 1 # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple: # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2" # TODO: title_uniq devrait etre plutot la liste des proprietes. # TODO: By clicking on the column names, we could change the order. dictHtmlLines[title_uniq] = "".join(columns) # Replace the first column by more useful information. numNodLst = len(nodLst) # WBEM and WMI classes have the syntax: "ns1/ns2/ns3:class" and the class it self can have base classes. # Survol classes have the syntax: "dir/dir/dir/class": This considers that namespaces are not really # necessary and can be replaced by classes. Also, there is a one-to-one match between the class inheritance # tree and its directory. # If Survol had to be started from scratch, there would be one Python class per survol class, # and they would be stored in the top dir "root/cimv2" ... it is not too late ! # # This strips the upper directories: "mysql/instance" or "oracle/table", if this is a Survol class eltNam = subEntityGraphicClass.split("/")[-1] # This strips the namespace: "root/cimv2:CIM_LogicalElement", if this is a WBEM or WMI class. eltNam = eltNam.split(":")[-1] if not eltNam: # TODO: This is not the right criteria. Must select if we are listing scripts. eltNam = "script" eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst) txtElements = "%d %s" % (numNodLst, eltNamPlural) header = '<td border="1">' + lib_exports.DotBold( txtElements) + "</td>" # TODO: Replace each column name with a link which sorts the line based on this column. # The order of columns could be specified with an extra cgi argument with the columns names. for key in fieldsKeys: columnTitle = lib_kbase.qname(key, grph) columnTitle = columnTitle.replace("_", " ").capitalize() header += "<td border='1'>" + lib_exports.DotBold( columnTitle) + "</td>" # With an empty key, it comes first when sorting. dictHtmlLines[""] = header # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!! subjUrlClean = subjUrl.replace('&', '&') # BEWARE: The shape and the color of this HTML table is from the subjects, # because the elements can be of different classes, even if the share the same predicate. # TODO: Each row should have its own color according to its class. numFields = len(fieldsKeys) + 1 # The rows of this HTML table could belong to different classes: # What the shared is the predicate. Hence, the predicate, property name is used as a title. propNamPlural = lib_grammar.ToPlural(propNam, None) helpText = "List of " + propNamPlural + " in " + labText # TODO: Le title and the content are not necessarily of the same class. # labTextWithBr is the first line of the table containing nodes linked with the # same property. Unfortunately we have lost this property. labText = lib_exports.TruncateInSpace(labText, 30) labTextWithBr = lib_exports.StrWithBr(labText) labTextWithBr += ": " + propNam if entity_id == "PLAINTEXTONLY": subjUrlClean = "" # This color is the table's contour. lib_patterns.WritePatterned(stream, subjEntityGraphicClass, subjNamTab, helpText, '"#000000"', subjUrlClean, numFields, labTextWithBr, dictHtmlLines)