Skip to content

Commit 9fbd525

Browse files
Merge pull request #13 from geoCML/on-constraint
Implement 'on' constraint
2 parents bd7dba7 + 86bd172 commit 9fbd525

6 files changed

+76
-6
lines changed

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ Tabor is a database modeling language for GIS based on YAML, but with additional
66
tabor: 0.1.2
77
layers:
88
9+
- name: grass
10+
schema: public
11+
owner: geocml
12+
geometry: polygon
13+
fields:
14+
- name: fid
15+
type: int
16+
pk: true
17+
918
- name: trees
1019
schema: public
1120
owner: geocml
@@ -22,6 +31,9 @@ layers:
2231
type: double
2332
- name: circumference_cm
2433
type: double
34+
constraints:
35+
- name: on
36+
layer: grass
2537
2638
- name: streams
2739
schema: public

src/constraint.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from tabor_constraint_type import TaborConstraintType
2+
3+
class Constraint(object):
4+
def __init__(self, constraint: dict, layer: str) -> None:
5+
self.constraint = constraint
6+
self.layer = layer
7+
8+
try:
9+
if self.constraint["name"] == True:
10+
self.constraint["name"] = "on"
11+
self.constraint_type = TaborConstraintType(self.constraint["name"])
12+
except KeyError:
13+
raise Exception(f"Constraint has no name")
14+
15+
16+
def on(self, other_layer: str) -> str:
17+
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}();"""
18+
19+
20+
def __str__(self) -> str:
21+
if self.constraint_type.type == "on":
22+
try:
23+
layer = self.constraint["layer"]
24+
except KeyError:
25+
raise Exception("Constraint 'on' needs a relative layer value")
26+
return self.on(layer)
27+
return ""

src/geometry_type.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ class GeometryType(object):
44
postgis_point_types = ("POINT", "Point")
55
postgis_multi_polygon_types = ("MULTIPOLYGON", "Multipolygon")
66
postgis_geometry_generic_types = ("GEOMETRY", "Geometry")
7+
valid_types = ("polyline", "multi polygon", "polygon", "point", "geometry")
78

89
def __init__(self, type: str):
9-
self.valid_types = ("polyline", "multi polygon", "polygon", "point", "geometry")
1010
self.set_type(type)
1111

1212

src/tabor_constraint_type.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class TaborConstraintType(object):
2+
valid_constraints = ("on")
3+
4+
def __init__(self, constraint: str) -> None:
5+
if constraint not in self.valid_constraints:
6+
raise Exception(f"{constraint} is not a valid constraint, valid constraints are {str(self.valid_constraints)}")
7+
8+
self.type = constraint

src/tabor_file.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,13 @@ def __init__(self, path: str, psql_data: dict | None = None) -> None:
2424
except KeyError:
2525
geometry = None
2626

27-
self.add_layer(layer["name"], layer["schema"], geometry, layer["owner"], layer["fields"])
27+
constraints: dict = {}
28+
try:
29+
constraints = layer["constraints"]
30+
except KeyError:
31+
pass
32+
33+
self.add_layer(layer["name"], layer["schema"], geometry, layer["owner"], layer["fields"], constraints)
2834

2935
except FileNotFoundError:
3036
raise Exception(f"Failed to read .tabor file at {self.path}, does that path exist?")
@@ -37,11 +43,18 @@ def __init__(self, path: str, psql_data: dict | None = None) -> None:
3743
except KeyError:
3844
geom = None
3945

40-
self.add_layer(table.split(".")[1], table.split(".")[0], geom, owner=values["owner"], fields=values["fields"])
4146

47+
constraints: dict = {}
48+
try:
49+
constraints = values["constraints"]
50+
except KeyError:
51+
pass
52+
53+
self.add_layer(table.split(".")[1], table.split(".")[0], geom, values["owner"], values["fields"], constraints)
4254

43-
def add_layer(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict) -> TaborLayer:
44-
self.layers.append(TaborLayer(name, schema, geometry, owner, fields))
55+
56+
def add_layer(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: dict) -> TaborLayer:
57+
self.layers.append(TaborLayer(name, schema, geometry, owner, fields, constraints))
4558
return self.layers[len(self.layers) - 1]
4659

4760

@@ -66,6 +79,10 @@ def to_psql(self) -> dict:
6679
result[layer.name]["schema"] = f"""CREATE TABLE IF NOT EXISTS "{layer.schema}"."{layer.name}" ({", ".join(fields)}{geom_query}{pk_query});"""
6780
result[layer.name]["owner"] = f"""ALTER TABLE "{layer.schema}"."{layer.name}" OWNER TO {layer.owner};"""
6881

82+
result[layer.name]["constraints"] = []
83+
for constraint in layer.constraints:
84+
result[layer.name]["constraints"].append(str(constraint))
85+
6986
if layer.geometry:
7087
result[layer.name]["geometry"] = f"""ALTER TABLE "{layer.schema}"."{layer.name}" ALTER COLUMN geom TYPE Geometry({layer.derive_geometry_type()});"""
7188

src/tabor_layer.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from geometry_type import GeometryType
2+
from constraint import Constraint
23
from tabor_field import TaborField
34

45

56
class TaborLayer(object):
6-
def __init__(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict) -> None:
7+
def __init__(self, name: str, schema: str, geometry: str | None, owner: str, fields: dict, constraints: dict) -> None:
78
self.name = name
89
self.schema = schema
910

@@ -12,6 +13,11 @@ def __init__(self, name: str, schema: str, geometry: str | None, owner: str, fie
1213
else:
1314
self.geometry = None
1415

16+
self.constraints: list[Constraint] = []
17+
if constraints:
18+
for constraint in constraints:
19+
self.constraints.append(Constraint(constraint, self.name))
20+
1521
self.owner = owner
1622
self.fields: list[TaborField] = []
1723

0 commit comments

Comments
 (0)