Skip to content

Commit f4f922f

Browse files
authored
Merge pull request #13 from OpenGeoMetadata/feature/opengeometadata-identity-cleanup
Rename project identity to OpenGeoMetadata
2 parents 96d7f8b + 7ba492f commit f4f922f

245 files changed

Lines changed: 966 additions & 787 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: lint format lint-check test test-fast test-no-coverage migrate reindex gazetteers ogm-nightly cache-prime cache-prime-background kamal-registry-login transfer-readiness transfer-readiness-full
1+
.PHONY: lint format lint-check identity-check test test-fast test-no-coverage migrate reindex gazetteers ogm-nightly cache-prime cache-prime-background kamal-registry-login transfer-readiness transfer-readiness-full
22

33
BACKEND_DIR = backend
44

@@ -16,6 +16,9 @@ lint-check:
1616
cd $(BACKEND_DIR) && ruff format --check app tests scripts
1717
cd $(BACKEND_DIR) && ruff check app tests scripts
1818

19+
identity-check:
20+
./scripts/verify_identity_cleanup.sh
21+
1922
test:
2023
@echo "Running backend test suite..."
2124
cd $(BACKEND_DIR) && pytest --full-trace

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ OpenGeoMetadata API is a deployable search, harvest, and delivery service for
44
public [OpenGeoMetadata](https://opengeometadata.org/) Aardvark records. It is
55
being prepared as the reference API node for the proposed
66
[OpenGeoMetadata API Mirror Network](https://github.com/OpenGeoMetadata/ogm-mirror-network).
7+
The current reference node is live at
8+
[ogm.geo4lib.app/api/docs](https://ogm.geo4lib.app/api/docs).
79

810
> [!IMPORTANT]
911
> The mirror-network proposal is a **Draft for Community Discussion**
@@ -62,9 +64,24 @@ public `GET` and `HEAD` routes.
6264
The backend began from the `geobtaa/api` backend subtree and now carries an
6365
OpenGeoMetadata-owned harvesting, branding, deployment, and cache overlay. It
6466
is not a Git fork with mergeable history. See
65-
[BTAA backend reconciliation](docs/upstream_reconciliation.md) for the pinned
67+
[Upstream backend reconciliation](docs/upstream_reconciliation.md) for the pinned
6668
source baseline, selective-port decisions, and accepted upstream fixes.
6769

70+
## Identity compatibility invariants
71+
72+
Product-facing names, routes, defaults, assets, and documentation use OGM or
73+
OpenGeoMetadata. A small number of historical strings remain deliberately:
74+
75+
- the production PostgreSQL database and populated gazetteer table retain
76+
their physical names until they can be changed with separate, staged data
77+
migrations;
78+
- API keys created with the previous fallback hash salt remain valid and are
79+
upgraded to the OGM salt after successful authentication; and
80+
- real upstream repository, GIN, linked-data, asset, and fixture-provenance
81+
URLs retain the names of the external resources they identify.
82+
83+
Run `make identity-check` to reject new, unapproved legacy branding.
84+
6885
## Local development
6986

7087
Copy the environment template:
@@ -154,10 +171,10 @@ Redis.
154171

155172
## Releases and upstream provenance
156173

157-
OGM product versions and BTAA backend provenance are separate:
174+
OGM product versions and upstream backend provenance are separate:
158175

159176
- the product version describes this repository's API contract and release;
160-
- `config/geobtaa-backend-source.env` records the last complete BTAA subtree
177+
- `config/upstream-backend-source.env` records the last complete upstream subtree
161178
import; and
162179
- `docs/upstream_reconciliation.md` records selective ports made after that
163180
import.
@@ -190,7 +207,7 @@ Read these before an operational change:
190207
- [Repository transfer runbook](docs/repository_transfer.md)
191208
- [Repository transfer rehearsal](docs/repository_transfer_rehearsal.md)
192209
- [Pre-transfer branch inventory](docs/repository_branch_inventory.md)
193-
- [BTAA backend reconciliation](docs/upstream_reconciliation.md)
210+
- [Upstream backend reconciliation](docs/upstream_reconciliation.md)
194211
- [Backend sync design](docs/backend_upstream_sync.md)
195212

196213
Never change the Kamal service name, persistent volume paths, repository owner,

backend/app/api/ogc/endpoints.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,17 +86,17 @@ async def get_collections(request: Request) -> Dict[str, Any]:
8686

8787

8888
@router.get(
89-
"/collections/btaa-records",
89+
"/collections/ogm-records",
9090
response_model=OGCCollectionResponse,
9191
responses=PUBLIC_ERROR_RESPONSES,
9292
)
9393
async def get_collection(request: Request) -> Dict[str, Any]:
9494
url = str(request.url)
95-
return OGCResponseProjector.build_collection(url, "btaa-records")
95+
return OGCResponseProjector.build_collection(url, "ogm-records")
9696

9797

9898
@router.get(
99-
"/collections/btaa-records/queryables",
99+
"/collections/ogm-records/queryables",
100100
response_model=OGCQueryablesResponse,
101101
responses=PUBLIC_ERROR_RESPONSES,
102102
)
@@ -106,7 +106,7 @@ async def get_queryables(request: Request) -> Dict[str, Any]:
106106

107107

108108
@router.get(
109-
"/collections/btaa-records/sortables",
109+
"/collections/ogm-records/sortables",
110110
response_model=OGCSortablesResponse,
111111
responses=PUBLIC_ERROR_RESPONSES,
112112
)
@@ -116,7 +116,7 @@ async def get_sortables(request: Request) -> Dict[str, Any]:
116116

117117

118118
@router.get(
119-
"/collections/btaa-records/items",
119+
"/collections/ogm-records/items",
120120
response_model=OGCFeatureCollectionResponse,
121121
responses=PUBLIC_ERROR_RESPONSES,
122122
)
@@ -142,19 +142,19 @@ async def get_items(
142142
limit=limit,
143143
sort=internal_sort,
144144
include_filters=include_filters,
145-
exclude_filters={}, # Empty dict to avoid parsing query params as BTAA filters
145+
exclude_filters={}, # Empty dict to avoid parsing query params as OGM filters
146146
request_query_params=None,
147147
)
148148

149149
if isinstance(results, dict) and "error" in results:
150150
logger.error("OGC search request failed in search service")
151151
raise HTTPException(status_code=503, detail="Elasticsearch search failed")
152152

153-
return OGCResponseProjector.build_items_response(url, results, page, limit, "btaa-records")
153+
return OGCResponseProjector.build_items_response(url, results, page, limit, "ogm-records")
154154

155155

156156
@router.get(
157-
"/collections/btaa-records/items/{recordId}",
157+
"/collections/ogm-records/items/{recordId}",
158158
response_model=OGCFeatureResponse,
159159
responses=PUBLIC_ERROR_RESPONSES,
160160
)
@@ -179,4 +179,4 @@ async def get_item(
179179
if not resource:
180180
raise HTTPException(status_code=404, detail="Resource not found")
181181

182-
return OGCResponseProjector.build_item(url, resource, "btaa-records")
182+
return OGCResponseProjector.build_item(url, resource, "ogm-records")

backend/app/api/v1/endpoint_modules/analytics.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ def _request_defaults(request: Request) -> Dict[str, Any]:
3131
headers = request.headers
3232
return {
3333
"visit_token": headers.get("X-Visit-Token"),
34-
"client_name": headers.get("X-BTAA-Client-Name"),
35-
"client_version": headers.get("X-BTAA-Client-Version"),
36-
"client_channel": headers.get("X-BTAA-Client-Channel"),
37-
"client_instance": headers.get("X-BTAA-Client-Instance"),
34+
"client_name": headers.get("X-OGM-Client-Name"),
35+
"client_version": headers.get("X-OGM-Client-Version"),
36+
"client_channel": headers.get("X-OGM-Client-Channel"),
37+
"client_instance": headers.get("X-OGM-Client-Instance"),
3838
"source_host": _extract_source_host(
3939
headers.get("Origin"),
4040
headers.get("Referer"),

backend/app/api/v1/endpoint_modules/gazetteer.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
)
2727
from db.database import database
2828
from db.models import (
29-
gazetteer_btaa,
3029
gazetteer_geonames,
30+
gazetteer_ogm,
3131
gazetteer_wof_ancestors,
3232
gazetteer_wof_concordances,
3333
gazetteer_wof_geojson,
@@ -62,7 +62,7 @@ async def list_gazetteers(
6262
select(func.count()).select_from(gazetteer_wof_spr)
6363
)
6464

65-
btaa_count = await database.fetch_val(select(func.count()).select_from(gazetteer_btaa))
65+
ogm_count = await database.fetch_val(select(func.count()).select_from(gazetteer_ogm))
6666

6767
# Additional WOF table counts
6868
wof_ancestors_count = await database.fetch_val(
@@ -110,13 +110,13 @@ async def list_gazetteers(
110110
},
111111
},
112112
{
113-
"id": "btaa",
113+
"id": "ogm",
114114
"type": "gazetteer",
115115
"attributes": {
116-
"name": "BTAA",
117-
"description": "Big Ten Academic Alliance Geoportal gazetteer",
118-
"record_count": btaa_count or 0,
119-
"website": "https://geo.btaa.org/",
116+
"name": "OGM",
117+
"description": "OpenGeoMetadata API gazetteer",
118+
"record_count": ogm_count or 0,
119+
"website": "https://opengeometadata.org/",
120120
},
121121
},
122122
]
@@ -147,16 +147,16 @@ async def search_all_gazetteers(
147147
return await search_geonames(request, q, limit, offset)
148148
elif gazetteer == "wof":
149149
return await search_wof(request, q, limit, offset)
150-
elif gazetteer == "btaa":
151-
return await search_btaa(request, q, limit, offset)
150+
elif gazetteer == "ogm":
151+
return await search_ogm(request, q, limit, offset)
152152
else:
153153
raise HTTPException(status_code=400, detail="Invalid gazetteer specified")
154154

155155
# Search all gazetteers
156156
results = {}
157157
results["geonames"] = await search_geonames(request, q, limit, offset)
158158
results["wof"] = await search_wof(request, q, limit, offset)
159-
results["btaa"] = await search_btaa(request, q, limit, offset)
159+
results["ogm"] = await search_ogm(request, q, limit, offset)
160160

161161
# Extract data from JSONResponse objects for the combined response
162162
combined_results = {}
@@ -214,15 +214,15 @@ async def search_nominatim(
214214
raise HTTPException(status_code=502, detail="Nominatim request failed") from exc
215215

216216

217-
@router.get("/gazetteers/btaa/search", response_model=JSONAPIResponse)
217+
@router.get("/gazetteers/ogm/search", response_model=JSONAPIResponse)
218218
@cached_endpoint(ttl=GAZETTEER_CACHE_TTL)
219-
async def search_btaa(
219+
async def search_ogm(
220220
request: Request,
221221
q: str = Query(..., description="Search query"),
222222
limit: int = Query(10, description="Maximum number of results", ge=1, le=100),
223223
offset: int = Query(0, description="Number of results to skip", ge=0),
224224
):
225-
"""Search BTAA gazetteer."""
225+
"""Search OGM gazetteer."""
226226
try:
227227
# Build search query
228228
search_terms = q.split()
@@ -231,14 +231,14 @@ async def search_btaa(
231231
for term in search_terms:
232232
conditions.append(
233233
or_(
234-
gazetteer_btaa.c.fast_area.ilike(f"%{term}%"),
234+
gazetteer_ogm.c.fast_area.ilike(f"%{term}%"),
235235
)
236236
)
237237

238238
query = (
239-
select(gazetteer_btaa)
239+
select(gazetteer_ogm)
240240
.where(and_(*conditions))
241-
.order_by(gazetteer_btaa.c.fast_area)
241+
.order_by(gazetteer_ogm.c.fast_area)
242242
.limit(limit)
243243
.offset(offset)
244244
)
@@ -255,14 +255,14 @@ async def search_btaa(
255255
# Format as JSON:API resource
256256
formatted_row = {
257257
"id": str(row_dict.get("id", "")),
258-
"type": "btaa",
258+
"type": "ogm",
259259
"attributes": row_dict,
260260
}
261261
data.append(formatted_row)
262262

263263
# Create meta and links using utility function with strong parameters
264264
meta, links = create_gazetteer_meta_and_links(
265-
request, q, limit, offset, len(data), "btaa", allowed_params=GAZETTEER_ALLOWED_PARAMS
265+
request, q, limit, offset, len(data), "ogm", allowed_params=GAZETTEER_ALLOWED_PARAMS
266266
)
267267

268268
# Create JSON:API compliant response
@@ -284,8 +284,8 @@ async def search_btaa(
284284
return JSONResponse(content=reordered_response)
285285

286286
except Exception as e:
287-
logger.error(f"Error searching BTAA: {str(e)}", exc_info=True)
288-
raise HTTPException(status_code=500, detail="Failed to search BTAA") from e
287+
logger.error(f"Error searching OGM: {str(e)}", exc_info=True)
288+
raise HTTPException(status_code=500, detail="Failed to search OGM") from e
289289

290290

291291
@router.get("/gazetteers/geonames/search", response_model=JSONAPIResponse)

backend/app/api/v1/endpoint_modules/resources/thumbnail.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ async def _probe_thumbnail_url(url: str) -> bool:
121121
Used to avoid serving a queued-thumbnail fallback when the source is 404 or invalid.
122122
"""
123123
try:
124-
headers = {"User-Agent": "BTAA-Geospatial-Data-API/1.0 (https://geo.btaa.org/)"}
124+
headers = {"User-Agent": "OpenGeoMetadata-API/1.0 (https://opengeometadata.org/)"}
125125
timeout = aiohttp.ClientTimeout(total=THUMBNAIL_PROBE_TIMEOUT)
126126
async with aiohttp.ClientSession(timeout=timeout) as session:
127127
async with session.get(url, headers=headers) as resp:

backend/app/api/v1/endpoint_modules/root.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ async def api_root(request: Request):
6464
"/api/v1/ogc/",
6565
"/api/v1/ogc/conformance",
6666
"/api/v1/ogc/collections",
67-
"/api/v1/ogc/collections/btaa-records",
68-
"/api/v1/ogc/collections/btaa-records/queryables",
69-
"/api/v1/ogc/collections/btaa-records/sortables",
70-
"/api/v1/ogc/collections/btaa-records/items",
71-
"/api/v1/ogc/collections/btaa-records/items/{recordId}",
67+
"/api/v1/ogc/collections/ogm-records",
68+
"/api/v1/ogc/collections/ogm-records/queryables",
69+
"/api/v1/ogc/collections/ogm-records/sortables",
70+
"/api/v1/ogc/collections/ogm-records/items",
71+
"/api/v1/ogc/collections/ogm-records/items/{recordId}",
7272
],
7373
},
7474
}

backend/app/api/v1/endpoint_modules/search.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ def _build_semantic_search_cache_key(
204204
return CacheService.generate_cache_key(
205205
SEARCH_RESULT_CACHE_NAMESPACE,
206206
version=SEARCH_RESULT_CACHE_VERSION,
207-
index=os.getenv("ELASTICSEARCH_INDEX", "btaa_geospatial_api"),
207+
index=os.getenv("ELASTICSEARCH_INDEX", "opengeometadata_api"),
208208
q=q or "",
209209
page=page,
210210
per_page=per_page,

backend/app/api/v1/endpoint_modules/shapefiles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
logger = logging.getLogger(__name__)
2323

2424
# DuckDB configuration
25-
DUCKDB_DATABASE_PATH = os.getenv("DUCKDB_DATABASE_PATH", "data/duckdb/btaa_ogm_api.duckdb")
25+
DUCKDB_DATABASE_PATH = os.getenv("DUCKDB_DATABASE_PATH", "data/duckdb/opengeometadata_api.duckdb")
2626

2727
# Ensure the DuckDB directory exists
2828
os.makedirs(os.path.dirname(DUCKDB_DATABASE_PATH), exist_ok=True)

backend/app/api/v1/endpoint_modules/slack.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
async def slack_info():
1616
return JSONResponse(
1717
content={
18-
"name": "BTAA Geoportal Slackbot",
18+
"name": "OpenGeoMetadata API Slackbot",
1919
"command_endpoint": "/api/v1/slack/commands",
2020
"configured": bool(os.getenv("SLACK_SIGNING_SECRET")),
2121
}

0 commit comments

Comments
 (0)