Source code for emod_api.config.dtk_pre_process_adhocevents
#!/usr/bin/pythonimportjsonimportosimportpathlibimporttime"""This is the pre-processing script to use ad-hoc events in your campaign files. It will 'scrape' your campaign.json for eventsthat aren't available in the bulit-in list and map them to GP_EVENT_xxx events. It will currently handle up to 100, though that can be changed if someone really truly needs more.Note that this works in conjunction with a post-proc script that maps all the GP_EVENT's back to the user-specified names.Several things to note about design choices and possible areas of improvement: 1) It would obviously be great to get the list of built-in events from the code but currently that would mean parsing a C header file (which relies on a macro). There are various kinds of 'meh' associated with that approach. For now I've copy-pasted the list of actual campaign-related events used in our regression tests from the built-in list here. Looking for a great idea for how to do this. 2) There is no perfect way of finding events in the campaign file so I've relied on parsing and our current conventions: - _Events, Choice_Names, Event_Trigger, Event_To_Broadcast. Ideally there'd be a hard-and-fast rule. 3) This code is super-simple except for the recursive march through the campaign file, but that's similar to how we do that same thing elsewhere and have been for some time."""adhoc_events=[]builtin_events=["StartedART","StoppedART","Births","TBTestPositive","TBTestNegative","TBTestDefault","Blackout"]def_recursive_json(ref_json,flat_input_json):forvalinref_json:try:#if not leaf, call recursive_json_leaf_readerifisinstance(ref_json[val],dict):#print( "recursing on nested object: val = " + val )_recursive_json(ref_json[val],flat_input_json)elifisinstance(ref_json[val],list):#print( "Iterating over list under val: " + val )forobjinref_json[val]:# Ignore lists of numbers and stringsifisinstance(obj,dict)orisinstance(obj,list):_recursive_json(obj,flat_input_json)elif"_Events"invalorval=="Choice_Names":foreventinref_json[val]:print(event)ifevent!="NoTrigger"andeventnotinadhoc_eventsandeventnotinbuiltin_eventsandlen(event)>0:adhoc_events.append(event)else:#print( "Considering " + str(val ))if("_Event"invalor"Event_Trigger"invalorval=="Event"orval=="Event_To_Broadcast")and"Event_Type"notinval:broadcast_event=ref_json[val]print(broadcast_event)ifbroadcast_eventnotinadhoc_eventsandbroadcast_eventnotinbuiltin_eventsandlen(broadcast_event)>0:adhoc_events.append(broadcast_event)ifvalnotinflat_input_json:flat_input_json[val]=ref_json[val]#else:#print( "Ignoring {0} because already present.".format( val ) )exceptExceptionasex:print("Exception processing {0} in {1}. Usually only happens in NChooser.".format(val,ref_json))
[docs]defdo_mapping_from_events(config,adhoc_events):""" Given a config file, a campaign file, and a list of adhoc_events, do the mappings. The adhoc_event list originally came from scraping an existing campaign file but now comes from emod_api.campaign. """camp_filename=json.loads(open(config).read())["parameters"]["Campaign_Filename"]camp_json=json.loads(open(camp_filename).read())event_map={}foreventinadhoc_events:counter=len(event_map)builtin="GP_EVENT_{:03d}".format(counter)event_map[event]=builtincamp_json_str=json.dumps(camp_json)foreventinevent_map:camp_json_str=camp_json_str.replace('"'+event+'"','"'+event_map[event]+'"')camp_json=json.loads(camp_json_str)withopen("campaign_xform.json","w")ascamp_json_handle:camp_json_handle.write(json.dumps(camp_json,sort_keys=True,indent=4))# Do event mapping in config.json# 1) Loadconfig_json=json.loads(open(config).read())# Could do a nifty for loop but obsessing about copy-pasting can sometimes lead to unnecessarily opaque codecrf=""if"Custom_Reports_Filename"inconfig_json["parameters"]:crf=config_json["parameters"]["Custom_Reports_Filename"]ifos.path.exists(crf):report_json=Nonewithopen(crf)ascrf_handle:# could just read string, do replace, and write string but seems nasty to skip json parsing....report_json_str=json.dumps(json.load(crf_handle))foreventinevent_map:report_json_str=report_json_str.replace('"'+event+'"','"'+event_map[event]+'"')report_json=json.loads(report_json_str)withopen("custom_reports_xform.json","w")asreport_json_handle:report_json_handle.write(json.dumps(report_json,sort_keys=True,indent=4))# 2) Make changesconfig_json["parameters"]["Campaign_Filename"]="campaign_xform.json"ifos.path.exists(crf):config_json["parameters"]["Custom_Reports_Filename"]="custom_reports_xform.json"config_json_str=json.dumps(config_json)reverse_map={}foruser_name,builtin_nameinevent_map.items():config_json_str=config_json_str.replace('"'+str(user_name)+'"','"'+str(builtin_name)+'"')reverse_map[builtin_name]=user_name# 3) Saveconfig_json=json.loads(config_json_str)config_json["parameters"]["Event_Map"]=reverse_mapwithopen("config_xform.json","w")asconf_json_handle:conf_json_handle.write(json.dumps(config_json,sort_keys=True,indent=4))
[docs]defapplication(config):""" This is the public interface function to the submodule. """ifpathlib.Path("config_xform.json").is_file():# This means (in the multcore context) that another 'thread' (core) got here first and is doing the work for us.# We know what the filename is going to be.# TBD: We should really add code that waits until config_xform.json does not equal "HOLD".whilepathlib.Path("config_xform.json").stat().st_size<=5:time.sleep(0.01)return"config_xform.json"withopen("config_xform.json","w")asf:f.write("HOLD")print("DTK PRE PROC SCRIPT: Scrape all ad-hoc events and map to GPIO_X.")camp_filename=json.loads(open("config.json").read())["parameters"]["Campaign_Filename"]camp_json=json.loads(open(camp_filename).read())forcamp_eventincamp_json["Events"]:output_json={}_recursive_json(camp_event,output_json)do_mapping_from_events(config,adhoc_events)return"config_xform.json"