Skip to content

Commit 5ceef52

Browse files
authored
Merge pull request #7 from hydroshare/5631-copy-shx
5631 include associated files along with .shp
2 parents 5e930e3 + 2a11997 commit 5ceef52

4 files changed

Lines changed: 126 additions & 27 deletions

File tree

docker-compose.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ services:
1717
restart: unless-stopped
1818

1919
geoserver:
20-
image: oscarfonts/geoserver:2.24.1
20+
image: oscarfonts/geoserver:2.24.5
2121
environment:
2222
- CUSTOM_UID={{IRODS_ACCESS_UID}} # A user ID on the host with read permissions to the iRODS directory
2323
- GEOSERVER_CSRF_WHITELIST={{HYDROSHARE_DOMAIN}} # e.g. hydroshare.org,hydroshare.io: https://docs.geoserver.org/latest/en/user/security/webadmin/csrf.html
2424
- CATALINA_OPTS=-server -Djava.awt.headless=true -Xms2g -Xmx10g -XX:NewSize=48m -DGEOSERVER_DATA_DIR=/var/local/geoserver
25+
# - GEOSERVER_CORS_ALLOWED_ORIGINS=*
2526
# https://github.com/oscarfonts/docker-geoserver/blob/master/2.24.1/Dockerfile#L35-L37
2627
# https://docs.geoserver.org/main/en/user/production/container.html
2728
volumes:

hs_data_services/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ channels:
55

66
dependencies:
77
- python=3.8
8+
- pip<24.1
89
- psycopg2=2.8.4
910
- gunicorn=20.0.4
1011
- lxml
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import requests
2+
import json
3+
from django.core.management.base import BaseCommand
4+
from hs_data_services import settings
5+
from hs_data_services_sync import utilities
6+
7+
8+
class Command(BaseCommand):
9+
help = "Unregister resource in GeoServer"
10+
11+
def add_arguments(self, parser):
12+
parser.add_argument('resource_ids', nargs='*', type=str)
13+
14+
def handle(self, *args, **options):
15+
resource_ids = options['resource_ids']
16+
if len(resource_ids) > 0:
17+
for resource_id in resource_ids:
18+
print(f"Unregistering resource: {resource_id}")
19+
result = utilities.unregister_geoserver_databases(resource_id)
20+
print(result)
21+
else:
22+
resources = self.get_list_of_public_geo_resources()
23+
num_resources = len(resources)
24+
print(f"Unregistering {num_resources} public resources")
25+
counter = 1
26+
for res_id in resources:
27+
print(f"{counter}/{num_resources} - Unregistering: {res_id}")
28+
result = utilities.unregister_geoserver_databases(res_id)
29+
print(result)
30+
counter += 1
31+
print("Done unregistering resources")
32+
33+
def get_list_of_public_geo_resources(self):
34+
hydroshare_url = "/".join(settings.HYDROSHARE_URL.split("/")[:-1])
35+
types = ["Geographic Feature (ESRI Shapefiles)", "Geographic Raster"]
36+
# replace spaces with + for the query string
37+
types = [t.replace(" ", "+") for t in types]
38+
params = {
39+
"type": types,
40+
"availability": ["public"],
41+
"geofilter": "false"
42+
}
43+
rest_url = f"{hydroshare_url}/discoverapi/?filter={json.dumps(params)}"
44+
rest_url = rest_url.replace(" ", "")
45+
print(f"Getting list of public geospatial resources from: {rest_url}")
46+
response = requests.get(rest_url)
47+
response_json = response.json()
48+
page_count = response_json.get('pagecount', 0)
49+
rescount = response_json.get('rescount', 0)
50+
perpage = response_json.get('perpage', 0)
51+
res_ids = []
52+
print(f"Iterating over {page_count} pages of {perpage} resources each, total resources: {rescount}")
53+
for i in range(1, page_count + 1):
54+
print(f"Getting page {i}")
55+
response = requests.get(f"{rest_url}&pnum={i}")
56+
response_json = response.json()
57+
resources = json.loads(response_json.get('resources', []))
58+
new_ids = [resource["short_id"] for resource in resources if resource.get('short_id', None)]
59+
res_ids.extend(new_ids)
60+
print(f"Found {len(res_ids)} public geospatial resources")
61+
print(f"Should match {rescount}")
62+
return res_ids

