diff --git a/api/__init__.py b/api/__init__.py index e31759f..a37ccdd 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -38,14 +38,21 @@ from .s6_pumps import get_pump_schema, add_pump, get_pump, set_pump, delete_pump from .s7_valves import VALVES_TYPE_PRV, VALVES_TYPE_PSV, VALVES_TYPE_PBV, VALVES_TYPE_FCV, VALVES_TYPE_TCV, VALVES_TYPE_GPV from .s7_valves import get_valve_schema, add_valve, get_valve, set_valve, delete_valve +from .s8_tags import TAG_TYPE_NODE, TAG_TYPE_LINK +from .s8_tags import get_tag_schema, get_tag, set_tag + from .s9_demands import get_demand_schema, get_demand, set_demand from .s10_status import LINK_STATUS_OPEN, LINK_STATUS_CLOSED, LINK_STATUS_ACTIVE from .s10_status import get_status_schema, get_status, set_status -from .s11_patterns import get_pattern_schema, get_pattern, set_pattern +from .s11_patterns import get_pattern_schema, get_pattern, set_pattern, add_pattern, delete_pattern -from .s12_curves import get_curve_schema, get_curve, set_curve +from .s12_curves import get_curve_schema, get_curve, set_curve, add_curve, delete_curve + +from .s13_controls import get_control_schema, get_control, set_control + +from .s14_rules import get_rule_schema, get_rule, set_rule from .s16_emitters import get_emitter_schema, get_emitter, set_emitter diff --git a/api/s11_patterns.py b/api/s11_patterns.py index 96e6ce2..f8ceb6e 100644 --- a/api/s11_patterns.py +++ b/api/s11_patterns.py @@ -39,11 +39,29 @@ def set_pattern_cache(name: str, cs: ChangeSet) -> SqlChangeSet: for f_factor in old['factors']: undo_sql += f"\ninsert into patterns (id, factor) values ({f_id}, {f_factor});" - redo_cs = g_update_prefix | { 'type': 'pattern' } | new - undo_cs = g_update_prefix | { 'type': 'pattern' } | old + redo_cs = { 'type': 'pattern' } | new + undo_cs = { 'type': 'pattern' } | old return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) def set_pattern(name: str, cs: ChangeSet) -> ChangeSet: - return execute_command(name, set_pattern_cache(name, cs)) + result = set_pattern_cache(name, cs) + result.redo_cs |= g_update_prefix + result.undo_cs |= g_update_prefix + return execute_command(name, result) + + +def add_pattern(name: str, cs: ChangeSet) -> ChangeSet: + result = set_pattern_cache(name, cs) + result.redo_cs |= g_add_prefix + result.undo_cs |= g_delete_prefix + return execute_command(name, result) + + +def delete_pattern(name: str, cs: ChangeSet) -> ChangeSet: + cs.operations[0]['factors'] = [] + result = set_pattern_cache(name, cs) + result.redo_cs |= g_delete_prefix + result.undo_cs |= g_add_prefix + return execute_command(name, result) diff --git a/api/s12_curves.py b/api/s12_curves.py index 4edc159..14bc2ba 100644 --- a/api/s12_curves.py +++ b/api/s12_curves.py @@ -43,11 +43,29 @@ def set_curve_cache(name: str, cs: ChangeSet) -> SqlChangeSet: f_x, f_y = xy['x'], xy['y'] undo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});" - redo_cs = g_update_prefix | { 'type': 'curve' } | new - undo_cs = g_update_prefix | { 'type': 'curve' } | old + redo_cs = { 'type': 'curve' } | new + undo_cs = { 'type': 'curve' } | old return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) def set_curve(name: str, cs: ChangeSet) -> ChangeSet: - return execute_command(name, set_curve_cache(name, cs)) + result = set_curve_cache(name, cs) + result.redo_cs |= g_update_prefix + result.undo_cs |= g_update_prefix + return execute_command(name, result) + + +def add_curve(name: str, cs: ChangeSet) -> ChangeSet: + result = set_curve_cache(name, cs) + result.redo_cs |= g_add_prefix + result.undo_cs |= g_delete_prefix + return execute_command(name, result) + + +def delete_curve(name: str, cs: ChangeSet) -> ChangeSet: + cs.operations[0]['coords'] = [] + result = set_curve_cache(name, cs) + result.redo_cs |= g_delete_prefix + result.undo_cs |= g_add_prefix + return execute_command(name, result) diff --git a/api/s13_controls.py b/api/s13_controls.py new file mode 100644 index 0000000..1deda79 --- /dev/null +++ b/api/s13_controls.py @@ -0,0 +1,26 @@ +from .operation import * + + +def get_control_schema(name: str) -> dict[str, dict[str, Any]]: + return { 'control' : {'type': 'str' , 'optional': False , 'readonly': False} } + + +def get_control(name: str) -> dict[str, Any]: + e = read(name, f"select * from controls") + return { 'control': e['control'] } + + +def set_control_cache(name: str, cs: ChangeSet) -> SqlChangeSet: + old = get_control(name) + + redo_sql = f"update controls set control = '{cs.operations[0]['control']}' where control = '{old['control']}';" + undo_sql = f"update controls set control = '{old['control']}' where control = '{cs.operations[0]['control']}';" + + redo_cs = g_update_prefix | { 'type': 'control', 'control': cs.operations[0]['control'] } + undo_cs = g_update_prefix | { 'type': 'control', 'control': old['control'] } + + return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) + + +def set_control(name: str, cs: ChangeSet) -> ChangeSet: + return execute_command(name, set_control_cache(name, cs)) diff --git a/api/s14_rules.py b/api/s14_rules.py new file mode 100644 index 0000000..f8bed6c --- /dev/null +++ b/api/s14_rules.py @@ -0,0 +1,26 @@ +from .operation import * + + +def get_rule_schema(name: str) -> dict[str, dict[str, Any]]: + return { 'rule' : {'type': 'str' , 'optional': False , 'readonly': False} } + + +def get_rule(name: str) -> dict[str, Any]: + e = read(name, f"select * from rules") + return { 'rule': e['rule'] } + + +def set_rule_cache(name: str, cs: ChangeSet) -> SqlChangeSet: + old = get_rule(name) + + redo_sql = f"update rules set rule = '{cs.operations[0]['rule']}' where rule = '{old['rule']}';" + undo_sql = f"update rules set rule = '{old['rule']}' where rule = '{cs.operations[0]['rule']}';" + + redo_cs = g_update_prefix | { 'type': 'rule', 'rule': cs.operations[0]['rule'] } + undo_cs = g_update_prefix | { 'type': 'rule', 'rule': old['rule'] } + + return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) + + +def set_rule(name: str, cs: ChangeSet) -> ChangeSet: + return execute_command(name, set_rule_cache(name, cs)) diff --git a/api/s23_options.py b/api/s23_options.py index 66afaf2..928379e 100644 --- a/api/s23_options.py +++ b/api/s23_options.py @@ -55,7 +55,7 @@ def get_option_schema(name: str) -> dict[str, dict[str, Any]]: 'QUALITY' : element_schema, 'DIFFUSIVITY' : element_schema, 'TOLERANCE' : element_schema, - #'QUALITY' : element_schema, + #'MAP' : element_schema, } diff --git a/api/s8_tags.py b/api/s8_tags.py new file mode 100644 index 0000000..67b53b0 --- /dev/null +++ b/api/s8_tags.py @@ -0,0 +1,79 @@ +from .operation import * + +TAG_TYPE_NODE = 'NODE' +TAG_TYPE_LINK = 'LINK' + +def get_tag_schema(name: str) -> dict[str, dict[str, Any]]: + return { 't_type' : {'type': 'str' , 'optional': False , 'readonly': False}, + 'id' : {'type': 'str' , 'optional': False , 'readonly': False}, + 'tag' : {'type': 'str' , 'optional': True , 'readonly': False},} + + +def get_tag(name: str, t_type: str, id: str) -> dict[str, Any]: + t = None + if t_type == TAG_TYPE_NODE: + t = try_read(name, f"select * from tags_node where id = '{id}'") + elif t_type == TAG_TYPE_LINK: + t = try_read(name, f"select * from tags_link where id = '{id}'") + else: + raise Exception('Only support NODE and Link') + if t == None: + return { 't_type': t_type, 'id': id, 'tag': None } + d = {} + d['t_type'] = t_type + d['id'] = str(t['id']) + d['tag'] = str(t['tag']) if t['tag'] != None else None + return d + + +class Tag(object): + def __init__(self, input: dict[str, Any]) -> None: + self.type = 'tag' + self.t_type = str(input['t_type']) + self.id = str(input['id']) + self.tag = str(input['tag']) if 'tag' in input and input['tag'] != None else None + + self.f_type = f"'{self.type}'" + self.f_t_type = f"'{self.t_type}'" + self.f_id = f"'{self.id}'" + self.f_tag = f"'{self.tag}'" if self.tag != None else 'null' + + def as_dict(self) -> dict[str, Any]: + return { 'type': self.type, 't_type': self.t_type, 'id': self.id, 'tag': self.tag } + + +def set_tag_cache(name: str, cs: ChangeSet) -> SqlChangeSet: + old = Tag(get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id'])) + raw_new = get_tag(name, cs.operations[0]['t_type'], cs.operations[0]['id']) + + new_dict = cs.operations[0] + schema = get_tag_schema(name) + for key, value in schema.items(): + if key in new_dict and not value['readonly']: + raw_new[key] = new_dict[key] + new = Tag(raw_new) + + table = '' + if cs.operations[0]['t_type'] == TAG_TYPE_NODE: + table = 'tags_node' + elif cs.operations[0]['t_type'] == TAG_TYPE_LINK: + table = 'tags_link' + else: + raise Exception('Only support NODE and Link') + + redo_sql = f"delete from {table} where id = {new.f_id};" + if new.tag != None: + redo_sql += f"\ninsert into {table} (id, tag) values ({new.f_id}, {new.f_tag});" + + undo_sql = f"delete from {table} where id = {old.f_id};" + if old.tag != None: + undo_sql += f"\ninsert into {table} (id, tag) values ({old.f_id}, {old.f_tag});" + + redo_cs = g_update_prefix | new.as_dict() + undo_cs = g_update_prefix | old.as_dict() + + return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) + + +def set_tag(name: str, cs: ChangeSet) -> ChangeSet: + return execute_command(name, set_tag_cache(name, cs)) diff --git a/script/sql/create/13.controls.sql b/script/sql/create/13.controls.sql index 335db45..38c925a 100644 --- a/script/sql/create/13.controls.sql +++ b/script/sql/create/13.controls.sql @@ -4,3 +4,5 @@ create table controls ( control text primary key ); + +insert into controls (control) values (''); diff --git a/script/sql/create/14.rules.sql b/script/sql/create/14.rules.sql index bfd74c3..8c09e06 100644 --- a/script/sql/create/14.rules.sql +++ b/script/sql/create/14.rules.sql @@ -4,3 +4,5 @@ create table rules ( rule text primary key ); + +insert into rules (rule) values (''); diff --git a/test_tjnetwork.py b/test_tjnetwork.py index 918f547..aa8e8c7 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -1174,6 +1174,130 @@ class TestApi: self.leave(p) + def test_tag(self): + p = 'test_tag' + self.enter(p) + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + add_junction(p, ChangeSet({'id': 'j2', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + assert is_junction(p, 'j1') + assert is_junction(p, 'j2') + + add_pipe(p, ChangeSet({'id': 'p0', 'node1': 'j1', 'node2': 'j2', 'length': 100.0, 'diameter': 10.0, 'roughness': 0.1, 'minor_loss': 0.5, 'status': PIPE_STATUS_OPEN })) + assert is_pipe(p, 'p0') + + t = get_tag(p, TAG_TYPE_NODE, 'j1') + assert t['tag'] == None + set_tag(p, ChangeSet({'t_type': TAG_TYPE_NODE, 'id': 'j1', 'tag': 'j1t' })) + t = get_tag(p, TAG_TYPE_NODE, 'j1') + assert t['tag'] == 'j1t' + + t = get_tag(p, TAG_TYPE_NODE, 'j2') + assert t['tag'] == None + set_tag(p, ChangeSet({'t_type': TAG_TYPE_NODE, 'id': 'j2', 'tag': 'j2t' })) + t = get_tag(p, TAG_TYPE_NODE, 'j2') + assert t['tag'] == 'j2t' + + t = get_tag(p, TAG_TYPE_LINK, 'p0') + assert t['tag'] == None + set_tag(p, ChangeSet({'t_type': TAG_TYPE_LINK, 'id': 'p0', 'tag': 'p0t' })) + t = get_tag(p, TAG_TYPE_LINK, 'p0') + assert t['tag'] == 'p0t' + + self.leave(p) + + + def test_tag_op(self): + p = 'test_tag_op' + self.enter(p) + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + add_junction(p, ChangeSet({'id': 'j2', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + assert is_junction(p, 'j1') + assert is_junction(p, 'j2') + + add_pipe(p, ChangeSet({'id': 'p0', 'node1': 'j1', 'node2': 'j2', 'length': 100.0, 'diameter': 10.0, 'roughness': 0.1, 'minor_loss': 0.5, 'status': PIPE_STATUS_OPEN })) + assert is_pipe(p, 'p0') + + cs = set_tag(p, ChangeSet({'t_type': TAG_TYPE_NODE, 'id': 'j1', 'tag': 'j1t' })).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_NODE + assert cs['id'] == 'j1' + assert cs['tag'] == 'j1t' + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_NODE + assert cs['id'] == 'j1' + assert cs['tag'] == None + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_NODE + assert cs['id'] == 'j1' + assert cs['tag'] == 'j1t' + + cs = set_tag(p, ChangeSet({'t_type': TAG_TYPE_LINK, 'id': 'p0', 'tag': 'p0t' })).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_LINK + assert cs['id'] == 'p0' + assert cs['tag'] == 'p0t' + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_LINK + assert cs['id'] == 'p0' + assert cs['tag'] == None + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'tag' + assert cs['t_type'] == TAG_TYPE_LINK + assert cs['id'] == 'p0' + assert cs['tag'] == 'p0t' + + self.leave(p) + + + def test_rule(self): + p = 'test_rule' + self.enter(p) + + assert get_rule(p)['rule'] == '' + + set_rule(p, ChangeSet({'rule': 'x'})) + assert get_rule(p)['rule'] == 'x' + + self.leave(p) + + + def test_rule_op(self): + p = 'test_rule_op' + self.enter(p) + + cs = set_rule(p, ChangeSet({'rule': 'x'})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'rule' + assert cs['rule'] == 'x' + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'rule' + assert cs['rule'] == '' + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'rule' + assert cs['rule'] == 'x' + + self.leave(p) + + def test_demand(self): p = 'test_demand' self.enter(p) diff --git a/tjnetwork.py b/tjnetwork.py index 3a269e9..862bb24 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -40,6 +40,9 @@ VALVES_TYPE_FCV = api.VALVES_TYPE_FCV VALVES_TYPE_TCV = api.VALVES_TYPE_TCV VALVES_TYPE_GPV = api.VALVES_TYPE_GPV +TAG_TYPE_NODE = api.TAG_TYPE_NODE +TAG_TYPE_LINK = api.TAG_TYPE_LINK + LINK_STATUS_OPEN = api.LINK_STATUS_OPEN LINK_STATUS_CLOSED = api.LINK_STATUS_CLOSED LINK_STATUS_ACTIVE = api.LINK_STATUS_ACTIVE @@ -334,6 +337,23 @@ def delete_valve(name: str, cs: ChangeSet) -> ChangeSet: return api.delete_valve(name, cs) +############################################################ +# tag 8.[TAGS] +############################################################ + +def get_tag_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_tag_schema(name) + +def get_tag(name: str, t_type: str, id: str) -> dict[str, Any]: + return api.get_tag(name, t_type, id) + +# example: +# set_tag(p, ChangeSet({'t_type': TAG_TYPE_NODE, 'id': 'j1', 'tag': 'j1t' })) +# set_tag(p, ChangeSet({'t_type': TAG_TYPE_LINK, 'id': 'p0', 'tag': 'p0t' })) +def set_tag(name: str, cs: ChangeSet) -> ChangeSet: + return api.set_tag(name, cs) + + ############################################################ # demand 9.[DEMANDS] ############################################################ @@ -378,6 +398,12 @@ def get_pattern(name: str, id: str) -> dict[str, Any]: def set_pattern(name: str, cs: ChangeSet) -> ChangeSet: return api.set_pattern(name, cs) +def add_pattern(name: str, cs: ChangeSet) -> ChangeSet: + return api.add_pattern(name, cs) + +def delete_pattern(name: str, cs: ChangeSet) -> ChangeSet: + return api.delete_pattern(name, cs) + ############################################################ # curve 12.[CURVES] @@ -393,6 +419,42 @@ def get_curve(name: str, id: str) -> dict[str, Any]: def set_curve(name: str, cs: ChangeSet) -> ChangeSet: return api.set_curve(name, cs) +def add_curve(name: str, cs: ChangeSet) -> ChangeSet: + return api.add_curve(name, cs) + +def delete_curve(name: str, cs: ChangeSet) -> ChangeSet: + return api.delete_curve(name, cs) + + +############################################################ +# control 12.[CONTROLS] +############################################################ + +def get_control_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_control_schema(name) + +def get_control(name: str) -> dict[str, Any]: + return api.get_control(name) + +# example: set_control(p, ChangeSet({'control': 'x'})) +def set_control(name: str, cs: ChangeSet) -> ChangeSet: + return api.set_control(name, cs) + + +############################################################ +# rule 13.[RULES] +############################################################ + +def get_rule_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_rule_schema(name) + +def get_rule(name: str) -> dict[str, Any]: + return api.get_rule(name) + +# example: set_rule(p, ChangeSet({'rule': 'x'})) +def set_rule(name: str, cs: ChangeSet) -> ChangeSet: + return api.set_rule(name, cs) + ############################################################ # emitter 16.[EMITTERS] @@ -410,7 +472,7 @@ def set_emitter(name: str, cs: ChangeSet) -> ChangeSet: ############################################################ -# time 21.[TIME] +# time 21.[TIMES] ############################################################ def get_time_schema(name: str) -> dict[str, dict[str, Any]]: