Merge branch 'master' of ssh://git.planet-lab.org/git/plstackapi
[plstackapi.git] / planetstack / apigen / modelgen
1 #!/usr/bin/python
2
3 import os
4 import pdb
5 import copy
6 import sys
7 import json
8 import re
9 from django.template import Context, Template
10
11 blacklist = ['SingletonModel','PlCoreBase']
12
13 # Django set up
14
15 sys.path.append('.')
16 os.environ.setdefault("DJANGO_SETTINGS_MODULE", "planetstack.settings")
17 from django.db.models.fields.related import ForeignKey, ManyToManyField
18 from core.models import *
19
20 def singular(foo, keys):
21         for k in keys:
22                 if (foo==k+'es'):
23                         return k
24                 elif (foo==k+'s'):
25                         return k
26         raise Exception('Plural to singular error for %s'%foo)
27
28 g = globals()
29
30 def enum_classes():
31         model_classes = []
32         for c in g.values():
33                 if type(c)==type(PlCoreBase) and c.__name__ not in blacklist:
34                         model_classes.append(c)
35         return model_classes
36
37
38 class GenObj(object):
39         def __str__(self):
40                 return str(self.model.__name__.lower())
41
42         def __init__(self, m):
43                 self.model = m
44                 self.props = []
45                 self.refs = []
46                 self.plural_name = None
47
48         def plural(self):
49                 if (self.plural_name):
50                         return self.plural_name
51                 else:
52                         name = str(self)
53                         if (name.endswith('s')):
54                                 return name+'es'
55                         else:
56                                 return name+'s'
57
58         def singular(self):
59             return str(self)
60
61         def rest_name(self):
62             # These are things that either for historic reasons or due to incorrect naming,
63             # got called something different than the autogen thinks they should be
64             # called.
65             REST_FIXUP = {'controllernetworkses': 'controllernetworks',
66                             'controllerimageses': 'controllerimages',
67                             'controllersliceses': 'controllerslices',
68                             'controlleruserses': 'controllerusers',
69                             'sitedeploymentses': 'sitedeployments',
70                             'siteroles': 'site_roles',
71                             'sliceprivileges': 'slice_privileges',
72                             'sliceroles': 'slice_roles',
73                             }
74             return REST_FIXUP.get(self.plural(), self.plural())
75
76         def camel(self):
77                 name = str(self.model.__name__)
78                 return name
79                 
80 class Generator(dict):
81         def all(self):
82                 return self.values()
83         
84         def regex(self, r):
85                 filtered = filter(lambda o:re.match(r,str(o)), self.values())
86                 return filtered
87
88         def add_object(self, o):
89                 obj = GenObj(o)
90                 fields = o._meta.fields
91                 self[str(obj).lower()]=obj
92
93         def compute_links(self):
94                 for obj in self.values():
95                         #if (str(obj)=='network'):
96                         #       pdb.set_trace()
97                         fields = obj.model._meta.fields
98                         for f in fields:
99                                 if (f and f.rel):
100                                         to_name = str(f.rel.to)
101                                 else:
102                                         to_name = None
103
104                                 if type(f)==ForeignKey and to_name and to_name in self.keys():
105                                         refobj = self[f.to_name]
106
107                                         if (str(obj)=='slice' and f.to_name=='networks'):
108                                                 obj.refs.append(refobj)
109                                         related_name = f.related_query_name()
110                                         if (related_name!='+' and related_name.lower()!=str(obj).lower()):
111                                                 cobj = copy.deepcopy(obj)
112                                                 cobj.multi = True
113                                                 cobj.plural_name = related_name
114                                                 refobj.refs.append(cobj)
115                                 else:
116                                         obj.props.append(f.name)
117
118                         m2m = obj.model._meta.many_to_many
119                         for f in m2m:
120                                 try:
121                                         related_model_name = f.m2m_reverse_field_name()
122                                 except:
123                                         related_model_name = f.m2m_db_table().rsplit('_',1)[-1]
124
125                                 related_name = f.related_query_name()
126                                 if related_model_name in self.keys():
127                                         #print "XXX1", obj, f, related_name, related_model_name
128                                         refobj = self[related_model_name]
129                                         cobj = copy.deepcopy(obj)
130                                         cobj.multi=True
131                                         refobj.refs.append(cobj)
132
133                                 # deal with upgradeFrom_rel_+
134                                 if (related_name.endswith("+")):
135                                     continue
136
137                                 if (related_name!='+' and related_name.lower()!=str(obj).lower()):
138                                         #print "XXX2", obj, f, related_name, related_model_name, refobj.plural_name
139                                         refobj = self[related_model_name]
140                                         cobj = copy.deepcopy(refobj)
141                                         cobj.multi = True
142
143                                         obj.refs.append(cobj)
144
145
146
147                         
148 def main():
149         try:
150                 output = sys.argv[1]
151         except:
152                 print 'Usage: modelgen <output template>'
153                 exit(1)
154
155         generator = Generator()
156
157         models = enum_classes()
158
159         for m in models:
160                 generator.add_object(m)
161
162         generator.compute_links()
163         template_contents = open(output).read()
164         template = Template(template_contents)
165         context = Context({'generator':generator})
166         print template.render(context)
167
168
169 if (__name__=='__main__'):
170         main()