hs_data_services/hs_data_services_sync/utilities.py

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,29 @@ def update_data_services(resource_id):
4242
return response
4343
db_info = register_geoserver_db(resource_id, db)
4444
if db_info['success'] is False:
45+
logging.error("Error registering GeoServer layer. Unregistering...")
4546
unregister_geoserver_db(resource_id, db)
4647
# TODO: ideally we "inform" HS that the registration failed
4748
# This is called from an async task, so we can't return a meaningful response to HS
48-
49+
logging.info("Removing copied files from GeoServer...")
4950
remove_copied_file_from_geoserver(resource_id, db)
5051

5152
geoserver_list = get_geoserver_list(resource_id)
5253

5354
if not geoserver_list:
55+
logging.info("No GeoServer layers found. Unregistering workspace...")
5456
unregister_geoserver_databases(resource_id)
5557
remove_files_for_entire_resource(resource_id)
5658

5759
else:
60+
logging.info("Resource is private. Unregistering GeoServer databases...")
5861
unregister_geoserver_databases(resource_id)
5962
remove_files_for_entire_resource(resource_id)
6063

6164
response['success'] = True
6265
response['message'] = f'Successfully unregistered GeoServer data services for resource: {resource_id}'
6366

64-
logger.info(f"Successfully updated data services for resource: {resource_id}")
67+
logger.info(f"Completed attempt to update data services for resource: {resource_id}")
6568
return response
6669

6770

@@ -132,6 +135,16 @@ def get_database_list(res_id):
132135
if result["content_type"] == "application/x-qgis" and layer_ext == "shp":
133136
registered_list.append(layer_name.replace("/", " "))
134137
if layer_name.replace("/", " ") not in [i[0] for i in geoserver_list]:
138+
# get the associated .shx, .dbf, and .prj files
139+
extensions = [".shx", ".dbf", ".prj"]
140+
associated_files = []
141+
for ext in extensions:
142+
expected_url = result["url"].replace(".shp", ext)
143+
logger.info(f"Checking for associated file: {expected_url}")
144+
if expected_url in [i["url"] for i in file_list]:
145+
associated_files.append(
146+
"/".join(expected_url.split("/")[4:])
147+
)
135148
db_list["geoserver"]["register"].append(
136149
{
137150
"layer_name": layer_name,
@@ -141,7 +154,8 @@ def get_database_list(res_id):
141154
"hs_path": layer_path,
142155
"store_type": "datastores",
143156
"layer_group": "featuretypes",
144-
"verification": "featureType"
157+
"verification": "featureType",
158+
"associated_files": associated_files,
145159
}
146160
)
147161

@@ -265,7 +279,7 @@ def unregister_geoserver_databases(res_id):
265279
else:
266280
response = None
267281

268-
logger.info(f"Successfully unregistered GeoServer databases for resource: {res_id}")
282+
logger.info(f"Completed attempt at unregistering GeoServer databases for resource: {res_id}")
269283
return response
270284

271285

@@ -295,36 +309,48 @@ def copy_file_to_geoserver(res_id, db):
295309
"message": "Error: Unable to copy GeoServer files."
296310
}
297311

