diff --git a/diagrams.py b/diagrams.py deleted file mode 100644 index 3afcc93..0000000 --- a/diagrams.py +++ /dev/null @@ -1,315 +0,0 @@ -from subprocess import Popen, PIPE -import sys -from sys import platform -from os.path import expanduser -from pathlib import Path -import os.path -import zlib -import re -import json - - -class Example(object): - """ Example handling """ - - @staticmethod - def get(generator): - scriptdir = os.path.dirname(os.path.abspath(__file__)) - example = scriptdir + "/../web/examples/example.%s" % generator - if os.path.isfile(example): - txt = Path(example).read_text() - else: - txt = "no example for %s found" % generator - return txt - - -class Command(object): - """ a command to be run using the shell environment """ - - def __init__(self, cmd, versionOption="--version", timeout=5, debug=False): - """ construct me """ - self.cmd = cmd - self.timeout = timeout - self.versionOption = versionOption - self.cmdpath = None - self.debug = debug - - def call(self, args): - """ call me with the given args""" - return self.docall(self.cmdpath, self.cmd, args) - - def callalias(self, alias, args): - """ call me with the given args""" - #print ("callalias cmdpath %s\nalias %s\nargs %s" % (self.cmdpath, alias, args), file=sys.stderr) - return self.docall(self.cmdpath, alias, args) - - def docall(self, cmdpath, cmd, args): - """ call with a specific path and command""" - cmdline = "%s%s %s" % (cmdpath, cmd, str(args)) - if self.debug: - print ("calling %s" % cmdline, file=sys.stderr) - process = Popen(cmdline, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE) - try: - stdout, stderr = process.communicate(timeout=self.timeout) - if self.debug: - if stdout is not None: - print ("stdout: %s" % stdout.decode('utf-8'), file=sys.stderr) - if stderr is not None: - print ("stderr: %s" % stderr.decode('utf-8'), file=sys.stderr) - return stdout, stderr - except Exception: - process.kill() - return None, None - - def check(self): - cmdpaths = [] - # do we know the cmdpath? - if self.cmdpath is None: - # no we need to try multiple options in a specific order - # prio #1: $HOME/bin - home = expanduser("~") - cmdpaths.append(home + "/bin/") - # prio #2: e.g. Macports - if platform == "darwin": - cmdpaths.append("/opt/local/bin/") - # prio #3: default path / no path - # no path - use default PATH - cmdpaths.append("") - else: - # we know the valid path - cmdpaths.append(self.cmdpath) - for cmdpath in cmdpaths: - stdout, stderr = self.docall(cmdpath, self.cmd, self.versionOption) - stdoutTxt = None - stderrTxt = None - if stdout is not None: - stdoutTxt = stdout.decode("utf-8") - if stderr is not None: - stderrTxt = stderr.decode("utf-8") - if not "not found" in stderrTxt and not "No such file or directory" in stderrTxt: - self.cmdpath = cmdpath - return stdoutTxt, stderrTxt - return None, None - - -class Generators(object): - generatorDict = {} - """ the available generators """ - - @staticmethod - def get(generator): - if len(Generators.generatorDict) is 0: - for gen in Generators.generators(): - Generators.generatorDict[gen.id] = gen - gen = None - if generator in Generators.generatorDict: - gen = Generators.generatorDict[generator] - return gen - - @staticmethod - def generatorIdForAlias(alias): - for gen in Generators.generators(): - if alias in gen.aliases: - return gen.id - return None - - @staticmethod - def generators(): - scriptdir = os.path.dirname(os.path.abspath(__file__)) - for plantumlpath in [".", ".."]: - plantumljar = scriptdir + "/" + plantumlpath + "/plantuml.jar"; - if os.path.isfile(plantumljar): - break; - if plantumljar is None: - raise Exception("plantuml.jar not found in %s or .. of it", scriptdir) - #else - gens = [ - Generator("graphviz", "GraphViz", "dot", "-V", logo="https://graphviz.gitlab.io/_pages/Resources/app.png", url="https://www.graphviz.org/", - aliases=[ 'dot', 'neato', 'twopi', 'circo', 'fdp', 'sfdp', 'patchwork', 'osage' ], - defaultType='png', - outputTypes=['dot', 'xdot', 'ps', 'pdf', 'svg', 'fig', 'png', 'gif', 'jpg', 'json', 'imap', 'cmapx'] - ), - Generator("mscgen", "Mscgen", "mscgen", "", logo="http://www.mcternan.me.uk/mscgen/img/msc-sig.png", url="http://www.mcternan.me.uk/mscgen/", defaultType='png', outputTypes=['png', 'eps', 'svg', 'ismap']), - Generator("plantuml", "PlantUML", "java -jar " + plantumljar, "-version", aliases=['plantuml'], - logo="https://useblocks.com/assets/img/posts/plantuml_logo.png", - url="https://plantuml.com", - defaultType='png', - download="http://sourceforge.net/projects/plantuml/files/plantuml.jar/download", - outputTypes=['png', 'svg', 'eps', 'pdf', 'vdx', 'xmi', 'scxml', 'html', 'txt', 'utxt', - 'latex', 'latex:nopreamble']) - ] - return gens - - -class GenerateResult(object): - - def __init__(self, crc32, outputType, path, stdout, stderr): - """ construct me """ - self.crc32 = crc32; - self.outputType = outputType; - self.path = path; - self.stdout = stdout - self.stderr = stderr - - def errMsg(self): - """ decode my stdout and stderr to an error message""" - msg = "" - if self.stdout is not None: - msg = msg + self.stdout.decode('utf-8') - if self.stderr is not None: - msg = msg + self.stderr.decode('utf-8') - return msg - - def asHtml(self): - """ return me as HTML""" - url = '/render/%s/%s' % (self.outputType, self.crc32) - if self.outputType in ['gif', 'jpg', 'png', 'svg']: - return "" % url; - elif self.outputType in ['pdf']: - return "" % url; - else: - return "%s %s" % (url, self.outputType, self.crc32) - - def isValid(self): - """ check if i am valid""" - valid = os.path.isfile(self.path) and not self.errMsg() - return valid - - def asJson(self, baseurl): - """ return my result as JSON for the Mediawiki diagrams extension""" - errMsg=self.errMsg(); - if errMsg: - jsonTxt="""{ - "error": "generating %s failed", - "message": %s - }""" % (self.outputType,json.dumps(errMsg)) - else: - jsonTxt = """{ - "diagrams": { - "png": { - "url": "%s/png/%s.png" - } - } -}""" % (baseurl,self.crc32) - return jsonTxt - - -class Generator(object): - """ a diagram generator """ - - @staticmethod - def getOutputDirectory(): - home = expanduser("~") - outputDir = home + "/.diagrams/" - if not os.path.isdir(outputDir): - os.mkdir(outputDir); - return outputDir - - def __init__(self, genid, name, cmd, versionOption, logo=None, url=None, download=None, defaultType=None, aliases=None, outputTypes=None, debug=False): - """ construct me """ - self.id = genid - self.name = name - self.cmd = cmd - self.gencmd = None - self.logo = logo - self.url = url - self.htmlInfo = None - self.download = download - self.versionOption = versionOption; - if aliases is None: - self.aliases = [cmd] - else: - self.aliases = aliases - self.defaultType = defaultType - self.selectedType = defaultType - self.outputTypes = outputTypes - self.debug = debug - pass - - def getHtmlInfo(self): - """ get info on this generator to be displayed via HTML""" - # cache the info since getVersion is a costly process - if self.htmlInfo is None: - version = self.getVersion() - self.htmlInfo = "" % (self.url, self.name, version, self.logo) - return self.htmlInfo - - def check(self): - """ check my version""" - self.gencmd = Command(self.cmd, self.versionOption, debug=self.debug) - return self.gencmd.check() - - def getVersion(self): - stdOutText, stdErrText = self.check() - if stdOutText is None: - stdOutText = '' - if stdErrText is None: - stdErrText = '' - outputText = stdOutText + stdErrText - found = re.search(r'version.*[,)]', outputText) - if found: - version = found.group() - else: - # actually an error message - version = outputText - # invalidate gencmd - self.gencmd = None - return version - - @staticmethod - def getHash(txt): - hashValue = zlib.crc32(txt.encode()) & 0xffffffff - hashId = hex(hashValue) - #print ("txt %s\nhashValue %s\nhashId %s" % (txt, hashValue, hashId), file=sys.stderr) - return hashId - - def wrap(self,txt): - """ wraot the given text""" - if self.id == "plantuml": - txt="@startuml\n%s\n@enduml\n" % txt - return txt - - def generate(self, alias, txt, outputType, renderer, useCached=True): - """ generate """ - txt=self.wrap(txt) - hashId = Generator.getHash(txt) - inputPath = "%s%s.%s" % (Generator.getOutputDirectory(), hashId, 'txt') - stdout = None - stderr = None - if not (os.path.isfile(inputPath) and useCached): - text_file = open(inputPath, "wt", encoding='utf-8') - try: - if self.debug: - print ("txt %s" % txt, file=sys.stderr) - text_file.write("%s" % txt) - except Exception as e: - print ("exception %s" % e, file=sys.stderr) - finally: - text_file.close() - outputPath = "%s%s.%s" % (Generator.getOutputDirectory(), hashId, outputType) - if os.path.isfile(outputPath) and useCached: - if self.debug: - print ("cached %s #%s from %s" % (outputType, hashId, outputPath), file=sys.stderr) - #else: - if True: - if self.debug: - print ("generating %s #%s to %s" % (outputType, hashId, outputPath), file=sys.stderr) - if self.id == "graphviz": - args = "-T%s %s -o %s" % (outputType, inputPath, outputPath) - if len(renderer) != 0: #exists and not empty - alias = renderer - else: - alias = "dot" - #print ("renderer %s\nalias %s" % (renderer, alias), file=sys.stderr) - elif self.id == "mscgen": - args = "-T%s -i %s -o %s" % (outputType, inputPath, outputPath) - elif self.id == "plantuml": - args = "-charset UTF-8 -t%s %s" % (outputType, inputPath) - alias = "java -jar /var/diagrams/plantuml.jar" - else: - print ("unknown generator %s" % self.id, file=sys.stderr) - if self.gencmd is None: - self.check() - stdout, stderr = self.gencmd.callalias(alias, args) - result = GenerateResult(hashId, outputType, outputPath, stdout, stderr) - return result