diff --git a/api/__init__.py b/api/__init__.py index 9a3d849..22fa887 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -48,6 +48,7 @@ from .s10_status import get_status_schema, get_status, set_status from .s11_patterns import get_pattern_schema, get_pattern, set_pattern, add_pattern, delete_pattern +from .s12_curves import CURVE_TYPE_PUMP, CURVE_TYPE_EFFICIENCY, CURVE_TYPE_VOLUME, CURVE_TYPE_HEADLOSS 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 diff --git a/api/s12_curves.py b/api/s12_curves.py index 14bc2ba..d20f1ec 100644 --- a/api/s12_curves.py +++ b/api/s12_curves.py @@ -1,34 +1,81 @@ from .operation import * +CURVE_TYPE_PUMP = 'PUMP' +CURVE_TYPE_EFFICIENCY = 'EFFICIENCY' +CURVE_TYPE_VOLUME = 'VOLUME' +CURVE_TYPE_HEADLOSS = 'HEADLOSS' def get_curve_schema(name: str) -> dict[str, dict[str, Any]]: return { 'id' : {'type': 'str' , 'optional': False , 'readonly': True }, + 'c_type' : {'type': 'str' , 'optional': False , 'readonly': False}, 'coords' : {'type': 'list' , 'optional': False , 'readonly': False, 'element': { 'x' : {'type': 'float' , 'optional': False , 'readonly': False }, 'y' : {'type': 'float' , 'optional': False , 'readonly': False } }}} def get_curve(name: str, id: str) -> dict[str, Any]: + c_one = read(name, f"select * from _curve where id = '{id}'") cus = read_all(name, f"select * from curves where id = '{id}' order by _order") cs = [] for r in cus: cs.append({ 'x': float(r['x']), 'y': float(r['y']) }) - return { 'id': id, 'coords': cs } + d = {} + d['id'] = id + d['c_type'] = c_one['type'] + d['coords'] = cs + return d def set_curve_cache(name: str, cs: ChangeSet) -> SqlChangeSet: id = cs.operations[0]['id'] + f_id = f"'{id}'" old = get_curve(name, id) - new = { 'id': id, 'coords': [] } + old_f_type = f"'{old['c_type']}'" - f_id = f"'{id}'" + new = { 'id': id } + if 'coords' in cs.operations[0]: + new['coords'] = cs.operations[0]['coords'] + else: + new['coords'] = old['coords'] + if 'c_type' in cs.operations[0]: + new['c_type'] = cs.operations[0]['c_type'] + else: + new['c_type'] = old['c_type'] + new_f_type = f"'{new['c_type']}'" # TODO: transaction ? redo_sql = f"delete from curves where id = {f_id};" - redo_sql += f"\ndelete from _curve where id = {f_id};" - if len(cs.operations[0]['coords']) > 0: - redo_sql += f"\ninsert into _curve (id) values ({f_id});" + redo_sql += f"\nupdate _curve set type = {new_f_type} where id = {f_id};" + for xy in new['coords']: + f_x, f_y = xy['x'], xy['y'] + redo_sql += f"\ninsert into curves (id, x, y) values ({f_id}, {f_x}, {f_y});" + + undo_sql = f"delete from curves where id = {f_id};" + undo_sql += f"\nupdate _curve set type = {old_f_type} where id = {f_id};" + for xy in old['coords']: + 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 + + 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)) + + +def add_curve_cache(name: str, cs: ChangeSet) -> SqlChangeSet: + id = cs.operations[0]['id'] + f_id = f"'{id}'" + + new = { 'id': id, 'c_type': cs.operations[0]['c_type'], 'coords': [] } + new_f_type = f"'{new['c_type']}'" + + # TODO: transaction ? + redo_sql = f"\ninsert into _curve (id, type) values ({f_id}, {new_f_type});" for xy in cs.operations[0]['coords']: x, y = float(xy['x']), float(xy['y']) f_x, f_y = x, y @@ -37,35 +84,38 @@ def set_curve_cache(name: str, cs: ChangeSet) -> SqlChangeSet: undo_sql = f"delete from curves where id = {f_id};" undo_sql += f"\ndelete from _curve where id = {f_id};" - if len(old['coords']) > 0: - undo_sql += f"\ninsert into _curve (id) values ({f_id});" - for xy in old['coords']: - 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 = { 'type': 'curve' } | new - undo_cs = { 'type': 'curve' } | old + redo_cs = g_add_prefix | { 'type': 'curve' } | new + undo_cs = g_delete_prefix | { 'type': 'curve' } | { 'id' : id } return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) -def set_curve(name: str, cs: ChangeSet) -> ChangeSet: - 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) + return execute_command(name, add_curve_cache(name, cs)) + + +def delete_curve_cache(name: str, cs: ChangeSet) -> SqlChangeSet: + id = cs.operations[0]['id'] + f_id = f"'{id}'" + + old = get_curve(name, id) + old_f_type = f"'{old['c_type']}'" + + redo_sql = f"delete from curves where id = {f_id};" + redo_sql += f"\ndelete from _curve where id = {f_id};" + + # TODO: transaction ? + undo_sql = f"\ninsert into _curve (id, type) values ({f_id}, {old_f_type});" + for xy in old['coords']: + 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_delete_prefix | { 'type': 'curve' } | { 'id' : id } + undo_cs = g_add_prefix | { 'type': 'curve' } | old + + return SqlChangeSet(redo_sql, undo_sql, redo_cs, undo_cs) 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) + return execute_command(name, delete_curve_cache(name, cs)) diff --git a/script/sql/create/0.base.sql b/script/sql/create/0.base.sql index 0d508f0..255d95b 100644 --- a/script/sql/create/0.base.sql +++ b/script/sql/create/0.base.sql @@ -2,6 +2,8 @@ create type _node_type as enum ('junction', 'reservoir', 'tank'); create type _link_type as enum ('pipe', 'pump', 'valve'); +create type _curve_type as enum ('PUMP', 'EFFICIENCY', 'VOLUME', 'HEADLOSS'); + create table _node ( id varchar(32) primary key @@ -17,6 +19,7 @@ create table _link create table _curve ( id varchar(32) primary key +, type _curve_type not null ); create table _pattern diff --git a/script/sql/drop/0.base.sql b/script/sql/drop/0.base.sql index adf7030..e76a8ad 100644 --- a/script/sql/drop/0.base.sql +++ b/script/sql/drop/0.base.sql @@ -6,6 +6,8 @@ drop table if exists _link; drop table if exists _node; +drop type if exists _curve_type; + drop type if exists _link_type; drop type if exists _node_type; diff --git a/test_tjnetwork.py b/test_tjnetwork.py index bc84873..4254d5c 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -1648,15 +1648,25 @@ class TestApi: self.enter(p) assert is_curve(p, 'c0') == False - c0 = get_curve(p, 'c0') - assert c0['id'] == 'c0' - assert c0['coords'] == [] - set_curve(p, ChangeSet({'id' : 'c0', 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) + add_curve(p, ChangeSet({'id' : 'c0', 'c_type' : CURVE_TYPE_PUMP, 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) assert is_curve(p, 'c0') c0 = get_curve(p, 'c0') assert c0['id'] == 'c0' + assert c0['c_type'] == CURVE_TYPE_PUMP + xys = c0['coords'] + assert len(xys) == 2 + assert xys[0]['x'] == 1.0 + assert xys[0]['y'] == 2.0 + assert xys[1]['x'] == 2.0 + assert xys[1]['y'] == 1.0 + + set_curve(p, ChangeSet({'id' : 'c0', 'c_type': CURVE_TYPE_EFFICIENCY})) + + c0 = get_curve(p, 'c0') + assert c0['id'] == 'c0' + assert c0['c_type'] == CURVE_TYPE_EFFICIENCY xys = c0['coords'] assert len(xys) == 2 assert xys[0]['x'] == 1.0 @@ -1666,11 +1676,15 @@ class TestApi: set_curve(p, ChangeSet({'id' : 'c0', 'coords': []})) - assert is_curve(p, 'c0') == False c0 = get_curve(p, 'c0') assert c0['id'] == 'c0' + assert c0['c_type'] == CURVE_TYPE_EFFICIENCY assert c0['coords'] == [] + delete_curve(p, ChangeSet({'id' : 'c0'})) + + assert is_curve(p, 'c0') == False + self.leave(p) @@ -1678,10 +1692,40 @@ class TestApi: p = 'test_curve_op' self.enter(p) - cs = set_curve(p, ChangeSet({'id' : 'c0', 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})).operations[0] + cs = add_curve(p, ChangeSet({'id' : 'c0', 'c_type' : CURVE_TYPE_PUMP, 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_PUMP + xys = cs['coords'] + assert len(xys) == 2 + assert xys[0]['x'] == 1.0 + assert xys[0]['y'] == 2.0 + assert xys[1]['x'] == 2.0 + assert xys[1]['y'] == 1.0 + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_DELETE + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_ADD + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_PUMP + xys = cs['coords'] + assert len(xys) == 2 + assert xys[0]['x'] == 1.0 + assert xys[0]['y'] == 2.0 + assert xys[1]['x'] == 2.0 + assert xys[1]['y'] == 1.0 + + cs = set_curve(p, ChangeSet({'id' : 'c0', 'c_type': CURVE_TYPE_EFFICIENCY})).operations[0] assert cs['operation'] == API_UPDATE assert cs['type'] == CURVE assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_EFFICIENCY xys = cs['coords'] assert len(xys) == 2 assert xys[0]['x'] == 1.0 @@ -1693,12 +1737,7 @@ class TestApi: assert cs['operation'] == API_UPDATE assert cs['type'] == CURVE assert cs['id'] == 'c0' - assert cs['coords'] == [] - - cs = execute_redo(p).operations[0] - assert cs['operation'] == API_UPDATE - assert cs['type'] == CURVE - assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_PUMP xys = cs['coords'] assert len(xys) == 2 assert xys[0]['x'] == 1.0 @@ -1706,6 +1745,44 @@ class TestApi: assert xys[1]['x'] == 2.0 assert xys[1]['y'] == 1.0 + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_EFFICIENCY + xys = cs['coords'] + assert len(xys) == 2 + assert xys[0]['x'] == 1.0 + assert xys[0]['y'] == 2.0 + assert xys[1]['x'] == 2.0 + assert xys[1]['y'] == 1.0 + + cs = set_curve(p, ChangeSet({'id' : 'c0', 'coords': []})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_EFFICIENCY + assert cs['coords'] == [] + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_EFFICIENCY + xys = cs['coords'] + assert len(xys) == 2 + assert xys[0]['x'] == 1.0 + assert xys[0]['y'] == 2.0 + assert xys[1]['x'] == 2.0 + assert xys[1]['y'] == 1.0 + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == CURVE + assert cs['id'] == 'c0' + assert cs['c_type'] == CURVE_TYPE_EFFICIENCY + assert cs['coords'] == [] + self.leave(p) @@ -1993,7 +2070,7 @@ class TestApi: assert ge['pattern'] == 'pa0' assert ge['effic'] == None - set_curve(p, ChangeSet({'id' : 'c0', 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) + add_curve(p, ChangeSet({'id' : 'c0', 'c_type' : CURVE_TYPE_PUMP, 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) set_pump_energy(p, ChangeSet({'pump' : 'p0', 'effic': 'c0'})) ge = get_pump_energy(p, 'p0') assert ge['pump'] == 'p0' @@ -2012,7 +2089,7 @@ class TestApi: add_junction(p, ChangeSet({'id': 'j2', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) add_pump(p, ChangeSet({'id': 'p0', 'node1': 'j1', 'node2': 'j2', 'power': 0.0})) add_pattern(p, ChangeSet({'id' : 'pa0', 'factors': [1.0, 2.0, 3.0]})) - set_curve(p, ChangeSet({'id' : 'c0', 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) + add_curve(p, ChangeSet({'id' : 'c0', 'c_type' : CURVE_TYPE_PUMP, 'coords': [{'x': 1.0, 'y': 2.0}, {'x': 2.0, 'y': 1.0}]})) cs = set_pump_energy(p, ChangeSet({'pump' : 'p0'})).operations[0] assert cs['operation'] == API_UPDATE diff --git a/tjnetwork.py b/tjnetwork.py index 0d9a391..9a560cd 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -47,6 +47,11 @@ LINK_STATUS_OPEN = api.LINK_STATUS_OPEN LINK_STATUS_CLOSED = api.LINK_STATUS_CLOSED LINK_STATUS_ACTIVE = api.LINK_STATUS_ACTIVE +CURVE_TYPE_PUMP = api.CURVE_TYPE_PUMP +CURVE_TYPE_EFFICIENCY = api.CURVE_TYPE_EFFICIENCY +CURVE_TYPE_VOLUME = api.CURVE_TYPE_VOLUME +CURVE_TYPE_HEADLOSS = api.CURVE_TYPE_HEADLOSS + TIME_STATISTIC_NONE = api.TIME_STATISTIC_NONE TIME_STATISTIC_AVERAGED = api.TIME_STATISTIC_AVERAGED TIME_STATISTIC_MINIMUM = api.TIME_STATISTIC_MINIMUM