generateatts.py 14.6 KB
Newer Older
1 2 3 4
"""
Python script to generate simulation attributes from input (text) description
"""

5 6 7 8
app_description = 'Python script to generate simulation attributes' + \
    ' from input (text) descriptions'


9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import argparse
import json
import sys

YAML_ENABLED = False
try:
    import yaml
    YAML_ENABLED = True
except ImportError:
    pass

try:
    import smtk
except ImportError:
    print
24
    print app_description
25 26 27 28 29 30
    print 'Not able to import smtk library. You might need to:'
    print '  - Use the PYTHONPATH variable to point to the smtk python lib'
    print
    sys.exit(-1)


31
def generate_model_items(system, model_description):
32 33 34
    '''
    Constructs model based on input description
    '''
35
    print 'Generating model items'
36
    model = system.refModel()
37 38 39 40 41
    for item_description in model_description:
        group = item_description.get('group')
        name = item_description.get('name')
        mask = item_description.get('mask')
        if (group is None) or (name is None) or (mask is None):
42
            print 'WARNING: model item description incomplete' + \
43 44 45 46 47 48 49 50
                ' - group %s, name %s, mask %s ' % (group, name, mask) + \
                ' - skipping'
            continue
        new_group = model.createModelGroup(name, group, mask)

    return model


51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
def set_item_value(item, value, index=0):
    '''
    Sets value, casting to type as needed
    '''
    methods = {
        smtk.attribute.Item.DOUBLE: float,
        smtk.attribute.Item.INT: int,
    }
    cast_method = methods.get(item.type())
    if cast_method is not None:
        value = cast_method(value)

    print 'Setting value to', value, type(value)
    item.setValue(index, value)


67
def set_item(item, item_description, refitem_list):
68
    '''
69 70
    Recursive method to set contents of Item instances
    Returns boolean indicating success
71
    '''
72
    print 'Set item %s' % item.name()
73 74 75 76
    enabled = item_description.get('enabled')
    if enabled is not None:
        item.setIsEnabled(enabled)

77
    if item.type() == smtk.attribute.Item.GROUP:
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
        # Check for "groups" keyword, which indicates multiple groups
        groups_description = item_description.get('groups')
        if groups_description is None:
            success = process_items(item, item_description, refitem_list)
            return success
        else:
            # Groups description is list of "items"
            n = len(groups_description)
            if not item.setNumberOfGroups(n):
                msg = 'Unabled to set number of groups to %d for item %s' % \
                    (n, item.name())
                print 'WARNING:', msg
                return False
            success = True

            # Process each sub group
            for i in range(n):
Ben Boeckel's avatar
Ben Boeckel committed
95 96 97
                # print groups_description[i]
                success &= process_items(
                    item, groups_description[i], refitem_list, i)
98
            return success
99

100 101 102
    if item.type() == smtk.attribute.Item.ATTRIBUTE_REF:
        # RefItem instances get set after all attributes have been created
        refitem_list.append((item, item_description))
Ben Boeckel's avatar
Ben Boeckel committed
103
        # print 'refitem_list', refitem_list
104 105
        return True

106 107
    expression = item_description.get('expression')
    if expression is not None:
108
        exp = system.findAttribute(expression)
109 110 111 112 113 114 115 116
        if exp is None:
            print 'Could not find expression named %s' % expression
            return False

        print 'Setting expression to %s' % expression
        success = item.setExpression(exp)
        return success

117 118
    discrete_index = item_description.get('discrete_index')
    if discrete_index is not None:
119 120
        print 'Setting discrete index to %d' % discrete_index
        success = item.setDiscreteIndex(discrete_index)
121 122
        # Discrete items can also have "children" items
        process_children_items(item, item_description, refitem_list)
123
        return success
124 125 126

    value = item_description.get('value')
    if value is not None:
127
        if isinstance(value, (list, tuple)):
128 129 130 131
            num_values = len(value)
            print 'num_values', num_values
            item.setNumberOfValues(num_values)
            for i in range(num_values):
132
                set_item_value(item, value[i], i)
133
        else:
134 135
            item.setNumberOfValues(1)
            set_item_value(item, value)
136 137


138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
def process_children_items(item, item_description, refitem_list):
    '''Updates children items of current item

    Only discrete items can have children items
    '''
    children_description = item_description.get('items')
    if children_description is None:
        return True

    child_item_map = item.childrenItems()
    for child_description in children_description:
        name = child_description.get('name')
        if name is None:
            print 'WARNING, child description has no name specified'
            continue
        child = child_item_map.get(name)
        if child is None:
            print 'WARNING, no child item with name \"%s\"' % name
            continue
        concrete_child = smtk.attribute.to_concrete(child)
        set_item(concrete_child, child_description, refitem_list)


