Package teamwork :: Package widgets :: Package PsychGUI :: Module CampaignAnalysis
[hide private]
[frames] | no frames]

Source Code for Module teamwork.widgets.PsychGUI.CampaignAnalysis

  1  import copy 
  2  import operator 
  3  import os 
  4  import string 
  5  from threading import * 
  6  import time 
  7  from Tkinter import * 
  8  import tkMessageBox 
  9  import Pmw 
 10   
11 -def multiply(x,y):
12 return x*y
13 14 from teamwork.widgets.images import getImage 15 from teamwork.widgets.WizardShell import WizardShell 16 from teamwork.widgets.cookbook import MultiListbox 17 from teamwork.utils.PsychUtils import load 18 from teamwork.messages.PsychMessage import * 19 from teamwork.math.probability import * 20 from teamwork.widgets.images import getImage 21
22 -class AnalysisWizard(WizardShell):
23 wizname = 'Message Analysis' 24 wizimage = 'analyzing_computer_tv_head_md_wht.gif' 25 ## busyimage = os.path.dirname(__file__)+\ 26 ## '/../../images/animate.gif' 27 frameWidth = 600 28 frameHeight = 500 29 phases = ['sender','receivers','overhearers','models','messages', 30 'progress','results'] 31
32 - def __init__(self, entities, sender=None, receivers=[], 33 messages=[], parent=None, **kw):
34 self.sequence = self.phases[:] 35 self.entities = entities 36 self.sender = sender 37 self.messages = {} 38 for msg in messages: 39 self.messages[msg['label']] = msg 40 # Determine which phases are necessary 41 if self.sender or len(self.messages) == 0: 42 self.sequence.remove('sender') 43 self.receivers = receivers 44 if self.receivers or len(self.messages) == 0: 45 self.sequence.remove('receivers') 46 for agent in self.entities.members(): 47 if len(agent.models) > 0: 48 break 49 else: 50 # No agents have possible stereotypes to vary 51 self.sequence.remove('models') 52 if len(self.messages) == 0: 53 # We're not looking at messages at all 54 self.sequence.remove('overhearers') 55 self.sequence.remove('messages') 56 self.panes = len(self.sequence) 57 self.handlers = {'sender':self.createSelectSender, 58 'receivers':self.createSelectReceivers, 59 'overhearers':self.createSelectOverhearers, 60 'models':self.createSelectModels, 61 'messages':self.createSelectMessages, 62 'progress':self.createShowProgress, 63 'results':self.createShowResults, 64 } 65 self.columns = [] 66 self.models = {} 67 WizardShell.__init__(self,parent,**kw) 68 self['image'] = getImage(self.wizimage)
69
70 - def createInterface(self):
71 WizardShell.createInterface(self) 72 # Add buttons 73 self.buttonAdd('Cancel', command=self.done, state=1) 74 self.nextB = self.buttonAdd('Next', command=self.next, state=1) 75 self.nextB.configure(default=ACTIVE) 76 self.prevB = self.buttonAdd('Prev', command=self.prev, state=0) 77 # Add main content of panes 78 for index in range(self.panes): 79 apply(self.handlers[self.sequence[index]],(index,))
80 81 ## Pane for selecting sender(s)
82 - def createSelectSender(self,pane):
83 senders = filter(lambda n:len(self.entities[n].entities) > 0 , 84 self.entities.keys()) 85 widget = self.createcomponent('senders', (), None, 86 Pmw.RadioSelect, 87 (self.pInterior(pane),), 88 labelpos='n', 89 label_text='Select sender:', 90 buttontype='radiobutton', 91 orient='vertical', 92 command=self.selectSender, 93 ) 94 for name in senders: 95 widget.add(name) 96 if self.sender: 97 widget.invoke(self.sender) 98 widget.pack(fill='both',expand='yes')
99
100 - def selectSender(self,name):
101 """Precludes the selected sender from being a potential receiver""" 102 try: 103 widget = self.component('receivers') 104 if self.sender and self.sender != name: 105 widget.component(self.sender).configure(state='normal') 106 widget.component(name).configure(state='disabled') 107 widget = self.component('hearers') 108 if self.sender and self.sender != name: 109 widget.component(self.sender).configure(state='normal') 110 widget.component(name).configure(state='disabled') 111 self.sender = name 112 except KeyError: 113 pass
114
115 - def getSender(self):
116 return self.sender
117 118 ## Pane for selecting receiver(s)
119 - def createSelectReceivers(self,pane):
120 receivers = filter(lambda n:len(self.entities[n].entities) > 0, 121 self.entities.keys()) 122 widget = self.createcomponent('receivers', (), None, 123 Pmw.RadioSelect, 124 (self.pInterior(pane),), 125 labelpos='n', 126 label_text='Select receiver(s):', 127 buttontype='checkbutton', 128 orient='vertical', 129 command=self.selectReceiver, 130 ) 131 for name in receivers: 132 widget.add(name) 133 if name == self.getSender(): 134 widget.component(name).configure(state='disabled') 135 widget.pack(fill='both',expand='yes')
136
137 - def selectReceiver(self,name,value):
138 """Updates the availability of the selected receiver as a possible sender/overhearer""" 139 if 'sender' in self.components(): 140 widget = self.component('sender') 141 if value: 142 self.nextB['state'] = 'normal' 143 widget.component(name).configure(state='disabled') 144 else: 145 if len(self.getReceivers()) == 0: 146 self.nextB['state'] = 'disabled' 147 widget.component(name).configure(state='normal') 148 if 'hearers' in self.components(): 149 widget = self.component('hearers') 150 if value: 151 widget.component(name).configure(state='disabled') 152 else: 153 widget.component(name).configure(state='normal')
154
155 - def getReceivers(self):
156 if self.receivers: 157 return self.receivers 158 else: 159 return list(self.component('receivers').getvalue())
160 161 ## Pane for selecting overhearer(s)
162 - def createSelectOverhearers(self,pane):
163 receivers = filter(lambda n:len(self.entities[n].entities) > 0, 164 self.entities.keys()) 165 widget = self.createcomponent('hearers', (), None, 166 Pmw.RadioSelect, 167 (self.pInterior(pane),), 168 labelpos='n', 169 label_text='Select overhearer(s):', 170 buttontype='checkbutton', 171 orient='vertical', 172 command=self.selectHearer, 173 ) 174 for name in receivers: 175 widget.add(name) 176 if name == self.getSender() or name in self.getReceivers(): 177 widget.component(name).configure(state='disabled') 178 widget.pack(fill='both',expand='yes')
179
180 - def selectHearer(self,name,value):
181 """Updates the availability of the selected overhearer as a possible sender/receiver""" 182 if 'sender' in self.components(): 183 # Make this hearer unavailable as a sender 184 widget = self.component('sender') 185 if value: 186 self.nextB['state'] = 'normal' 187 widget.component(name).configure(state='disabled') 188 else: 189 if len(self.getHearers()) == 0: 190 self.nextB['state'] = 'disabled' 191 widget.component(name).configure(state='normal') 192 if 'receivers' in self.components(): 193 # Make this hearer unavailable as a sender 194 widget = self.component('receivers') 195 if value: 196 widget.component(name).configure(state='disabled') 197 else: 198 widget.component(name).configure(state='normal')
199
200 - def getHearers(self):
201 return list(self.component('hearers').getvalue())
202 203 ## Pane for selecting messages
204 - def createSelectMessages(self,pane):
205 if len(self.messages) > 0: 206 widget = self.createcomponent('messages',(),None,Pmw.RadioSelect, 207 (self.pInterior(pane),), 208 labelpos='n', 209 label_text='Select message(s):', 210 buttontype='checkbutton', 211 orient='vertical', 212 ) 213 for msg in self.messages.keys(): 214 widget.add(msg) 215 widget.component(msg).invoke() 216 widget.pack(fill='both',expand='yes') 217 else: 218 self.createSelectModels(pane)
219
220 - def getMessages(self):
221 selection = self.component('messages').getvalue() 222 return filter(lambda m:m in selection,self.messages.keys())
223 224 ## Pane for selecting which mental model variations to explore
225 - def createSelectModels(self,pane):
226 book = self.createcomponent('modelbook',(),None,Pmw.NoteBook, 227 (self.pInterior(pane),)) 228 book.pack(fill=BOTH,expand=YES)
229
230 - def setupModels(self):
231 """Configures model selection pane based on current selections""" 232 book = self.component('modelbook') 233 # add any new entities 234 for entity in self.entities.members(): 235 if len(entity.models) == 0: 236 continue 237 try: 238 page = book.add(entity) 239 except ValueError: 240 # Already have a page for this entity 241 continue 242 widget = Pmw.Group(page,tag_text='Select the model variations for this agent:') 243 try: 244 entry = self.models[entity] 245 except KeyError: 246 entry = self.models[entity] = {} 247 # Add checkbuttons for each action 248 for label,model in self.entities[entity].models.items(): 249 if not entry.has_key(label): 250 # Create new variable 251 var = BooleanVar() 252 entry[label] = {'variable':var, 253 'model':model} 254 # Create new button 255 button = Checkbutton(widget.interior(),variable=var, 256 text=label,anchor=W,justify='left') 257 button.pack(fill=X,expand=YES) 258 # Select by default 259 var.set(1) 260 widget.pack(fill=BOTH)
261
262 - def getModels(self):
263 """Returns a dictionary of available models for each entity""" 264 models = {} 265 for entity in self.entities.members(): 266 if self.models.has_key(entity.name): 267 models[entity.name] = [] 268 for label,entry in self.models[entity.name].items(): 269 if entry['variable'].get(): 270 models[entity.name].append(label) 271 elif len(entity.models) > 0: 272 models[entity.name] = [] 273 return models
274 275 ## Pane for showing partial progress 276
277 - def createShowProgress(self,pane):
278 pass
279
280 - def setupProgress(self,pane):
281 """Set up the table of variations and the progress through them""" 282 try: 283 self.destroycomponent('progress') 284 except KeyError: 285 # Haven't created them yet (this must be the first time through) 286 pass 287 # Figure out what the columns look like 288 columns = map(lambda n:(n,8),self.models.keys()) 289 columns.sort() 290 self.columns = map(lambda c:{'type':'model', 291 'name':c, 292 'values':self.models[c]},columns) 293 for msg in self.getMessages(): 294 columns.append((msg,16)) 295 self.columns.append({'type':'message', 296 'name':msg, 297 'values':[True,False]}) 298 for hearer in self.getHearers(): 299 label = '%s Hears' % (hearer) 300 columns.append((label,12)) 301 self.columns.append({'type':'hearer', 302 'name':'%s hears' % (hearer), 303 'values':[True,False]}) 304 columns.append(('Status',6)) 305 # Create the listbox 306 widget = self.createcomponent('progress',(),None,MultiListbox, 307 (self.pInterior(pane),columns)) 308 widget.pack(fill='both',expand='yes') 309 count = reduce(operator.mul,map(lambda c:len(c['values']) , 310 self.columns)) 311 for index in xrange(count): 312 variation = self.getVariation(index) 313 item = ['Pending'] 314 for column in self.columns: 315 entry = variation[column['name']] 316 if isinstance(entry,bool): 317 if entry: 318 entry = 'Yes' 319 else: 320 entry = 'No' 321 item.insert(0,entry) 322 self.root.after(10,lambda w=widget,i=item:w.insert(END,i))
323
324 - def getVariation(self,index):
325 variation = {} 326 for column in self.columns: 327 subIndex = index % len(column['values']) 328 variation[column['name']] = column['values'][subIndex] 329 index /= len(column['values']) 330 return variation
331 332 ## Pane for showing the results of the exploration 333
334 - def createShowResults(self,pane):
335 self.createcomponent('Overall',(),None,Label, 336 (self.pInterior(pane),), 337 text='Overall Analysis').pack(fill=X,expand=YES) 338 self.result = self.createcomponent('Results',(),None, 339 Pmw.ScrolledText, 340 (self.pInterior(pane),), 341 usehullsize=1, 342 hull_height=280) 343 self.result.pack(fill=BOTH,expand=YES)
344
345 - def next(self):
346 """Handle context-sensitive transitions from pane to pane""" 347 WizardShell.next(self) 348 last = self.sequence[self.pCurrent-1] 349 phase = self.sequence[self.pCurrent] 350 if phase == 'actions': 351 self.setupActions() 352 elif phase == 'models': 353 self.setupModels() 354 elif phase == 'progress': 355 self.setupProgress(self.pCurrent) 356 self.results = [] 357 thread = Thread(target=self.analyze) 358 thread.start() 359 elif phase == 'receiver': 360 pass 361 elif phase == 'messages': 362 pass 363 ## if len(self.messages) == 0: 364 ## # Hello. I am a hack. 365 ## entity = self.entities[self.entity] 366 ## if entity.name == 'FirstResponder': 367 ## theme = ('Specific',) 368 ## self.messages.append(theme) 369 ## theme = ('Average',) 370 ## self.messages.append(theme) 371 ## theme = ('General',) 372 ## self.messages.append(theme) 373 ## # Sorry for the interruption, we now return you to your 374 ## # regulary scheduled high-quality programming 375 elif phase == 'results': 376 self.results.sort(lambda x,y: 377 cmp(x['count'],y['count'])) 378 content = '' 379 for result in self.results: 380 variation = self.getVariation(result['variation']) 381 hearers = [] 382 for name in self.entities.keys(): 383 try: 384 model = variation[name] 385 content += '%s: %s\n' % (name,model) 386 except KeyError: 387 pass 388 try: 389 if variation['%s hears' % (name)]: 390 hearers.append(name) 391 except KeyError: 392 pass 393 msgList = [] 394 for msg in self.getMessages(): 395 if variation[msg]: 396 msgList.append(msg) 397 if len(msgList) > 0: 398 content += 'Messages: \n\t%s\n' % ('\n\t'.join(msgList)) 399 if len(hearers) > 0: 400 content += 'Overheard by: ' 401 content += ', '.join(hearers) 402 content += '\n' 403 else: 404 content += 'No message\n' 405 content += '\tViolations: %d\n\n' % (result['count']) 406 self.result.settext(content)
407
408 - def describeObjective(self,value):
409 if value < 0.0: 410 return 'Fail' 411 elif value < 0.2: 412 return 'Neutral' 413 else: 414 return 'Succeed'
415
416 - def analyze(self):
417 """Explore the selected variations and store results""" 418 self.root.after(10,lambda w=self.nextB:w.__setitem__('state',DISABLED)) 419 count = reduce(operator.mul,map(lambda c:len(c['values']) , 420 self.columns)) 421 self.root.after(10,self.busyStart) 422 for index in xrange(count): 423 variation = self.getVariation(index) 424 self.simulate(variation,index) 425 self.root.after(10,self.busyEnd) 426 self.root.after(10,lambda w=self.nextB:w.__setitem__('state',NORMAL))
427
428 - def changeStatus(self,index,status):
429 """Changes status entry of the indexed row to be the given string""" 430 self.root.after(100,lambda s=self,i=index,st=status: 431 s._changeStatus(i,st))
432
433 - def _changeStatus(self,index,status):
434 item = self.component('progress').get(index) 435 item[-1] = status 436 self.component('progress').delete(index) 437 self.component('progress').insert(index,item)
438
439 - def simulate(self,variation,index):
440 """Simulates the result of the current configuration""" 441 self.changeStatus(index,'Projecting') 442 if len(self.models) > 0: 443 sim = copy.deepcopy(self.entities) 444 else: 445 sim = self.entities 446 messages = [] 447 hearers = [] 448 for column in self.columns: 449 key = column['name'] 450 if column['type'] == 'model': 451 sim[key].setModel(variation[key]) 452 elif column['type'] == 'message': 453 if variation[key]: 454 messages.append(self.messages[key]) 455 elif column['type'] == 'hearer': 456 if variation[key]: 457 hearers.append(key[:-6]) 458 self.processMsg(messages,hearers,index,sim) 459 self.changeStatus(index,'Done')
460
461 - def processMsg(self,messages,hearers,index,sim=None):
462 if sim == None: 463 self.changeStatus(index,'Projecting') 464 sim = copy.deepcopy(self.entities) 465 self.changeStatus(index,'Sending') 466 factors = [] 467 for msg in messages: 468 factors.append({'topic':'state', 469 'lhs':['entities',msg['subject'],'state', 470 msg['type']], 471 'value':msg['value'], 472 'relation':'='}) 473 message = Message({'factors':factors}) 474 receivers = self.getReceivers() + hearers 475 violations = {'variation':index,'count':0,'total':0} 476 for name in receivers: 477 # Apply the message 478 receiver = self.entities[name] 479 beliefs = receiver.getAllBeliefs() 480 if len(factors) > 0: 481 delta,explanation = receiver.incorporateMessage(message) 482 receiver.entities.applyChanges(delta,beliefs=beliefs) 483 # Figure out what the agent will do 484 self.changeStatus(index,'Evaluating') 485 actions = receiver.actions.getOptions() 486 decision,explanation = receiver.applyPolicy(beliefs,actions) 487 violations[name] = decision 488 # Identify any violations 489 for action in decision: 490 violations['total'] += len(self.entities.objectives) 491 result = self.entities.detectViolations(action) 492 for objective in result: 493 violations['count'] += 1 494 try: 495 violations[str(objective)] += 1 496 except KeyError: 497 violations[str(objective)] = 1 498 self.results.append(violations)
499
500 - def done(self):
501 self.quit() 502 self.root.destroy()
503 504 if __name__ == '__main__': 505 test = AnalysisWizard() 506 test.run() 507