Skip to content

Commit 68cc257

Browse files
authored
Merge pull request #123 from UW-Macrostrat/line-orientations
Line orientations
2 parents e63ea67 + 7db87f4 commit 68cc257

File tree

5 files changed

+127
-11
lines changed

5 files changed

+127
-11
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS raster_url text;
2+
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS scale_denominator integer;
3+
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS is_finalized boolean DEFAULT false;
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""
2+
Macrostrat line orientation management
3+
"""
4+
5+
from macrostrat.core.migrations import Migration, has_columns
6+
from macrostrat.database import Database
7+
8+
_has_column = has_columns("maps", "sources", "lines_oriented")
9+
10+
11+
class MapsLinesOriented(Migration):
12+
name = "maps-lines-oriented"
13+
subsystem = "maps"
14+
description = "Create a flag for line orientations in maps.sources table."
15+
16+
depends_on = ["baseline"]
17+
18+
postconditions = [_has_column]
19+
20+
def apply(self, db: Database):
21+
db.run_sql("ALTER TABLE maps.sources ADD COLUMN lines_oriented boolean")
22+
23+
24+
# Legacy maps with consistently-oriented linework that needs to be reversed
25+
valid_maps = [229, 210, 74, 75, 40, 205, 154]
26+
27+
# Legacy maps with consistently-oriented linework that does not need to be reversed
28+
reversed_maps = [4]
29+
# Note: we have flipped the logic here relative to how we did this in the original iteration
30+
# of the system, to aloign with FGDC recommendations (we think)
31+
32+
33+
def matching_sources(
34+
db: Database, sources: list[int], condition: str = "true"
35+
) -> list[int]:
36+
return list(
37+
db.run_query(
38+
f"SELECT source_id FROM maps.sources WHERE source_id = ANY(:sources) AND {condition}",
39+
dict(sources=sources),
40+
).scalars()
41+
)
42+
43+
44+
def some_maps_are_unoriented(db: Database) -> bool:
45+
if not _has_column(db):
46+
return False
47+
ids = matching_sources(
48+
db, valid_maps + reversed_maps, "NOT coalesce(lines_oriented, false)"
49+
)
50+
return len(ids) > 0
51+
52+
53+
def all_maps_are_oriented(db: Database) -> bool:
54+
if not _has_column(db):
55+
return False
56+
_all_maps = valid_maps + reversed_maps
57+
ids = matching_sources(db, _all_maps, "coalesce(lines_oriented, false)")
58+
return len(ids) == len(_all_maps)
59+
60+
61+
class MapsLinesOrientedDataMigration(Migration):
62+
name = "maps-lines-oriented-data"
63+
subsystem = "maps"
64+
depends_on = ["maps-lines-oriented"]
65+
66+
destructive = True
67+
68+
preconditions = [some_maps_are_unoriented]
69+
postconditions = [all_maps_are_oriented]
70+
71+
def apply(self, db: Database):
72+
# Get the maps that aren't oriented but appear in our list of maps to migrate
73+
all_maps = valid_maps + reversed_maps
74+
unoriented_maps = matching_sources(
75+
db, all_maps, "NOT coalesce(lines_oriented, false)"
76+
)
77+
for source_id in unoriented_maps:
78+
if source_id in reversed_maps:
79+
db.run_sql(
80+
"UPDATE maps.lines SET geom = ST_Reverse(geom) WHERE source_id = :source_id",
81+
dict(source_id=source_id),
82+
)
83+
db.run_sql(
84+
"UPDATE maps.sources SET lines_oriented = true WHERE source_id = :source_id",
85+
dict(source_id=source_id),
86+
)

cli/macrostrat/cli/database/migrations/map_sources/01-maps-sources.sql renamed to cli/macrostrat/cli/database/migrations/maps_sources_api/01-maps-sources.sql

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS raster_url text;
2-
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS scale_denominator integer;
3-
ALTER TABLE maps.sources ADD COLUMN IF NOT EXISTS is_finalized boolean DEFAULT false;
4-
5-
DROP VIEW IF EXISTS macrostrat_api.sources_metadata CASCADE;
6-
DROP VIEW IF EXISTS maps.sources_metadata CASCADE;
7-
DROP VIEW IF EXISTS macrostrat_api.sources CASCADE;
1+
DROP VIEW IF EXISTS macrostrat_api.sources_metadata;
2+
DROP VIEW IF EXISTS maps.sources_metadata;
3+
DROP VIEW IF EXISTS macrostrat_api.sources;
84

95
CREATE OR REPLACE VIEW maps.sources_metadata AS
106
SELECT
@@ -26,7 +22,8 @@ SELECT
2622
status_code,
2723
raster_url,
2824
scale_denominator,
29-
is_finalized
25+
is_finalized,
26+
lines_oriented
3027
FROM maps.sources AS s
3128
ORDER BY source_id DESC;
3229

@@ -89,5 +86,6 @@ SELECT
8986
s.raster_url,
9087
s.web_geom envelope,
9188
s.is_finalized,
92-
s.scale_denominator
89+
s.scale_denominator,
90+
s.lines_oriented
9391
FROM maps.sources s;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from macrostrat.core.migrations import Migration, has_columns, view_exists
2+
3+
4+
class MapsSourcesAPIMigration(Migration):
5+
name = "maps-sources-api"
6+
subsystem = "maps"
7+
description = """
8+
Create views for sources_metadata and ingest_process in the maps and macrostrat_api schemas
9+
"""
10+
11+
depends_on = ["baseline"]
12+
13+
postconditions = [
14+
view_exists(
15+
"macrostrat_api", "sources_metadata", "sources_ingestion", "sources"
16+
),
17+
has_columns(
18+
"macrostrat_api",
19+
"sources",
20+
"is_finalized",
21+
"scale_denominator",
22+
"lines_oriented",
23+
allow_view=True,
24+
),
25+
]

core/macrostrat/core/migrations/__init__.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,15 @@ def custom_type_exists(schema: str, *type_names: str) -> DbEvaluator:
5858
return lambda db: all(db.inspector.has_type(t, schema=schema) for t in type_names)
5959

6060

61-
def has_columns(schema: str, table: str, *fields: str) -> DbEvaluator:
61+
def has_columns(schema: str, table: str, *fields: str, allow_view=False) -> DbEvaluator:
6262
"""Return a function that evaluates to true when every given field in the given table exists"""
6363

6464
def _has_fields(db: Database) -> bool:
65-
if not db.inspector.has_table(table, schema=schema):
65+
_has_table = db.inspector.has_table(table, schema=schema)
66+
if not _has_table and not allow_view:
67+
return False
68+
_has_view = table in db.inspector.get_view_names(schema)
69+
if not _has_table and not _has_view:
6670
return False
6771
columns = db.inspector.get_columns(table, schema=schema)
6872
col_names = [c["name"] for c in columns]

0 commit comments

Comments
 (0)