slight improvements to mocha ui
This commit is contained in:
parent
ca1269f84d
commit
2df465286a
@ -9,16 +9,38 @@ from getopt import getopt
|
||||
if __name__ == "__main__":
|
||||
|
||||
port = 8081
|
||||
libraries = [ ]
|
||||
|
||||
(opts, remaining) = getopt(sys.argv[1:], "p", [ "port=" ])
|
||||
(opts, remaining) = getopt(sys.argv[1:], "p", [ "port=", "library=" ])
|
||||
for opt in opts:
|
||||
if opt[0] == "--port":
|
||||
port = int(opt[1])
|
||||
|
||||
print("Mocha User Interface Service - running on port", port)
|
||||
print("Mocha User Interface Service v2.1")
|
||||
|
||||
oms = MemoryOms()
|
||||
from mocha.web.manager import ServerManager
|
||||
svrmgr = ServerManager()
|
||||
|
||||
server = WebServer(("127.0.0.1", port), oms)
|
||||
server.serve_forever()
|
||||
for library in remaining:
|
||||
svrmgr.add_server_config(library)
|
||||
|
||||
svrmgr.start()
|
||||
|
||||
# from mocha.lib.LibraryManager import MochaLibraryManager
|
||||
# manager = MochaLibraryManager()
|
||||
|
||||
# from mocha.lib.parser.YAMLLibraryParser import YAMLLibraryParser
|
||||
# yamlmgr = YAMLLibraryParser(manager)
|
||||
|
||||
# print("loading entity definitions...")
|
||||
# for library in remaining:
|
||||
# print("\t" + library)
|
||||
# yamlmgr.load_entity_definitions_from_file(library)
|
||||
|
||||
# print("loading instances...")
|
||||
# for library in remaining:
|
||||
# print("\t" + library)
|
||||
# yamlmgr.load_instances_from_file(library)
|
||||
|
||||
# oms = MemoryOms()
|
||||
|
||||
|
||||
57
python/mocha/lib/InstanceCache.py
Normal file
57
python/mocha/lib/InstanceCache.py
Normal file
@ -0,0 +1,57 @@
|
||||
from .Normalization import Normalization
|
||||
from framework import Guid
|
||||
|
||||
class InstanceCache:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.next_inst_id = dict()
|
||||
self.next_inst_id[1] = 1
|
||||
|
||||
self.inst_indices = dict()
|
||||
self.inst_guids = dict()
|
||||
|
||||
self.dbids_gid = dict()
|
||||
|
||||
def add_instance(self, inst_key, inst_gid):
|
||||
self.inst_guids[inst_key] = inst_gid
|
||||
self.inst_indices[inst_gid] = inst_key
|
||||
|
||||
if not inst_key[0] in self.next_inst_id:
|
||||
self.next_inst_id[inst_key[0]] = 1
|
||||
|
||||
if inst_key[1] >= self.next_inst_id[inst_key[0]]:
|
||||
self.next_inst_id[inst_key[0]] = inst_key[1] + 1
|
||||
|
||||
def get_global_identifier(self, inst_key):
|
||||
return self.inst_guids[inst_key]
|
||||
|
||||
def get_instance_key(self, inst_gid):
|
||||
return self.inst_indices[inst_gid]
|
||||
|
||||
def get_next_instance_id(self, class_index):
|
||||
if not class_index in self.next_inst_id:
|
||||
# this is the first instance of this class
|
||||
self.next_inst_id[class_index] = 1
|
||||
|
||||
val = self.next_inst_id[class_index]
|
||||
self.next_inst_id[class_index] += 1
|
||||
return val
|
||||
|
||||
def has_instance_by_global_identifier(self, gid):
|
||||
return gid in self.inst_indices
|
||||
|
||||
def count(self) -> int:
|
||||
"""
|
||||
Returns the number of instances stored in this OMS.
|
||||
"""
|
||||
return len(self.inst_indices)
|
||||
|
||||
def get_instance_keys(self):
|
||||
return self.inst_indices
|
||||
|
||||
def set_database_id_by_global_identifier(self, gid : Guid, dbid):
|
||||
self.dbids_gid[Normalization.normalize_uuid(gid.get_value())] = dbid
|
||||
# self.dbids_idx[self.inst_indices[gid]] = dbid
|
||||
def get_database_id_by_global_identifier(self, gid : Guid):
|
||||
return self.dbids_gid[Normalization.normalize_uuid(gid.get_value())]
|
||||
442
python/mocha/lib/LibraryManager.py
Normal file
442
python/mocha/lib/LibraryManager.py
Normal file
@ -0,0 +1,442 @@
|
||||
import MySQLdb
|
||||
|
||||
from .parser import XMLLibraryParser, JSONLibraryParser, YAMLLibraryParser
|
||||
|
||||
from .LibraryOperation import LibraryOperation
|
||||
from .operations.AssignAttributeOperation import AssignAttributeOperation
|
||||
from .operations.AssignRelationshipOperation import AssignRelationshipOperation
|
||||
from .operations.PrepareInstanceOperation import PrepareInstanceOperation
|
||||
from .operations.StoredProcedureOperation import StoredProcedureOperation
|
||||
|
||||
from .Normalization import Normalization
|
||||
|
||||
from framework import Guid
|
||||
from mocha.definitions import KnownClassGuids, KnownAttributeGuids, KnownInstanceGuids, KnownRelationshipGuids
|
||||
|
||||
from .InstanceCache import InstanceCache
|
||||
from .SQLExpression import SQLExpression
|
||||
|
||||
# from redis import Redis
|
||||
|
||||
class MochaLibraryManager:
|
||||
|
||||
def __init__(self):
|
||||
self.entityReferences = dict()
|
||||
self.entityReferenceFileNames = dict()
|
||||
self.db = None
|
||||
self._instances = []
|
||||
self._attOps = []
|
||||
self._relOps = []
|
||||
self.redis = None
|
||||
|
||||
def connect(self, hostname, database, username, password):
|
||||
self.db = MySQLdb.connect(host=hostname, user=username, passwd=password, db=database)
|
||||
# self.redis = Redis(host=hostname, port=6379, decode_responses=True)
|
||||
|
||||
def process_instance_ops(self):
|
||||
# this version is much faster as it prepares everything in advanced on the CPU before
|
||||
# pushing it out to the database
|
||||
|
||||
instances = InstanceCache()
|
||||
|
||||
for key in self._instances:
|
||||
(class_gid, inst_gid, class_index, inst_index) = key
|
||||
# first import all the known classes
|
||||
if class_gid is not None and class_index is not None:
|
||||
# print ("known class import")
|
||||
# print ("class_gid = " + class_gid)
|
||||
# print ("inst_gid = " + inst_gid + " (" + str(class_index) + "$" + str(inst_index) + ")")
|
||||
if not instances.has_instance_by_global_identifier(class_gid):
|
||||
instances.add_instance((1, class_index), class_gid)
|
||||
|
||||
for key in self._instances:
|
||||
(class_gid, inst_gid, class_index, inst_index) = key
|
||||
# now get all the classes without specified IIDs
|
||||
if class_gid is not None and class_index is None:
|
||||
# print ("unknown class import")
|
||||
if not instances.has_instance_by_global_identifier(class_gid):
|
||||
class_index = instances.get_next_instance_id(class_index)
|
||||
else:
|
||||
class_index = instances.get_instance_key(class_gid)[1]
|
||||
|
||||
if inst_index is None:
|
||||
inst_index = instances.get_next_instance_id(class_index)
|
||||
|
||||
instances.add_instance((1, inst_index), inst_gid)
|
||||
|
||||
# print ("class_gid = " + class_gid)
|
||||
# print ("inst_gid = " + inst_gid + " (" + str(1) + "$" + str(inst_index) + ")")
|
||||
|
||||
for key in self._instances:
|
||||
(class_gid, inst_gid, class_index, inst_index) = key
|
||||
if class_gid is not None and class_index is not None and inst_gid is not None and inst_index is not None:
|
||||
# we know all there is to know about this one
|
||||
pass
|
||||
elif class_gid is not None and inst_gid is not None:
|
||||
if class_index is None:
|
||||
# we need to know the class index
|
||||
if instances.has_instance_by_global_identifier(class_gid):
|
||||
class_index = instances.get_instance_key(class_gid)[1]
|
||||
else:
|
||||
class_index = instances.get_next_instance_id(1)
|
||||
|
||||
if inst_index is None:
|
||||
# we need to know the instance index as well
|
||||
if instances.has_instance_by_global_identifier(inst_gid):
|
||||
inst_index = instances.get_instance_key(inst_gid)[1]
|
||||
else:
|
||||
inst_index = instances.get_next_instance_id(class_index)
|
||||
else:
|
||||
print("error: not implemened class_gid is None or inst_gid is None")
|
||||
|
||||
instances.add_instance((class_index, inst_index), inst_gid)
|
||||
|
||||
print (str(instances.count()) + " instances preloaded")
|
||||
|
||||
c = 0
|
||||
query = "INSERT INTO mocha_instances (tenant_id, class_id, inst_id, global_identifier) VALUES "
|
||||
for inst_gid in instances.get_instance_keys():
|
||||
(class_index, inst_index) = instances.get_instance_key(inst_gid)
|
||||
print("preparing inst " + str(class_index) + "$" + str(inst_index) + " with guid " + inst_gid)
|
||||
|
||||
inst_key_str = str(class_index) + "$" + str(inst_index)
|
||||
if self.redis is not None:
|
||||
self.redis.set(inst_key_str + ".gid", Normalization.normalize_uuid(inst_gid))
|
||||
self.redis.set(inst_key_str + ".attributes.count", "0")
|
||||
self.redis.set(inst_key_str + ".relationships.count", "0")
|
||||
self.redis.set(Normalization.normalize_uuid(inst_gid) + ".iid", inst_key_str)
|
||||
self.redis.set(Normalization.normalize_uuid(inst_gid) + ".attributes.count", "0")
|
||||
self.redis.set(Normalization.normalize_uuid(inst_gid) + ".relationships.count", "0")
|
||||
|
||||
query += "(1, " + str(class_index) + ", " + str(inst_index) + ", '" + Normalization.normalize_uuid(inst_gid) + "')"
|
||||
|
||||
if c < instances.count() - 1:
|
||||
query += ", "
|
||||
|
||||
c += 1
|
||||
|
||||
print ("process_instance_ops: query was: ")
|
||||
print (query)
|
||||
self.db.query(query)
|
||||
|
||||
self.instances = instances
|
||||
return True
|
||||
|
||||
def process_attribute_ops(self, ops):
|
||||
query = "INSERT INTO mocha_attributes (tenant_id, src_inst_id, att_inst_id, usr_inst_id, att_effective_date, att_value) VALUES "
|
||||
for op in ops:
|
||||
instanceId = op.instanceId
|
||||
attributeInstanceId = op.attributeInstanceId
|
||||
value = op.value
|
||||
|
||||
if attributeInstanceId.get_value() == "":
|
||||
print("skipping invalid blank attribute")
|
||||
continue
|
||||
|
||||
print (instanceId.get_value() + " : " + attributeInstanceId.get_value() + " = '" + str(value) + "'")
|
||||
|
||||
# query += "(1, " \
|
||||
# + "mocha_get_instance_by_global_identifier('" + Normalization.normalize_uuid(instanceId.get_value()) + "'), " \
|
||||
# + "mocha_get_instance_by_global_identifier('" + Normalization.normalize_uuid(attributeInstanceId.get_value()) + "'), " \
|
||||
# + "NULL, NOW(), " + SQLExpression.normalize_parm(value) + ")"
|
||||
|
||||
query += "(1, " \
|
||||
+ str(self.instances.get_database_id_by_global_identifier(instanceId)) + ", " \
|
||||
+ str(self.instances.get_database_id_by_global_identifier(attributeInstanceId)) + ", " \
|
||||
+ "NULL, NOW(), " + SQLExpression.normalize_parm(value) + ")"
|
||||
|
||||
if ops.index(op) < len(ops) - 1:
|
||||
query += ", "
|
||||
|
||||
# op.execute(self.db)
|
||||
|
||||
print ("process_attribute_ops: query was: ")
|
||||
print (query)
|
||||
self.db.query(query)
|
||||
|
||||
def process_relationship_ops(self, ops):
|
||||
query = "INSERT INTO mocha_relationships (tenant_id, source_inst_id, relationship_inst_id, destination_inst_id, user_inst_id, effective_date) VALUES "
|
||||
|
||||
for op in ops:
|
||||
instanceId = op.instanceId
|
||||
relationshipInstanceId = op.relationshipInstanceId
|
||||
targetInstanceId = op.targetInstanceId
|
||||
|
||||
print (instanceId.get_value() + " : " + relationshipInstanceId.get_value() + " = " + targetInstanceId.get_value())
|
||||
|
||||
query += "(1, " \
|
||||
+ str(self.instances.get_database_id_by_global_identifier(instanceId)) + ", " \
|
||||
+ str(self.instances.get_database_id_by_global_identifier(relationshipInstanceId)) + ", " \
|
||||
+ str(self.instances.get_database_id_by_global_identifier(targetInstanceId)) + ", " \
|
||||
+ "NULL, NOW() )"
|
||||
|
||||
if ops.index(op) < len(ops) - 1:
|
||||
query += ", "
|
||||
|
||||
# op.execute(self.db)
|
||||
|
||||
print ("process_relationship_ops: query was: ")
|
||||
print (query)
|
||||
self.db.query(query)
|
||||
|
||||
def before_commit(self):
|
||||
"""
|
||||
Called before the unstructured information is sorted and written to the database.
|
||||
---
|
||||
This is your last chance to add instances, set attributes, and assign
|
||||
relationships before the compilation is finalized. After this stage, calls made
|
||||
to modify the compilation are undefined and will almost certainly not do what you
|
||||
want.
|
||||
"""
|
||||
|
||||
self.write_debug_information()
|
||||
|
||||
def write_debug_information(self):
|
||||
"""
|
||||
Writes debugging information, such as entity definitions ('&IDC_...;') and source
|
||||
code definitions (filename, line number, column number, etc.)
|
||||
"""
|
||||
|
||||
print ("preparing debug information (use --no-debug or undefine DEBUG or etc. [NOT IMPLEMENTED] to turn off)")
|
||||
for key in self.entityReferences:
|
||||
val = self.entityReferences[key]
|
||||
|
||||
gidEntityDefinition = Guid.create()
|
||||
self.add_instance(IDC_EntityDefinition, gidEntityDefinition, None, None)
|
||||
self.set_attribute_value(gidEntityDefinition, IDA_Name, key)
|
||||
self.set_attribute_value(gidEntityDefinition, IDA_Value, val)
|
||||
|
||||
gidSourceDefinition = Guid.create()
|
||||
self.add_instance(IDC_SourceDefinition, gidSourceDefinition, None, None)
|
||||
self.set_attribute_value(gidSourceDefinition, IDA_DebugDefinitionFileName, self.entityReferenceFileNames[key])
|
||||
|
||||
self.assign_relationship(gidEntityDefinition, IDR_Instance__has__Source_Definition, gidSourceDefinition)
|
||||
|
||||
# Source Definition.has X
|
||||
# self.add_instance(KnownClassGuids.CLASS, Guid.parse("{dc0a5dd2-22e0-471f-87cf-a5ef1b764efa}"), 1, index)
|
||||
# self.assign_relationship(instGid, Guid.parse("{dc0a5dd2-22e0-471f-87cf-a5ef1b764efa}"), targetInstanceId)
|
||||
|
||||
def commit(self):
|
||||
|
||||
self.before_commit()
|
||||
|
||||
# first record all the instances
|
||||
if not self.process_instance_ops():
|
||||
return False
|
||||
|
||||
print("reading database ids...")
|
||||
query = "SELECT id, global_identifier FROM mocha_instances"
|
||||
|
||||
cur = self.db.cursor()
|
||||
cur.execute(query)
|
||||
rows = cur.fetchall()
|
||||
|
||||
for (dbid, gid) in rows:
|
||||
self.instances.set_database_id_by_global_identifier(Guid(gid), dbid)
|
||||
|
||||
print("assigning attributes...")
|
||||
self.process_attribute_ops(self._attOps)
|
||||
|
||||
print("assigning relationships...")
|
||||
self.process_relationship_ops(self._relOps)
|
||||
|
||||
hasSiblingRelationships = dict()
|
||||
for op in self._relOps:
|
||||
relationshipKey = self.instances
|
||||
|
||||
if op.relationshipInstanceId.get_value() == "{656110FF-4502-48B8-A7F3-D07F017AEA3F}":
|
||||
# Relationship.has sibling Relationship
|
||||
hasSiblingRelationships[op.instanceId.get_value()] = op.targetInstanceId
|
||||
|
||||
siblingOps = []
|
||||
for op in self._relOps:
|
||||
if op.relationshipInstanceId.get_value() in hasSiblingRelationships:
|
||||
siblingOp = AssignRelationshipOperation(op.targetInstanceId, hasSiblingRelationships[op.relationshipInstanceId.get_value()], op.instanceId)
|
||||
siblingOps.append(siblingOp)
|
||||
# print ("assigning sibling relationship " + siblingOp.instanceId.get_value() + " . " + siblingOp.relationshipInstanceId.get_value() + " = " + siblingOp.instanceId.get_value())
|
||||
# siblingOp.execute(self.db)
|
||||
|
||||
print("assigning sibling relationships...")
|
||||
self.process_relationship_ops(siblingOps)
|
||||
|
||||
print("setting creation user...")
|
||||
|
||||
cur = self.db.cursor()
|
||||
# cur.execute("UPDATE mocha_instances SET user_inst_id = mocha_get_instance_by_global_identifier(mocha_normalize_uuid('{B066A54B-B160-4510-A805-436D3F90C2E6}'))")
|
||||
cur.execute("UPDATE mocha_attributes SET usr_inst_id = " + str(self.instances.get_database_id_by_global_identifier(Guid.parse("{B066A54B-B160-4510-A805-436D3F90C2E6}"))))
|
||||
cur.execute("UPDATE mocha_relationships SET user_inst_id = " + str(self.instances.get_database_id_by_global_identifier(Guid.parse("{B066A54B-B160-4510-A805-436D3F90C2E6}"))))
|
||||
# rows = cur.fetchall()
|
||||
# print(rows)
|
||||
|
||||
# print ("ok , applying `Class.has Instance`...")
|
||||
|
||||
# for row in rows:
|
||||
# print(row)
|
||||
# id = row[0]
|
||||
# tenant_id = row[1]
|
||||
# class_id = row[2]
|
||||
# inst_id = row[3]
|
||||
# global_identifier = row[4]
|
||||
|
||||
# if class_id is not None and id is not None:
|
||||
|
||||
# # Class.has Instance
|
||||
# query2 = ("CALL mocha_assign_relationship (" +
|
||||
# "mocha_get_instance_by_key(1, " + str(class_id) + "), " +
|
||||
# "mocha_get_instance_by_global_identifier(mocha_normalize_uuid('7EB41D3C-2AE9-4884-83A4-E59441BCAEFB'))" + ", " +
|
||||
# str(id) + ", NULL, NULL)")
|
||||
# cur.execute(query2)
|
||||
|
||||
# # Instance.for Class
|
||||
# query3 = ("CALL mocha_assign_relationship (" +
|
||||
# str(id) + ", " +
|
||||
# "mocha_get_instance_by_global_identifier(mocha_normalize_uuid('494D5A6D-04BE-477B-8763-E3F57D0DD8C8'))" + ", " +
|
||||
# "mocha_get_instance_by_key(1, " + str(class_id) + "), NULL, NULL)")
|
||||
# cur.execute(query3)
|
||||
|
||||
self.db.commit()
|
||||
|
||||
def close(self):
|
||||
self.db.close()
|
||||
|
||||
def print_error(self, cur):
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
if 'error_description' in row:
|
||||
print (row['error_description'])
|
||||
|
||||
def select_tenant(self, tenantName):
|
||||
cur = self.db.cursor()
|
||||
cur.execute("CALL mocha_select_tenant(mocha_get_tenant_by_name('" + tenantName + "'))")
|
||||
self.print_error(cur)
|
||||
|
||||
def release_tenant(self):
|
||||
cur = self.db.cursor()
|
||||
cur.execute("CALL mocha_release_tenant()")
|
||||
self.print_error(cur)
|
||||
|
||||
def assign_relationship(self, instanceId : Guid, relationshipInstanceId : Guid, targetInstanceId : Guid):
|
||||
print("-- assigning relationship " + relationshipInstanceId.get_value() + " = '" + targetInstanceId.get_value() + "'")
|
||||
self._relOps.append(AssignRelationshipOperation(instanceId, relationshipInstanceId, targetInstanceId))
|
||||
# self._relOps.append(AssignRelationshipOperation(instanceId, relationshipInstanceId, targetInstanceId))
|
||||
# query = "CALL mocha_assign_relationship(mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + instanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + relationshipInstanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + targetInstanceId.get_value() + "')), NULL, NULL);"
|
||||
# print(query)
|
||||
# cur.execute(query)
|
||||
# self.print_error(cur)
|
||||
|
||||
def set_attribute_value(self, instanceId : Guid, attributeInstanceId : Guid, value):
|
||||
print("-- assigning attribute " + attributeInstanceId.get_value() + " = '" + str(value) + "'")
|
||||
self._attOps.append(AssignAttributeOperation(instanceId, attributeInstanceId, value))
|
||||
|
||||
def install_from_path(self, path):
|
||||
|
||||
from glob import glob
|
||||
|
||||
xl = XMLLibraryParser(self)
|
||||
jl = JSONLibraryParser(self)
|
||||
yl = YAMLLibraryParser(self)
|
||||
|
||||
#xml_files = glob(path + "/**/*.xml", recursive=True)
|
||||
#for xml_file in xml_files:
|
||||
#s xl.load_file(xml_file)
|
||||
|
||||
|
||||
#json_files = glob(path + "/**/*.json", recursive=True)
|
||||
#for json_file in json_files:
|
||||
# jl.load_file(json_file)
|
||||
|
||||
yaml_files = sorted(glob(path + "/**/*.yaml", recursive=True))
|
||||
if len(yaml_files) == 0:
|
||||
print ("no files found ; does the path exist?")
|
||||
return 3
|
||||
|
||||
# first, load the entity defs
|
||||
for yaml_file in yaml_files:
|
||||
yl.load_entity_definitions_from_file(yaml_file)
|
||||
|
||||
try:
|
||||
# then, load instance definitions (also loads sugar elements into memory for later use)
|
||||
for yaml_file in yaml_files:
|
||||
yl.load_instances_from_file(yaml_file)
|
||||
|
||||
# finally, apply syntactic sugar
|
||||
yl.apply_sugar()
|
||||
|
||||
return True
|
||||
|
||||
except NameError as ex:
|
||||
|
||||
print (ex)
|
||||
|
||||
rgx = "&(.*);"
|
||||
|
||||
# go through and get all entity references across all files
|
||||
import re
|
||||
import fileinput
|
||||
|
||||
stuff = []
|
||||
|
||||
for yaml_file in yaml_files:
|
||||
f = open(yaml_file, "r")
|
||||
text = f.read()
|
||||
matches = re.findall(rgx, text)
|
||||
for match in matches:
|
||||
stuff.append(match)
|
||||
f.close()
|
||||
|
||||
missingEntities = []
|
||||
for stuf in stuff:
|
||||
if not stuf in self.entityReferences:
|
||||
if not stuf in missingEntities:
|
||||
missingEntities.append(stuf)
|
||||
|
||||
if len(missingEntities) > 0:
|
||||
print("\nNOTE: there were undefined referenced entities:\n")
|
||||
for missingEntity in missingEntities:
|
||||
print("\t" + missingEntity)
|
||||
print("\n")
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def exists_entity_reference(self, name):
|
||||
return name in self.entityReferences
|
||||
|
||||
def register_entity_reference(self, name, value, filename = None):
|
||||
self.entityReferences[name] = value
|
||||
self.entityReferenceFileNames[name] = filename
|
||||
|
||||
def define_entity_reference(self, name):
|
||||
return self.entityReferences[name]
|
||||
def expand_entity_references(self, value):
|
||||
insideName = False
|
||||
name = ""
|
||||
retval = ""
|
||||
for i in range(0, len(value)):
|
||||
if value[i] == "&":
|
||||
insideName = True
|
||||
elif value[i] == ';':
|
||||
insideName = False
|
||||
|
||||
if name in self.entityReferences:
|
||||
retval += self.define_entity_reference(name)
|
||||
else:
|
||||
raise NameError("unknown entity ref '" + name + "'")
|
||||
|
||||
name = ""
|
||||
elif insideName:
|
||||
name += value[i]
|
||||
else:
|
||||
retval += value[i]
|
||||
return retval
|
||||
|
||||
def add_instance(self, classGid : Guid, instGid : Guid, classIndex : int, index : int):
|
||||
print("adding instance for class '" + classGid.get_value() + "' with globalid '" + instGid.get_value() + "' [" + str(index) + "]")
|
||||
self._instances.append((classGid.get_value(), instGid.get_value(), classIndex, index))
|
||||
|
||||
# assign relationship `Instance.for Class`
|
||||
self.assign_relationship(instGid, Guid.parse("{494D5A6D-04BE-477B-8763-E3F57D0DD8C8}"), classGid)
|
||||
|
||||
# assign relationship `Class.has Instance`
|
||||
self.assign_relationship(classGid, Guid.parse("{7EB41D3C-2AE9-4884-83A4-E59441BCAEFB}"), instGid)
|
||||
|
||||
21
python/mocha/lib/LibraryOperation.py
Normal file
21
python/mocha/lib/LibraryOperation.py
Normal file
@ -0,0 +1,21 @@
|
||||
import MySQLdb
|
||||
|
||||
from MySQLdb.connections import Connection
|
||||
|
||||
class LibraryOperation:
|
||||
def build_query(self):
|
||||
return ''
|
||||
|
||||
def print_error(self, cur):
|
||||
rows = cur.fetchall()
|
||||
for row in rows:
|
||||
if 'error_description' in row:
|
||||
print (row['error_description'])
|
||||
|
||||
def execute(self, db : Connection):
|
||||
cur = db.cursor()
|
||||
|
||||
query = self.build_query()
|
||||
print(query)
|
||||
cur.execute(query)
|
||||
self.print_error(cur)
|
||||
5
python/mocha/lib/Normalization.py
Normal file
5
python/mocha/lib/Normalization.py
Normal file
@ -0,0 +1,5 @@
|
||||
class Normalization:
|
||||
@staticmethod
|
||||
def normalize_uuid(val : str):
|
||||
val = val.replace("{", "").replace("}", "").replace("-", "").strip().lower()
|
||||
return val
|
||||
47
python/mocha/lib/SQLExpression.py
Normal file
47
python/mocha/lib/SQLExpression.py
Normal file
@ -0,0 +1,47 @@
|
||||
from framework import Guid
|
||||
from .Normalization import Normalization
|
||||
|
||||
class SQLExpression:
|
||||
|
||||
@staticmethod
|
||||
def sqlescape(parm : str):
|
||||
return parm.replace("'", "\\'")
|
||||
|
||||
@staticmethod
|
||||
def normalize_parm(parm : str):
|
||||
query = ""
|
||||
if parm == None:
|
||||
query += "NULL"
|
||||
elif isinstance(parm, Guid):
|
||||
gid = None
|
||||
if parm is not None:
|
||||
gid = "'" + Normalization.normalize_uuid(parm.get_value()) + "'"
|
||||
else:
|
||||
gid = "NULL"
|
||||
|
||||
query += gid
|
||||
elif isinstance(parm, str):
|
||||
query += "'" + SQLExpression.sqlescape(str(parm)) + "'"
|
||||
else:
|
||||
query += str(parm)
|
||||
|
||||
return query
|
||||
|
||||
@staticmethod
|
||||
def to_string(parm):
|
||||
return SQLExpression.normalize_parm(parm)
|
||||
|
||||
@staticmethod
|
||||
def array_to_string(parms):
|
||||
i = 0
|
||||
query = ''
|
||||
for parm in parms:
|
||||
|
||||
query += SQLExpression.to_string(parm)
|
||||
|
||||
if i < len(parms) - 1:
|
||||
query += ", "
|
||||
|
||||
i += 1
|
||||
|
||||
return query
|
||||
20
python/mocha/lib/SQLFunctionCall.py
Normal file
20
python/mocha/lib/SQLFunctionCall.py
Normal file
@ -0,0 +1,20 @@
|
||||
from .SQLExpression import SQLExpression
|
||||
|
||||
class SQLFunctionCall (SQLExpression):
|
||||
|
||||
def __init__(self, funcname, parms):
|
||||
self.name = funcname
|
||||
self.parms = parms
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def get_parameters(self):
|
||||
return self.parms
|
||||
|
||||
def get_parameter_list(self):
|
||||
return SQLExpression.array_to_string(self.parms)
|
||||
|
||||
def __str__(self):
|
||||
return self.get_name() + "(" + self.get_parameter_list() + ")"
|
||||
|
||||
12
python/mocha/lib/SQLParameter.py
Normal file
12
python/mocha/lib/SQLParameter.py
Normal file
@ -0,0 +1,12 @@
|
||||
from .SQLExpression import SQLExpression
|
||||
|
||||
class SQLParameter (SQLExpression):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def get_name(self):
|
||||
return self.name
|
||||
|
||||
def __str__(self):
|
||||
return "@" + self.get_name()
|
||||
21
python/mocha/lib/operations/AssignAttributeOperation.py
Normal file
21
python/mocha/lib/operations/AssignAttributeOperation.py
Normal file
@ -0,0 +1,21 @@
|
||||
from .StoredProcedureOperation import StoredProcedureOperation
|
||||
from framework import Guid
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
|
||||
class AssignAttributeOperation(StoredProcedureOperation):
|
||||
|
||||
def __init__(self, instanceId : Guid, attributeInstanceId : Guid, value):
|
||||
self.instanceId = instanceId
|
||||
self.attributeInstanceId = attributeInstanceId
|
||||
self.value = value
|
||||
|
||||
def get_sp_name(self):
|
||||
return "mocha_set_attribute_value"
|
||||
|
||||
def get_sp_parameters(self):
|
||||
return [ SQLFunctionCall('mocha_get_instance_by_global_identifier', [ self.instanceId ]), SQLFunctionCall('mocha_get_instance_by_global_identifier', [ self.attributeInstanceId ]), str(self.value), None, None ]
|
||||
#query = "CALL mocha_set_attribute_value(mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + instanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + attributeInstanceId.get_value() + "')), '" + str(value) + "', NULL, NULL);"
|
||||
#print(query)
|
||||
#cur.execute(query)
|
||||
#self.print_error(cur)
|
||||
21
python/mocha/lib/operations/AssignRelationshipOperation.py
Normal file
21
python/mocha/lib/operations/AssignRelationshipOperation.py
Normal file
@ -0,0 +1,21 @@
|
||||
from .StoredProcedureOperation import StoredProcedureOperation
|
||||
from framework import Guid
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
|
||||
class AssignRelationshipOperation(StoredProcedureOperation):
|
||||
|
||||
def __init__(self, instanceId : Guid, relationshipInstanceId : Guid, targetInstanceId : Guid):
|
||||
self.instanceId = instanceId
|
||||
self.relationshipInstanceId = relationshipInstanceId
|
||||
self.targetInstanceId = targetInstanceId
|
||||
|
||||
def get_sp_name(self):
|
||||
return "mocha_assign_relationship"
|
||||
|
||||
def get_sp_parameters(self):
|
||||
return [ SQLFunctionCall('mocha_get_instance_by_global_identifier', [ self.instanceId ]), SQLFunctionCall('mocha_get_instance_by_global_identifier', [ self.relationshipInstanceId ]), SQLFunctionCall('mocha_get_instance_by_global_identifier', [ self.targetInstanceId ]), None, None ]
|
||||
#query = "CALL mocha_set_attribute_value(mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + instanceId.get_value() + "')), mocha_get_instance_by_global_identifier(mocha_normalize_uuid('" + attributeInstanceId.get_value() + "')), '" + str(value) + "', NULL, NULL);"
|
||||
#print(query)
|
||||
#cur.execute(query)
|
||||
#self.print_error(cur)
|
||||
17
python/mocha/lib/operations/CreateClassOperation.py
Normal file
17
python/mocha/lib/operations/CreateClassOperation.py
Normal file
@ -0,0 +1,17 @@
|
||||
from .StoredProcedureOperation import StoredProcedureOperation
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
from ..Guid import Guid
|
||||
|
||||
class CreateClassOperation (StoredProcedureOperation):
|
||||
|
||||
def __init__(self, globalIdentifier : Guid, classIndex : int):
|
||||
self.globalIdentifier = globalIdentifier
|
||||
self.classIndex = classIndex
|
||||
|
||||
def get_sp_name(self):
|
||||
return 'mocha_create_class'
|
||||
|
||||
def get_sp_parameters(self):
|
||||
return [ self.classIndex, self.globalIdentifier, None, None, SQLParameter("dummy") ]
|
||||
|
||||
17
python/mocha/lib/operations/CreateInstanceOperation.py
Normal file
17
python/mocha/lib/operations/CreateInstanceOperation.py
Normal file
@ -0,0 +1,17 @@
|
||||
from .StoredProcedureOperation import StoredProcedureOperation
|
||||
from ..Guid import Guid
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
|
||||
class CreateInstanceOperation(StoredProcedureOperation):
|
||||
|
||||
def __init__(self, globalIdentifier : Guid, classIndex : int, instanceIndex : int):
|
||||
self.globalIdentifier = globalIdentifier
|
||||
self.classIndex = classIndex
|
||||
self.instanceIndex = instanceIndex
|
||||
|
||||
def get_sp_name(self):
|
||||
return 'mocha_create_instance'
|
||||
|
||||
def get_sp_parameters(self):
|
||||
return [ self.globalIdentifier, self.classIndex, self.instanceIndex, None, None, SQLParameter('assigned_inst_id') ]
|
||||
36
python/mocha/lib/operations/PrepareInstanceOperation.py
Normal file
36
python/mocha/lib/operations/PrepareInstanceOperation.py
Normal file
@ -0,0 +1,36 @@
|
||||
from .StoredProcedureOperation import StoredProcedureOperation
|
||||
from framework import Guid
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
|
||||
class PrepareInstanceOperation(StoredProcedureOperation):
|
||||
|
||||
def __init__(self, classGlobalIdentifier : Guid, globalIdentifier : Guid, classIndex : int, instanceIndex : int):
|
||||
self.classGlobalIdentifier = classGlobalIdentifier
|
||||
self.globalIdentifier = globalIdentifier
|
||||
self.classIndex = classIndex
|
||||
self.instanceIndex = instanceIndex
|
||||
|
||||
def get_sp_name(self):
|
||||
return "mocha_prepare_instance"
|
||||
|
||||
def get_sp_parameters(self):
|
||||
parms = []
|
||||
|
||||
globalId = None
|
||||
if self.globalIdentifier is not None:
|
||||
globalId = self.globalIdentifier
|
||||
|
||||
classGlobalId = None
|
||||
if self.classGlobalIdentifier is not None:
|
||||
classGlobalId = self.classGlobalIdentifier
|
||||
|
||||
strCid = None
|
||||
if self.classIndex is not None:
|
||||
strCid = self.classIndex
|
||||
|
||||
strIid = None
|
||||
if self.instanceIndex is not None:
|
||||
strIid = self.instanceIndex
|
||||
|
||||
return [strCid, strIid, classGlobalId, globalId, None, None, SQLParameter("p_assigned_inst_id")]
|
||||
23
python/mocha/lib/operations/StoredProcedureOperation.py
Normal file
23
python/mocha/lib/operations/StoredProcedureOperation.py
Normal file
@ -0,0 +1,23 @@
|
||||
import MySQLdb
|
||||
|
||||
from ..LibraryOperation import LibraryOperation
|
||||
from MySQLdb.connections import Connection
|
||||
from ..SQLExpression import SQLExpression
|
||||
from ..SQLParameter import SQLParameter
|
||||
from ..SQLFunctionCall import SQLFunctionCall
|
||||
|
||||
class StoredProcedureOperation (LibraryOperation):
|
||||
def get_sp_name(self):
|
||||
return ''
|
||||
|
||||
def get_sp_parameters(self):
|
||||
return []
|
||||
|
||||
def build_query(self):
|
||||
query = "CALL " + self.get_sp_name() + "("
|
||||
parms = self.get_sp_parameters()
|
||||
query += SQLExpression.array_to_string(parms)
|
||||
|
||||
query += ")"
|
||||
return query
|
||||
|
||||
30
python/mocha/lib/parser/JSONLibraryParser.py
Normal file
30
python/mocha/lib/parser/JSONLibraryParser.py
Normal file
@ -0,0 +1,30 @@
|
||||
from .LibraryParser import LibraryParser
|
||||
|
||||
class JSONLibraryParser (LibraryParser):
|
||||
|
||||
def cstyle_strip_comments(self, val):
|
||||
val2 = ""
|
||||
# possible values for comment: 0 = none, 1 = multiline, 2 = singleline
|
||||
comment = 0
|
||||
|
||||
xr = iter(range(0, len(val) - 1))
|
||||
for i in xr:
|
||||
if val[i] == '\n' and comment == 2:
|
||||
comment = 0
|
||||
|
||||
check = val[i:i+2]
|
||||
if check == "/*" and comment == 0:
|
||||
comment = 1
|
||||
next(xr)
|
||||
elif check == "*/" and comment == 1:
|
||||
comment = 0
|
||||
next(xr)
|
||||
elif check == "//":
|
||||
comment = 2
|
||||
next(xr)
|
||||
elif comment == 0:
|
||||
val2 += val[i]
|
||||
|
||||
val2 += val[-1:]
|
||||
return val2
|
||||
|
||||
4
python/mocha/lib/parser/LibraryParser.py
Normal file
4
python/mocha/lib/parser/LibraryParser.py
Normal file
@ -0,0 +1,4 @@
|
||||
class LibraryParser():
|
||||
def __init__(self, manager):
|
||||
|
||||
self.__manager = manager
|
||||
5
python/mocha/lib/parser/MochaSyntaxError.py
Normal file
5
python/mocha/lib/parser/MochaSyntaxError.py
Normal file
@ -0,0 +1,5 @@
|
||||
class MochaSyntaxError(RuntimeError):
|
||||
def __init__(self, *args):
|
||||
RuntimeError.__init__(self, args)
|
||||
|
||||
pass
|
||||
46
python/mocha/lib/parser/XMLLibraryParser.py
Normal file
46
python/mocha/lib/parser/XMLLibraryParser.py
Normal file
@ -0,0 +1,46 @@
|
||||
from .LibraryParser import LibraryParser
|
||||
|
||||
from xml.dom import minidom, Node
|
||||
from xml.dom.minidom import Entity
|
||||
|
||||
class XMLLibraryParser(LibraryParser):
|
||||
|
||||
def load_instance(self, xmlnode):
|
||||
print("instance : [" + xmlnode.getAttribute("id") + "]")
|
||||
|
||||
def load_library(self, xmlnode):
|
||||
"""
|
||||
Loads a library from the specified XML node.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
if xmlnode.tagName != "library":
|
||||
return
|
||||
|
||||
id = xmlnode.getAttribute("id")
|
||||
for child in xmlnode.childNodes:
|
||||
if child.tagName == "instances":
|
||||
for child2 in child.childNodes:
|
||||
self.load_library_instance_from_xml(child)
|
||||
|
||||
|
||||
def load_file(self, filename):
|
||||
print("loading xml from " + filename + "... ")
|
||||
|
||||
dom = minidom.getDOMImplementation()
|
||||
dt = dom.createDocumentType("mocha", "-//MBS//DTD Mocha 1.0 Dev//EN", "https://doctype.schemas.alcetech.net/Mocha/1.0/mocha-1.0.dtd")
|
||||
dt.entities.setNamedItem(Entity("IDC_ReturnInstanceSetMethodBinding", "{AADC20F9-7559-429B-AEF0-97E059295C76}", None, None))
|
||||
|
||||
dom = minidom.parse(filename)
|
||||
if (dom.documentElement.tagName != "mocha"):
|
||||
print(filename + ": top-level tag is not 'mocha'")
|
||||
return
|
||||
|
||||
for child in dom.documentElement.childNodes:
|
||||
if child.nodeType == Node.ELEMENT_NODE:
|
||||
if child.tagName == "libraries":
|
||||
for child2 in child.childNodes:
|
||||
self.load_library(child)
|
||||
|
||||
print ("\n")
|
||||
345
python/mocha/lib/parser/YAMLLibraryParser.py
Normal file
345
python/mocha/lib/parser/YAMLLibraryParser.py
Normal file
@ -0,0 +1,345 @@
|
||||
from .LibraryParser import LibraryParser
|
||||
from framework import Guid
|
||||
from .MochaSyntaxError import MochaSyntaxError
|
||||
|
||||
class YAMLLibraryParser (LibraryParser):
|
||||
|
||||
def __init__(self, manager):
|
||||
LibraryParser.__init__(self, manager)
|
||||
|
||||
self.yamlIds = dict()
|
||||
self.templates = dict()
|
||||
|
||||
# these two are special reserved tagNames
|
||||
# self.yamlIds["entityDefinitions"] = None
|
||||
# self.yamlIds["instance"] = { }
|
||||
|
||||
self.manager = manager
|
||||
self._instances_awaiting_sugar = []
|
||||
|
||||
def apply_sugar(self):
|
||||
print("applying syntactic sugar to remaining instances...")
|
||||
# _instances_also_awaiting_sugar = []
|
||||
|
||||
for inst in self._instances_awaiting_sugar:
|
||||
print("applying sugar from '" + inst["from_file_name"] + "'")
|
||||
self.load_instance(inst)
|
||||
print("\n")
|
||||
|
||||
# do recursively until we're all out of instances awaiting sugar
|
||||
# if len(self._instances_awaiting_sugar) > 0:
|
||||
# self.apply_sugar()
|
||||
|
||||
def load_library(self, elem):
|
||||
print("loading library from elem")
|
||||
|
||||
def load_tenant(self, elem):
|
||||
print("loading tenant from elem")
|
||||
|
||||
def find_custom_tag_name(self, elem) -> (str, Guid):
|
||||
# return tuple (tagname, value)
|
||||
if "instance" in elem:
|
||||
return ("instance", Guid.parse(self.manager.expand_entity_references(elem["instance"])))
|
||||
|
||||
for key in self.yamlIds:
|
||||
if key in elem:
|
||||
k = str(key)
|
||||
v = Guid.parse(self.manager.expand_entity_references(elem[key]))
|
||||
return (k, v)
|
||||
|
||||
return (None, None)
|
||||
|
||||
|
||||
def load_entity_definitions_from_file(self, filename):
|
||||
print("loading entity defs from " + filename)
|
||||
|
||||
import yaml
|
||||
with open(filename, 'r') as file:
|
||||
content = yaml.safe_load_all(file)
|
||||
for doc in content:
|
||||
for elem in doc:
|
||||
if "entityDefinitions" in elem:
|
||||
entities = elem["entityDefinitions"]
|
||||
if not entities is None:
|
||||
for entity in entities:
|
||||
for key in entity:
|
||||
if self.manager.exists_entity_reference(key):
|
||||
print("duplicate entity definition: '" + key + "'")
|
||||
exit(1)
|
||||
|
||||
self.manager.register_entity_reference(key, entity[key], filename)
|
||||
|
||||
def load_instance_with_template(self, elem, template):
|
||||
(parentCreatorKey, parentInstanceId) = self.find_custom_tag_name(template)
|
||||
(creatorKey, instanceId) = self.find_custom_tag_name(elem)
|
||||
|
||||
self.apply_template(elem, template, instanceId)
|
||||
|
||||
def assign_relationship_value(self, instanceId, rel, rel_iid, relationshipValue):
|
||||
if (isinstance(relationshipValue, str)):
|
||||
# single instance, this should be a GUID or entityref
|
||||
# we should only need to refer to existing instance in this case
|
||||
relationshipValueInst = Guid.parse(self.manager.expand_entity_references(relationshipValue))
|
||||
if "instance" in rel:
|
||||
self.manager.assign_relationship(instanceId, Guid.parse(self.manager.expand_entity_references(rel["instance"])), relationshipValueInst)
|
||||
else:
|
||||
print("no relationship instance specified for relationship sugar")
|
||||
|
||||
else:
|
||||
# dynamically created instance
|
||||
#FIXME: this 'instance' isn't always the tag name, this can be subject to customTagName as well!and should be processed accordingly
|
||||
(relinst_key, relinst_iid) = self.find_custom_tag_name(relationshipValue)
|
||||
if relinst_key is not None:
|
||||
# for example, 'relinst_key' is 'elementContent' and 'relinst_iid' is '{8ECA14A4...}'
|
||||
|
||||
if rel_iid is not None:
|
||||
print("found customTagName '" + str(relinst_key) + "' with value '" + str(relinst_iid.get_value()) + "'")
|
||||
|
||||
# relval = Guid.parse(self.manager.expand_entity_references(elem[customTagName]))
|
||||
# we need to create the new instance and assign relationship
|
||||
self.manager.assign_relationship(instanceId, rel_iid, relinst_iid)
|
||||
else:
|
||||
if "globalIdentifier" in relationshipValue:
|
||||
globalIdentifier = Guid.parse(relationshipValue["globalIdentifier"])
|
||||
else:
|
||||
globalIdentifier = Guid.create()
|
||||
|
||||
print("creating new instance for relationship '%s' with globalid '%s'" % (rel_iid, globalIdentifier.get_value()))
|
||||
|
||||
createsInstanceOf = None
|
||||
if "type" in relationshipValue:
|
||||
print("found relationship override type '%s'" % (relationshipValue['type']))
|
||||
createsInstanceOf = Guid.parse(self.manager.expand_entity_references(relationshipValue["type"]))
|
||||
|
||||
elif "customTagNameCreatesInstanceOf" in rel:
|
||||
createsInstanceOf = Guid.parse(self.manager.expand_entity_references(rel["customTagNameCreatesInstanceOf"]))
|
||||
|
||||
if not createsInstanceOf is None:
|
||||
# create the new instance
|
||||
self.manager.add_instance(createsInstanceOf, globalIdentifier, None, None)
|
||||
|
||||
# assign relationships
|
||||
self.manager.assign_relationship(instanceId, rel_iid, globalIdentifier)
|
||||
# self.manager.assign_relationship(globalIdentifier, relationshipInstanceId, instanceId)
|
||||
|
||||
# FIXME: apply relationships from the parent class template
|
||||
|
||||
#? NOTE: You MUST specify 'registerForTemplate: yes' on the template definition for
|
||||
#? sibling relationships to be properly applied!
|
||||
|
||||
if createsInstanceOf.get_value() in self.templates:
|
||||
print("applying template for createsInstanceOf '%s'" % (createsInstanceOf.get_value()))
|
||||
createsInstanceOfTemplate = self.templates[createsInstanceOf.get_value()]
|
||||
self.apply_template(relationshipValue, createsInstanceOfTemplate, globalIdentifier)
|
||||
else:
|
||||
print("no template registered for createsInstanceOf '%s'" % (createsInstanceOf.get_value()))
|
||||
else:
|
||||
print ("neither createsInstanceOf nor override 'type' present for relationship value")
|
||||
|
||||
def apply_template(self, elem, template, instanceId):
|
||||
|
||||
if "value" in elem:
|
||||
print("VALUE found in ELEM:")
|
||||
print(elem["value"])
|
||||
|
||||
if "attributes" in template:
|
||||
attrs = template["attributes"]
|
||||
if isinstance(attrs, list):
|
||||
for attr in attrs:
|
||||
if "customTagName" in attr:
|
||||
customTagName = attr["customTagName"]
|
||||
if customTagName in elem:
|
||||
attrinst = Guid.parse(self.manager.expand_entity_references(attr["instance"]))
|
||||
attrval = elem[customTagName]
|
||||
self.manager.set_attribute_value(instanceId, attrinst, attrval)
|
||||
|
||||
if "relationships" in template:
|
||||
rels = template["relationships"]
|
||||
for rel in rels:
|
||||
if "instance" in rel:
|
||||
rel_iid = rel["instance"] # the globalId of the relationship instance
|
||||
else:
|
||||
print("no relationship instance specified for relationship sugar")
|
||||
continue
|
||||
|
||||
if "customTagName" in rel:
|
||||
customTagName = rel["customTagName"]
|
||||
if customTagName in elem:
|
||||
relationshipValue = elem[customTagName]
|
||||
if relationshipValue is None:
|
||||
continue
|
||||
|
||||
if isinstance(relationshipValue, list):
|
||||
# multiple instances
|
||||
for v in relationshipValue:
|
||||
self.assign_relationship_value(instanceId, rel, Guid.parse(self.manager.expand_entity_references(rel_iid)), v)
|
||||
|
||||
else:
|
||||
self.assign_relationship_value(instanceId, rel, Guid.parse(self.manager.expand_entity_references(rel_iid)), relationshipValue)
|
||||
|
||||
def get_instance_sugar_for_elem(self, elem):
|
||||
if "instance" in elem:
|
||||
return (elem["instance"], "instance", None)
|
||||
else:
|
||||
for key in self.yamlIds:
|
||||
# no 'instance', see if we
|
||||
if key in elem:
|
||||
template = self.yamlIds[key]
|
||||
templateKey = key
|
||||
return (elem[templateKey], templateKey, template)
|
||||
|
||||
return (None, None, None) # should never get here
|
||||
|
||||
|
||||
def load_instance(self, elem, elemParent = None):
|
||||
# 'class' and 'relationship' are both at the 'same level'
|
||||
# so we can't define 'relationship' without first defining 'class'
|
||||
|
||||
# we could try to keep looping as long as we're still getting "unknown tag name"
|
||||
# (instanceID is None), but we could get stuck that way...
|
||||
|
||||
# for now, let's just forward define important classes (e.g. IDC_Relationship)
|
||||
|
||||
|
||||
# all of these problems could be avoided by simply
|
||||
# LOADING EVERYTHING INTO MEMORY (like the .NET version does)
|
||||
# and only committing to the DB at the very end
|
||||
# this will resolve problems where we can't find instances, or use syntactic sugar
|
||||
|
||||
(globalIdentifier, templateKey, template) = self.get_instance_sugar_for_elem(elem)
|
||||
print ("globalIdentifier = " + str(globalIdentifier) + ", templateKey = " + str(templateKey))
|
||||
|
||||
if template is None:
|
||||
# no sugar
|
||||
pass
|
||||
|
||||
customTagName = None
|
||||
if "customTagName" in elem:
|
||||
customTagName = elem["customTagName"]
|
||||
self.yamlIds[customTagName] = elem
|
||||
print("registering customTagName '" + customTagName + "'")
|
||||
|
||||
if "registerForTemplate" in elem:
|
||||
if elem["registerForTemplate"] == True:
|
||||
self.templates[self.manager.expand_entity_references(globalIdentifier)] = elem
|
||||
print("registering elem for template")
|
||||
|
||||
classId = None
|
||||
classIndex = None
|
||||
|
||||
if template is None and elemParent is not None:
|
||||
template = elemParent
|
||||
|
||||
if template is not None:
|
||||
if "instance" in template:
|
||||
classId = Guid.parse(self.manager.expand_entity_references(template["instance"]))
|
||||
else:
|
||||
for key in self.yamlIds:
|
||||
if key in template:
|
||||
classId = Guid.parse(self.manager.expand_entity_references(template[key]))
|
||||
break
|
||||
|
||||
if "index" in template:
|
||||
classIndex = template["index"]
|
||||
|
||||
instanceId = None
|
||||
creatorKey = None
|
||||
if "instance" in elem:
|
||||
if "templateOnly" in elem and elem["templateOnly"] == True:
|
||||
# instanceId = None
|
||||
instanceId = Guid.parse(self.manager.expand_entity_references(elem["instance"]))
|
||||
else:
|
||||
instanceId = Guid.parse(self.manager.expand_entity_references(elem["instance"]))
|
||||
|
||||
creatorKey = 'instance'
|
||||
else:
|
||||
for key in self.yamlIds:
|
||||
if key in elem:
|
||||
instanceId = Guid.parse(self.manager.expand_entity_references(elem[key]))
|
||||
creatorKey = key
|
||||
break
|
||||
|
||||
index = None
|
||||
if "index" in elem:
|
||||
index = elem["index"]
|
||||
# else:
|
||||
# print("index not found for element")
|
||||
# print(elem)
|
||||
|
||||
if classId is None and instanceId is not None:
|
||||
#!!! HACK HACK HACK !!!
|
||||
classId = instanceId
|
||||
if classIndex is None and index is None:
|
||||
classIndex = 1
|
||||
index = 1
|
||||
print("WARNING: class hack used for instanceId " + instanceId.get_value() + " ( index " + str(classIndex) + "$" + str(index) + ")")
|
||||
|
||||
if instanceId is not None:
|
||||
self.manager.add_instance(classId, instanceId, classIndex, index)
|
||||
|
||||
if creatorKey == 'class': #FIXME: remove this special-case behavior
|
||||
print("creating class " + instanceId.get_value())
|
||||
|
||||
# # assign relationship `Instance.for Class`
|
||||
# self.manager.assign_relationship(instanceId, Guid.parse('494D5A6D04BE477B8763E3F57D0DD8C8'), Guid.parse('B9C9B9B7AD8A4CBDAA6BE05784630B6B'))
|
||||
|
||||
# # assign relationship `Class.has Instance`
|
||||
# self.manager.assign_relationship(Guid.parse('B9C9B9B7AD8A4CBDAA6BE05784630B6B'), Guid.parse('7EB41D3C2AE9488483A4E59441BCAEFB'), instanceId)
|
||||
|
||||
if "instances" in elem:
|
||||
classInsts = elem["instances"]
|
||||
for inst in classInsts:
|
||||
self.load_instance(inst, elem)
|
||||
|
||||
else:
|
||||
print("creating instance " + instanceId.get_value() + " (of class " + classId.get_value() + ")")
|
||||
|
||||
# # assign relationship `Instance.for Class`
|
||||
# self.manager.assign_relationship(instanceId, Guid.parse('494D5A6D04BE477B8763E3F57D0DD8C8'), classId)
|
||||
|
||||
# # assign relationship `Class.has Instance`
|
||||
# self.manager.assign_relationship(classId, Guid.parse('7EB41D3C2AE9488483A4E59441BCAEFB'), instanceId)
|
||||
|
||||
# self.manager.create_instance_of(classId, instanceId, classIndex, index)
|
||||
|
||||
if templateKey is not None:
|
||||
if template is not None:
|
||||
print("using template (" + templateKey + ")")
|
||||
#print(template)
|
||||
|
||||
self.load_instance_with_template(elem, template)
|
||||
else:
|
||||
#print("ignoring '" + templateKey + "'")
|
||||
pass
|
||||
|
||||
def load_instances_from_file(self, filename):
|
||||
print("loading instances from " + filename)
|
||||
|
||||
import yaml
|
||||
with open(filename, 'r') as file:
|
||||
content = yaml.safe_load_all(file)
|
||||
for doc in content:
|
||||
for elem in doc:
|
||||
if "entityDefinitions" in elem:
|
||||
continue
|
||||
elif "library" in elem:
|
||||
self.load_library(filename, elem)
|
||||
elif "tenant" in elem:
|
||||
self.load_tenant(filename, elem)
|
||||
else:
|
||||
raise MochaSyntaxError("neither 'library' nor 'tenant' top level element found")
|
||||
|
||||
def load_library(self, filename, elem):
|
||||
if "instances" in elem:
|
||||
instances = elem["instances"]
|
||||
for elem1 in instances:
|
||||
if "instance" in elem1:
|
||||
# this is an instance definition (no sugar), so just load it
|
||||
self.load_instance(elem1)
|
||||
else:
|
||||
# this is a syntactic sugar definition, so store it for later use
|
||||
elem1["from_file_name"] = filename
|
||||
self._instances_awaiting_sugar.append(elem1)
|
||||
|
||||
def load_tenant(self, filename, elem):
|
||||
pass
|
||||
4
python/mocha/lib/parser/__init__.py
Normal file
4
python/mocha/lib/parser/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .LibraryParser import LibraryParser
|
||||
from .JSONLibraryParser import JSONLibraryParser
|
||||
from .XMLLibraryParser import XMLLibraryParser
|
||||
from .YAMLLibraryParser import YAMLLibraryParser
|
||||
7
python/mocha/web/PathMapping.py
Normal file
7
python/mocha/web/PathMapping.py
Normal file
@ -0,0 +1,7 @@
|
||||
class PathMapping:
|
||||
|
||||
def __init__(self, source_pattern : str, destination_path : str):
|
||||
|
||||
self.source_pattern = source_pattern
|
||||
self.destination_path = destination_path
|
||||
|
||||
@ -13,6 +13,7 @@ class WebPage (WebControl):
|
||||
self.__style_sheet = WebStyleSheet()
|
||||
|
||||
self.add(self.__head)
|
||||
|
||||
self.__head.add(self.__style_sheet)
|
||||
|
||||
self.add(self.__body)
|
||||
|
||||
@ -64,6 +64,11 @@ class WebRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
path = self.url.path.split('/')
|
||||
path = path[1:]
|
||||
|
||||
mapping = self.server._server.find_path_mapping(path)
|
||||
if mapping is not None:
|
||||
self.respond_with_content(200, "OK", mapping.content_type, mapping.content)
|
||||
return
|
||||
|
||||
if (len(path) == 1 and path[0] == ""):
|
||||
self.respond_with_redirect("/" + default_tenant_name)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
from .WebRequestHandler import WebRequestHandler
|
||||
from . import WebRequestHandler, PathMapping
|
||||
|
||||
from ..oms import Oms
|
||||
|
||||
class WebServer():
|
||||
@ -10,10 +11,31 @@ class WebServer():
|
||||
"""
|
||||
self.__tup = endpoint
|
||||
self._oms = oms
|
||||
self.path_mappings = [ ]
|
||||
|
||||
def match_path_pattern(self, source_pattern, path_format):
|
||||
vars = [ ]
|
||||
|
||||
if source_pattern == path_format:
|
||||
return (True, vars)
|
||||
|
||||
# TODO: add intelligent path parsing from PHP/Phast
|
||||
# from framework.web import WebPathParser... (e.g.)
|
||||
return (False, vars)
|
||||
|
||||
def find_path_mapping(self, path_format : str):
|
||||
for mapping in self.path_mappings:
|
||||
(success, vars) = self.match_path_pattern(mapping.source_pattern, path_format)
|
||||
if success:
|
||||
for varname in vars:
|
||||
mapping.set_property_value(varname, vars[varname])
|
||||
return mapping
|
||||
return None
|
||||
|
||||
def serve_forever(self):
|
||||
from http.server import HTTPServer
|
||||
|
||||
server = HTTPServer(self.__tup, WebRequestHandler)
|
||||
server._oms = self._oms
|
||||
server._server = self
|
||||
server.serve_forever()
|
||||
12
python/mocha/web/WebStyleSheetReference.py
Normal file
12
python/mocha/web/WebStyleSheetReference.py
Normal file
@ -0,0 +1,12 @@
|
||||
from .xml import XmlTag
|
||||
|
||||
class WebStyleSheetReference (XmlTag):
|
||||
|
||||
def __init__(self, path):
|
||||
XmlTag.__init__(self, 'link')
|
||||
self.add_attribute("rel", "stylesheet")
|
||||
self.add_attribute("type", "text/css")
|
||||
self.add_attribute("href", path)
|
||||
|
||||
def has_content_or_controls(self):
|
||||
return False
|
||||
7
python/mocha/web/__init__.py
Normal file
7
python/mocha/web/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .PathMapping import PathMapping
|
||||
from .WebPage import WebPage
|
||||
from .WebRequestHandler import WebRequestHandler
|
||||
from .WebScript import WebScript
|
||||
from .WebServer import WebServer
|
||||
from .WebStyleSheet import WebStyleSheet
|
||||
from .WebStyleSheetReference import WebStyleSheetReference
|
||||
46
python/mocha/web/manager/ServerManager.py
Normal file
46
python/mocha/web/manager/ServerManager.py
Normal file
@ -0,0 +1,46 @@
|
||||
from ...oms.memory import MemoryOms
|
||||
from ..WebServer import WebServer
|
||||
|
||||
from ..PathMapping import PathMapping
|
||||
|
||||
class ServerManager:
|
||||
|
||||
def __init__(self):
|
||||
self._servers = [ ]
|
||||
|
||||
def add_server_config(self, filename):
|
||||
import yaml
|
||||
|
||||
with open(filename, 'r') as file:
|
||||
content = yaml.safe_load_all(file)
|
||||
for doc in content:
|
||||
for d in doc:
|
||||
for s in d["server"]:
|
||||
if not "port" in s:
|
||||
print("error: 'port' not defined for server definition")
|
||||
return
|
||||
|
||||
port = s["port"]
|
||||
|
||||
print("[ INFO ]: running server on port " + str(port))
|
||||
|
||||
oms = MemoryOms()
|
||||
svr = WebServer(("127.0.0.1", port), oms)
|
||||
|
||||
if "pathMappings" in s:
|
||||
pathMappings = s["pathMappings"]
|
||||
for pathMapping in pathMappings:
|
||||
svr.path_mappings.append(PathMapping(pathMapping["source"], pathMapping["destination"]))
|
||||
print("[ INFO ]: map path '" + pathMapping["destination"] + "' to file path '" + pathMapping["source"] + "'")
|
||||
|
||||
self._servers.append(svr)
|
||||
|
||||
|
||||
def start(self):
|
||||
import threading
|
||||
for server in self._servers:
|
||||
t = threading.Thread(target = self._serve, args = [ server ])
|
||||
t.start()
|
||||
|
||||
def _serve(self, server):
|
||||
server.serve_forever()
|
||||
1
python/mocha/web/manager/__init__.py
Normal file
1
python/mocha/web/manager/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .ServerManager import ServerManager
|
||||
@ -2,6 +2,8 @@ from ...WebScript import WebScript
|
||||
from ...WebPage import WebPage
|
||||
from ....oms import Oms
|
||||
|
||||
from ...WebStyleSheetReference import WebStyleSheetReference
|
||||
|
||||
class BaseWebPage (WebPage):
|
||||
|
||||
def __init__(self):
|
||||
@ -11,6 +13,7 @@ class BaseWebPage (WebPage):
|
||||
def on_initializing(self):
|
||||
self.get_style_sheet().add_rule('table', { 'width': '100%' })
|
||||
self.get_head().add(self.get_client_config_script())
|
||||
self.get_head().add(WebStyleSheetReference("/madi/asset/ui-html/2024.03.09/css/main.css"))
|
||||
|
||||
def get_oms(self):
|
||||
return self._oms
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user