In [ ]:
from os import chdir
chdir('C:\\sandboxes\\django-xmi\\django_xmi')

In [ ]:
from xmi.parser import XmiParser, DotDict, camel_to_snake, make_name_safe

Load XMI from OMG


In [ ]:
sysml = XmiParser()

In [ ]:
sysml.parse('../notebooks/UML.xmi')

In [ ]:
sysml.parse('../notebooks/SysML.xmi')

In [ ]:
sysml.parse_profile(sysml.packages.SysML)

In [ ]:
sysml.parse_profile(sysml.packages.UML, 'Package')

In [ ]:
sysml.process_literals()

In [ ]:
sysml.process_attributes()

In [ ]:
sysml.process_operations_and_rules()

Write Django models to files


In [ ]:
from os import path, remove
from textwrap import wrap
from warnings import warn

In [ ]:
for element_name in self.ordered_elements():
    element = self.elements.get(element_name, None)
    if element is None:
        warn("Could not find '{}' in order to write it to a file".format(element_name))
        continue
    
    element.__classdec__ = ['class {}(models.Model):'.format(element.name)]
    
    docstring = element.get('__docstring__', '')
    if isinstance(docstring, str):    
        element.__docstring__ = ([INDENT + '"""'] +
                                 [(INDENT + s) for s in wrap('{}'.format(docstring), 108)] +
                                 [INDENT + '"""\n'])
    if isinstance(element.__package__, str):
        element.__package__ = [INDENT + "__package__ = '{}'\n".format(element.__package__)]
        
    # Get fields and operations from superclasses:
    element.__fields__ = {}
    element.__methods__ = {}
    element.__literals__ = {}
    if inherit:
        elem_attrs = element.get('attributes', {}).keys()
        elem_methods = set(element.get('rules', {}).keys()).union(set(element.get('operations', {}).keys()))
        elem_literals = element.get('literals', {}).keys()
        for superclass in element.__modelclass__.split(','):
            superclass = self.elements.get(camel_to_snake(superclass.strip()), None)
            if superclass is not None:
                element.__fields__.update({k: v for k, v in superclass.__fields__.items() if k not in elem_attrs})
                element.__methods__.update({k: v for k, v in superclass.__methods__.items() if k not in elem_attrs})
                element.__literals__.update({k: v for k, v in superclass.__literals__.items() if k not in elem_attrs})
    else:
        for i, other in enumerate(element.__modelclass__.split(',')):
            other = other.strip()
            if 'models.Model' in other:
                continue
            args = ["'{}'".format(other)]
            if i == 0:
                args += ['on_delete=models.CASCADE', 'primary_key=True']
            var_name = make_name_safe(other)
            if var_name in element.__fields__:
                warn("\n\tOverwriting field '{}.{}'\n".format(element.name, var_name))
            element.__fields__.update({var_name: '    {} = models.OneToOneField({})'.format(var_name, ', '.join(args))})
            first = False

    for attr in element.get('attributes', {}).values():
        if attr.name in element.__fields__:
            warn("\n\tOverwriting field '{}.{}'\n".format(element.name, attr.name))
        if '__print__' not in attr:
            warn("Could not find __print__ method in '{}.{}'".format(element.name, attr.name))
            continue
            
        args = ', '.join(attr.__print__.args + [attr.__print__.get('help_text', '')])
        element.__fields__.update({attr.name: "{}({})".format(attr.__print__.field, args)})
        
        if '__choices__' in attr:
            if attr.name in element.__literals__:
                warn("\n\tOverwriting literal '{}.{}'\n".format(element.name, attr.name))
            element.__literals__.update({attr.name: attr.__choices__})
        
    for method_name, method in {**element.get('operations', {}), **element.get('rules', {})}.items():
        if method_name in element.__methods__:
            warn("\n\tOverwriting method '{}.{}'\n".format(element.name, method_name))
        if '__print__' not in method:
            warn("Could not find __print__ method in '{}.{}'".format(element.name, method_name))
        element.__methods__.update({method_name: '\n'.join(method.__print__)})

In [ ]:
for element in sysml.elements.values():
    literals = []
    if element.__literals__:
        literals = ['\n'.join(('\n'+i[-1]+'\n') for i in sorted(element.__literals__.items()))]
    element.__django_model__ = (element.__classdec__ + 
                                element.__docstring__ + 
                                element.__package__ + 
                                literals + 
                                [i[1] for i in sorted(element.__fields__.items())] + 
                                ['\n' + i[1] for i in sorted(element.__methods__.items())])

In [ ]:
BASE_DIR = './models'
self = sysml

In [ ]:
filenames = set(e.__profile__ for e in self.elements.values())
filenames = {f: path.join(BASE_DIR, f.lower() +'.py') for f in filenames}

for file in filenames.values():
    if path.exists(file):
        remove(file)

loaded = {}
for elem_name in self.ordered_elements():
    element = self.elements.get(elem_name)
    filename = filenames[element.__profile__]
    with open(filename, 'a') as file:
        if filename not in loaded:
            file.write('from django.db import models\n')
            for module in loaded.values():
                file.write('from .{} import *\n'.format(module))
            loaded[filename] = element.__profile__.lower()
        file.write('\n\n' + '\n'.join(element.__django_model__))

In [ ]:
from shutil import copyfile

copyfile('./models/uml.py', '../../cortex-django/cortex/cerebral/models/uml.py')
copyfile('./models/sysml.py', '../../cortex-django/cortex/cerebral/models/sysml.py')

In [ ]:
sysml.elements.opaque_behavior.attributes.body.ownedComment

TODOs

Remove the reverse accessors which Django creates automatically

  • e.g., Namespace.named_element: (fields.E302) Reverse accessor for 'Namespace.named_element' clashes with field name 'NamedElement.namespace'.
  • DONE -- kindof, the IGNORED_ACCESSORS set is a workaround to handle this, need a better way to handle it though.

Parse multiplicity relationships

  • Look at how OSLC's SysML profile figure out how to determine if a relationship is zero-to-one, one-to-many, or zero-to-many

  • DONE -- This was a matter of setting ManyToMany if upperValue.value == '*' and blank=True if 'upperValue' in attribute

Profile Loading

  • right now things just "get loaded", make it so UML is loaded as a package, or a profile loads its imported packages
  • add feature to load a profile and have the profile load the imported packages and then write the model for only the required elements of that profile

Fix issue with fields referencing abstract models

  • May have to do away with declaring things as abstract and make everything inherit from models.Model and use OneToOneField

Add proper pluralization to the models

Add better repr to the objects

  • Should really use __str__