From c1b99bc7eb5285b5b9a60ee674ead77bcd816592 Mon Sep 17 00:00:00 2001 From: "WQY\\qiong" Date: Sat, 22 Oct 2022 11:51:48 +0800 Subject: [PATCH] Add status api and test --- api/__init__.py | 3 ++ api/command.py | 3 ++ api/operation.py | 6 +++ api/s10_status.py | 64 +++++++++++++++++++++++++++ script/sql/create/10.status.sql | 6 +-- test_tjnetwork.py | 76 ++++++++++++++++++++++++++++++++- tjnetwork.py | 18 ++++++++ 7 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 api/s10_status.py diff --git a/api/__init__.py b/api/__init__.py index 71a3948..88d57d6 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -40,4 +40,7 @@ from .s7_valves import get_valve_schema, add_valve, get_valve, set_valve, delete from .s9_demands import get_demand_schema, get_demand, set_demand +from .s10_status import LINK_STATUS_OPEN, LINK_STATUS_CLOSED, LINK_STATUS_ACTIVE +from .s10_status import get_status_schema, get_status, set_status + from .s24_coordinates import get_node_coord diff --git a/api/command.py b/api/command.py index c471cd2..37d06da 100644 --- a/api/command.py +++ b/api/command.py @@ -6,6 +6,7 @@ from .s5_pipes import * from .s6_pumps import * from .s7_valves import * from .s9_demands import * +from .s10_status import * def execute_add_command(name: str, cs: ChangeSet) -> ChangeSet: @@ -46,6 +47,8 @@ def execute_update_command(name: str, cs: ChangeSet) -> ChangeSet: return set_valve(name, cs) elif type == 'demand': return set_demand(name, cs) + elif type == 'status': + return set_status(name, cs) return ChangeSet() diff --git a/api/operation.py b/api/operation.py index d418874..8ec30ef 100644 --- a/api/operation.py +++ b/api/operation.py @@ -57,6 +57,12 @@ def read_all(name: str, sql: str) -> list[Row]: return cur.fetchall() +def try_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) diff --git a/api/s10_status.py b/api/s10_status.py new file mode 100644 index 0000000..6088dc0 --- /dev/null +++ b/api/s10_status.py @@ -0,0 +1,64 @@ +from .operation import * +from .s0_base import * + + +LINK_STATUS_OPEN = 'OPEN' +LINK_STATUS_CLOSED = 'CLOSED' +LINK_STATUS_ACTIVE = 'ACTIVE' + + +def get_status_schema(name: str) -> dict[str, dict[str, Any]]: + return { 'link' : {'type': 'str' , 'optional': False , 'readonly': True }, + 'status' : {'type': 'str' , 'optional': True , 'readonly': False}, + 'setting' : {'type': 'float' , 'optional': True , 'readonly': False} } + + +def get_status(name: str, link: str) -> dict[str, Any]: + s = try_read(name, f"select * from status where link = '{link}'") + if s == None: + return { 'link': link, 'status': None, 'setting': None } + d = {} + d['link'] = str(s['link']) + d['status'] = str(s['status']) if s['status'] != None else None + d['setting'] = float(s['setting']) if s['setting'] != None else None + return d + + +class Status(object): + def __init__(self, input: dict[str, Any]) -> None: + self.type = 'status' + self.link = str(input['link']) + self.status = str(input['status']) if 'status' in input and input['status'] != None else None + self.setting = float(input['setting']) if 'setting' in input and input['setting'] != None else None + + self.f_type = f"'{self.type}'" + self.f_link = f"'{self.link}'" + self.f_status = f"'{self.status}'" if self.status != None else 'null' + self.f_setting = self.setting if self.setting != None else 'null' + + def as_dict(self) -> dict[str, Any]: + return { 'type': self.type, 'link': self.link, 'status': self.status, 'setting': self.setting } + + +def set_status(name: str, cs: ChangeSet) -> ChangeSet: + old = Status(get_status(name, cs.operations[0]['link'])) + raw_new = get_status(name, cs.operations[0]['link']) + + new_dict = cs.operations[0] + schema = get_status_schema(name) + for key, value in schema.items(): + if key in new_dict and not value['readonly']: + raw_new[key] = new_dict[key] + new = Status(raw_new) + + redo_sql = f"delete from status where link = {new.f_link};" + redo_sql += f"\ninsert into status (link, status, setting) values ({new.f_link}, {new.f_status}, {new.f_setting});" + + undo_sql = f"delete from status where link = {old.f_link};" + if old.status != None or old.setting != None: + undo_sql += f"\ninsert into status (link, status, setting) values ({old.f_link}, {old.f_status}, {old.f_setting});" + + redo_cs = g_update_prefix | new.as_dict() + undo_cs = g_update_prefix | old.as_dict() + + return execute_command(name, redo_sql, undo_sql, redo_cs, undo_cs) diff --git a/script/sql/create/10.status.sql b/script/sql/create/10.status.sql index 68f001a..63a8256 100644 --- a/script/sql/create/10.status.sql +++ b/script/sql/create/10.status.sql @@ -4,8 +4,8 @@ create type link_status as enum ('OPEN', 'CLOSED', 'ACTIVE'); create table status ( - id varchar(32) primary key references _link(id) -, l_status link_status + link varchar(32) primary key references _link(id) +, status link_status , setting numeric -, check (l_status is not null or setting is not null) +, check (status is not null or setting is not null) ); diff --git a/test_tjnetwork.py b/test_tjnetwork.py index a0d88aa..e7a66b8 100644 --- a/test_tjnetwork.py +++ b/test_tjnetwork.py @@ -1198,7 +1198,7 @@ class TestApi: def test_demand_op(self): - p = 'test_demand' + p = 'test_demand_op' self.enter(p) add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) @@ -1240,6 +1240,78 @@ class TestApi: self.leave(p) + def test_status(self): + p = 'test_status' + self.enter(p) + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + add_junction(p, ChangeSet({'id': 'j2', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + assert is_junction(p, 'j1') + assert is_junction(p, 'j2') + + add_pipe(p, ChangeSet({'id': 'p0', 'node1': 'j1', 'node2': 'j2', 'length': 100.0, 'diameter': 10.0, 'roughness': 0.1, 'minor_loss': 0.5, 'status': PIPE_STATUS_OPEN })) + assert is_pipe(p, 'p0') + + s = get_status(p, 'p0') + assert s['link'] == 'p0' + assert s['status'] == None + assert s['setting'] == None + + set_status(p, ChangeSet({'link': 'p0', 'status': LINK_STATUS_OPEN, 'setting': 10.0})) + s = get_status(p, 'p0') + assert s['link'] == 'p0' + assert s['status'] == LINK_STATUS_OPEN + assert s['setting'] == 10.0 + + self.leave(p) + + + def test_status_op(self): + p = 'test_status_op' + self.enter(p) + + add_junction(p, ChangeSet({'id': 'j1', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + add_junction(p, ChangeSet({'id': 'j2', 'x': 0.0, 'y': 10.0, 'elevation': 20.0})) + assert is_junction(p, 'j1') + assert is_junction(p, 'j2') + + add_pipe(p, ChangeSet({'id': 'p0', 'node1': 'j1', 'node2': 'j2', 'length': 100.0, 'diameter': 10.0, 'roughness': 0.1, 'minor_loss': 0.5, 'status': PIPE_STATUS_OPEN })) + assert is_pipe(p, 'p0') + + s = get_status(p, 'p0') + assert s['link'] == 'p0' + assert s['status'] == None + assert s['setting'] == None + + cs = set_status(p, ChangeSet({'link': 'p0', 'status': LINK_STATUS_OPEN, 'setting': 10.0})).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'status' + assert cs['link'] == 'p0' + assert cs['status'] == LINK_STATUS_OPEN + assert cs['setting'] == 10.0 + + cs = execute_undo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'status' + assert cs['link'] == 'p0' + assert cs['status'] == None + assert cs['setting'] == None + + s = get_status(p, 'p0') + assert s['link'] == 'p0' + assert s['status'] == None + assert s['setting'] == None + + cs = execute_redo(p).operations[0] + assert cs['operation'] == API_UPDATE + assert cs['type'] == 'status' + assert cs['link'] == 'p0' + assert cs['status'] == LINK_STATUS_OPEN + assert cs['setting'] == 10.0 + + self.leave(p) + + def test_snapshot(self): p = "test_snapshot" self.enter(p) @@ -1286,7 +1358,7 @@ class TestApi: def test_batch_commands(self): - p = 'test_valve_op' + p = 'test_batch_commands' self.enter(p) cs = ChangeSet() diff --git a/tjnetwork.py b/tjnetwork.py index 72e193e..fc90d54 100644 --- a/tjnetwork.py +++ b/tjnetwork.py @@ -38,6 +38,10 @@ VALVES_TYPE_FCV = api.VALVES_TYPE_FCV VALVES_TYPE_TCV = api.VALVES_TYPE_TCV VALVES_TYPE_GPV = api.VALVES_TYPE_GPV +LINK_STATUS_OPEN = api.LINK_STATUS_OPEN +LINK_STATUS_CLOSED = api.LINK_STATUS_CLOSED +LINK_STATUS_ACTIVE = api.LINK_STATUS_ACTIVE + ############################################################ # project @@ -305,6 +309,20 @@ def set_demand(name: str, cs: ChangeSet) -> ChangeSet: return api.set_demand(name, cs) +############################################################ +# status 10.[STATUS] +############################################################ + +def get_status_schema(name: str) -> dict[str, dict[str, Any]]: + return api.get_status_schema(name) + +def get_status(name: str, link: str) -> dict[str, Any]: + return api.get_status(name, link) + +def set_status(name: str, cs: ChangeSet) -> ChangeSet: + return api.set_status(name, cs) + + ############################################################ # coord 24.[COORDINATES] ############################################################