312+
def get_and_write_file(file_url, file_path):
313+
try:
314+
logger.info(f"Copying file to GeoServer from: {file_url}")
315+
response = requests.get(file_url)
316+
# create the directory if it doesn't exist
317+
dir_path = os.path.dirname(file_path) + "/"
318+
logger.info(f"Creating directory: {dir_path}")
319+
os.makedirs(os.path.dirname(dir_path), exist_ok=True)
320+
with open(file_path, 'w+b') as f:
321+
logger.info(f"Writing file to GeoServer: {file_path}")
322+
f.write(response.content)
323+
except Exception as e:
324+
message = f"Error getting/writing file: {e}"
325+
logger.error(message)
326+
raise Exception(message)
327+
298328
try:
299329
hydroshare_url = "/".join(settings.HYDROSHARE_URL.split("/")[:-1])
300330
file_url = f"{hydroshare_url}/resource/{db['hs_path']}"
301-
logger.info(f"Copying file to GeoServer from: {file_url}")
302-
response = requests.get(file_url)
303-
except Exception as e:
304-
message = f"Error requesting files from HydroShare: {e}"
305-
error_response["message"] = message
306-
logger.error(message)
307-
return error_response
308-
309-
# Now move the file in the response to the geoServer directory
310-
try:
311331
file_path = os.path.join(geoserver_directory, db["hs_path"])
312-
os.makedirs(os.path.dirname(file_path), exist_ok=True)
313-
with open(file_path, 'wb') as f:
314-
logger.info(f"Writing file to GeoServer: {file_path}")
315-
f.write(response.content)
332+
get_and_write_file(file_url, file_path)
333+
# Get not only the .shp file but also the .shx, .dbf, and .prj files
334+
# https://github.com/hydroshare/hydroshare/issues/5631
335+
logger.info(f"Checking for associated files for resource: {res_id}")
336+
if db.get("associated_files", None):
337+
for file in db["associated_files"]:
338+
logger.info(f"Copying associated file to GeoServer from: {file}")
339+
file_url = f"{hydroshare_url}/resource/{file}"
340+
file_path = os.path.join(geoserver_directory, file)
341+
get_and_write_file(file_url, file_path)
342+
logger.info(f"Successfully copied files to GeoServer for resource: {res_id}")
343+
return {
344+
"success": True,
345+
"type": layer_type,
346+
"layer_name": layer_name,
347+
"message": "Successfully copied GeoServer files."
348+
}
316349
except Exception as e:
317-
message = f"Error writing files to GeoServer: {e}"
350+
message = f"Error copying files to geoserver: {e}"
318351
error_response["message"] = message
319352
logger.error(message)
320353
return error_response
321-
logger.info(f"Successfully copied files to GeoServer for resource: {res_id}")
322-
return {
323-
"success": True,
324-
"type": layer_type,
325-
"layer_name": layer_name,
326-
"message": "Successfully copied GeoServer files."
327-
}
328354

329355

330356
def remove_copied_file_from_geoserver(res_id, db):
@@ -352,6 +378,10 @@ def remove_copied_file_from_geoserver(res_id, db):
352378
file_path = os.path.join(geoserver_directory, db["hs_path"])
353379
logger.info(f"Removing file from GeoServer: {file_path}")
354380
os.remove(file_path)
381+
for file in db["associated_files"]:
382+
file_path = os.path.join(geoserver_directory, file)
383+
logger.info(f"Removing associated file from GeoServer: {file_path}")
384+
os.remove(file_path)
355385
except Exception as e:
356386
message = f"Error removing files from geoserver: {e}"
357387
error_response["message"] = message
@@ -419,22 +449,26 @@ def register_geoserver_db(res_id, db):
419449
error_response = {"success": False, "type": db["layer_type"], "layer_name": db["layer_name"], "message": error_message}
420450

421451
if any(i in db['layer_name'] for i in [".", ","]):
452+
logging.error(f"Invalid layer name: {db['layer_name']}")
422453
return error_response
423454

424455
rest_url = f"{geoserver_url}/workspaces/{workspace_id}/{db['store_type']}/{str(db['layer_name']).replace('/', ' ')}/external.{db['file_type']}"
425456
data = f"file://{geoserver_directory}/{db['hs_path']}"
426457
response = requests.put(rest_url, data=data, headers=headers, auth=geoserver_auth)
427458

428459
if response.status_code != 201:
460+
logging.error(f"Error registering GeoServer layer at {rest_url}: {response}")
429461
return error_response
430462

431463
rest_url = f"{geoserver_url}/workspaces/{workspace_id}/{db['store_type']}/{str(db['layer_name']).replace('/', ' ')}/{db['layer_group']}/{db['file_name']}.json"
432464
response = requests.get(rest_url, headers=headers, auth=geoserver_auth)
433465

434466
try:
435467
if json.loads(response.content.decode('utf-8'))[db["verification"]]["enabled"] is False:
468+
logging.error(f"Verification not enabled for layer: {db['layer_name']}")
436469
return error_response
437470
except:
471+
logging.error(f"Error checking verification for layer: {db['layer_name']}")
438472
return error_response
439473

440474
bbox = json.loads(response.content)[db["verification"]]["nativeBoundingBox"]
@@ -443,6 +477,7 @@ def register_geoserver_db(res_id, db):
443477
response = requests.put(rest_url, headers=headers, auth=geoserver_auth, data=data)
444478

445479
if response.status_code != 200:
480+
logging.error(f"Error attempting to put layer data at {rest_url}")
446481
return error_response
447482

448483
if db["layer_type"] == "GeographicRaster":

0 commit comments

Comments
 (0)