218 lines
9.9 KiB
Python
218 lines
9.9 KiB
Python
# -*- 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
|
|
|
|
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 = str("")
|
|
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 += str("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 clases.items():
|
|
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")
|