161
def process_items(parent, parent_description, refitem_list, group_index=None):
162 163 164 165 166 167
    '''
    Traverses all items contained by parent
    Note that parent may be either Attribute or GroupItem
    Returns boolean indicating success
    '''
    success = True
168 169
    debug_flag = False

170
    item_list = parent_description.get('items', list())
Ben Boeckel's avatar
Ben Boeckel committed
171
    # if debug_flag:
172
    # print 'item_list', item_list
173 174
    for item_description in item_list:
        item_name = item_description.get('name')
175 176
        if debug_flag:
            print 'debug item name', item_name
177 178 179
        if group_index is None:
            item = parent.find(item_name)
        else:
180
            # item = parent.find(group_index, item_name)
181 182 183
            item = fetch_subgroup_item(parent, group_index, item_name)
            if debug_flag:
                print 'item:', item
184
        if item is None:
185
            print 'WARNING: no item %s for parent %s - skipping' % \
186
                (item_name, parent.name())
187
            if isinstance(parent, smtk.attribute.Attribute):
188
                system.removeAttribute(name)
189 190 191 192 193
                count -= 1
                success = False
            break

        concrete_item = smtk.attribute.to_concrete(item)
194
        set_item(concrete_item, item_description, refitem_list)
195 196 197
    return success


198
def fetch_attribute(system, att_type, name, att_id):
199 200 201
    '''
    Retrieves or creates attribute as needed
    '''
202 203 204
    att = None   # return value

    # First check that Definition exists
205
    defn = system.findDefinition(att_type)
206
    if defn is None:
207
        print 'WARNING: no attribute definition for %s type' % \
208 209 210 211
            att_type + ' - skipping'
        return None

    if name is not None:
212
        # Check for attribute with given name
213
        att = system.findAttribute(name)
214 215
    else:
        # Check for single attribute instance
216
        att_list = system.findAttributes(att_type)
217
        if len(att_list) == 1:
Ben Boeckel's avatar
Ben Boeckel committed
218
            # print 'Found single attribute type \"%s\"' % att_type
219
            att = att_list[0]
220

221
    if att is None:
222
        print 'Creating %s attribute' % att_type
223 224
        if att_id is not None:
            # First check that id not already in use
225
            test_id_att = system.findAttribute(att_id)
226 227 228 229
            if test_id_att is not None:
                print 'Cannot generate attribute %s with id %d' % \
                    (name, att_id) + \
                    ' because id is already in use.'
230
                if name is None:
231
                    att = system.createAttribute(att_type)
232
                else:
233
                    att = system.createAttribute(name, att_type)
234
            else:
235
                att = system.createAttribute(name, att_type, att_id)
236
        elif name is None:
237
            att = system.createAttribute(att_type)
238
        else:
239
            att = system.createAttribute(name, att_type)
240 241

        if att is None:
242
            print 'WARNING: Manager did not create attribute of type %s -skipping' % \
243 244
                att_type
    else:
245
        print 'Found attribute type \"%s\" name \"%s\"' % (att_type, att.name())
246
        if att_id is not None and att_id != att.id():
247
            print 'WARNING: attribute id (%d) does not match input %d' % \
248 249 250 251
                (att.id(), att_id)
    return att


252
def fetch_subgroup_item(group_item, group_index, item_name):
Ben Boeckel's avatar
Ben Boeckel committed
253
    '''Finds item in one subgroup.
254

Ben Boeckel's avatar
Ben Boeckel committed
255 256 257 258 259 260 261 262 263 264 265
    Returns None if not found.
    This function is needed because GroupItem.find(size_t, std::string)
    returns a const Item and we need a non-const item
    '''
    n = group_item.numberOfItemsPerGroup()
    for i in range(n):
        item = group_item.item(group_index, i)
        if item.name() == item_name:
            return item
    # else
    return None
266 267


268
def generate_atts(system, attributes_description, refitem_list):
269 270 271 272
    '''
    Constructs attributes based on input description
    Returns number of attributes that got created
    '''
273
    print 'Generating attributes'
274
    count = 0
275
    model = system.refModel()
276 277 278 279 280
    for att_description in attributes_description:
        att_type = att_description.get('att')
        name = att_description.get('name')
        att_id = att_description.get('id')

281
        """
282
        if (att_type is None) or (name is None):
283
            print 'WARNING: attribute description incomplete' + \
284
                ' - type %s, name %s, id %s ' % (att_type, name, att_id) + \
285 286
                ' - skipping'
            continue
287
        """
288 289

        # Attribute may have been automatically instanced
290
        att = fetch_attribute(system, att_type, name, att_id)
291 292 293 294 295 296 297 298 299
        if att is None:
            continue

        # Only support 1 associated model entity
        if model is not None:
            model_item_id = att_description.get('model_item')
            if model_item_id is not None:
                model_item = model.getModelItem(model_item_id)
                if model_item is None:
300
                    print 'WARNING: Did not find model item %d for attribute type %s - skipping' % \
301 302
                        (model_item_id, name)
                else:
303
                    print 'Associate model item %d to att %s' % (model_item_id, att.name())
304 305 306
                    att.associateEntity(model_item)

        count += 1
307
        process_items(att, att_description, refitem_list)
308 309

    return count
310 311


312
def generate_sim(system, description):
313
    '''
314
    Generates smtk attribute system
315 316 317 318 319 320
    Returns number of attributes that got created
    '''
    count = 0
    model = None
    model_description = description.get('model')
    if model_description is not None:
321
        generate_model_items(system, model_description)
322 323 324

    att_description = description.get('attributes')
    if att_description is None:
325
        print 'WARNING: no attributes found in input description'
326
    else:
327
        refitem_list = list()
328
        count = generate_atts(system, att_description, refitem_list)
329 330

        # Process RefItem instances after all attributes created:
Ben Boeckel's avatar
Ben Boeckel committed
331
        # print 'refitem_list', refitem_list
332 333 334
        for item, description in refitem_list:
            attname = description.get('attributeref')
            if attname is None:
335
                print 'WARNING: no attributeref specified for', item.name()
336 337
                continue

338
            att = system.findAttribute(attname)
339 340 341
            refitem = smtk.attribute.to_concrete(item)
            print 'Setting RefItem %s to %s' % (refitem.name(), attname)
            refitem.setValue(att)
342

343
    return count
344 345 346 347


if __name__ == '__main__':
    epilog = 'Note: you must specify EITHER --yaml_filename OR --json_filename'
Ben Boeckel's avatar
Ben Boeckel committed
348 349
    parser = argparse.ArgumentParser(
        description=app_description, epilog=epilog)
350 351 352 353 354 355 356 357 358 359 360 361
    parser.add_argument('-t', '--template_filename', required=True)
    parser.add_argument('-s', '--sim_filename')

    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-y', '--yaml_filename', help='Input description file')
    group.add_argument('-j', '--json_filename', help='Input description file')
    args = parser.parse_args()

    # Check that yaml module loaded if input has yaml file
    if args.yaml_filename is not None and not YAML_ENABLED:
        print
        print 'Sorry, cannot run because python yaml module was not found.'
362
        print 'Either install PyYaml or set PYTHONPATH to include PyYaml.'
363 364
        print 'Note that PyYaml is available in the SMTK thirdparty folder.'
        print 'e.g. \"export PYTHONPATH=/path-to-SMTK/thirdparty/PyYaml\"'
365 366 367 368 369 370
        print
        sys.exit(-2)

    #  Load template file
    logger = smtk.util.Logger()
    print 'Loading template file %s' % args.template_filename
371
    system = smtk.attribute.Collection()
372
    reader = smtk.util.AttributeReader()
373
    err = reader.read(system, args.template_filename, logger)
374
    if err:
375 376
        print 'Abort: Could not load template file'
        print logger.convertToString()
377
        sys.exit(-3)
378
    model = smtk.model.Model.New()
379
    system.setRefModel(model)
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410

    # Load json input
    sim_description = None
    if args.json_filename is not None:
        print 'Loading description file %s' % args.json_filename
        done = False
        with open(json_filename, 'r') as json_file:
            contents = json_file.read()
            done = True
        if not done:
            print 'Abort: Unable to load json description file'
            sys.exit(-4)
        try:
            sim_description = json.loads(contents)
        except Exception:
            print 'Abort: Unable to parse json description file.'
            sys.exit(-4)
    elif args.yaml_filename is not None:
        print 'Loading description file', args.yaml_filename
        done = False
        with open(args.yaml_filename, 'r') as yaml_file:
            contents = yaml_file.read()
            if contents.find('\t') >= 0:
                print 'WARNING: yaml file contains tab char - not valid'
                print 'Will replace with 4 spaces'
                contents = contents.replace('\t', '    ')
            done = True
        if not done:
            print 'Abort: Unable to load yaml description file'
            sys.exit(-4)
        try:
411
            sim_description = yaml.safe_load(contents)
412 413 414 415 416
        except:
            print 'Abort: Unable to parse yaml description file'
            sys.exit(-4)

    # Generate the attributes
417
    count = generate_sim(system, sim_description)
418
    print 'Number of attributes created or updated: %d' % count
419 420

    # Write output
421 422 423 424 425 426
    if args.sim_filename is None:
        print '(No output file specified)'
    else:
        print 'Writing output file %s' % args.sim_filename
        writer = smtk.util.AttributeWriter()
        logger.reset()
427
        err = writer.write(system, args.sim_filename, logger)
428 429 430
        if err:
            print 'Error writing output file'

431
    print 'Done'