Skip to content

Commit f25d63e

Browse files
Merge pull request #15 from geoCML/write-constraints
Write constraints from Postgres database to Tabor file
2 parents 9fbd525 + ee2e342 commit f25d63e

File tree

5 files changed

+45
-10
lines changed

5 files changed

+45
-10
lines changed

src/constraint.py

+25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from tabor_constraint_type import TaborConstraintType
22

3+
34
class Constraint(object):
45
def __init__(self, constraint: dict, layer: str) -> None:
56
self.constraint = constraint
@@ -17,6 +18,10 @@ def on(self, other_layer: str) -> str:
1718
return f"""CREATE OR REPLACE FUNCTION {self.layer}_on_{other_layer}() RETURNS trigger AS $$ DECLARE overlap boolean; BEGIN SELECT Count(*) INTO overlap FROM {other_layer} WHERE ST_Contains({other_layer}.geom, NEW.geom); IF NOT overlap THEN RAISE EXCEPTION '{self.layer} is not on {other_layer}'; END IF; RETURN NEW; END; $$ LANGUAGE 'plpgsql'; CREATE CONSTRAINT TRIGGER {self.layer}_on_{other_layer} AFTER INSERT OR UPDATE ON {self.layer} FOR EACH ROW EXECUTE FUNCTION {self.layer}_on_{other_layer}();"""
1819

1920

21+
def as_dict(self) -> dict:
22+
return self.constraint
23+
24+
2025
def __str__(self) -> str:
2126
if self.constraint_type.type == "on":
2227
try:
@@ -25,3 +30,23 @@ def __str__(self) -> str:
2530
raise Exception("Constraint 'on' needs a relative layer value")
2631
return self.on(layer)
2732
return ""
33+
34+
35+
class Trigger(object):
36+
"""
37+
Triggers are strings that can be used to derive Constraints
38+
39+
e.g. trees_on_grass -> { "name": "on", "layer": "grass" }
40+
"""
41+
def __init__(self, name: str) -> None:
42+
self.constraint = self.derive_constraint_from_name(name)
43+
44+
45+
def derive_constraint_from_name(self, name) -> Constraint:
46+
if "_on_" in name:
47+
return Constraint({
48+
"name": "on",
49+
"layer": name.split("_on_")[1]
50+
}, name.split("_on_")[0])
51+
52+
raise Exception(f"Cannot derive a constraint from trigger '{name}'")

src/db_connector.py

+5
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,8 @@ def get_geometry_type_for_table(self, schema: str, table: str) -> str:
6262
return ""
6363

6464
return result[0]
65+
66+
67+
def get_triggers_for_table(self, table: str) -> set[str]:
68+
self.cursor.execute(f"""SELECT trigger_name FROM information_schema.triggers WHERE event_object_table = '{table}'""")
69+
return {trigger[0] for trigger in self.cursor.fetchall()}

src/tabor.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def write(file_path: str, db: str, username: str, password: str, host: str, port
3131

3232
data[table] = {}
3333
data[table]["fields"] = db_connector.get_fields_for_table(schema, table_name)
34-
34+
data[table]["constraints"] = db_connector.get_triggers_for_table(table_name)
3535

3636
geom_type = db_connector.get_geometry_type_for_table(schema, table_name)
3737
if geom_type:

src/tabor_file.py

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import re
22
from yaml import safe_dump, safe_load
33

4+
from constraint import Constraint, Trigger
45
from tabor_layer import TaborLayer
56
from consts import VERSION
67

@@ -24,9 +25,9 @@ def __init__(self, path: str, psql_data: dict | None = None) -> None:
2425
except KeyError:
2526
geometry = None
2627

27-
constraints: dict = {}
28+
constraints: list[dict] = []
2829
try:
29-
constraints = layer["constraints"]
30+
constraints: list[dict] = layer["constraints"]
3031
except KeyError:
3132
pass
3233

@@ -43,17 +44,17 @@ def __init__(self, path: str, psql_data: dict | None = None) -> None:
4344
except KeyError:
4445
geom = None
4546

46-
47-
constraints: dict = {}
47+
derived_constraints: list[dict] = []
4848
try:
49-
constraints = values["constraints"]
49+
for trigger in values["constraints"]:
50+
derived_constraints.append(Trigger(trigger).constraint.constraint)
5051
except KeyError:
5152
pass
5253

53-
self.add_layer(table.split(".")[1], table.split(".")[0], geom, values["owner"], values["fields"], constraints)
54+
self.add_layer(table.split(".")[1], table.split(".")[0], geom, values["owner"], values["fields"], derived_constraints)
5455

5556

56-
def add_layer(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: dict) -> TaborLayer:
57+
def add_layer(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: list[dict]) -> TaborLayer:
5758
self.layers.append(TaborLayer(name, schema, geometry, owner, fields, constraints))
5859
return self.layers[len(self.layers) - 1]
5960

src/tabor_layer.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55

66
class TaborLayer(object):
7-
def __init__(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: dict) -> None:
7+
def __init__(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: list[dict]) -> None:
88
self.name = name
99
self.schema = schema
1010

@@ -43,14 +43,18 @@ def get_pk_field(self) -> str:
4343
return field.name
4444
return ""
4545

46+
4647
def as_dict(self) -> dict:
4748
var_dict = {
4849
"name": self.name,
4950
"schema": self.schema,
5051
"owner": self.owner,
51-
"fields": [field.as_dict() for field in self.fields]
52+
"fields": [field.as_dict() for field in self.fields],
5253
}
5354

55+
if self.constraints:
56+
var_dict["constraints"] = [constraint.as_dict() for constraint in self.constraints]
57+
5458
if self.geometry:
5559
var_dict["geometry"] = str(self.geometry)
5660

0 commit comments

Comments
 (0)