PyEmofUC

Sample project: CountyCity

J.M. Drake, P. López Martínez and C. Cuevas

Software Engineering and Real-Time (ISTR) - University of Cantabria

Table of Contents

CountyCity project

CountyCity is a sample project in order to show the capabilities and functionality of the PyEmofUC environment.

The project is provided when the environment is installed (download the PyEmofUC environment).
It comprises a set of meta-models and models organized according to the following folder structure:


PyEmofUC => Root folder that results from unpacking the PyEmofUC_0_0.zip archive. It represents the starting point for the inner relative paths. 
CountyCity => Root folder for the CountyCity sample project.

DumpedFiles => Folder containing those files generated at runtime. Its contents can be removed.
Meta-models => Folder containing the meta-models (M2) of the example.
City.xmi => The City meta-model in XMI format.
City.xsd => W3C-Schema that drives the formulation of models compliant to the City meta-model.
County.xmi => The County meta-model in XMI format.
County.xsd => W3C-Schema that drives the formulation of models compliant to the County meta-model.
TaxCensus.xmi => The TaxCensus meta-model in XMI format.
TaxCensus.xsd => W3C-Schema that drives the formulation of models compliant to the TaxCensus meta-model.
Models => Folder containing the models (M1) of the example.
EagleCity.xmi =>
Sample model instance compliant to the City meta-model.
EagleCounty.xmi => Sample model instance compliant to the County meta-model.
Scripts => Folder containing the scripts of the example.
CountyCity_CountyToTaxCensus_Declarative_Script.py =>
Script implementing a sample M2M transformation using a declarative style.
CountyCity_CountyToTaxCensus_Imperative_Script.py => Script implementing a sample M2M transformation using an imperative style.
CountyCity_LoadDump_Script.py => Script implementing a model loading/processing/serialization process.
schemas =>
Folder containing general purpose W3C-Schemas required by the PyEmofUC environment for auxiliary tasks.

EMOF.xsd => Schema for EmofUC meta-models. It is referenced for XML validation of any meta-model in XMI format.
XMI.xsd => Schema of the OMG XMI. It is imported by any meta-model in XMI format.
src => Root folder containing the PyEmof Python code. Its path should be included in the PYTHONPATH environment variable.
 
The project includes:

Metamodels

County meta-model: It formalizes the concepts home (Home class) and school (School class) of a county (County class).
A home is populated by members
that can be either adults (Adult class) or children (Infant class).
Adults may have a number of jobs (Employ class) among the ones declared in a model compliant to the
City meta-model and may be parents.
Children have an assigned school (School class).




The County.xmi file formulates the County meta-model in XMI format, while the County.txt file formulates the same meta-model in plain text.
Finally, the
County.rtf file formulates it in a enhanced (coloured) textual format. When the County meta-model is saved, it generates the W3C-Schema County.xsd (TODO) defining the XMI files as well as the County.bnf BNF grammar (TODO) supporting the models in the RTF format.

City meta-model: It formalizes the concepts of company (Company class) and job (Employ class) in a county.

Each company has a workforce consisting of a set of jobs (Employ class). Each job may be assigned to an employee (worker) who must bte an adult (Adult) whose personal data are described in a model compliant to the County meta-model.

The City.xmi file formulates the City meta-model in XMI format, while the City.txt file formulates the same meta-model in plain text.
Finally, the City.rtf file formulates
it in a enhanced (coloured) textual format.

TaxCensus meta-model: It formalizes the concepts of tax (Tax class), tax payer (TaxPayer class) and tax census (CountyTaxCensus class). The adults resident of a county whose personal data are described in a model compliant to the County meta-model and whose income are described in another model that is compliant to the City meta-model.
A model compliant to this meta-model formulates the county name
(countyName), the debtors census (CountyCesusTax) with the list of adults (taxPayer) in the county with their debts (currentTax).

Each debt has two data: the amount due (amount) and the date (date):

The TaxCensus.xmi file formulates the TaxCensus meta-model in XMI format, while the TaxCensus.txt file formulates the same meta-model in plain text.
Finally, the
TaxCensus.rtf file formulates it in an enhanced (coloured) textual format.


Models

A sample model compliant to the County meta-model is formulated by the EagleCounty.xmi, EagleCounty.txt and EagleCounty.rtf files, each of them using one of the supported formats.

Processing models

The LoadDump script constitutes a simple example of model processing using the PyEmofUC environment.
The script input parameter represents the locator of a model compliant to the County meta-model and the following operations will be performed on it:

The table below shows the script with the Python code.

import pyEmofUC.resource as resource

import pyEmofUC.xmlcore as xmlcore

