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__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
port = 8081
|
port = 8081
|
||||||
|
libraries = [ ]
|
||||||
|
|
||||||
(opts, remaining) = getopt(sys.argv[1:], "p", [ "port=" ])
|
(opts, remaining) = getopt(sys.argv[1:], "p", [ "port=", "library=" ])
|
||||||
for opt in opts:
|
for opt in opts:
|
||||||
if opt[0] == "--port":
|
if opt[0] == "--port":
|
||||||
port = int(opt[1])
|
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)
|
for library in remaining:
|
||||||
server.serve_forever()
|
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.__style_sheet = WebStyleSheet()
|
||||||
|
|
||||||
self.add(self.__head)
|
self.add(self.__head)
|
||||||
|
|
||||||
self.__head.add(self.__style_sheet)
|
self.__head.add(self.__style_sheet)
|
||||||
|
|
||||||
self.add(self.__body)
|
self.add(self.__body)
|
||||||
|
|||||||
@ -64,6 +64,11 @@ class WebRequestHandler(BaseHTTPRequestHandler):
|
|||||||
|
|
||||||
path = self.url.path.split('/')
|
path = self.url.path.split('/')
|
||||||
path = path[1:]
|
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] == ""):
|
if (len(path) == 1 and path[0] == ""):
|
||||||
self.respond_with_redirect("/" + default_tenant_name)
|
self.respond_with_redirect("/" + default_tenant_name)
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
from .WebRequestHandler import WebRequestHandler
|
from . import WebRequestHandler, PathMapping
|
||||||
|
|
||||||
from ..oms import Oms
|
from ..oms import Oms
|
||||||
|
|
||||||
class WebServer():
|
class WebServer():
|
||||||
@ -10,10 +11,31 @@ class WebServer():
|
|||||||
"""
|
"""
|
||||||
self.__tup = endpoint
|
self.__tup = endpoint
|
||||||
self._oms = oms
|
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):
|
def serve_forever(self):
|
||||||
from http.server import HTTPServer
|
from http.server import HTTPServer
|
||||||
|
|
||||||
server = HTTPServer(self.__tup, WebRequestHandler)
|
server = HTTPServer(self.__tup, WebRequestHandler)
|
||||||
server._oms = self._oms
|
server._oms = self._oms
|
||||||
|
server._server = self
|
||||||
server.serve_forever()
|
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 ...WebPage import WebPage
|
||||||
from ....oms import Oms
|
from ....oms import Oms
|
||||||
|
|
||||||
|
from ...WebStyleSheetReference import WebStyleSheetReference
|
||||||
|
|
||||||
class BaseWebPage (WebPage):
|
class BaseWebPage (WebPage):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -11,6 +13,7 @@ class BaseWebPage (WebPage):
|
|||||||
def on_initializing(self):
|
def on_initializing(self):
|
||||||
self.get_style_sheet().add_rule('table', { 'width': '100%' })
|
self.get_style_sheet().add_rule('table', { 'width': '100%' })
|
||||||
self.get_head().add(self.get_client_config_script())
|
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):
|
def get_oms(self):
|
||||||
return self._oms
|
return self._oms
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user