1 |
johnpye |
280 |
import gtk |
2 |
|
|
import gtk.glade |
3 |
johnpye |
463 |
import ascpy |
4 |
johnpye |
280 |
from itertools import groupby |
5 |
|
|
from operator import itemgetter |
6 |
johnpye |
283 |
import math |
7 |
johnpye |
284 |
import re |
8 |
johnpye |
280 |
|
9 |
johnpye |
351 |
import config |
10 |
johnpye |
735 |
from infodialog import * |
11 |
johnpye |
351 |
|
12 |
johnpye |
284 |
ZOOM_RE = re.compile(r"([0-9]+)\s*%?") |
13 |
|
|
MAX_ZOOM_SIZE = 2000 |
14 |
|
|
MAX_ZOOM_RATIO = 16 |
15 |
johnpye |
287 |
AT_BOUND_TOL = 0.0001; |
16 |
johnpye |
284 |
|
17 |
johnpye |
280 |
class DiagnoseWindow: |
18 |
johnpye |
351 |
def __init__(self,browser,block=0): |
19 |
johnpye |
280 |
self.browser=browser |
20 |
johnpye |
437 |
_xml = gtk.glade.XML(browser.glade_file,"diagnosewin") |
21 |
johnpye |
280 |
_xml.signal_autoconnect(self) |
22 |
|
|
|
23 |
|
|
self.window = _xml.get_widget("diagnosewin") |
24 |
johnpye |
328 |
self.window.set_transient_for(self.browser.window) |
25 |
|
|
|
26 |
johnpye |
533 |
try: |
27 |
|
|
_icon = gtk.Image() |
28 |
|
|
_iconpath = browser.assets_dir+'diagnose'+config.ICON_EXTENSION |
29 |
|
|
print "ICON PATH =",_iconpath |
30 |
|
|
_icon.set_from_file(_iconpath) |
31 |
|
|
print "ICON = ",_icon |
32 |
|
|
self.window.set_icon(_icon) |
33 |
|
|
except: |
34 |
|
|
pass |
35 |
johnpye |
455 |
|
36 |
johnpye |
284 |
self.imagescroll = _xml.get_widget("imagescroll") |
37 |
johnpye |
282 |
self.image = _xml.get_widget("image") |
38 |
johnpye |
280 |
self.blockentry = _xml.get_widget("blockentry") |
39 |
johnpye |
284 |
self.zoomentry = _xml.get_widget("zoomentry") |
40 |
johnpye |
280 |
|
41 |
johnpye |
286 |
self.varname = _xml.get_widget("varname"); |
42 |
|
|
self.varval = _xml.get_widget("varval"); |
43 |
johnpye |
735 |
self.varinfobutton = _xml.get_widget("varinfobutton"); |
44 |
johnpye |
286 |
self.relname = _xml.get_widget("relname"); |
45 |
|
|
self.relresid = _xml.get_widget("relresid"); |
46 |
johnpye |
735 |
self.relinfobutton = _xml.get_widget("relinfobutton") |
47 |
johnpye |
286 |
|
48 |
johnpye |
280 |
self.varview = _xml.get_widget("varview") |
49 |
|
|
self.varbuf = gtk.TextBuffer() |
50 |
|
|
self.varview.set_buffer(self.varbuf) |
51 |
|
|
self.varcollapsed = _xml.get_widget("varcollapsed") |
52 |
|
|
self.relview = _xml.get_widget("relview") |
53 |
|
|
self.relcollapsed = _xml.get_widget("relcollapsed") |
54 |
|
|
self.relvalues = _xml.get_widget("relvalues") |
55 |
|
|
self.rellabels = _xml.get_widget("rellabels") |
56 |
|
|
self.relrels = _xml.get_widget("relrels") |
57 |
|
|
self.relresids = _xml.get_widget("relresids") |
58 |
|
|
self.relbuf = gtk.TextBuffer() |
59 |
|
|
self.relview.set_buffer(self.relbuf) |
60 |
|
|
|
61 |
johnpye |
294 |
self.im = None |
62 |
johnpye |
285 |
self.block = 0 |
63 |
johnpye |
294 |
self.apply_prefs() |
64 |
|
|
|
65 |
johnpye |
280 |
self.prepare_data() |
66 |
johnpye |
285 |
self.fill_values(block) # block zero |
67 |
johnpye |
280 |
|
68 |
|
|
def run(self): |
69 |
|
|
self.window.run() |
70 |
|
|
self.window.hide() |
71 |
|
|
|
72 |
johnpye |
294 |
def apply_prefs(self): |
73 |
johnpye |
319 |
vc = self.browser.prefs.getBoolPref("Diagnose","varcollapsed",True) |
74 |
|
|
|
75 |
johnpye |
294 |
print "VARCOLLAPSED =",vc |
76 |
|
|
self.varcollapsed.set_active(vc) |
77 |
johnpye |
319 |
self.relcollapsed.set_active(self.browser.prefs.getBoolPref("Diagnose","relcollapsed",True)) |
78 |
johnpye |
294 |
|
79 |
johnpye |
280 |
def prepare_data(self): |
80 |
|
|
# convert incidence map to pylab numarray type: |
81 |
|
|
print "PREPARING DATA" |
82 |
|
|
self.im = self.browser.sim.getIncidenceMatrix() |
83 |
|
|
self.data = self.im.getIncidenceData() |
84 |
|
|
print "DATA LOADED" |
85 |
|
|
|
86 |
johnpye |
284 |
self.zoom=1; |
87 |
johnpye |
280 |
|
88 |
|
|
def fill_values(self, block): |
89 |
johnpye |
319 |
|
90 |
johnpye |
283 |
try: |
91 |
|
|
rl,cl,rh,ch = self.im.getBlockLocation(block) |
92 |
|
|
except IndexError: |
93 |
johnpye |
319 |
if block >= self.im.getNumBlocks(): |
94 |
|
|
block = self.im.getNumBlocks() - 1 |
95 |
|
|
rl,cl,rh,ch = self.im.getBlockLocation(block) |
96 |
|
|
else: |
97 |
|
|
print "BLOCK INDEX ERROR: block =",block |
98 |
|
|
self.blockentry.set_text(str(self.block)) |
99 |
|
|
return |
100 |
|
|
except RuntimeError,e: |
101 |
|
|
print "ERROR GETTING BLOCK LOCATION:",str(e) |
102 |
johnpye |
285 |
self.blockentry.set_text(str(self.block)) |
103 |
johnpye |
283 |
return |
104 |
johnpye |
285 |
|
105 |
johnpye |
280 |
self.block = block |
106 |
|
|
self.blockentry.set_text(str(block)) |
107 |
johnpye |
283 |
|
108 |
johnpye |
286 |
self.rl = rl |
109 |
|
|
self.cl = cl |
110 |
|
|
self.rh = rh |
111 |
|
|
self.ch = ch |
112 |
|
|
|
113 |
johnpye |
283 |
nr = int(rh-rl+1); |
114 |
|
|
nc = int(ch-cl+1); |
115 |
|
|
|
116 |
johnpye |
669 |
print "STARTING IMAGE CREATION" |
117 |
johnpye |
282 |
# refer http://pygtk.org/pygtk2tutorial/sec-DrawingMethods.html |
118 |
|
|
c = chr(255) |
119 |
johnpye |
283 |
b = nr*nc*3*[c] |
120 |
|
|
rowstride = 3 * nc |
121 |
|
|
|
122 |
|
|
blackdot = [chr(0)]*3; |
123 |
|
|
reddot = [chr(255), chr(0), chr(0)] |
124 |
|
|
pinkdot = [chr(255), chr(127), chr(127)] |
125 |
|
|
skydot = [chr(127), chr(127), chr(255)] |
126 |
|
|
bluedot = [chr(0), chr(0), chr(255)] |
127 |
johnpye |
285 |
hotpinkdot = [chr(255), chr(47), chr(179)] # very big (+/-) |
128 |
|
|
brightbluedot = [chr(71), chr(157), chr(255)] # very small (+/-) |
129 |
|
|
greendot = [chr(87), chr(193), chr(70)] # close to 1 |
130 |
|
|
orangedot = [chr(255), chr(207), chr(61)] # 10-1000 |
131 |
|
|
bluegreendot = [chr(70), chr(221), chr(181)] # 0.001 - 0.1 |
132 |
johnpye |
280 |
for i in self.data: |
133 |
johnpye |
283 |
if i.row < rl or i.row > rh or i.col < cl or i.col > ch: |
134 |
|
|
continue |
135 |
|
|
r = i.row - rl; |
136 |
|
|
c = i.col - cl; |
137 |
|
|
pos = rowstride*r + 3*c |
138 |
|
|
dot = blackdot; |
139 |
|
|
var = self.im.getVariable(i.col); |
140 |
johnpye |
287 |
if abs( (var.getValue()-var.getUpperBound())/ var.getNominal() ) < AT_BOUND_TOL: |
141 |
|
|
dot = reddot |
142 |
|
|
elif abs( var.getValue() - var.getLowerBound() ) / var.getNominal() < AT_BOUND_TOL: |
143 |
|
|
dot = reddot |
144 |
|
|
else: |
145 |
|
|
rat = var.getValue() / var.getNominal() |
146 |
|
|
if rat!=0: |
147 |
|
|
try: |
148 |
|
|
val = abs(rat) |
149 |
|
|
if abs(rat) > 1000: |
150 |
|
|
dot = hotpinkdot |
151 |
|
|
elif abs(rat) > 10: |
152 |
|
|
dot = orangedot |
153 |
|
|
elif abs(rat) < 0.001: |
154 |
|
|
dot = brightbluedot |
155 |
|
|
elif abs(rat) < 10 and abs(rat) > 0.1: |
156 |
|
|
dot = greendot |
157 |
|
|
elif abs(rat) > 0.001 and abs(rat) < 0.1: |
158 |
|
|
dot = bluegreendot |
159 |
|
|
else: |
160 |
|
|
dot = blackdot |
161 |
|
|
except ValueError, e: |
162 |
|
|
pass |
163 |
johnpye |
284 |
#print "DOT: ",dot |
164 |
johnpye |
283 |
b[pos], b[pos+1], b[pos+2] = dot |
165 |
|
|
|
166 |
johnpye |
282 |
d = ''.join(b) |
167 |
johnpye |
283 |
|
168 |
johnpye |
669 |
print "DONE IMAGE CREATION" |
169 |
johnpye |
282 |
|
170 |
johnpye |
284 |
self.pixbuf = gtk.gdk.pixbuf_new_from_data(d, gtk.gdk.COLORSPACE_RGB, False, 8 \ |
171 |
|
|
, nc, nr, rowstride); |
172 |
johnpye |
283 |
|
173 |
johnpye |
284 |
self.nr = nr |
174 |
|
|
self.nc = nc |
175 |
|
|
self.zoom = -1 # to fit, up to max 16x |
176 |
|
|
self.do_zoom() |
177 |
johnpye |
283 |
|
178 |
johnpye |
669 |
print "DONE IMAGE TRANSFER TO SERVER" |
179 |
johnpye |
283 |
|
180 |
johnpye |
284 |
self.fill_var_names() |
181 |
|
|
self.fill_rel_names() |
182 |
johnpye |
286 |
|
183 |
johnpye |
735 |
self.fill_selection_info() |
184 |
johnpye |
280 |
|
185 |
johnpye |
669 |
print "DONE FILL VALUES" |
186 |
johnpye |
286 |
|
187 |
johnpye |
735 |
def fill_selection_info(self): |
188 |
|
|
if self.var: |
189 |
|
|
self.varname.set_text(self.var.getName()) |
190 |
|
|
self.varval.set_text(str(self.var.getValue())) |
191 |
|
|
self.varinfobutton.set_sensitive(True) |
192 |
|
|
else: |
193 |
|
|
self.varname.set_text("") |
194 |
|
|
self.varval.set_text("") |
195 |
|
|
self.varinfobutton.set_sensitive(False) |
196 |
|
|
|
197 |
|
|
if self.rel: |
198 |
|
|
self.relname.set_text(self.rel.getName()) |
199 |
|
|
self.relresid.set_text(str(self.rel.getResidual())) |
200 |
|
|
self.relinfobutton.set_sensitive(True) |
201 |
|
|
else: |
202 |
|
|
self.relname.set_text("") |
203 |
|
|
self.relresid.set_text("") |
204 |
|
|
self.relinfobutton.set_sensitive(False) |
205 |
|
|
|
206 |
johnpye |
284 |
def do_zoom(self): |
207 |
|
|
if self.zoom == -1: |
208 |
|
|
w, h = self.imagescroll.size_request() |
209 |
|
|
#print "SCALE TO FIX, w=%d, h=%d" % (w,h) |
210 |
|
|
if self.nc/self.nr > w/h: |
211 |
|
|
# a 'wide' image |
212 |
|
|
self.zoom = w / self.nc |
213 |
|
|
else: |
214 |
|
|
self.zoom = h / self.nr |
215 |
johnpye |
283 |
|
216 |
johnpye |
284 |
if self.zoom > MAX_ZOOM_RATIO: |
217 |
|
|
self.zoom = MAX_ZOOM_RATIO |
218 |
johnpye |
280 |
|
219 |
johnpye |
284 |
if self.zoom * self.nc > MAX_ZOOM_SIZE or self.zoom * self.nr > MAX_ZOOM_SIZE: |
220 |
|
|
self.zoom = MAX_ZOOM_SIZE / max(self.nc,self.nr) |
221 |
johnpye |
283 |
|
222 |
johnpye |
284 |
w = int(self.zoom * self.nc); |
223 |
|
|
h = int(self.zoom * self.nr); |
224 |
|
|
|
225 |
|
|
self.zoomentry.set_text("%d %%" % (int(self.zoom*100)) ) |
226 |
johnpye |
280 |
|
227 |
johnpye |
284 |
if self.zoom < 2: |
228 |
|
|
pb1 = self.pixbuf.scale_simple(w,h,gtk.gdk.INTERP_BILINEAR) |
229 |
|
|
else: |
230 |
|
|
pb1 = self.pixbuf.scale_simple(w,h,gtk.gdk.INTERP_NEAREST) |
231 |
|
|
|
232 |
|
|
self.image.set_from_pixbuf(pb1) |
233 |
|
|
|
234 |
johnpye |
280 |
def fill_var_names(self): |
235 |
johnpye |
669 |
print "FILL VAR NAMES" |
236 |
|
|
|
237 |
johnpye |
280 |
names = [str(i) for i in self.im.getBlockVars(self.block)] |
238 |
johnpye |
669 |
|
239 |
|
|
print "NAMES:",names |
240 |
|
|
|
241 |
johnpye |
280 |
if self.varcollapsed.get_active(): |
242 |
johnpye |
287 |
res = reduce(names) |
243 |
johnpye |
280 |
rows = [] |
244 |
|
|
for k in res: |
245 |
|
|
if k=="": |
246 |
|
|
for r in res[k]: |
247 |
|
|
rows.append(r) |
248 |
|
|
else: |
249 |
|
|
rows.append( '%s:\n\t%s' % (k, "\n\t".join(res[k])) ) |
250 |
|
|
text = "\n".join(rows) |
251 |
|
|
else: |
252 |
|
|
text = "\n".join(names) |
253 |
|
|
self.varbuf.set_text(text) |
254 |
|
|
|
255 |
johnpye |
669 |
print "DONE VAR NAMES" |
256 |
|
|
|
257 |
johnpye |
280 |
def fill_rel_names(self): |
258 |
johnpye |
669 |
print "REL NAMES" |
259 |
|
|
|
260 |
|
|
rels = self.im.getBlockRels(self.block) |
261 |
|
|
|
262 |
|
|
print "GOT RELS, NOW GETTING NAMES" |
263 |
|
|
|
264 |
|
|
names = [str(i) for i in rels] |
265 |
|
|
|
266 |
|
|
print "NAMES =",names |
267 |
|
|
|
268 |
johnpye |
280 |
if self.relcollapsed.get_active(): |
269 |
johnpye |
287 |
res = reduce(names) |
270 |
|
|
rows = [] |
271 |
|
|
for k in res: |
272 |
|
|
if k=="": |
273 |
|
|
for r in res[k]: |
274 |
|
|
rows.append(r) |
275 |
|
|
else: |
276 |
|
|
rows.append( '%s:\n\t%s' % (k, "\n\t".join(res[k])) ) |
277 |
|
|
text = "\n".join(rows) |
278 |
johnpye |
280 |
else: |
279 |
|
|
text = "\n".join(names) |
280 |
|
|
self.relbuf.set_text(text) |
281 |
|
|
|
282 |
johnpye |
669 |
print "DONE REL NAMES" |
283 |
|
|
|
284 |
johnpye |
280 |
def set_block(self, block): |
285 |
|
|
self.fill_values(block) |
286 |
|
|
|
287 |
johnpye |
284 |
def set_zoom(self,zoom): |
288 |
|
|
self.zoom = zoom |
289 |
|
|
self.do_zoom() |
290 |
|
|
|
291 |
johnpye |
286 |
def show_cursor(self,x,y): |
292 |
|
|
c = self.cl + int(x/self.zoom) |
293 |
|
|
r = self.rl + int(y / self.zoom) |
294 |
|
|
if c > self.ch or r > self.rh: |
295 |
|
|
#print "OUT OF RANGE" |
296 |
|
|
return |
297 |
johnpye |
735 |
self.var = self.im.getVariable(c) |
298 |
|
|
self.rel = self.im.getRelation(r) |
299 |
|
|
self.fill_selection_info() |
300 |
johnpye |
286 |
|
301 |
|
|
# GUI EVENT HOOKS----------------------------------------------------------- |
302 |
|
|
|
303 |
johnpye |
735 |
def on_diagnosewin_close(self,*args): |
304 |
|
|
self.window.response(gtk.RESPONSE_CLOSE); |
305 |
|
|
|
306 |
|
|
# incidence data view |
307 |
|
|
|
308 |
johnpye |
280 |
def on_varcollapsed_toggled(self,*args): |
309 |
johnpye |
294 |
vc = self.varcollapsed.get_active() |
310 |
|
|
self.browser.prefs.setBoolPref("Diagnose","varcollapsed",vc) |
311 |
|
|
if self.im: |
312 |
|
|
self.fill_var_names() |
313 |
johnpye |
280 |
|
314 |
|
|
def on_relcollapsed_toggled(self,*args): |
315 |
johnpye |
294 |
rc = self.varcollapsed.get_active() |
316 |
|
|
self.browser.prefs.setBoolPref("Diagnose","relcollapsed",rc) |
317 |
|
|
if self.im: |
318 |
|
|
self.fill_rel_names() |
319 |
johnpye |
280 |
|
320 |
johnpye |
735 |
# detailed information about vars and rels (solver-side information!) |
321 |
|
|
|
322 |
|
|
def on_varinfobutton_clicked(self,*args): |
323 |
|
|
title = "Variable '%s'" % self.var |
324 |
|
|
text = "%s\n%s\n" % (title,"(from the solver's view)") |
325 |
|
|
text += "\n%-30s%15f" % ("Value", self.var.getValue()) |
326 |
|
|
text += "\n%-30s%15f" % ("Nominal", self.var.getNominal()) |
327 |
|
|
text += "\n%-30s%15f" % ("Lower bound", self.var.getLowerBound()) |
328 |
|
|
text += "\n%-30s%15f" % ("Upper bound", self.var.getUpperBound()) |
329 |
|
|
|
330 |
|
|
text += "\n\nIncidence with %d relations:" % self.var.getNumIncidentRelations() |
331 |
|
|
for r in self.var.getIncidentRelations(): |
332 |
|
|
text += "\n%s" % r.getName() |
333 |
|
|
|
334 |
|
|
_dialog = InfoDialog(self.browser,self.window,text,title) |
335 |
|
|
_dialog.run() |
336 |
|
|
|
337 |
|
|
def on_relinfobutton_clicked(self,*args): |
338 |
|
|
title = "Relation '%s'" % self.rel |
339 |
|
|
text = "%s\n%s\n" % (title,"(from the solver's view)") |
340 |
|
|
text += "\n%-30s%15f" % ("Residual", self.rel.getResidual()) |
341 |
|
|
|
342 |
|
|
_dialog = InfoDialog(self.browser,self.window,text,title) |
343 |
|
|
_dialog.run() |
344 |
|
|
|
345 |
|
|
|
346 |
|
|
# block navigation |
347 |
|
|
|
348 |
johnpye |
280 |
def on_nextbutton_clicked(self,*args): |
349 |
|
|
self.set_block(self.block + 1) |
350 |
|
|
|
351 |
|
|
def on_prevbutton_clicked(self,*args): |
352 |
johnpye |
290 |
self.set_block(self.block - 1) |
353 |
johnpye |
280 |
|
354 |
johnpye |
290 |
def on_prevbigbutton_clicked(self,*args): |
355 |
|
|
b = self.block - 1 |
356 |
johnpye |
291 |
while b >= 0: |
357 |
johnpye |
290 |
rl,cl,rh,ch = self.im.getBlockLocation(b) |
358 |
|
|
if rh-rl > 0 or ch-cl>0: |
359 |
|
|
self.set_block(b) |
360 |
|
|
b = b - 1 |
361 |
|
|
print "NO PRECEDING 'BIG' BLOCKS" |
362 |
|
|
|
363 |
|
|
def on_nextbigbutton_clicked(self,*args): |
364 |
|
|
b = self.block + 1 |
365 |
|
|
n = self.im.getNumBlocks() |
366 |
|
|
while b < n: |
367 |
|
|
rl,cl,rh,ch = self.im.getBlockLocation(b) |
368 |
|
|
if rh-rl > 0 or ch-cl>0: |
369 |
|
|
self.set_block(b) |
370 |
|
|
b = b + 1 |
371 |
|
|
print "NO FOLLOWING 'BIG' BLOCKS" |
372 |
|
|
|
373 |
johnpye |
283 |
def on_blockentry_key_press_event(self,widget,event): |
374 |
|
|
keyname = gtk.gdk.keyval_name(event.keyval) |
375 |
|
|
print "KEY ",keyname |
376 |
|
|
if keyname=="Return": |
377 |
|
|
self.set_block( int(self.blockentry.get_text()) ) |
378 |
johnpye |
280 |
|
379 |
johnpye |
735 |
# zoom in and out |
380 |
|
|
|
381 |
johnpye |
284 |
def on_zoominbutton_clicked(self,*args): |
382 |
|
|
z = int( math.log(self.zoom)/math.log(2) ) |
383 |
|
|
z = pow(2,z + 1); |
384 |
|
|
self.set_zoom(z) |
385 |
|
|
|
386 |
|
|
def on_zoomoutbutton_clicked(self,*args): |
387 |
|
|
z = int( math.log(self.zoom)/math.log(2) + 0.999) |
388 |
|
|
z = pow(2,z - 1); |
389 |
|
|
self.set_zoom(z) |
390 |
|
|
|
391 |
|
|
def on_zoomentry_key_press_event(self,widget,event): |
392 |
|
|
keyname = gtk.gdk.keyval_name(event.keyval) |
393 |
|
|
print "KEY ",keyname |
394 |
|
|
if keyname=="Return": |
395 |
|
|
t = self.zoomentry.get_text() |
396 |
|
|
m = ZOOM_RE.match(t) |
397 |
|
|
if not m: |
398 |
|
|
self.zoomentry.set_text("%d %%" % int(self.zoom*100)) |
399 |
|
|
for mm in m: |
400 |
|
|
print m |
401 |
|
|
self.set_zoom( int(self.zoomentry.get_text()) ) |
402 |
|
|
|
403 |
johnpye |
735 |
# clicking in incidence matrix to get updated information at RHS |
404 |
|
|
|
405 |
johnpye |
286 |
def on_imageevent_motion_notify_event(self,widget,event): |
406 |
|
|
self.show_cursor(event.x, event.y) |
407 |
|
|
|
408 |
|
|
def on_imageevent_button_press_event(self,widget,event): |
409 |
|
|
self.show_cursor(event.x, event.y) |
410 |
|
|
|
411 |
|
|
|
412 |
johnpye |
280 |
# The following is from |
413 |
|
|
# http://www.experts-exchange.com/Programming/Programming_Languages/Python/Q_21719649.html |
414 |
johnpye |
284 |
# it's still buggy. |
415 |
johnpye |
280 |
|
416 |
|
|
def fold(data): |
417 |
|
|
""" fold sorted numeric sequence data into ranged representation: |
418 |
|
|
>>> fold([1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]) |
419 |
|
|
'[1,4-6,10,15-18,22,25-28]' |
420 |
|
|
""" |
421 |
|
|
folded = [] |
422 |
johnpye |
287 |
for k, g in groupby(enumerate(sorted(data)), lambda (i,x):i-x): |
423 |
johnpye |
280 |
seq = map(itemgetter(1), g) |
424 |
|
|
if len(seq) > 1: |
425 |
|
|
x = '%s-%s' % (seq[0], seq[-1]) |
426 |
|
|
else: |
427 |
|
|
x = str(seq[0]) |
428 |
|
|
folded.append(x) |
429 |
|
|
return folded and '[%s]' % ','.join(folded) or '' |
430 |
|
|
|
431 |
johnpye |
287 |
def reduce(names): |
432 |
|
|
"""reduce a list of items nto something more readable: |
433 |
johnpye |
280 |
>>> data = 'C.x C.p C.T C.delta[1] C.delta[2] C.delta[3] C.sat.x C.sat.p C.h C.delta[5]'.split() |
434 |
|
|
>>> res = reduce(data) |
435 |
|
|
>>> for k in sorted(res): |
436 |
|
|
... print '%s: %s' % (k, res[k]) |
437 |
|
|
C: T, delta[1-3,5], h, p, x |
438 |
|
|
C.sat: p, x |
439 |
|
|
""" |
440 |
johnpye |
287 |
data = sorted([n.split('.') for n in sorted(names)], key=len) |
441 |
johnpye |
280 |
res = {} |
442 |
|
|
for k, g in groupby(data, lambda x: len(x)): |
443 |
johnpye |
287 |
if k == 1: |
444 |
|
|
indexed = {} |
445 |
|
|
seq = set(get(indexed, item) for item in g) |
446 |
|
|
res['[global]'] = [ i+fold(indexed.get(i, [])) for i in sorted(seq) ] |
447 |
|
|
else: |
448 |
|
|
for key, g1 in groupby(g, lambda x: '.'.join(x[:-1])): |
449 |
|
|
indexed = {} |
450 |
|
|
seq = set(get(indexed, item) for item in g1) |
451 |
|
|
res[key] = [ i+fold(indexed.get(i, [])) for i in sorted(seq) ] |
452 |
johnpye |
280 |
return res |
453 |
|
|
|
454 |
|
|
def get(indexed, item): |
455 |
|
|
item = item[-1] |
456 |
|
|
if item.endswith(']'): |
457 |
|
|
item, idx = item[:-1].split('[') |
458 |
|
|
indexed.setdefault(item, []).append(int(idx)) |
459 |
|
|
return item |