import pyEmofUC.GLOBAL as GLOBAL

 

xmlcore.PLATFORM = 'C:\CountyCityWorkspace'

 

# Set VERBOSE MODE

GLOBAL.IS_VERBOSE = True

 

# CountyCity metamodel URIs

countyURI = "http://unican.es/istr/PyEmofUC/CountyCity/County"

cityURI = "http://unican.es/istr/PyEmofUC/CountyCity/Cyty"

 

# The application repository is built

aRep = resource.AppRepository()

 

# Loads the EagleCity model

eagleCounty_URL = "platform:CountyCity/models/EagleCounty.xmi"

eagleCounty = aRep.loadModel(eagleCounty_URL)

 

# Example of model managing: Displays the loaded models

print '\n' + 'PRINTS LIST OF LOADED MODELS:'

for loadedModelURI in aRep.mdlRepositories.keys():

    print 5 * ' ' + loadedModelURI

   

# Example of model processing: List the name of adults without job

print '\n' + 'PRINT LIST OF ADULTS WITHOUT JOB:'

adultWithoutJob = (adult for home in eagleCounty.rootPackage.home

                             for adult in home.member

                                 if adult.isInstanceOf('cnty.Adult') and not adult.job)

for theAdult in adultWithoutJob: print 5 * ' ' + theAdult.name

 

# Dumps eagleCounty model with RTF format

dumpedEagleCounty_URL = 'platform:CountyCity/DumpedFiles/EagleCounty.rtf' 

eagleCounty.dump(dumpedEagleCounty_URL)

 

# Delete all models

del aRep

 

# eof

The snapshot below shows a console window printing the results of executing the script.


In addition, the script execution generates the plain text file (EagleCounty.rtf) in the folder designated by the corresponding locator ("platform:CountyCity/DumpedFiles/EagleCounty.rtf"). 

Model transformations

County_To_TaxCensus transformation: The input model must comply to the County meta-model and the output model is compliant to the TaxCensus meta-model.

Since the input model references to a City model, loading it implies loading that second one.



Transformation formulation (declarative style): The table below shows the County_To_TaxCensus M2M transformation in an ATL-like declarative style.
This ATL specification is just a form of documentation, since ATL is not supported by the
PyEmofUC
environment.

# @nsURI county_MM=http://unican.es/istr/PyEmofUC/CountyCity/County

# @nsURI taxCensus_MM= http://unican.es/istr/PyEmofUC/CountyCity/TaxCensus

 

module County_To_TaxCensus;

create outTaxCensusModel : taxCensus_MM from inCountyModel : county_MM;

 

helper def : rootContainer() : county_MM!County = . . .;

 

helper def : currentDate() : String = . . .;

 

helper def : accumulatedSalary(theAdult: county_MM!Adult) : Real= . . .;

 

lazy rule createTax {

      from

             theAdult : county_MM!Adult

      to

             theTax : TaxCensus!Tax (

                   amount <- (0.2 * accumulatedSalary(theAdult)) –

                                                         (100.0 * len(theAdult.offspring)),

                   date <- thisModule.currentDate()

             )

}

 

entrypoint rule County_To_TaxCensus () {

      to

             theCountyTaxCensus : taxCensus_MM!CountyTaxCensus(

                   countyName <- thisModule.rootContainer().countyName,

                   taxPayer <- thisModule.rootContainer().home ->

             collect(theHome | theHome.member ->

                   select(theMember | theMember.isInstanceOf(County_MM!Adult))

             )

             )

}

 

