diff --git a/api/__init__.py b/api/__init__.py index 4cd493b..543fa84 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -5,10 +5,8 @@ from .project import copy_project from .change_set import ChangeSet from .operation import get_current_operation -from .operation import execute_undo as undo -from .operation import execute_redo as redo -from .operation import have_snapshot, take_snapshot, pick_snapshot -from .operation import have_transaction, start_transaction, commit_transaction, abort_transaction +from .operation import execute_undo, execute_redo +from .operation import have_snapshot, take_snapshot, pick_snapshot, sync_with_server from .s0_base import JUNCTION, RESERVOIR, TANK, PIPE, PUMP, VALVE from .s0_base import is_node, is_junction, is_reservoir, is_tank @@ -20,31 +18,19 @@ from .s0_base import get_node_links from .s1_title import set_title, get_title -from .s2_junctions import add_junction, delete_junction -from .s2_junctions import set_junction_elevation, set_junction_demand, set_junction_pattern, set_junction_coord -from .s2_junctions import get_junction_property_names, get_junction_properties +from .s2_junctions import get_junction_schema, add_junction, get_junction, set_junction, delete_junction -from .s3_reservoirs import add_reservoir, delete_reservoir -from .s3_reservoirs import set_reservoir_head, set_reservoir_pattern, set_reservoir_coord -from .s3_reservoirs import get_reservoir_property_names, get_reservoir_properties +from .s3_reservoirs import get_reservoir_schema, add_reservoir, get_reservoir, set_reservoir, delete_reservoir from .s4_tanks import OVERFLOW_YES, OVERFLOW_NO -from .s4_tanks import add_tank, delete_tank -from .s4_tanks import set_tank_elevation, set_tank_init_level, set_tank_min_level, set_tank_max_level, set_tank_diameter, set_tank_min_vol, set_tank_vol_curve, set_tank_overflow, set_tank_coord -from .s4_tanks import get_tank_property_names, get_tank_properties +from .s4_tanks import get_tank_schema, add_tank, get_tank, set_tank, delete_tank from .s5_pipes import PIPE_STATUS_OPEN, PIPE_STATUS_CLOSED, PIPE_STATUS_CV -from .s5_pipes import add_pipe, delete_pipe -from .s5_pipes import set_pipe_node1, set_pipe_node2, set_pipe_length, set_pipe_diameter, set_pipe_roughness, set_pipe_minor_loss, set_pipe_status -from .s5_pipes import get_pipe_property_names, get_pipe_properties +from .s5_pipes import get_pipe_schema, add_pipe, get_pipe, set_pipe, delete_pipe -from .s6_pumps import add_pump, delete_pump -from .s6_pumps import set_pump_node1, set_pump_node2 -from .s6_pumps import get_pump_property_names, get_pump_properties +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 add_valve, delete_valve -from .s7_valves import set_valve_node1, set_valve_node2, set_valve_diameter, set_valve_type, set_valve_setting, set_valve_minor_loss -from .s7_valves import get_valve_property_names, get_valve_properties +from .s7_valves import get_valve_schema, add_valve, get_valve, set_valve, delete_valve # from .s24_coordinates import get_node_coord, set_node_coord \ No newline at end of file diff --git a/api/change_set.py b/api/change_set.py index 7e91608..fbcd489 100644 --- a/api/change_set.py +++ b/api/change_set.py @@ -1,12 +1,22 @@ class ChangeSet: - def __init__(self) -> None: + def __init__(self): self.operations : list[dict[str, str]] = [] - def add(self, type: str, id: str) -> None: + def add(self, type: str, id: str): self.operations.append({ 'operation': 'add', 'type': type, 'id': id }) + return self - def delete(self, type: str, id: str) -> None: + def delete(self, type: str, id: str): self.operations.append({ 'operation': 'delete', 'type': type, 'id': id }) + return self - def update(self, type: str, id: str, property: str) -> None: - self.operations.append({ 'operation': 'update', 'type': type, 'id': id, 'property': property }) \ No newline at end of file + def update(self, type: str, id: str, properties: list[str]): + self.operations.append({ 'operation': 'update', 'type': type, 'id': id, 'properties': properties }) + return self + + def append(self, other) -> None: + self.operations += other.operations + return self + + def compress(self) -> None: + return self diff --git a/api/operation.py b/api/operation.py index ef7e081..a10e413 100644 --- a/api/operation.py +++ b/api/operation.py @@ -1,111 +1,129 @@ from psycopg.rows import dict_row, Row from .connection import g_conn_dict as conn +from .utility import * +from .change_set import * -def _get_current_transaction(name: str) -> Row | None: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select * from transaction_operation") - return cur.fetchone() -def _get_current_transaction_id(name: str) -> int: - row = _get_current_transaction(name) - return int(row['id']) +API_ADD = 'add' +API_DELETE = 'delete' +API_UPDATE = 'update' -def _remove_transaction(name: str) -> None: - with conn[name].cursor() as cur: - cur.execute(f"delete from transaction_operation") def _remove_operation(name: str, id: int) -> None: - with conn[name].cursor(row_factory=dict_row) as cur: - # can not be >= to cascade delete since there is a tree ! - cur.execute(f"delete from transaction_operation where id = {id}") # this should not happen - cur.execute(f"delete from snapshot_operation where id = {id}") # this may happen - cur.execute(f"delete from operation where id = {id}") + row = read(name, f'select * from operation where parent = {id}') + if row != None: + raise Exception('Disallow to remove parent operation !') + + sql += f'delete from snapshot_operation where id = {id};' + sql += f'delete from operation where id = {id}' + write(name, sql) + def _get_parents(name: str, id: int) -> list[int]: ids = [id] - with conn[name].cursor(row_factory=dict_row) as cur: - while ids[-1] != 0: - cur.execute(f"select parent from operation where id = {ids[-1]}") - ids.append(int(cur.fetchone()['parent'])) + while ids[-1] != 0: + row = read(name, f'select parent from operation where id = {ids[-1]}') + ids.append(int(row['parent'])) return ids + def get_current_operation(name: str) -> int: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select id from current_operation") - return int(cur.fetchone()['id']) + row = read(name, f'select id from current_operation') + return int(row['id']) + def _update_current_operation(name: str, old_id: int, id: int) -> None: - with conn[name].cursor() as cur: - cur.execute(f"update current_operation set id = {id} where id = {old_id}") + return write(name, f'update current_operation set id = {id} where id = {old_id}') -def _add_redo_undo(name: str, redo: str, undo: str) -> int: - with conn[name].cursor(row_factory=dict_row) as cur: - parent = get_current_operation(name) - cur.execute(f"insert into operation (id, redo, undo, parent) values (default, '{redo}', '{undo}', {parent})") - cur.execute("select max(id) from operation") - return int(cur.fetchone()['max']) -# execute curr undo -def _query_undo(name: str, id: str) -> dict[str, str]: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select undo, parent from operation where id = {id}") - return cur.fetchone() +def _add_redo_undo(name: str, redo: str, undo: str, api_id: str, api_op: str, api_object_type: str, api_object_id: str, api_object_properties: list[str]) -> int: + parent = get_current_operation(name) + ps = [] + for p in api_object_properties: + ps.append(f'"{p}"') + if len(ps) > 0: + ps = ','.join(ps) + ps = '{' + ps + '}' + sql = f"insert into operation (id, redo, undo, parent, api_id, api_op, api_object_type, api_object_id, api_object_properties) values (default, '{redo}', '{undo}', {parent}, '{api_id}', '{api_op}', '{api_object_type}', '{api_object_id}', '{ps}')" + else: + sql = f"insert into operation (id, redo, undo, parent, api_id, api_op, api_object_type, api_object_id) values (default, '{redo}', '{undo}', {parent}, '{api_id}', '{api_op}', '{api_object_type}', '{api_object_id}')" + write(name, sql) + + return int(read(name, 'select max(id) from operation')['max']) + + +def _query_operation(name: str, id: str) -> dict[str, str]: + return read(name, f'select * from operation where id = {id}') + -# execute next redo def _query_redo_child(name: str, id: str) -> str: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select redo_child from operation where id = {id}") - return cur.fetchone()['redo_child'] + row = read(name, f'select redo_child from operation where id = {id}') + return row['redo_child'] -def _query_redo(name: str, id: str) -> dict[str, str]: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select redo from operation where id = {id}") - return cur.fetchone()['redo'] def _set_redo_child(name: str, id: int, child: int | str) -> None: - with conn[name].cursor() as cur: - cur.execute(f"update operation set redo_child = {child} where id = {id}") + return write(name, f'update operation set redo_child = {child} where id = {id}') -def _execute(name: str, sql: str) -> None: - with conn[name].cursor() as cur: - sql = sql.replace("\"", "\'") - cur.execute(sql) -def add_operation(name: str, redo: str, undo: str) -> None: - curr = _add_redo_undo(name, redo, undo) +def add_operation(name: str, redo: str, undo: str, api_id: str, api_op: str, api_object_type: str, api_object_id: str, api_object_properties: list[str] = []) -> None: + curr = _add_redo_undo(name, redo, undo, api_id, api_op, api_object_type, api_object_id, api_object_properties) old = get_current_operation(name) _update_current_operation(name, old, curr) -def execute_undo(name: str, discard: bool = False) -> None: + +def _reverser_op(op: str) -> str: + if op == API_ADD: + return API_DELETE + elif op == API_DELETE: + return API_ADD + else: + return op + + +def _get_change_set(row: dict[str, str], undo: bool) -> ChangeSet: + op = row['api_op'] + if undo: + op = _reverser_op(op) + + type = row['api_object_type'] + id = row['api_object_id'] + + change = ChangeSet() + + if op == API_ADD: + change.add(type, id) + elif op == API_DELETE: + change.delete(type, id) + elif op == API_UPDATE: + change.update(type, id, row['api_object_properties']) + + return change + + +def execute_undo(name: str, discard: bool) -> ChangeSet: curr = get_current_operation(name) - # transaction control - if have_transaction(name): - tran = _get_current_transaction(name) - if tran != None and int(tran['id']) >= 0: - if bool(tran['strict']): # strict mode disallow undo - print("Do not allow to undo in strict transaction mode!") - return - elif int(tran['id']) >= curr: # normal mode disallow undo start point, and there is foreign key constraint - print("Do not allow to undo transaction start point!") - return - - row = _query_undo(name, curr) + row = _query_operation(name, curr) undo = row['undo'] if undo == '': print("nothing to undo!") return - parent = int(row['parent']) - _set_redo_child(name, parent, 'NULL' if discard else curr) + change = _get_change_set(row, True) - _execute(name, undo) + parent = int(row['parent']) + _set_redo_child(name, parent, 'null' if discard else curr) + + write(name, undo) _update_current_operation(name, curr, parent) if discard: _remove_operation(name, curr) -def execute_redo(name: str) -> None: + return change + + +def execute_redo(name: str) -> ChangeSet: curr = get_current_operation(name) redoChild = _query_redo_child(name, curr) if redoChild == None: @@ -113,108 +131,127 @@ def execute_redo(name: str) -> None: return child = int(redoChild) - redo = _query_redo(name, child) + row = _query_operation(name, child) + redo = row['redo'] - _execute(name, redo) + change = _get_change_set(row, False) + + write(name, redo) _update_current_operation(name, curr, child) -# snapshot support to checkout between different version of database -# snapshot is persistent -# since redo always remember the recently undo path + return change + + +def _get_operation_by_tag(name: str, tag: str) -> int | None: + row = read(name, f"select id from snapshot_operation where tag = '{tag}'") + return int(row['id']) if row != None else None + def have_snapshot(name: str, tag: str) -> bool: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select id from snapshot_operation where tag = '{tag}'") - return cur.rowcount > 0 + return _get_operation_by_tag(name, tag) != None -def take_snapshot(name: str, tag: str) -> None: + +def take_snapshot(name: str, tag: str) -> int | None: if tag == None or tag == '': print('Non empty tag is expected!') - return + return None curr = get_current_operation(name) + write(name, f"insert into snapshot_operation (id, tag) values ({curr}, '{tag}')") + return curr - with conn[name].cursor() as cur: - cur.execute(f"insert into snapshot_operation (id, tag) values ({curr}, '{tag}')") -def pick_snapshot(name: str, tag: str) -> None: +def pick_snapshot(name: str, tag: str, discard: bool) -> ChangeSet: if tag == None or tag == '': print('Non empty tag is expected!') - return + return ChangeSet() - if not have_snapshot(name, tag): + target = _get_operation_by_tag(name, tag) + if target == None: print('No such snapshot!') - return + return ChangeSet() curr = get_current_operation(name) + curr_parents = _get_parents(name, curr) + target_parents = _get_parents(name, target) - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select id from snapshot_operation where tag = '{tag}'") - target = int(cur.fetchone()['id']) - if target in curr_parents: # target -> curr - for i in range(curr_parents.index(target)): - execute_undo(name) - else: - target_parents = _get_parents(name, target) - if curr in target_parents: # curr -> target - for i in range(target_parents.index(curr)): - execute_redo(name) - else: - ancestor_index = -1 - while curr_parents[ancestor_index] == target_parents[ancestor_index]: - ancestor_index -= 1 + change = ChangeSet() - # ancestor -> curr - ancestor = curr_parents[ancestor_index + 1] # ancestor_index + 1 is common parent - for i in range(curr_parents.index(ancestor)): - execute_undo(name) - # ancestor -> redo, need assign redo_child - while target_parents[ancestor_index] != target: - cur.execute(f"update operation set redo_child = '{target_parents[ancestor_index]}' where id = '{target_parents[ancestor_index + 1]}'") - execute_redo(name) - ancestor_index -= 1 - cur.execute(f"update operation set redo_child = '{target}' where id = '{target_parents[1]}'") - execute_redo(name) + if target in curr_parents: + for _ in range(curr_parents.index(target)): + change.append(execute_undo(name, discard)) -# transaction is volatile, commit/abort will destroy transaction. -# can not redo an aborted transaction. -# but can undo a committed transaction... inconsistent... -# it may remove snapshot if it is in aborted transaction + elif curr in target_parents: + target_parents.reverse() + curr_index = target_parents.index(curr) + for i in range(curr_index, len(target_parents) - 1): + write(name, f"update operation set redo_child = '{target_parents[i + 1]}' where id = '{target_parents[i]}'") + change.append(execute_redo(name)) -def have_transaction(name: str) -> bool: - with conn[name].cursor(row_factory=dict_row) as cur: - cur.execute(f"select * from transaction_operation") - return cur.rowcount > 0 + else: + ancestor_index = -1 + while curr_parents[ancestor_index] == target_parents[ancestor_index]: + ancestor_index -= 1 + ancestor = curr_parents[ancestor_index + 1] -def start_transaction(name: str, strict: bool = False) -> None: - if have_transaction(name): - print("Only support single transaction now, please commit/abort current transaction!") - return + for _ in range(curr_parents.index(ancestor)): + change.append(execute_undo(name, discard)) - curr = get_current_operation(name) + target_parents.reverse() + curr_index = target_parents.index(ancestor) + for i in range(curr_index, len(target_parents) - 1): + write(name, f"update operation set redo_child = '{target_parents[i + 1]}' where id = '{target_parents[i]}'") + change.append(execute_redo(name)) - with conn[name].cursor() as cur: - cur.execute(f"insert into transaction_operation (id, strict) values ({curr}, {strict});") + return change.compress() -def commit_transaction(name: str) -> None: - if not have_transaction(name): - print("No active transaction!") - return - _remove_transaction(name) +def get_change_set(name: str, operation: int, undo: bool) -> ChangeSet: + row = read(name, f'select api_id, api_op, api_object_type, api_object_id, api_object_properties from operation where id = {operation}') + return _get_change_set(row, undo) -def abort_transaction(name: str) -> None: - if not have_transaction(name): - print("No active transaction!") - return - tran = _get_current_transaction_id(name) +def get_current_change_set(name: str) -> ChangeSet: + return get_change_set(name, get_current_operation(name), False) - curr = get_current_operation(name) - curr_parents = _get_parents(name, curr) - for i in range(curr_parents.index(tran)): - execute_undo(name, True) - - _remove_transaction(name) +def sync_with_server(name: str, operation: int) -> ChangeSet: + fr = operation + to = get_current_operation(name) + + fr_parents = _get_parents(name, fr) + to_parents = _get_parents(name, to) + + change = ChangeSet() + + if fr in to_parents: + index = to_parents.index(fr) - 1 + while index >= 0: + change.append(get_change_set(name, to_parents[index], False)) + index -= 1 + + elif to in fr_parents: + index = 0 + while index <= fr_parents.index(to) - 1: + change.append(get_change_set(name, fr_parents[index], True)) + index += 1 + + else: + ancestor_index = -1 + while fr_parents[ancestor_index] == to_parents[ancestor_index]: + ancestor_index -= 1 + + ancestor = fr_parents[ancestor_index + 1] + + index = 0 + while index <= fr_parents.index(ancestor) - 1: + change.append(get_change_set(name, fr_parents[index], True)) + index += 1 + + index = to_parents.index(ancestor) - 1 + while index >= 0: + change.append(get_change_set(name, to_parents[index], False)) + index -= 1 + + return change.compress() diff --git a/api/s0_base.py b/api/s0_base.py index c966e66..6a01d9b 100644 --- a/api/s0_base.py +++ b/api/s0_base.py @@ -99,15 +99,15 @@ def add_node(name: str, node_type: str, id: str, x: float, y: float, table_sql: return ChangeSet() with conn[name].cursor() as cur: - sql = f"insert into _node (id, type) values ('{id}', '{node_type}'); " + sql = f"insert into _node (id, type) values ('{id}', '{node_type}');" sql += table_sql - sql += f" insert into coordinates (node, coord) values ('{id}', '({x}, {y})');" + sql += f"insert into coordinates (node, coord) values ('{id}', '({x}, {y})');" cur.execute(sql) redo = sql.replace("'", '"') - undo = f'delete from coordinates where node = "{id}"; ' + undo = f'delete from coordinates where node = "{id}";' undo += table_undo_sql - undo += f' delete from _node where id = "{id}";' + undo += f'delete from _node where id = "{id}";' add_operation(name, redo, undo) change = ChangeSet() @@ -133,9 +133,9 @@ def delete_node(name: str, node_type: str, id: str, table_sql: str, table_undo_s cur.execute(sql) redo = sql.replace("'", '"') - undo = f'insert into _node (id, type) values ("{id}", "{node_type}"); ' + undo = f'insert into _node (id, type) values ("{id}", "{node_type}");' undo += table_undo_sql - undo += f' insert into coordinates (node, coord) values ("{id}", "{coord}");' + undo += f'insert into coordinates (node, coord) values ("{id}", "{coord}");' add_operation(name, redo, undo) change = ChangeSet() @@ -148,13 +148,13 @@ def add_link(name: str, link_type: str, id: str, table_sql: str, table_undo_sql: return ChangeSet() with conn[name].cursor() as cur: - sql = f"insert into _link (id, type) values ('{id}', '{link_type}'); " + sql = f"insert into _link (id, type) values ('{id}', '{link_type}');" sql += table_sql cur.execute(sql) redo = sql.replace("'", '"') undo = table_undo_sql - undo += f' delete from _link where id = "{id}";' + undo += f'delete from _link where id = "{id}";' add_operation(name, redo, undo) change = ChangeSet() diff --git a/api/s1_title.py b/api/s1_title.py index 3d0e3a3..84ca978 100644 --- a/api/s1_title.py +++ b/api/s1_title.py @@ -11,14 +11,8 @@ def get_title(name: str) -> str: def set_title(name: str, value: str) -> ChangeSet: old = get_title(name) - with conn[name].cursor() as cur: - sql = f"update title set value = '{value}'" - cur.execute(sql) - - redo = sql.replace("'", '"') - undo = f'update title set value = "{old}"' - add_operation(name, redo, undo) - - change = ChangeSet() - change.update('title', 'null', 'value') - return change + sql = f"update title set value = '{value}'" + undo = f"update title set value = ''{old}''" + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_title', API_UPDATE, 'title', '') + return get_current_change_set(name) diff --git a/api/s24_coordinates.py b/api/s24_coordinates.py index ddb8df1..0f5ed48 100644 --- a/api/s24_coordinates.py +++ b/api/s24_coordinates.py @@ -5,10 +5,8 @@ from .operation import * from .change_set import ChangeSet -def _to_point(coord: str) -> dict[str, float]: - coord = coord.removeprefix('(') - coord = coord.removesuffix(')') - coord = coord.split(',') +def _to_client_point(coord: str) -> dict[str, float]: + coord = coord.removeprefix('(').removesuffix(')').split(',') return { 'x': float(coord[0]), 'y': float(coord[1]) } @@ -20,7 +18,7 @@ def get_node_coord(name: str, id: str) -> dict[str, float] | None: return None coord = str(row['coord']) - return _to_point(coord) + return _to_client_point(coord) def set_node_coord(name: str, node_type: str, id: str, x: float, y: float) -> ChangeSet: diff --git a/api/s2_junctions.py b/api/s2_junctions.py index fdfee6d..5a39446 100644 --- a/api/s2_junctions.py +++ b/api/s2_junctions.py @@ -4,82 +4,102 @@ from .s0_base import * from .change_set import ChangeSet from .s24_coordinates import * from .utility import * +from .schema import * + + +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'elevation' : define_property(float_type), \ + 'demand' : define_property(float_type, True), \ + 'pattern' : define_property(str_type, True), \ + 'coord' : define_property(client_point_type), \ + 'links' : define_property(str_list_type, False, True)} + + +def get_junction_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_junction(name: str, id: str) -> Row | None: + return read(name, f"select * from junctions where id = '{id}'") def add_junction(name: str, id: str, x: float, y: float, elevation: float) -> ChangeSet: - sql = f"insert into junctions (id, elevation) values ('{id}', {elevation});" - undo_sql = f'delete from junctions where id = "{id}";' - return add_node(name, JUNCTION, id, x, y, sql, undo_sql) - - -def _get_junction(name: str, id: str) -> Row | None: - return query(name, f"select elevation, demand, pattern from junctions where id = '{id}'") - - -def delete_junction(name: str, id: str) -> ChangeSet: - if not is_junction(name, id): + if is_junction(name, id): return ChangeSet() - row = _get_junction(name, id) - if row == None: - return ChangeSet() + sql = f"insert into _node (id, type) values ('{id}', '{JUNCTION}');" + sql += f"\ninsert into junctions (id, elevation) values ('{id}', {elevation});" + sql += f"\ninsert into coordinates (node, coord) values ('{id}', '({x}, {y})');" - elevation = row['elevation'] - demand = decorate(row['demand'], 'float', True) - pattern = decorate(row['pattern'], 'str', True) + undo = f"delete from coordinates where node = ''{id}'';" + undo += f"\ndelete from junctions where id = ''{id}'';" + undo += f"\ndelete from _node where id = ''{id}'';" - sql = f"delete from junctions where id = '{id}';" - undo_sql = f'insert into junctions (id, elevation, demand, pattern) values ("{id}", {elevation}, {demand}, {pattern});' - - return delete_node(name, JUNCTION, id, sql, undo_sql) + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_junction', API_ADD, JUNCTION, id) + return get_current_change_set(name) -def _set_junction(name: str, id: str, key: str, key_type: str, value: str, optional: bool = False) -> ChangeSet: - if not is_junction(name, id): - return ChangeSet() - - row = _get_junction(name, id) - if row == None: - return ChangeSet() - - return update(name, JUNCTION, 'junctions', 'id', id, key, key_type, row[key], value, optional) - - -def set_junction_elevation(name: str, id: str, elevation: float) -> ChangeSet: - return _set_junction(name, id, 'elevation', 'float', str(elevation)) - - -def set_junction_demand(name: str, id: str, demand: float) -> ChangeSet: - return _set_junction(name, id, 'demand', 'float', str(demand), True) - - -def set_junction_pattern(name: str, id: str, pattern: str) -> ChangeSet: - if not is_pattern(name, pattern): - return ChangeSet() - - return _set_junction(name, id, 'pattern', 'str', pattern, True) - - -def set_junction_coord(name: str, id: str, x: float, y: float) -> ChangeSet: - if not is_junction(name, id): - return ChangeSet() - - return set_node_coord(name, JUNCTION, id, x, y) - - -def get_junction_property_names(name: str) -> list[str]: - return ['elevation', 'demand', 'pattern', 'coord', 'links'] - - -def get_junction_properties(name: str, id: str) -> dict[str, Any] | None: - row = _get_junction(name, id) +def get_junction(name: str, id: str) -> dict[str, Any] | None: + row = _query_junction(name, id) if row == None: return None ps: dict[str, str] = {} + ps['id'] = id ps['elevation'] = float(row['elevation']) - ps['demand'] = float(row['demand']) if row != None and row['demand'] != None else None - ps['pattern'] = row['pattern'] if row != None and row['pattern'] != None else None + ps['demand'] = float(row['demand']) if row['demand'] != None else None + ps['pattern'] = row['pattern'] ps['coord'] = get_node_coord(name, id) ps['links'] = get_node_links(name, id) return ps + + +def set_junction(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_junction(name, id): + return ChangeSet() + if 'pattern' in properties: + if not is_pattern(properties['pattern']): + return ChangeSet() + + old = Serialize(get_junction(name, id), schema).to_storage() + + new = get_junction(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update junctions set elevation = {new['elevation']}, demand = {new['demand']}, pattern = {new['pattern']} where id = '{id}';" + undo = "" + if 'coord' in ps: + sql += f"\nupdate coordinates set coord = {new['coord']} where node = '{id}';" + undo = f"update coordinates set coord = {old['coord']} where node = ''{id}'';" + undo += f"\nupdate junctions set elevation = {old['elevation']}, demand = {old['demand']}, pattern = {old['pattern']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_junction', API_UPDATE, JUNCTION, id, ps) + return get_current_change_set(name) + + +def delete_junction(name: str, id: str) -> ChangeSet: + row = get_junction(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_junction(name, id), schema).to_storage() + + sql = f"delete from coordinates where node = '{id}';" + sql += f"\ndelete from junctions where id = '{id}';" + sql += f"\ndelete from _node where id = '{id}';" + + undo = f"insert into _node (id, type) values (''{id}'', ''{JUNCTION}'');" + undo += f"\ninsert into junctions (id, elevation, demand, pattern) values (''{id}'', {old['elevation']}, {old['demand']}, {old['pattern']});" + undo += f"\ninsert into coordinates (node, coord) values (''{id}'', {old['coord']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_junction', API_DELETE, JUNCTION, id) + return get_current_change_set(name) diff --git a/api/s3_reservoirs.py b/api/s3_reservoirs.py index a8c5430..56bc67a 100644 --- a/api/s3_reservoirs.py +++ b/api/s3_reservoirs.py @@ -4,76 +4,100 @@ from .s0_base import * from .change_set import ChangeSet from .s24_coordinates import * from .utility import * +from .schema import * + + +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'head' : define_property(float_type), \ + 'pattern' : define_property(str_type, True), \ + 'coord' : define_property(client_point_type), \ + 'links' : define_property(str_list_type, False, True)} + + +def get_reservoir_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_reservoir(name: str, id: str) -> Row | None: + return read(name, f"select * from reservoirs where id = '{id}'") def add_reservoir(name: str, id: str, x: float, y: float, head: float) -> ChangeSet: - sql = f"insert into reservoirs (id, head) values ('{id}', {head});" - undo_sql = f'delete from reservoirs where id = "{id}";' - return add_node(name, RESERVOIR, id, x, y, sql, undo_sql) - - -def _get_reservoir(name: str, id: str) -> Row | None: - return query(name, f"select head, pattern from reservoirs where id = '{id}'") - - -def delete_reservoir(name: str, id: str) -> ChangeSet: - if not is_reservoir(name, id): + if is_reservoir(name, id): return ChangeSet() - row = _get_reservoir(name, id) - if row == None: - return ChangeSet() + sql = f"insert into _node (id, type) values ('{id}', '{RESERVOIR}');" + sql += f"\ninsert into reservoirs (id, head) values ('{id}', {head});" + sql += f"\ninsert into coordinates (node, coord) values ('{id}', '({x}, {y})');" - head = row['head'] - pattern = decorate(row['pattern'], 'str', True) + undo = f"delete from coordinates where node = ''{id}'';" + undo += f"\ndelete from reservoirs where id = ''{id}'';" + undo += f"\ndelete from _node where id = ''{id}'';" - sql = f"delete from reservoirs where id = '{id}';" - undo_sql = f'insert into reservoirs (id, head, pattern) values ("{id}", {head}, {pattern});' - - return delete_node(name, RESERVOIR, id, sql, undo_sql) + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_reservoir', API_ADD, RESERVOIR, id) + return get_current_change_set(name) -def _set_reservoir(name: str, id: str, key: str, key_type: str, value: str, optional: bool = False) -> ChangeSet: - if not is_reservoir(name, id): - return ChangeSet() - - row = _get_reservoir(name, id) - if row == None: - return ChangeSet() - - return update(name, RESERVOIR, 'reservoirs', 'id', id, key, key_type, row[key], value, optional) - - -def set_reservoir_head(name: str, id: str, head: float) -> ChangeSet: - return _set_reservoir(name, id, 'head', 'float', str(head)) - - -def set_reservoir_pattern(name: str, id: str, pattern: str) -> ChangeSet: - if not is_pattern(name, id): - return ChangeSet() - - return _set_reservoir(name, id, 'pattern', 'str', pattern, True) - - -def set_reservoir_coord(name: str, id: str, x: float, y: float) -> ChangeSet: - if not is_reservoir(name, id): - return ChangeSet() - - return set_node_coord(name, RESERVOIR, id, x, y) - - -def get_reservoir_property_names(name: str) -> list[str]: - return ['head', 'pattern', 'coord', 'links'] - - -def get_reservoir_properties(name: str, id: str) -> dict[str, Any] | None: - row = _get_reservoir(name, id) +def get_reservoir(name: str, id: str) -> dict[str, Any] | None: + row = _query_reservoir(name, id) if row == None: return None ps: dict[str, str] = {} - ps['head'] = float(row['head']) if row != None else None - ps['pattern'] = row['pattern'] if row != None and row['pattern'] != None else None + ps['id'] = id + ps['head'] = float(row['head']) + ps['pattern'] = row['pattern'] ps['coord'] = get_node_coord(name, id) ps['links'] = get_node_links(name, id) return ps + + +def set_reservoir(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_reservoir(name, id): + return ChangeSet() + if 'pattern' in properties: + if not is_pattern(properties['pattern']): + return ChangeSet() + + old = Serialize(get_reservoir(name, id), schema).to_storage() + + new = get_reservoir(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update reservoirs set head = {new['head']}, pattern = {new['pattern']} where id = '{id}';" + undo = "" + if 'coord' in ps: + sql += f"\nupdate coordinates set coord = {new['coord']} where node = '{id}';" + undo = f"update coordinates set coord = {old['coord']} where node = ''{id}'';" + undo += f"\nupdate reservoirs set head = {old['head']}, pattern = {old['pattern']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_reservoir', API_UPDATE, RESERVOIR, id, ps) + return get_current_change_set(name) + + +def delete_reservoir(name: str, id: str) -> ChangeSet: + row = get_reservoir(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_reservoir(name, id), schema).to_storage() + + sql = f"delete from coordinates where node = '{id}';" + sql += f"\ndelete from reservoirs where id = '{id}';" + sql += f"\ndelete from _node where id = '{id}';" + + undo = f"insert into _node (id, type) values (''{id}'', ''{RESERVOIR}'');" + undo += f"\ninsert into reservoirs (id, head, pattern) values (''{id}'', {old['head']}, {old['pattern']});" + undo += f"\ninsert into coordinates (node, coord) values (''{id}'', {old['coord']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_reservoir', API_DELETE, RESERVOIR, id) + return get_current_change_set(name) diff --git a/api/s4_tanks.py b/api/s4_tanks.py index ae3d37d..d9c1b07 100644 --- a/api/s4_tanks.py +++ b/api/s4_tanks.py @@ -4,119 +4,124 @@ from .s0_base import * from .change_set import ChangeSet from .s24_coordinates import * from .utility import * +from .schema import * OVERFLOW_YES = 'yes' OVERFLOW_NO = 'no' +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'elevation' : define_property(float_type), \ + 'init_level': define_property(float_type), \ + 'min_level' : define_property(float_type), \ + 'max_level' : define_property(float_type), \ + 'diameter' : define_property(float_type), \ + 'min_vol' : define_property(float_type), \ + 'vol_curve' : define_property(str_type, True), \ + 'overflow' : define_property(str_type, True), \ + 'coord' : define_property(client_point_type), \ + 'links' : define_property(str_list_type, False, True)} + + +def get_tank_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_tank(name: str, id: str) -> Row | None: + return read(name, f"select * from tanks where id = '{id}'") + + def add_tank(name: str, id: str, x: float, y: float, elevation: float, init_level: float = 0, min_level: float = 0, max_level: float = 0, diameter: float = 0, min_vol: float = 0) -> ChangeSet: - sql = f"insert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol) values ('{id}', {elevation}, {init_level}, {min_level}, {max_level}, {diameter}, {min_vol});" - undo_sql = f'delete from tanks where id = "{id}";' - return add_node(name, TANK, id, x, y, sql, undo_sql) - - -def _get_tank(name: str, id: str) -> Row | None: - return query(name, f"select elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow from tanks where id = '{id}'") - - -def delete_tank(name: str, id: str) -> ChangeSet: - if not is_tank(name, id): + if is_tank(name, id): return ChangeSet() - row = _get_tank(name, id) - if row == None: - return ChangeSet() + sql = f"insert into _node (id, type) values ('{id}', '{TANK}');" + sql += f"\ninsert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol) values ('{id}', {elevation}, {init_level}, {min_level}, {max_level}, {diameter}, {min_vol});" + sql += f"\ninsert into coordinates (node, coord) values ('{id}', '({x}, {y})');" - elevation = row['elevation'] - init_level = row['init_level'] - min_level = row['min_level'] - max_level = row['max_level'] - diameter = row['diameter'] - min_vol = row['min_vol'] - vol_curve = decorate(row['vol_curve'], 'str', True) - overflow = decorate(row['overflow'], 'str', True) + undo = f"delete from coordinates where node = ''{id}'';" + undo += f"\ndelete from tanks where id = ''{id}'';" + undo += f"\ndelete from _node where id = ''{id}'';" - sql = f"delete from tanks where id = '{id}';" - undo_sql = f'insert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) values ("{id}", {elevation}, {init_level}, {min_level}, {max_level}, {diameter}, {min_vol}, {vol_curve}, {overflow});' - - return delete_node(name, TANK, id, sql, undo_sql) + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_tank', API_ADD, TANK, id) + return get_current_change_set(name) -def _set_tank(name: str, id: str, key: str, key_type: str, value: str, optional: bool = False) -> ChangeSet: - if not is_tank(name, id): - return ChangeSet() - - row = _get_tank(name, id) - if row == None: - return ChangeSet() - - return update(name, TANK, 'tanks', 'id', id, key, key_type, row[key], value, optional) - - -def set_tank_elevation(name: str, id: str, elevation: float) -> ChangeSet: - return _set_tank(name, id, 'elevation', 'float', elevation) - - -def set_tank_init_level(name: str, id: str, init_level: float) -> ChangeSet: - return _set_tank(name, id, 'init_level', 'float', init_level) - - -def set_tank_min_level(name: str, id: str, min_level: float) -> ChangeSet: - return _set_tank(name, id, 'min_level', 'float', min_level) - - -def set_tank_max_level(name: str, id: str, max_level: float) -> ChangeSet: - return _set_tank(name, id, 'max_level', 'float', max_level) - - -def set_tank_diameter(name: str, id: str, diameter: float) -> ChangeSet: - return _set_tank(name, id, 'diameter', 'float', diameter) - - -def set_tank_min_vol(name: str, id: str, min_vol: float) -> ChangeSet: - return _set_tank(name, id, 'min_vol', 'float', min_vol) - - -def set_tank_vol_curve(name: str, id: str, vol_curve: str) -> ChangeSet: - if not is_curve(name, vol_curve): - return ChangeSet() - - return _set_tank(name, id, 'vol_curve', 'str', vol_curve, True) - - -def set_tank_overflow(name: str, id: str, overflow: str) -> ChangeSet: - if overflow != OVERFLOW_YES and overflow != OVERFLOW_NO: - return ChangeSet() - - return _set_tank(name, id, 'overflow', 'str', overflow) - - -def set_tank_coord(name: str, id: str, x: float, y: float) -> ChangeSet: - if not is_tank(name, id): - return ChangeSet() - - return set_node_coord(name, TANK, id, x, y) - - -def get_tank_property_names(name: str) -> list[str]: - return ['elevation', 'init_level', 'min_level', 'max_level', 'diameter', 'min_vol', 'vol_curve', 'overflow', 'coord', 'links'] - - -def get_tank_properties(name: str, id: str) -> dict[str, Any] | None: - row = _get_tank(name, id) +def get_tank(name: str, id: str) -> dict[str, Any] | None: + row = _query_tank(name, id) if row == None: return None ps: dict[str, str] = {} - ps['elevation'] = float(row['elevation']) if row != None else None - ps['init_level'] = float(row['init_level']) if row != None else None - ps['min_level'] = float(row['min_level']) if row != None else None - ps['max_level'] = float(row['max_level']) if row != None else None - ps['diameter'] = float(row['diameter']) if row != None else None - ps['min_vol'] = float(row['min_vol']) if row != None else None - ps['vol_curve'] = row['vol_curve'] if row != None and row['vol_curve'] != None else None - ps['overflow'] = row['overflow'] if row != None and row['overflow'] != None else None + ps['id'] = id + ps['elevation'] = float(row['elevation']) + ps['init_level'] = float(row['init_level']) + ps['min_level'] = float(row['min_level']) + ps['max_level'] = float(row['max_level']) + ps['diameter'] = float(row['diameter']) + ps['min_vol'] = float(row['min_vol']) + ps['vol_curve'] = row['vol_curve'] + ps['overflow'] = row['overflow'] ps['coord'] = get_node_coord(name, id) ps['links'] = get_node_links(name, id) return ps + + +def set_tank(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_tank(name, id): + return ChangeSet() + if 'vol_curve' in properties: + if not is_curve(properties['vol_curve']): + return ChangeSet() + if 'overflow' in properties: + if properties['overflow'] != OVERFLOW_YES and properties['overflow'] != OVERFLOW_NO: + return ChangeSet() + + old = Serialize(get_tank(name, id), schema).to_storage() + + new = get_tank(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update tanks set elevation = {new['elevation']}, \ + init_level = {new['init_level']}, min_level = {new['min_level']}, max_level = {new['max_level']}, \ + diameter = {new['diameter']}, min_vol = {new['min_vol']}, vol_curve = {new['vol_curve']}, overflow = {new['overflow']} where id = '{id}';" + undo = "" + if 'coord' in ps: + sql += f"\nupdate coordinates set coord = {new['coord']} where node = '{id}';" + undo = f"update coordinates set coord = {old['coord']} where node = ''{id}'';" + undo += f"\nupdate tanks set elevation = {old['elevation']}, \ + init_level = {old['init_level']}, min_level = {old['min_level']}, max_level = {old['max_level']}, \ + diameter = {old['diameter']}, min_vol = {old['min_vol']}, vol_curve = {old['vol_curve']}, overflow = {old['overflow']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_tank', API_UPDATE, TANK, id, ps) + return get_current_change_set(name) + + +def delete_tank(name: str, id: str) -> ChangeSet: + row = get_tank(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_tank(name, id), schema).to_storage() + + sql = f"delete from coordinates where node = '{id}';" + sql += f"\ndelete from tanks where id = '{id}';" + sql += f"\ndelete from _node where id = '{id}';" + + undo = f"insert into _node (id, type) values (''{id}'', ''{TANK}'');" + undo += f"\ninsert into tanks (id, elevation, init_level, min_level, max_level, diameter, min_vol, vol_curve, overflow) \ + values (''{id}'', {old['elevation']}, {old['init_level']}, {old['min_level']}, {old['max_level']}, {old['diameter']}, {old['min_vol']}, {old['vol_curve']}, {old['overflow']});" + undo += f"\ninsert into coordinates (node, coord) values (''{id}'', {old['coord']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_tank', API_DELETE, TANK, id) + return get_current_change_set(name) diff --git a/api/s5_pipes.py b/api/s5_pipes.py index 07ccc6c..fc2f587 100644 --- a/api/s5_pipes.py +++ b/api/s5_pipes.py @@ -2,7 +2,9 @@ from typing import Any from psycopg.rows import Row from .s0_base import * from .change_set import ChangeSet +from .s24_coordinates import * from .utility import * +from .schema import * PIPE_STATUS_OPEN = 'open' @@ -10,119 +12,125 @@ PIPE_STATUS_CLOSED = 'closed' PIPE_STATUS_CV = 'cv' +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'node1' : define_property(str_type), \ + 'node2' : define_property(str_type), \ + 'length' : define_property(float_type), \ + 'diameter' : define_property(float_type), \ + 'roughness' : define_property(float_type), \ + 'minor_loss': define_property(float_type), \ + 'status' : define_property(str_type)} + + +def get_pipe_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_pipe(name: str, id: str) -> Row | None: + return read(name, f"select * from pipes where id = '{id}'") + + +def _get_pipe_node1(name: str, id: str) -> str | None: + row = _query_pipe(name, id) + return row['node1'] if row != None else None + + +def _get_pipe_node2(name: str, id: str) -> str | None: + row = _query_pipe(name, id) + return row['node2'] if row != None else None + + def add_pipe(name: str, id: str, node1: str, node2: str, length: float = 0, diameter: float = 0, roughness: float = 0, minor_loss: float = 0, status: str = PIPE_STATUS_OPEN) -> ChangeSet: + if is_pipe(name, id): + return ChangeSet() if not is_node(name, node1): return ChangeSet() if not is_node(name, node2): return ChangeSet() if node1 == node2: return ChangeSet() - if status != PIPE_STATUS_OPEN and status != PIPE_STATUS_CLOSED and status != PIPE_STATUS_CV: return ChangeSet() - sql = f"insert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ('{id}', '{node1}', '{node2}', {length}, {diameter}, {roughness}, {minor_loss}, '{status}');" - undo_sql = f'delete from pipes where id = "{id}";' - return add_link(name, PIPE, id, sql, undo_sql) + sql = f"insert into _link (id, type) values ('{id}', '{PIPE}');" + sql += f"\ninsert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ('{id}', '{node1}', '{node2}', {length}, {diameter}, {roughness}, {minor_loss}, '{status}');" + + undo = f"delete from pipes where id = ''{id}'';" + undo += f"\ndelete from _link where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_pipe', API_ADD, PIPE, id) + return get_current_change_set(name) -def _get_pipe(name: str, id: str) -> Row | None: - return query(name, f"select node1, node2, length, diameter, roughness, minor_loss, status from pipes where id = '{id}'") - - -def delete_pipe(name: str, id: str) -> ChangeSet: - if not is_pipe(name, id): - return ChangeSet() - - row = _get_pipe(name, id) - if row == None: - return ChangeSet() - - node1, node2, length, diameter, roughness, minor_loss, status = row['node1'], row['node2'], row['length'], row['diameter'], row['roughness'], row['minor_loss'], row['status'] - - sql = f"delete from pipes where id = '{id}';" - undo_sql = f'insert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) values ("{id}", "{node1}", "{node2}", {length}, {diameter}, {roughness}, {minor_loss}, "{status}");' - - return delete_link(name, PIPE, id, sql, undo_sql) - - -def _get_pipe_node1(name: str, id: str) -> str | None: - row = _get_pipe(name, id) - return row['node1'] if row != None else None - - -def _get_pipe_node2(name: str, id: str) -> str | None: - row = _get_pipe(name, id) - return row['node2'] if row != None else None - - -def _set_pipe(name: str, id: str, key: str, key_type: str, value: str, optional: bool = False) -> ChangeSet: - if not is_pipe(name, id): - return ChangeSet() - - row = _get_pipe(name, id) - if row == None: - return ChangeSet() - - return update(name, PIPE, 'pipes', 'id', id, key, key_type, row[key], value, optional) - - -def set_pipe_node1(name: str, id: str, node1: str) -> ChangeSet: - if not is_node(name, node1): - return ChangeSet() - if _get_pipe_node2(name, id) == node1: - return ChangeSet() - - return _set_pipe(name, id, 'node1', 'str', str(node1)) - - -def set_pipe_node2(name: str, id: str, node2: str) -> ChangeSet: - if not is_node(name, node2): - return ChangeSet() - if _get_pipe_node1(name, id) == node2: - return ChangeSet() - - return _set_pipe(name, id, 'node2', 'str', str(node2)) - - -def set_pipe_length(name: str, id: str, length: float) -> ChangeSet: - return _set_pipe(name, id, 'length', 'float', str(length)) - - -def set_pipe_diameter(name: str, id: str, diameter: float) -> ChangeSet: - return _set_pipe(name, id, 'diameter', 'float', str(diameter)) - - -def set_pipe_roughness(name: str, id: str, roughness: float) -> ChangeSet: - return _set_pipe(name, id, 'roughness', 'float', str(roughness)) - - -def set_pipe_minor_loss(name: str, id: str, minor_loss: float) -> ChangeSet: - return _set_pipe(name, id, 'minor_loss', 'float', str(minor_loss)) - - -def set_pipe_status(name: str, id: str, status: float) -> ChangeSet: - if status != PIPE_STATUS_OPEN and status != PIPE_STATUS_CLOSED and status != PIPE_STATUS_CV: - return ChangeSet() - - return _set_pipe(name, id, 'status', 'str', str(status)) - - -def get_pipe_property_names(name: str) -> list[str]: - return ['node1', 'node2', 'length', 'diameter', 'roughness', 'minor_loss', 'status'] - - -def get_pipe_properties(name: str, id: str) -> dict[str, Any] | None: - row = _get_pipe(name, id) +def get_pipe(name: str, id: str) -> dict[str, Any] | None: + row = _query_pipe(name, id) if row == None: return None ps: dict[str, str] = {} - ps['node1'] = row['node1'] if row != None else None - ps['node2'] = row['node2'] if row != None else None - ps['length'] = float(row['length']) if row != None else None - ps['diameter'] = float(row['diameter']) if row != None else None - ps['roughness'] = float(row['roughness']) if row != None else None - ps['minor_loss'] = float(row['minor_loss']) if row != None else None - ps['status'] = row['status'] if row != None else None - return ps \ No newline at end of file + ps['id'] = id + ps['node1'] = row['node1'] + ps['node2'] = row['node2'] + ps['length'] = float(row['length']) + ps['diameter'] = float(row['diameter']) + ps['roughness'] = float(row['roughness']) + ps['minor_loss'] = float(row['minor_loss']) + ps['status'] = row['status'] + return ps + + +def set_pipe(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_pipe(name, id): + return ChangeSet() + if 'node1' in properties: + if not is_node(name, properties['node1']) or _get_pipe_node2(name, id) == properties['node1']: + return ChangeSet() + if 'node2' in properties: + if not is_node(name, properties['node2']) or _get_pipe_node1(name, id) == properties['node2']: + return ChangeSet() + if 'node1' in properties and 'node2' in properties: + if properties['node1'] == properties['node2']: + return ChangeSet() + if 'status' in properties: + if properties['status'] != PIPE_STATUS_OPEN and properties['status'] != PIPE_STATUS_CLOSED and properties['status'] != PIPE_STATUS_CV: + return ChangeSet() + + old = Serialize(get_pipe(name, id), schema).to_storage() + + new = get_pipe(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update pipes set node1 = {new['node1']}, node2 = {new['node2']}, \ + length = {new['length']}, diameter = {new['diameter']}, roughness = {new['roughness']}, minor_loss = {new['minor_loss']}, status = {new['status']} where id = '{id}';" + undo = f"update pipes set node1 = {old['node1']}, node2 = {old['node2']}, \ + length = {old['length']}, diameter = {old['diameter']}, roughness = {old['roughness']}, minor_loss = {old['minor_loss']}, status = {old['status']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_pipe', API_UPDATE, PIPE, id, ps) + return get_current_change_set(name) + + +def delete_pipe(name: str, id: str) -> ChangeSet: + row = get_pipe(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_pipe(name, id), schema).to_storage() + + sql = f"delete from pipes where id = '{id}';" + sql += f"\ndelete from _link where id = '{id}';" + + undo = f"insert into _link (id, type) values (''{id}'', ''{PIPE}'');" + undo += f"\ninsert into pipes (id, node1, node2, length, diameter, roughness, minor_loss, status) \ + values (''{id}'', {old['node1']}, {old['node2']}, {old['length']}, {old['diameter']}, {old['roughness']}, {old['minor_loss']}, {old['status']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_pipe', API_DELETE, PIPE, id) + return get_current_change_set(name) diff --git a/api/s6_pumps.py b/api/s6_pumps.py index 1d1fdfa..f4cde9a 100644 --- a/api/s6_pumps.py +++ b/api/s6_pumps.py @@ -2,10 +2,38 @@ from typing import Any from psycopg.rows import Row from .s0_base import * from .change_set import ChangeSet +from .s24_coordinates import * from .utility import * +from .schema import * + + +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'node1' : define_property(str_type), \ + 'node2' : define_property(str_type)} + + +def get_pump_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_pump(name: str, id: str) -> Row | None: + return read(name, f"select * from pumps where id = '{id}'") + + +def _get_pump_node1(name: str, id: str) -> str | None: + row = _query_pump(name, id) + return row['node1'] if row != None else None + + +def _get_pump_node2(name: str, id: str) -> str | None: + row = _query_pump(name, id) + return row['node2'] if row != None else None def add_pump(name: str, id: str, node1: str, node2: str) -> ChangeSet: + if is_pump(name, id): + return ChangeSet() if not is_node(name, node1): return ChangeSet() if not is_node(name, node2): @@ -13,86 +41,73 @@ def add_pump(name: str, id: str, node1: str, node2: str) -> ChangeSet: if node1 == node2: return ChangeSet() - sql = f"insert into pumps (id, node1, node2) values ('{id}', '{node1}', '{node2}');" - undo_sql = f'delete from pumps where id = "{id}";' - return add_link(name, PUMP, id, sql, undo_sql) + sql = f"insert into _link (id, type) values ('{id}', '{PUMP}');" + sql += f"\ninsert into pumps (id, node1, node2) values ('{id}', '{node1}', '{node2}');" + + undo = f"delete from pumps where id = ''{id}'';" + undo += f"\ndelete from _link where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_pump', API_ADD, PUMP, id) + return get_current_change_set(name) -def _get_pump(name: str, id: str) -> Row | None: - return query(name, f"select node1, node2 from pumps where id = '{id}'") - - -def delete_pump(name: str, id: str) -> ChangeSet: - if not is_pump(name, id): - return ChangeSet() - - row = _get_pump(name, id) - if row == None: - return ChangeSet() - - node1, node2 = row['node1'], row['node2'] - - sql = f"delete from pumps where id = '{id}';" - undo_sql = f'insert into pumps (id, node1, node2) values ("{id}", "{node1}", "{node2}");' - - return delete_link(name, PUMP, id, sql, undo_sql) - - -def _get_pump_node1(name: str, id: str) -> str | None: - row = _get_pump(name, id) - return row['node1'] if row != None else None - - -def _get_pump_node2(name: str, id: str) -> str | None: - row = _get_pump(name, id) - return row['node2'] if row != None else None - - -def _set_pump(name: str, id: str, key: str, key_type: str, value: str, optional: bool = False) -> ChangeSet: - if not is_pump(name, id): - return ChangeSet() - - row = _get_pump(name, id) - if row == None: - return ChangeSet() - - return update(name, PUMP, 'pumps', 'id', id, key, key_type, row[key], value, optional) - - -def set_pump_node1(name: str, id: str, node1: str) -> ChangeSet: - if not is_node(name, node1): - return ChangeSet() - if _get_pump_node2(name, id) == node1: - return ChangeSet() - - return _set_pump(name, id, 'node1', 'str', str(node1)) - - -def set_pump_node2(name: str, id: str, node2: str) -> ChangeSet: - if not is_node(name, node2): - return ChangeSet() - if _get_pump_node1(name, id) == node2: - return ChangeSet() - - return _set_pump(name, id, 'node2', 'str', str(node2)) - - -def set_pump_power(name: str, id: str) -> ChangeSet: pass -def set_pump_speed(name: str, id: str) -> ChangeSet: pass -def set_pump_head(name: str, id: str) -> ChangeSet: pass -def set_pump_pattern(name: str, id: str) -> ChangeSet: pass - - -def get_pump_property_names(name: str) -> list[str]: - return ['node1', 'node2'] - - -def get_pump_properties(name: str, id: str) -> dict[str, Any] | None: - row = _get_pump(name, id) +def get_pump(name: str, id: str) -> dict[str, Any] | None: + row = _query_pump(name, id) if row == None: return None ps: dict[str, str] = {} - ps['node1'] = row['node1'] if row != None else None - ps['node2'] = row['node2'] if row != None else None + ps['id'] = id + ps['node1'] = row['node1'] + ps['node2'] = row['node2'] return ps + + +def set_pump(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_pump(name, id): + return ChangeSet() + if 'node1' in properties: + if not is_node(name, properties['node1']) or _get_pump_node2(name, id) == properties['node1']: + return ChangeSet() + if 'node2' in properties: + if not is_node(name, properties['node2']) or _get_pump_node1(name, id) == properties['node2']: + return ChangeSet() + if 'node1' in properties and 'node2' in properties: + if properties['node1'] == properties['node2']: + return ChangeSet() + + old = Serialize(get_pump(name, id), schema).to_storage() + + new = get_pump(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update pumps set node1 = {new['node1']}, node2 = {new['node2']} where id = '{id}';" + undo = f"update pumps set node1 = {old['node1']}, node2 = {old['node2']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_pump', API_UPDATE, PUMP, id, ps) + return get_current_change_set(name) + + +def delete_pump(name: str, id: str) -> ChangeSet: + row = get_pump(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_pump(name, id), schema).to_storage() + + sql = f"delete from pumps where id = '{id}';" + sql += f"\ndelete from _link where id = '{id}';" + + undo = f"insert into _link (id, type) values (''{id}'', ''{PUMP}'');" + undo += f"\ninsert into pumps (id, node1, node2) values (''{id}'', {old['node1']}, {old['node2']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_pump', API_DELETE, PUMP, id) + return get_current_change_set(name) diff --git a/api/s7_valves.py b/api/s7_valves.py index 64dd5c1..8007a21 100644 --- a/api/s7_valves.py +++ b/api/s7_valves.py @@ -2,7 +2,9 @@ from typing import Any from psycopg.rows import Row from .s0_base import * from .change_set import ChangeSet +from .s24_coordinates import * from .utility import * +from .schema import * VALVES_TYPE_PRV = 'prv' @@ -13,6 +15,129 @@ VALVES_TYPE_TCV = 'tcv' VALVES_TYPE_GPV = 'gpv' +schema: dict[str, dict[str, Any]] = { \ + 'id' : define_property(str_type, False, True), \ + 'node1' : define_property(str_type), \ + 'node2' : define_property(str_type), \ + 'diameter' : define_property(float_type), \ + 'type' : define_property(str_type), \ + 'setting' : define_property(float_type), \ + 'minor_loss': define_property(float_type)} + + +def get_valve_schema(name: str) -> dict[str, dict[str, Any]]: + return schema + + +def _query_valve(name: str, id: str) -> Row | None: + return read(name, f"select * from valves where id = '{id}'") + + +def _get_valve_node1(name: str, id: str) -> str | None: + row = _query_valve(name, id) + return row['node1'] if row != None else None + + +def _get_valve_node2(name: str, id: str) -> str | None: + row = _query_valve(name, id) + return row['node2'] if row != None else None + + +def add_valve(name: str, id: str, node1: str, node2: str, diameter: float = 0, type: str = VALVES_TYPE_PRV, setting: float = 0, minor_loss: float = 0) -> ChangeSet: + if is_valve(name, id): + return ChangeSet() + if not is_node(name, node1): + return ChangeSet() + if not is_node(name, node2): + return ChangeSet() + if node1 == node2: + return ChangeSet() + if type != VALVES_TYPE_PRV and type != VALVES_TYPE_PSV and type != VALVES_TYPE_PBV and type != VALVES_TYPE_FCV and type != VALVES_TYPE_TCV and type != VALVES_TYPE_GPV: + return ChangeSet() + + sql = f"insert into _link (id, type) values ('{id}', '{VALVE}');" + sql += f"\ninsert into valves (id, node1, node2, diameter, type, setting, minor_loss) values ('{id}', '{node1}', '{node2}', {diameter}, '{type}', {setting}, {minor_loss});" + + undo = f"delete from valves where id = ''{id}'';" + undo += f"\ndelete from _link where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'add_valve', API_ADD, VALVE, id) + return get_current_change_set(name) + + +def get_valve(name: str, id: str) -> dict[str, Any] | None: + row = _query_valve(name, id) + if row == None: + return None + + ps: dict[str, str] = {} + ps['id'] = id + ps['node1'] = row['node1'] + ps['node2'] = row['node2'] + ps['diameter'] = float(row['diameter']) + ps['type'] = row['type'] + ps['setting'] = float(row['setting']) + ps['minor_loss'] = float(row['minor_loss']) + return ps + + +def set_valve(name: str, id: str, properties: dict[str, Any]) -> ChangeSet: + if not is_valve(name, id): + return ChangeSet() + if 'node1' in properties: + if not is_node(name, properties['node1']) or _get_valve_node2(name, id) == properties['node1']: + return ChangeSet() + if 'node2' in properties: + if not is_node(name, properties['node2']) or _get_valve_node1(name, id) == properties['node2']: + return ChangeSet() + if 'node1' in properties and 'node2' in properties: + if properties['node1'] == properties['node2']: + return ChangeSet() + if 'type' in properties: + t = properties['type'] + if t != VALVES_TYPE_PRV and t != VALVES_TYPE_PSV and t != VALVES_TYPE_PBV and t != VALVES_TYPE_FCV and t != VALVES_TYPE_TCV and t != VALVES_TYPE_GPV: + return ChangeSet() + + old = Serialize(get_valve(name, id), schema).to_storage() + + new = get_valve(name, id) + ps: list[str] = [] + for key in properties: + if key in schema and schema[key]['readonly'] == False: + new[key] = properties[key] + ps.append(key) + new = Serialize(new, schema).to_execution() + + sql = f"update valves set node1 = {new['node1']}, node2 = {new['node2']}, \ + diameter = {new['diameter']}, type = {new['type']}, setting = {new['setting']}, minor_loss = {new['minor_loss']} where id = '{id}';" + undo = f"update valves set node1 = {old['node1']}, node2 = {old['node2']}, \ + diameter = {old['diameter']}, type = {old['type']}, setting = {old['setting']}, minor_loss = {old['minor_loss']} where id = ''{id}'';" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'set_valve', API_UPDATE, VALVE, id, ps) + return get_current_change_set(name) + + +def delete_valve(name: str, id: str) -> ChangeSet: + row = get_valve(name, id) + if row == None: + return ChangeSet() + + old = Serialize(get_valve(name, id), schema).to_storage() + + sql = f"delete from valves where id = '{id}';" + sql += f"\ndelete from _link where id = '{id}';" + + undo = f"insert into _link (id, type) values (''{id}'', ''{VALVE}'');" + undo += f"\ninsert into valves (id, node1, node2, diameter, type, setting, minor_loss) \ + values (''{id}'', {old['node1']}, {old['node2']}, {old['diameter']}, {old['type']}, {old['setting']}, {old['minor_loss']});" + + write(name, sql) + add_operation(name, sql.replace("'", "''"), undo, 'delete_valve', API_DELETE, VALVE, id) + return get_current_change_set(name) + +''' def add_valve(name: str, id: str, node1: str, node2: str, diameter: float = 0, type: str = VALVES_TYPE_PRV, setting: float = 0, minor_loss: float = 0) -> ChangeSet: if not is_node(name, node1): return ChangeSet() @@ -123,3 +248,4 @@ def get_valve_properties(name: str, id: str) -> dict[str, Any] | None: ps['setting'] = float(row['setting']) if row != None else None ps['minor_loss'] = float(row['minor_loss']) if row != None else None return ps +''' \ No newline at end of file diff --git a/api/schema.py b/api/schema.py new file mode 100644 index 0000000..4388775 --- /dev/null +++ b/api/schema.py @@ -0,0 +1,74 @@ +from typing import Any + + +float_type = type(0.0).__name__ +str_type = type('').__name__ +server_point_type = type((0.0,0.0)).__name__ +client_point_type = type({'x': 0.0, 'y': 0.0}).__name__ +str_list_type = type(['']).__name__ + + +def define_property(type: str, optional: bool = False, readonly: bool = False) -> dict[str, Any]: + return { 'type': type, 'optional': optional, 'readonly': readonly } + + +class Serialize(object): + def __init__(self, row, schema) -> None: + self.row = row + self.schema = schema + + def to_execution(self, name) -> str: + value = self.row[name] + type = self.schema[name]['type'] + + if value == None: + return 'null' + + if type == float_type: + return value + + if type == str_type: + return f"'{value}'" + + raise Exception(f"Fail to serialize {name} for execution!") + + def to_storage(self, name) -> str: + value = self.row[name] + type = self.schema[name]['type'] + + if value == None: + return 'null' + + if type == float_type: + return value + + if type == str_type: + return f"''{value}''" + + raise Exception(f"Fail to serialize {name} for storage!") + + def to_execution(self): + row = self.row.copy() + + for key, value in row.items(): + if value == None: + row[key] = 'null' + elif self.schema[key]['type'] == str_type: + row[key] = f"'{row[key]}'" + elif self.schema[key]['type'] == client_point_type: + row[key] = f"'({value['x']},{value['y']})'" + + return row + + def to_storage(self): + row = self.row.copy() + + for key, value in row.items(): + if value == None: + row[key] = 'null' + elif self.schema[key] == str_type: + row[key] = f"''{row[key]}''" + elif self.schema[key]['type'] == client_point_type: + row[key] = f"''({value['x']},{value['y']})''" + + return row diff --git a/api/utility.py b/api/utility.py index da09b0e..bbd1187 100644 --- a/api/utility.py +++ b/api/utility.py @@ -4,12 +4,17 @@ from .operation import * from .change_set import ChangeSet -def query(name: str, sql: str) -> Row | None: +def read(name: str, sql: str) -> Row | None: with conn[name].cursor(row_factory=dict_row) as cur: cur.execute(sql) return cur.fetchone() +def write(name: str, sql: str) -> None: + with conn[name].cursor() as cur: + cur.execute(sql) + + def decorate(value: str | None, type: str, optional: bool) -> str: if optional: value = 'NULL' if value == None else value diff --git a/script/sql/create/operation.sql b/script/sql/create/operation.sql index 5ad2071..fa968a3 100644 --- a/script/sql/create/operation.sql +++ b/script/sql/create/operation.sql @@ -1,4 +1,4 @@ -CREATE TYPE API_OPERATION AS ENUM ('add', 'delete', 'update'); +CREATE TYPE API_OPERATION AS ENUM ('init', 'add', 'delete', 'update'); CREATE TABLE OPERATION ( @@ -7,14 +7,14 @@ CREATE TABLE OPERATION , Undo TEXT NOT NULL , Parent INTEGER REFERENCES OPERATION(ID) , Redo_Child INTEGER REFERENCES OPERATION(ID) -, Api TEXT NOT NULL -, Api_Operation API_OPERATION NOT NULL -, Api_Operation_Type TEXT NOT NULL -, Api_Operation_Id TEXT NOT NULL -- VARCHAR(32) -, Api_Operation_Property TEXT NOT NULL +, Api_Id TEXT NOT NULL +, Api_Op API_OPERATION NOT NULL +, Api_Object_Type TEXT NOT NULL +, Api_Object_Id TEXT NOT NULL +, Api_Object_Properties TEXT[] ); -INSERT INTO OPERATION (ID, Redo, Undo) VALUES (0, '', ''); +INSERT INTO OPERATION (ID, Redo, Undo, Api_Id, Api_Op, Api_Object_Type, Api_Object_Id) VALUES (0, '', '', '', 'init', '', ''); CREATE TABLE CURRENT_OPERATION ( diff --git a/script/sql/drop/operation.sql b/script/sql/drop/operation.sql index 998ee2f..13982ba 100644 --- a/script/sql/drop/operation.sql +++ b/script/sql/drop/operation.sql @@ -5,3 +5,5 @@ DROP TABLE IF EXISTS SNAPSHOT_OPERATION; DROP TABLE IF EXISTS CURRENT_OPERATION; DROP TABLE IF EXISTS OPERATION; + +DROP TYPE IF EXISTS API_OPERATION; diff --git a/test_tjnetwork.py b/test_tjnetwork.py index 7ab6a56..7623f59 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -17,7 +17,7 @@ class TestApi: delete_project(p) def test_project(self): - p = "test_title" + p = "test_project" assert not have_project(p) assert not is_project_open(p) @@ -66,7 +66,10 @@ class TestApi: assert get_title(p) == "" - set_title(p, "title") + change = set_title(p, "title").operations[0] + assert change['operation'] == 'update' + assert change['type'] == 'title' + assert change['id'] == '' assert get_title(p) == "title" set_title(p, "test") @@ -84,22 +87,22 @@ class TestApi: assert change_set.operations[0]['operation'] == 'add' assert change_set.operations[0]['type'] == JUNCTION assert change_set.operations[0]['id'] == "j0" - coord = get_junction_properties(p, 'j0')['coord'] + coord = get_junction(p, 'j0')['coord'] assert coord['x'] == 0.0 assert coord['y'] == 10.0 - z = get_junction_properties(p, 'j0')['elevation'] + z = get_junction(p, 'j0')['elevation'] assert z == 20.0 - assert get_junction_properties(p, 'j') == None - assert get_junction_properties(p, 'j0')['demand'] == None - assert get_junction_properties(p, 'j0')['demand'] == None - change_set = set_junction_demand(p, 'j0', 100.0) + assert get_junction(p, 'j') == None + assert get_junction(p, 'j0')['demand'] == None + assert get_junction(p, 'j0')['demand'] == None + change_set = set_junction(p, 'j0', {'demand': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == JUNCTION assert change_set.operations[0]['id'] == 'j0' - assert change_set.operations[0]['property'] == 'demand' - assert get_junction_properties(p, 'j0')['demand'] == 100.0 + assert change_set.operations[0]['properties'] == ['demand'] + assert get_junction(p, 'j0')['demand'] == 100.0 # TODO: pattern @@ -145,9 +148,9 @@ class TestApi: add_pipe(p, 'p1', 'j1', 'j2') add_pump(p, 'p2', 'j1', 'j2') add_valve(p, 'v1', 'j2', 'j3') - assert get_junction_properties(p, 'j1')['links'] == ['p1', 'p2'] - assert get_junction_properties(p, 'j2')['links'] == ['p1', 'p2', 'v1'] - assert get_junction_properties(p, 'j3')['links'] == ['v1'] + assert get_junction(p, 'j1')['links'] == ['p1', 'p2'] + assert get_junction(p, 'j2')['links'] == ['p1', 'p2', 'v1'] + assert get_junction(p, 'j3')['links'] == ['v1'] self.leave(p) @@ -165,21 +168,21 @@ class TestApi: change_set = add_reservoir(p, "r0", 0.0, 10.0, 20.0) assert len(change_set.operations) == 0 - assert get_reservoir_properties(p, 'r0')['head'] == 20.0 - assert get_reservoir_properties(p, 'r0')['head'] == 20.0 - change_set = set_reservoir_head(p, 'r0', 100.0) + assert get_reservoir(p, 'r0')['head'] == 20.0 + assert get_reservoir(p, 'r0')['head'] == 20.0 + change_set = set_reservoir(p, 'r0', {'head': 100.0}) assert change_set.operations[0]['type'] == RESERVOIR assert change_set.operations[0]['id'] == 'r0' - assert change_set.operations[0]['property'] == 'head' - assert get_reservoir_properties(p, 'r0')['head'] == 100.0 + assert change_set.operations[0]['properties'] == ['head'] + assert get_reservoir(p, 'r0')['head'] == 100.0 - assert get_reservoir_properties(p, 'r0')['coord'] == { 'x': 0.0, 'y': 10.0 } - assert get_reservoir_properties(p, 'r0')['coord'] == { 'x': 0.0, 'y': 10.0 } - change_set = set_reservoir_coord(p, 'r0', 100.0, 200.0) + assert get_reservoir(p, 'r0')['coord'] == { 'x': 0.0, 'y': 10.0 } + assert get_reservoir(p, 'r0')['coord'] == { 'x': 0.0, 'y': 10.0 } + change_set = set_reservoir(p, 'r0', {'coord': {'x': 100.0, 'y': 200.0}}) assert change_set.operations[0]['type'] == RESERVOIR assert change_set.operations[0]['id'] == 'r0' - assert change_set.operations[0]['property'] == 'coord' - assert get_reservoir_properties(p, 'r0')['coord'] == { 'x': 100.0, 'y': 200.0 } + assert change_set.operations[0]['properties'] == ['coord'] + assert get_reservoir(p, 'r0')['coord'] == { 'x': 100.0, 'y': 200.0 } # TODO: pattern @@ -220,73 +223,73 @@ class TestApi: assert tanks[0] == 't0' assert tanks[1] == 't1' - assert get_tank_properties(p, 't0')['elevation'] == 10.0 - change_set = set_tank_elevation(p, 't0', 100.0) + assert get_tank(p, 't0')['elevation'] == 10.0 + change_set = set_tank(p, 't0', {'elevation': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'elevation' - assert get_tank_properties(p, 't0')['elevation'] == 100.0 + assert change_set.operations[0]['properties'] == ['elevation'] + assert get_tank(p, 't0')['elevation'] == 100.0 - assert get_tank_properties(p, 't0')['init_level'] == 10.0 - change_set = set_tank_init_level(p, 't0', 100.0) + assert get_tank(p, 't0')['init_level'] == 10.0 + change_set = set_tank(p, 't0', {'init_level': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'init_level' - assert get_tank_properties(p, 't0')['init_level'] == 100.0 + assert change_set.operations[0]['properties'] == ['init_level'] + assert get_tank(p, 't0')['init_level'] == 100.0 - assert get_tank_properties(p, 't0')['min_level'] == 10.0 - change_set = set_tank_min_level(p, 't0', 100.0) + assert get_tank(p, 't0')['min_level'] == 10.0 + change_set = set_tank(p, 't0', {'min_level': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'min_level' - assert get_tank_properties(p, 't0')['min_level'] == 100.0 + assert change_set.operations[0]['properties'] == ['min_level'] + assert get_tank(p, 't0')['min_level'] == 100.0 - assert get_tank_properties(p, 't0')['max_level'] == 10.0 - change_set = set_tank_max_level(p, 't0', 100.0) + assert get_tank(p, 't0')['max_level'] == 10.0 + change_set = set_tank(p, 't0', {'max_level': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'max_level' - assert get_tank_properties(p, 't0')['max_level'] == 100.0 + assert change_set.operations[0]['properties'] == ['max_level'] + assert get_tank(p, 't0')['max_level'] == 100.0 - assert get_tank_properties(p, 't0')['diameter'] == 10.0 - change_set = set_tank_diameter(p, 't0', 100.0) + assert get_tank(p, 't0')['diameter'] == 10.0 + change_set = set_tank(p, 't0', {'diameter': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'diameter' - assert get_tank_properties(p, 't0')['diameter'] == 100.0 + assert change_set.operations[0]['properties'] == ['diameter'] + assert get_tank(p, 't0')['diameter'] == 100.0 - assert get_tank_properties(p, 't0')['min_vol'] == 10.0 - change_set = set_tank_min_vol(p, 't0', 100.0) + assert get_tank(p, 't0')['min_vol'] == 10.0 + change_set = set_tank(p, 't0', {'min_vol': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'min_vol' - assert get_tank_properties(p, 't0')['min_vol'] == 100.0 + assert change_set.operations[0]['properties'] == ['min_vol'] + assert get_tank(p, 't0')['min_vol'] == 100.0 # TODO: vol_curve - assert get_tank_properties(p, 't') == None - assert get_tank_properties(p, 't0')['overflow'] == None - change_set = set_tank_overflow(p, 't0', "XXX") + assert get_tank(p, 't') == None + assert get_tank(p, 't0')['overflow'] == None + change_set = set_tank(p, 't0', {'overflow': "XXX"}) assert len(change_set.operations) == 0 - change_set = set_tank_overflow(p, 't0', OVERFLOW_YES) + change_set = set_tank(p, 't0', {'overflow': OVERFLOW_YES}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == TANK assert change_set.operations[0]['id'] == "t0" - assert change_set.operations[0]['property'] == 'overflow' - assert get_tank_properties(p, 't0')['overflow'] == OVERFLOW_YES + assert change_set.operations[0]['properties'] == ['overflow'] + assert get_tank(p, 't0')['overflow'] == OVERFLOW_YES change_set = delete_tank(p, "t0") assert len(change_set.operations) == 1 @@ -348,77 +351,77 @@ class TestApi: pipes = get_links(p) assert len(pipes) == 1 - assert get_pipe_properties(p, 'p1')['node1'] == 'j1' - assert get_pipe_properties(p, 'p1')['node2'] == 'j2' - assert get_pipe_properties(p, 'p1')['length'] == 10.0 - assert get_pipe_properties(p, 'p1')['diameter'] == 10.0 - assert get_pipe_properties(p, 'p1')['roughness'] == 10.0 - assert get_pipe_properties(p, 'p1')['minor_loss'] == 10.0 - assert get_pipe_properties(p, 'p1')['status'] == PIPE_STATUS_CLOSED + assert get_pipe(p, 'p1')['node1'] == 'j1' + assert get_pipe(p, 'p1')['node2'] == 'j2' + assert get_pipe(p, 'p1')['length'] == 10.0 + assert get_pipe(p, 'p1')['diameter'] == 10.0 + assert get_pipe(p, 'p1')['roughness'] == 10.0 + assert get_pipe(p, 'p1')['minor_loss'] == 10.0 + assert get_pipe(p, 'p1')['status'] == PIPE_STATUS_CLOSED - change_set = set_pipe_node1(p, 'p1', 'j2') + change_set = set_pipe(p, 'p1', {'node1': 'j2'}) assert len(change_set.operations) == 0 - change_set = set_pipe_node2(p, 'p1', 'j1') + change_set = set_pipe(p, 'p1', {'node2': 'j1'}) assert len(change_set.operations) == 0 - change_set = set_pipe_status(p, 'p1', "XXX") + change_set = set_pipe(p, 'p1', {'status': 'XXX'}) assert len(change_set.operations) == 0 - change_set = set_pipe_node1(p, 'p1', 'j3') + change_set = set_pipe(p, 'p1', {'node1': 'j3'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'node1' + assert change_set.operations[0]['properties'] == ['node1'] - change_set = set_pipe_node2(p, 'p1', 'j4') + change_set = set_pipe(p, 'p1', {'node2': 'j4'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'node2' + assert change_set.operations[0]['properties'] == ['node2'] - change_set = set_pipe_length(p, 'p1', 100.0) + change_set = set_pipe(p, 'p1', {'length': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'length' + assert change_set.operations[0]['properties'] == ['length'] - change_set = set_pipe_diameter(p, 'p1', 100.0) + change_set = set_pipe(p, 'p1', {'diameter': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'diameter' + assert change_set.operations[0]['properties'] == ['diameter'] - change_set = set_pipe_roughness(p, 'p1', 100.0) + change_set = set_pipe(p, 'p1', {'roughness': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'roughness' + assert change_set.operations[0]['properties'] == ['roughness'] - change_set = set_pipe_minor_loss(p, 'p1', 100.0) + change_set = set_pipe(p, 'p1', {'minor_loss': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'minor_loss' + assert change_set.operations[0]['properties'] == ['minor_loss'] - change_set = set_pipe_status(p, 'p1', PIPE_STATUS_OPEN) + change_set = set_pipe(p, 'p1', {'status': PIPE_STATUS_OPEN}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PIPE assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'status' + assert change_set.operations[0]['properties'] == ['status'] - assert get_pipe_properties(p, 'p1')['node1'] == 'j3' - assert get_pipe_properties(p, 'p1')['node2'] == 'j4' - assert get_pipe_properties(p, 'p1')['length'] == 100.0 - assert get_pipe_properties(p, 'p1')['diameter'] == 100.0 - assert get_pipe_properties(p, 'p1')['roughness'] == 100.0 - assert get_pipe_properties(p, 'p1')['minor_loss'] == 100.0 - assert get_pipe_properties(p, 'p1')['status'] == PIPE_STATUS_OPEN + assert get_pipe(p, 'p1')['node1'] == 'j3' + assert get_pipe(p, 'p1')['node2'] == 'j4' + assert get_pipe(p, 'p1')['length'] == 100.0 + assert get_pipe(p, 'p1')['diameter'] == 100.0 + assert get_pipe(p, 'p1')['roughness'] == 100.0 + assert get_pipe(p, 'p1')['minor_loss'] == 100.0 + assert get_pipe(p, 'p1')['status'] == PIPE_STATUS_OPEN change_set = add_pipe(p, 'p2', 'j1', 'j2', 10.0, 10.0, 10.0, 10.0, PIPE_STATUS_CLOSED) assert len(change_set.operations) == 1 @@ -485,30 +488,30 @@ class TestApi: pumps = get_links(p) assert len(pumps) == 1 - assert get_pump_properties(p, 'p1')['node1'] == 'j1' - assert get_pump_properties(p, 'p1')['node2'] == 'j2' + assert get_pump(p, 'p1')['node1'] == 'j1' + assert get_pump(p, 'p1')['node2'] == 'j2' - change_set = set_pump_node1(p, 'p1', 'j2') + change_set = set_pump(p, 'p1', {'node1': 'j2'}) assert len(change_set.operations) == 0 - change_set = set_pump_node2(p, 'p1', 'j1') + change_set = set_pump(p, 'p1', {'node2': 'j1'}) assert len(change_set.operations) == 0 - change_set = set_pump_node1(p, 'p1', 'j3') + change_set = set_pump(p, 'p1', {'node1': 'j3'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PUMP assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'node1' + assert change_set.operations[0]['properties'] == ['node1'] - change_set = set_pump_node2(p, 'p1', 'j4') + change_set = set_pump(p, 'p1', {'node2': 'j4'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == PUMP assert change_set.operations[0]['id'] == 'p1' - assert change_set.operations[0]['property'] == 'node2' + assert change_set.operations[0]['properties'] == ['node2'] - assert get_pump_properties(p, 'p1')['node1'] == 'j3' - assert get_pump_properties(p, 'p1')['node2'] == 'j4' + assert get_pump(p, 'p1')['node1'] == 'j3' + assert get_pump(p, 'p1')['node2'] == 'j4' change_set = add_pump(p, 'p2', 'j1', 'j2') assert len(change_set.operations) == 1 @@ -578,68 +581,68 @@ class TestApi: valves = get_links(p) assert len(valves) == 1 - assert get_valve_properties(p, 'v1')['node1'] == 'j1' - assert get_valve_properties(p, 'v1')['node2'] == 'j2' - assert get_valve_properties(p, 'v1')['diameter'] == 10.0 - assert get_valve_properties(p, 'v1')['type'] == VALVES_TYPE_FCV - assert get_valve_properties(p, 'v1')['setting'] == 10.0 - assert get_valve_properties(p, 'v1')['minor_loss'] == 10.0 + assert get_valve(p, 'v1')['node1'] == 'j1' + assert get_valve(p, 'v1')['node2'] == 'j2' + assert get_valve(p, 'v1')['diameter'] == 10.0 + assert get_valve(p, 'v1')['type'] == VALVES_TYPE_FCV + assert get_valve(p, 'v1')['setting'] == 10.0 + assert get_valve(p, 'v1')['minor_loss'] == 10.0 - change_set = set_valve_node1(p, 'v1', 'j2') + change_set = set_valve(p, 'v1', {'node1': 'j2'}) assert len(change_set.operations) == 0 - change_set = set_valve_node2(p, 'v1', 'j1') + change_set = set_valve(p, 'v1', {'node2': 'j1'}) assert len(change_set.operations) == 0 - change_set = set_valve_type(p, 'v1', "XXX") + change_set = set_valve(p, 'v1', {'type': "XXX"}) assert len(change_set.operations) == 0 - change_set = set_valve_node1(p, 'v1', 'j3') + change_set = set_valve(p, 'v1', {'node1': 'j3'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'node1' + assert change_set.operations[0]['properties'] == ['node1'] - change_set = set_valve_node2(p, 'v1', 'j4') + change_set = set_valve(p, 'v1', {'node2': 'j4'}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'node2' + assert change_set.operations[0]['properties'] == ['node2'] - change_set = set_valve_diameter(p, 'v1', 100.0) + change_set = set_valve(p, 'v1', {'diameter': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'diameter' + assert change_set.operations[0]['properties'] == ['diameter'] - change_set = set_valve_type(p, 'v1', VALVES_TYPE_GPV) + change_set = set_valve(p, 'v1', {'type': VALVES_TYPE_GPV}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'type' + assert change_set.operations[0]['properties'] == ['type'] - change_set = set_valve_setting(p, 'v1', 100.0) + change_set = set_valve(p, 'v1', {'setting': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'setting' + assert change_set.operations[0]['properties'] == ['setting'] - change_set = set_valve_minor_loss(p, 'v1', 100.0) + change_set = set_valve(p, 'v1', {'minor_loss': 100.0}) assert len(change_set.operations) == 1 assert change_set.operations[0]['operation'] == 'update' assert change_set.operations[0]['type'] == VALVE assert change_set.operations[0]['id'] == 'v1' - assert change_set.operations[0]['property'] == 'minor_loss' + assert change_set.operations[0]['properties'] == ['minor_loss'] - assert get_valve_properties(p, 'v1')['node1'] == 'j3' - assert get_valve_properties(p, 'v1')['node2'] == 'j4' - assert get_valve_properties(p, 'v1')['diameter'] == 100.0 - assert get_valve_properties(p, 'v1')['type'] == VALVES_TYPE_GPV - assert get_valve_properties(p, 'v1')['setting'] == 100.0 - assert get_valve_properties(p, 'v1')['minor_loss'] == 100.0 + assert get_valve(p, 'v1')['node1'] == 'j3' + assert get_valve(p, 'v1')['node2'] == 'j4' + assert get_valve(p, 'v1')['diameter'] == 100.0 + assert get_valve(p, 'v1')['type'] == VALVES_TYPE_GPV + assert get_valve(p, 'v1')['setting'] == 100.0 + assert get_valve(p, 'v1')['minor_loss'] == 100.0 change_set = add_valve(p, 'v2', 'j1', 'j2', 10.0, VALVES_TYPE_FCV, 10.0, 10.0) assert len(change_set.operations) == 1 @@ -672,5 +675,6 @@ class TestApi: self.leave(p) + if __name__ == '__main__': pytest.main() diff --git a/tjnetwork.py b/tjnetwork.py index 2ca8ed2..c5176c5 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -71,32 +71,23 @@ def copy_project(source: str, new: str) -> None: def get_current_operation(name: str) -> int: return api.get_current_operation(name) -def undo(name: str) -> None: - return api.undo(name) +def execute_undo(name: str, discard: bool = False) -> ChangeSet: + return api.execute_undo(name, discard) -def redo(name: str) -> None: - return api.redo(name) +def execute_redo(name: str) -> ChangeSet: + return api.execute_redo(name) def have_snapshot(name: str, tag: str) -> bool: return api.have_snapshot(name, tag) -def take_snapshot(name: str, tag: str) -> None: +def take_snapshot(name: str, tag: str) -> int: return api.take_snapshot(name, tag) -def pick_snapshot(name: str, tag: str) -> None: - return api.pick_snapshot(name, tag) +def pick_snapshot(name: str, tag: str, discard: bool = False) -> ChangeSet: + return api.pick_snapshot(name, tag, discard) -def have_transaction(name: str) -> bool: - return api.have_transaction(name) - -def start_transaction(name: str, strict: bool = False) -> None: - return api.start_transaction(name, strict) - -def commit_transaction(name: str) -> None: - return api.commit_transaction(name) - -def abort_transaction(name: str) -> None: - return api.abort_transaction(name) +def sync_with_server(name: str, operation: int) -> ChangeSet: + return api.sync_with_server(name, operation) ############################################################ @@ -161,192 +152,117 @@ def get_title(name: str) -> str: # junction 2.[JUNCTIONS] ############################################################ -def get_junction_property_names(name: str) -> list[str]: - return api.get_junction_property_names(name) +def get_junction_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_junction_schema(name) def add_junction(name: str, junction_id: str, x: float, y: float, elevation: float) -> ChangeSet: return api.add_junction(name, junction_id, x, y, elevation) +def get_junction(name: str, junction_id: str) -> dict[str, Any] | None: + return api.get_junction(name, junction_id) + +def set_junction(name: str, junction_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_junction(name, junction_id, properties) + def delete_junction(name: str, junction_id: str) -> ChangeSet: return api.delete_junction(name, junction_id) -def set_junction_elevation(name: str, junction_id: str, elevation: float) -> ChangeSet: - return api.set_junction_elevation(name, junction_id, elevation) - -def set_junction_demand(name: str, junction_id: str, demand: float) -> ChangeSet: - return api.set_junction_demand(name, junction_id, demand) - -def set_junction_pattern(name: str, junction_id: str, pattern: str) -> ChangeSet: - return api.set_junction_pattern(name, junction_id, pattern) - -def set_junction_coord(name: str, junction_id: str, x: float, y: float) -> ChangeSet: - return api.set_junction_coord(name, junction_id, x, y) - -def get_junction_properties(name: str, junction_id: str) -> dict[str, Any] | None: - return api.get_junction_properties(name, junction_id) - ############################################################ # reservoir 3.[RESERVOIRS] ############################################################ -def get_reservoir_property_names(name: str) -> list[str]: - return api.get_reservoir_property_names(name) +def get_reservoir_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_reservoir_schema(name) def add_reservoir(name: str, reservoir_id: str, x: float, y: float, head: float) -> ChangeSet: return api.add_reservoir(name, reservoir_id, x, y, head) +def get_reservoir(name: str, reservoir_id: str) -> dict[str, Any] | None: + return api.get_reservoir(name, reservoir_id) + +def set_reservoir(name: str, reservoir_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_reservoir(name, reservoir_id, properties) + def delete_reservoir(name: str, reservoir_id: str) -> ChangeSet: return api.delete_reservoir(name, reservoir_id) -def set_reservoir_head(name: str, reservoir_id: str, head: float) -> ChangeSet: - return api.set_reservoir_head(name, reservoir_id, head) - -def set_reservoir_pattern(name: str, reservoir_id: str, pattern: str) -> ChangeSet: - return api.set_reservoir_pattern(name, reservoir_id, pattern) - -def set_reservoir_coord(name: str, reservoir_id: str, x: float, y: float) -> ChangeSet: - return api.set_reservoir_coord(name, reservoir_id, x, y) - -def get_reservoir_properties(name: str, reservoir_id: str) -> dict[str, Any] | None: - return api.get_reservoir_properties(name, reservoir_id) - ############################################################ # tank 4.[TANKS] ############################################################ -def get_tank_property_names(name: str) -> list[str]: - return api.get_tank_property_names(name) +def get_tank_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_tank_schema(name) def add_tank(name: str, tank_id: str, x: float, y: float, elevation: float, init_level: float = 0, min_level: float = 0, max_level: float = 0, diameter: float = 0, min_vol: float = 0) -> ChangeSet: return api.add_tank(name, tank_id, x, y, elevation, init_level, min_level, max_level, diameter, min_vol) +def get_tank(name: str, tank_id: str) -> dict[str, Any] | None: + return api.get_tank(name, tank_id) + +def set_tank(name: str, tank_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_tank(name, tank_id, properties) + def delete_tank(name: str, tank_id: str) -> ChangeSet: return api.delete_tank(name, tank_id) -def set_tank_elevation(name: str, tank_id: str, elevation: float) -> ChangeSet: - return api.set_tank_elevation(name, tank_id, elevation) - -def set_tank_init_level(name: str, tank_id: str, init_level: float) -> ChangeSet: - return api.set_tank_init_level(name, tank_id, init_level) - -def set_tank_min_level(name: str, tank_id: str, min_level: float) -> ChangeSet: - return api.set_tank_min_level(name, tank_id, min_level) - -def set_tank_max_level(name: str, tank_id: str, max_level: float) -> ChangeSet: - return api.set_tank_max_level(name, tank_id, max_level) - -def set_tank_diameter(name: str, tank_id: str, diameter: float) -> ChangeSet: - return api.set_tank_diameter(name, tank_id, diameter) - -def set_tank_min_vol(name: str, tank_id: str, min_vol: float) -> ChangeSet: - return api.set_tank_min_vol(name, tank_id, min_vol) - -def set_tank_vol_curve(name: str, tank_id: str, vol_curve: str) -> ChangeSet: - return api.set_tank_vol_curve(name, tank_id, vol_curve) - -def set_tank_overflow(name: str, tank_id: str, overflow: str) -> ChangeSet: - return api.set_tank_overflow(name, tank_id, overflow) - -def set_tank_coord(name: str, tank_id: str, x: float, y: float) -> ChangeSet: - return api.set_tank_coord(name, tank_id, x, y) - -def get_tank_properties(name: str, tank_id: str) -> dict[str, Any] | None: - return api.get_tank_properties(name, tank_id) - ############################################################ # pipe 4.[PIPES] ############################################################ -def get_pipe_property_names(name: str) -> list[str]: - return api.get_pipe_property_names(name) +def get_pipe_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_pipe_schema(name) def add_pipe(name: str, pipe_id: str, node1: str, node2: str, length: float = 0, diameter: float = 0, roughness: float = 0, minor_loss: float = 0, status: str = PIPE_STATUS_OPEN) -> ChangeSet: return api.add_pipe(name, pipe_id, node1, node2, length, diameter, roughness, minor_loss, status) +def get_pipe(name: str, pipe_id: str) -> dict[str, Any] | None: + return api.get_pipe(name, pipe_id) + +def set_pipe(name: str, pipe_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_pipe(name, pipe_id, properties) + def delete_pipe(name: str, pipe_id: str) -> ChangeSet: return api.delete_pipe(name, pipe_id) -def set_pipe_node1(name: str, pipe_id: str, node1: str) -> ChangeSet: - return api.set_pipe_node1(name, pipe_id, node1) - -def set_pipe_node2(name: str, pipe_id: str, node2: str) -> ChangeSet: - return api.set_pipe_node2(name, pipe_id, node2) - -def set_pipe_length(name: str, pipe_id: str, length: float) -> ChangeSet: - return api.set_pipe_length(name, pipe_id, length) - -def set_pipe_diameter(name: str, pipe_id: str, diameter: float) -> ChangeSet: - return api.set_pipe_diameter(name, pipe_id, diameter) - -def set_pipe_roughness(name: str, pipe_id: str, roughness: float) -> ChangeSet: - return api.set_pipe_roughness(name, pipe_id, roughness) - -def set_pipe_minor_loss(name: str, pipe_id: str, minor_loss: float) -> ChangeSet: - return api.set_pipe_minor_loss(name, pipe_id, minor_loss) - -def set_pipe_status(name: str, pipe_id: str, status: float) -> ChangeSet: - return api.set_pipe_status(name, pipe_id, status) - -def get_pipe_properties(name: str, pipe_id: str) -> dict[str, Any] | None: - return api.get_pipe_properties(name, pipe_id) - ############################################################ # pump 4.[PUMPS] ############################################################ -def get_pump_property_names(name: str) -> list[str]: - return api.get_pump_property_names(name) +def get_pump_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_pump_schema(name) def add_pump(name: str, pump_id: str, node1: str, node2: str) -> ChangeSet: return api.add_pump(name, pump_id, node1, node2) +def get_pump(name: str, pump_id: str) -> dict[str, Any] | None: + return api.get_pump(name, pump_id) + +def set_pump(name: str, pump_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_pump(name, pump_id, properties) + def delete_pump(name: str, pump_id: str) -> ChangeSet: return api.delete_pump(name, pump_id) -def set_pump_node1(name: str, pump_id: str, node1: str) -> ChangeSet: - return api.set_pump_node1(name, pump_id, node1) - -def set_pump_node2(name: str, pump_id: str, node2: str) -> ChangeSet: - return api.set_pump_node2(name, pump_id, node2) - -def get_pump_properties(name: str, pump_id: str) -> dict[str, Any] | None: - return api.get_pump_properties(name, pump_id) - ############################################################ # valve 4.[VALVES] ############################################################ -def get_valve_property_names(name: str) -> list[str]: - return api.get_valve_property_names(name) +def get_valve_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_valve_schema(name) def add_valve(name: str, valve_id: str, node1: str, node2: str, diameter: float = 0, type: str = VALVES_TYPE_PRV, setting: float = 0, minor_loss: float = 0) -> ChangeSet: return api.add_valve(name, valve_id, node1, node2, diameter, type, setting, minor_loss) +def get_valve(name: str, valve_id: str) -> dict[str, Any] | None: + return api.get_valve(name, valve_id) + +def set_valve(name: str, valve_id: str, properties: dict[str, Any]) -> ChangeSet: + return api.set_valve(name, valve_id, properties) + def delete_valve(name: str, valve_id: str) -> ChangeSet: return api.delete_valve(name, valve_id) - -def set_valve_node1(name: str, valve_id: str, node1: str) -> ChangeSet: - return api.set_valve_node1(name, valve_id, node1) - -def set_valve_node2(name: str, valve_id: str, node2: str) -> ChangeSet: - return api.set_valve_node2(name, valve_id, node2) - -def set_valve_diameter(name: str, valve_id: str, diameter: float) -> ChangeSet: - return api.set_valve_diameter(name, valve_id, diameter) - -def set_valve_type(name: str, valve_id: str, type: str) -> ChangeSet: - return api.set_valve_type(name, valve_id, type) - -def set_valve_setting(name: str, valve_id: str, setting: float) -> ChangeSet: - return api.set_valve_setting(name, valve_id, setting) - -def set_valve_minor_loss(name: str, valve_id: str, minor_loss: float) -> ChangeSet: - return api.set_valve_minor_loss(name, valve_id, minor_loss) - -def get_valve_properties(name: str, pump_id: str) -> dict[str, Any] | None: - return api.get_valve_properties(name, pump_id) diff --git a/try_operation_api.py b/try_operation_api.py new file mode 100644 index 0000000..03c34a6 --- /dev/null +++ b/try_operation_api.py @@ -0,0 +1,51 @@ +from tjnetwork import * + +p = "demo" + +if is_project_open(p): + close_project(p) + +if have_project(p): + delete_project(p) + +create_project(p) +open_project(p) + +print(get_junction_schema(p)) +print(get_reservoir_schema(p)) +print(get_tank_schema(p)) + +print(add_junction(p, 'j1', 0.0, 0.0, 0.0).operations) +print(add_junction(p, 'j2', 0.0, 0.0, 0.0).operations) +print(add_junction(p, 'j3', 0.0, 0.0, 0.0).operations) +print(add_junction(p, 'j4', 0.0, 0.0, 0.0).operations) + +client_op = get_current_operation(p) +print(client_op) +print(take_snapshot(p, 'x')) + +print(execute_undo(p).operations) +print(execute_undo(p).operations) +print(add_junction(p, 'j5', 0.0, 0.0, 0.0).operations) +print(add_junction(p, 'j6', 0.0, 0.0, 0.0).operations) + +print(take_snapshot(p, 'xx')) + +print(sync_with_server(p, client_op).operations) + +print(pick_snapshot(p, 'x').operations) + +print(get_junction(p, 'j1')) +print(get_junction(p, 'j2')) +print(get_junction(p, 'j3')) +print(get_junction(p, 'j4')) +print(get_junction(p, 'j5')) +print(get_junction(p, 'j6')) + +print(set_junction(p, 'j1', {'elevation': 10.0, 'coord': {'x': 10.0, 'y': 10.0}, 'demand': 10.0}).operations) +print(get_junction(p, 'j1')) +print(execute_undo(p).operations) +print(get_junction(p, 'j1')) + +close_project(p) +delete_project(p) \ No newline at end of file