123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- # -*- coding: utf-8 -*-
- """
- Author Igor Támara igor@tamarapatino.org
- Use this little program as you wish, if you
- include it in your work, let others know you
- are using it preserving this note, you have
- the right to make derivative works, Use it
- at your own risk.
- Tested to work on(etch testing 13-08-2007):
- Python 2.4.4 (#2, Jul 17 2007, 11:56:54)
- [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13)] on linux2
- """
- import codecs
- import gzip
- import re
- import sys
- from xml.dom.minidom import Node, parseString
- import six
- dependclasses = ["User", "Group", "Permission", "Message"]
- # Type dictionary translation types SQL -> Django
- tsd = {
- "text": "TextField",
- "date": "DateField",
- "varchar": "CharField",
- "int": "IntegerField",
- "float": "FloatField",
- "serial": "AutoField",
- "boolean": "BooleanField",
- "numeric": "FloatField",
- "timestamp": "DateTimeField",
- "bigint": "IntegerField",
- "datetime": "DateTimeField",
- "time": "TimeField",
- "bool": "BooleanField",
- }
- # convert varchar -> CharField
- v2c = re.compile(r'varchar\((\d+)\)')
- def find_index(fks, id_):
- """
- Look for the id on fks, fks is an array of arrays, each array has on [1]
- the id of the class in a dia diagram. When not present returns None, else
- it returns the position of the class with id on fks
- """
- for i, _ in fks.items():
- if fks[i][1] == id_:
- return i
- return None
- def addparentstofks(rels, fks):
- """
- Get a list of relations, between parents and sons and a dict of
- clases named in dia, and modifies the fks to add the parent as fk to get
- order on the output of classes and replaces the base class of the son, to
- put the class parent name.
- """
- for j in rels:
- son = find_index(fks, j[1])
- parent = find_index(fks, j[0])
- fks[son][2] = fks[son][2].replace("models.Model", parent)
- if parent not in fks[son][0]:
- fks[son][0].append(parent)
- def dia2django(archivo):
- models_txt = ''
- f = codecs.open(archivo, "rb")
- # dia files are gzipped
- data = gzip.GzipFile(fileobj=f).read()
- ppal = parseString(data)
- # diagram -> layer -> object -> UML - Class -> name, (attribs : composite -> name,type)
- datos = ppal.getElementsByTagName("dia:diagram")[0].getElementsByTagName("dia:layer")[0].getElementsByTagName("dia:object")
- clases = {}
- herit = []
- imports = six.u("")
- for i in datos:
- # Look for the classes
- if i.getAttribute("type") == "UML - Class":
- myid = i.getAttribute("id")
- for j in i.childNodes:
- if j.nodeType == Node.ELEMENT_NODE and j.hasAttributes():
- if j.getAttribute("name") == "name":
- actclas = j.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
- myname = "\nclass %s(models.Model) :\n" % actclas
- clases[actclas] = [[], myid, myname, 0]
- if j.getAttribute("name") == "attributes":
- for ll in j.getElementsByTagName("dia:composite"):
- if ll.getAttribute("type") == "umlattribute":
- # Look for the attribute name and type
- for k in ll.getElementsByTagName("dia:attribute"):
- if k.getAttribute("name") == "name":
- nc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
- elif k.getAttribute("name") == "type":
- tc = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
- elif k.getAttribute("name") == "value":
- val = k.getElementsByTagName("dia:string")[0].childNodes[0].data[1:-1]
- if val == '##':
- val = ''
- elif k.getAttribute("name") == "visibility" and k.getElementsByTagName("dia:enum")[0].getAttribute("val") == "2":
- if tc.replace(" ", "").lower().startswith("manytomanyfield("):
- # If we find a class not in our model that is marked as being to another model
- newc = tc.replace(" ", "")[16:-1]
- if dependclasses.count(newc) == 0:
- dependclasses.append(newc)
- if tc.replace(" ", "").lower().startswith("foreignkey("):
- # If we find a class not in our model that is marked as being to another model
- newc = tc.replace(" ", "")[11:-1]
- if dependclasses.count(newc) == 0:
- dependclasses.append(newc)
- # Mapping SQL types to Django
- varch = v2c.search(tc)
- if tc.replace(" ", "").startswith("ManyToManyField("):
- myfor = tc.replace(" ", "")[16:-1]
- if actclas == myfor:
- # In case of a recursive type, we use 'self'
- tc = tc.replace(myfor, "'self'")
- elif clases[actclas][0].count(myfor) == 0:
- # Adding related class
- if myfor not in dependclasses:
- # In case we are using Auth classes or external via protected dia visibility
- clases[actclas][0].append(myfor)
- tc = "models." + tc
- if len(val) > 0:
- tc = tc.replace(")", "," + val + ")")
- elif tc.find("Field") != -1:
- if tc.count("()") > 0 and len(val) > 0:
- tc = "models.%s" % tc.replace(")", "," + val + ")")
- else:
- tc = "models.%s(%s)" % (tc, val)
- elif tc.replace(" ", "").startswith("ForeignKey("):
- myfor = tc.replace(" ", "")[11:-1]
- if actclas == myfor:
- # In case of a recursive type, we use 'self'
- tc = tc.replace(myfor, "'self'")
- elif clases[actclas][0].count(myfor) == 0:
- # Adding foreign classes
- if myfor not in dependclasses:
- # In case we are using Auth classes
- clases[actclas][0].append(myfor)
- tc = "models." + tc
- if len(val) > 0:
- tc = tc.replace(")", "," + val + ")")
- elif varch is None:
- tc = "models." + tsd[tc.strip().lower()] + "(" + val + ")"
- else:
- tc = "models.CharField(max_length=" + varch.group(1) + ")"
- if len(val) > 0:
- tc = tc.replace(")", ", " + val + " )")
- if not (nc == "id" and tc == "AutoField()"):
- clases[actclas][2] += " %s = %s\n" % (nc, tc)
- elif i.getAttribute("type") == "UML - Generalization":
- mycons = ['A', 'A']
- a = i.getElementsByTagName("dia:connection")
- for j in a:
- if len(j.getAttribute("to")):
- mycons[int(j.getAttribute("handle"))] = j.getAttribute("to")
- print(mycons)
- if 'A' not in mycons:
- herit.append(mycons)
- elif i.getAttribute("type") == "UML - SmallPackage":
- a = i.getElementsByTagName("dia:string")
- for j in a:
- if len(j.childNodes[0].data[1:-1]):
- imports += six.u("from %s.models import *" % j.childNodes[0].data[1:-1])
- addparentstofks(herit, clases)
- # Ordering the appearance of classes
- # First we make a list of the classes each classs is related to.
- ordered = []
- for j, k in six.iteritems(clases):
- k[2] += "\n def __str__(self):\n return u\"\"\n"
- for fk in k[0]:
- if fk not in dependclasses:
- clases[fk][3] += 1
- ordered.append([j] + k)
- i = 0
- while i < len(ordered):
- mark = i
- j = i + 1
- while j < len(ordered):
- if ordered[i][0] in ordered[j][1]:
- mark = j
- j += 1
- if mark == i:
- i += 1
- else:
- # swap %s in %s" % ( ordered[i] , ordered[mark]) to make ordered[i] to be at the end
- if ordered[i][0] in ordered[mark][1] and ordered[mark][0] in ordered[i][1]:
- # Resolving simplistic circular ForeignKeys
- print("Not able to resolve circular ForeignKeys between %s and %s" % (ordered[i][1], ordered[mark][0]))
- break
- a = ordered[i]
- ordered[i] = ordered[mark]
- ordered[mark] = a
- if i == len(ordered) - 1:
- break
- ordered.reverse()
- if imports:
- models_txt = str(imports)
- for i in ordered:
- models_txt += '%s\n' % str(i[3])
- return models_txt
- if __name__ == '__main__':
- if len(sys.argv) == 2:
- dia2django(sys.argv[1])
- else:
- print(" Use:\n \n " + sys.argv[0] + " diagram.dia\n\n")
|