rule Adult_To_TaxPayer {

      from

             theAdult : county_MM!Adult

      to

             theTaxPayer : taxCensus_MM!TaxPayer (

                   payerName <- theAdult.name,

                   address <- theAdult.home.address+ ’ ‘ +

                                      thisModule.rootContainer().countyAddress,

                   currentTax <- if not theAdult.job -> isEmpty()

                                      then thisModule.createTax(theAdult),

                                      else OclUndefined endif,

                   holder <- theAdult

}

 

county_MM represents the URI of the County  meta-model

taxCensus_MM represents the URI of the TaxCensus meta-model

The module County_To_TaxCensus represents the generation of the outTaxCensusModel model from the inCountyModel model

Returns the input model main container.

Returns the date of the invocation instant

 

Returns the accumulated salary of the Adult element as the sum of the salaries corresponding to the jobs assigned to him/her.

Callable rule which generates an element of Tax type in the output model using the data of the referenced Adult element

 

 

 

 

 

 

 

 

Initial rule which generates the output model main container using the data in the input model

 

 

 

 

 

 

 

 

 

 

Rule which generates a TaxPayer element in the output model for each Adult element in the input model



The County_To_TaxCensus_Declarative.py file contains the transformation Python code in a declarative style.

Python code

ATL code

import emofUC.resource as resource

import emofUC.xmlcore as xmlcore

import emofUC.GLOBAL as GLOBAL

 

aRep = resource.AppRepository()

xmlcore.PLATFORM = "C:\Users\. . .\PyEMOF_UC\Workspace"

 

 

@path county_MM_URL= platform:CountyCity/County.xmi

@ path taxCensus_MM_URL= platform:CountyCity/TaxCensus.xmi

 

county_MM_URL = 'platform:CountyCity/County.xmi'

taxCensus_MM_URL = 'platform:CountyCity/TaxCensus.xmi'

aRep.loadModel(taxCensus_MM_URL)

 

 

module CountyCity_To_TaxCensus;

create outTaxCensusModel : taxCensus_MM from inCountyModel : county_MM;

outTaxCensusModelURI = "http://unican.es/istr/PyEmofUC/CountyCity/EagleTaxCensus"

outTaxCensusModelNsPrefix = 'etxc'

outTaxCensusModel = aRep.createMdlRepository(uri=outTaxCensusModelURI, mmUrl=taxCensus_MM_URL, nsPrefix=outTaxCensusModelNsPrefix)

inCountyModelURL = "platform:CountyCity/EagleCounty.xmi"

inCountyModel = aRep.loadModel(inCountyModelURL)

 

 

helper def : rootContainer() : county_MM!County = . . .;

def rootContainer(): return inCountyModel.rootPackage

 

 

helper def : currentDate() : String = . . .;

def currentDate(): return GLOBAL.currentDate()

 

 

helper def : accumulatedSalary(theAdult: county_MM!Adult) : Real= . . .;

def accumulatedSalary(theAdult): return reduce(lambda x, y:x + y, [job.salary for job in theAdult.job])

 

 

lazy rule createTax {

      from

            theAdult : county_MM!Adult

      to

             theTax : TaxCensus!Tax (

                   amount <- (0.2 * accumulatedSalary(theAdult)) –  (100.0 * len(theAdult.offspring)),

                   date <- thisModule.currentDate()

             )

}

def createTax(theAdult):

    if len(theAdult.job) > 0:

        theTax = outTaxCensusModel.createElement('txc.Tax', theAdult.xmi_id.replace('cnty', 'etxc') + '.theTax')

        theTax.amount = (0.2 * accumulatedSalary(theAdult)) - (100.0 * len(theAdult.offspring))

        theTax.date = currentDate()

        return theTax

    else: return None

 

 

entrypoint rule County_To_TaxCensus () {

      to theCountyTaxCensus : taxCensus_MM!CountyTaxCensus(

             countyName <- thisModule.rootContainer().countyName,

             taxPayer <- thisModule.rootContainer().home ->

      collect(theHome | theHome.member ->

             select(theMember | theMember.isInstanceOf(County_MM!Adult))

      )

          )

}

theCounty = rootContainer()

theCountyTaxCensus = outTaxCensusModel.createElement('txc.CountyTaxCensus', rootContainer().xmi_id.replace('cnty', 'etxc'))

theCountyTaxCensus.countyName = rootContainer().countyName

theCountyTaxCensus.taxPayer = [theMember.xmi_id.replace('cnty', 'etxc') + '.theTaxPayer' for theHome in rootContainer().home for theMember intheHome.member if theMember.isInstanceOf('cnty.Adult')]

 

 

rule Adult_To_TaxPayer {

      from theAdult : county_MM!Adult

      to theTaxPayer : taxCensus_MM!TaxPayer (

             payerName <- theAdult.name,

             address <- theAdult.home.address+ ’ ‘ + thisModule.rootContainer().countyAddress,

             currentTax <- thisModule.createTax(theAdult),

             holder <- theAdult

}

for theAdult in (theAdult for theAdult in inCountyModel.getElements() if theAdult.isInstanceOf('cnty.Adult')):

    theTaxPayer = outTaxCensusModel.createElement('txc.TaxPayer', theAdult.xmi_id.replace('cnty', 'etxc') + '.theTaxPayer')

    theTaxPayer.payerName = theAdult.name

    theTaxPayer.address = theAdult.home.address + ' ' + inCountyModel.rootPackage.countyAddress

    theTaxPayer.holder = theAdult

    theTaxPayer.currentTax = createTax(theAdult)

 

 

#Updating of references between objects created in the output model

outTaxCensusModel.rootPackage = theCountyTaxCensus

theCountyTaxCensus.taxPayer =

       [outTaxCensusModel.getElement(xmiid) for xmiid in theCountyTaxCensus.taxPayer]

 


The output model generated by the County_To_TaxCensus transformation can be found in the EagleTaxCensus_Declarative.xmi file.



Transformation formulation (imperative style): The table below shows the County_To_TaxCensus M2M transformation in an imperative style.

The imperative algorithm is based on an iteration over the input model elements, using conditional sentences to evaluate the guard conditions.

For the appropriate elements, their attributes are processed and the corresponding output model elements are created.

1.      Creates an application repository to store and process the models.

2.      Opens the input file and loads the in the repository the model data.

3.      Load the metamodel of the output model

4.      Creates an empty output model

5.      Creates the root container element of the output model.

5.1.       Assigns the attributes of the output model head.

5.2.       FOR each element Adult in the input model, creates a TaxPayer element in the output model.

5.2.1.  Assigns value to the attributes of the created TaxPayer element.

5.2.2.  IF  the Adult element has referenced jobs:

5.2.2.1.  Creates a Tax element and assigns the values to the attributes.

5.2.2.2.    Aggregates the created element (or None if is not created) to the TaxPayer element.



The County_To_TaxCensus_Imperative.py file contains the Python code implementing the M2M transformation in an imperative style.

Python code

Pseudocode

 

1.     Creates an application repository to store and process the models.

import emofUC.resource as resource

import emofUC.xmlcore as xmlcore

import emofUC.GLOBAL as GLOBAL

 

aRep = resource.AppRepository()

xmlcore.PLATFORM = "C:\Users\. . .\PyEMOF_UC\Workspace"

 

 

2.    Opens the input file and loads the in the repository the model data.

inCountyModelURL = "platform:CountyCity/EagleCounty.xmi"

inCountyModel = aRep.loadModel(inCountyModelURL)

 

 

3.    Load the metamodel of the output model.

taxCensus_MM_URL = 'platform:CountyCity/TaxCensus.xmi'

 

 

4.    Creates an empty output model

aRep.loadModel(taxCensus_MM_URL)

outTaxCensusModelURI = "http://unican.es/istr/PyEmofUC/CountyCity/EagleTaxCensus"

outTaxCensusModelNsPrefix = 'etxc'

outTaxCensusModel = aRep.createMdlRepository(

                uri=outTaxCensusModelURI,

                mmUrl=taxCensus_MM_URL,

                nsPrefix=outTaxCensusModelNsPrefix)

 

 

5.   Creates the root container element of the output model:

rootContainer_xmiid = 'etxc.Id_' + str(outTaxCensusModel.nextXmiId())

theCountyTaxCensus = outTaxCensusModel.createElement(

                metaclass='txc.CountyTaxCensus',

                xmi_id=rootContainer_xmiid)

outTaxCensusModel.rootPackage = theCountyTaxCensus

 

 

5.1 Assigns the attributes of the output model head.

theCountyTaxCensus.countyName = inCountyModel.rootPackage.countyName

 

 

5.2  FOR each element Adult in the input model, creates a TaxPayer element in the output model.

for theAdult in (theAdult for theAdult in inCountyModel.getElements() if theAdult.isInstanceOf('cnty.Adult')):

 

 

5.2.1. Assigns value to the attributes of the created TaxPayer element.

    theTaxPayer = outTaxCensusModel.createElement('txc.TaxPayer', 'etxc.Id_' +

                                                                                                            str(outTaxCensusModel.nextXmiId()))

    theTaxPayer.payerName = theAdult.name

    theTaxPayer.address = theAdult.home.address + ' ' + inCountyModel.rootPackage.countyAddress

    theTaxPayer.holder = theAdult

 

 

5.2.2.  IF  the Adult element has referenced jobs:

    theTax = None

    if len(theAdult.job) > 0:

 

 

 

5.2.2.1.  Creates a Tax element and assigns the values to the attributes.

        theTax = outTaxCensusModel.createElement('txc.Tax', 'etxc.Id_' +

                                                                                                         str(outTaxCensusModel.nextXmiId()))

        theTax.amount = -100.0 * len(theAdult.offspring)

        for theJob in theAdult.job:

            theTax.amount += 0.2 * theJob.salary

            theTax.date = GLOBAL.currentDate()

 

 

  5.2.2.2.      Aggregates the created element (or None if is not created) to the TaxPayer element.

    theTaxPayer.currentTax = theTax

    theCountyTaxCensus.taxPayer.append(theTaxPayer)

 


The output model generated by the transformation is EagleTaxCensus_Imperative.rtf.

Documents


Links of interest