Connor Sutton commited on
Commit
59879a5
β€’
1 Parent(s): a489e19

added conversion tests

Browse files
.github/workflows/docker-hub.yml CHANGED
@@ -23,9 +23,9 @@ jobs:
23
  - name: Build Docker image
24
  run: |
25
  docker build \
26
- -t joshuasundance/geospatial-data-converter:${{ github.ref_name }} \
27
- -t joshuasundance/geospatial-data-converter:latest \
28
  .
29
 
30
  - name: Push to Docker Hub
31
- run: docker push -a joshuasundance/geospatial-data-converter
 
23
  - name: Build Docker image
24
  run: |
25
  docker build \
26
+ -t joshuasundance/geospatial_data_converter:${{ github.ref_name }} \
27
+ -t joshuasundance/geospatial_data_converter:latest \
28
  .
29
 
30
  - name: Push to Docker Hub
31
+ run: docker push -a joshuasundance/geospatial_data_converter
.github/workflows/hf-space.yml CHANGED
@@ -18,4 +18,4 @@ jobs:
18
  env:
19
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
  run: |
21
- git push https://joshuasundance:[email protected]/spaces/joshuasundance/geospatial-data-converter main
 
18
  env:
19
  HF_TOKEN: ${{ secrets.HF_TOKEN }}
20
  run: |
21
+ git push https://joshuasundance:[email protected]/spaces/joshuasundance/geospatial_data_converter main
Dockerfile CHANGED
@@ -12,9 +12,9 @@ COPY ./requirements.txt /home/appuser/requirements.txt
12
  RUN pip install --user --no-cache-dir --upgrade -r /home/appuser/requirements.txt
13
 
14
  COPY geospatial_data_converter/ /home/appuser/geospatial_data_converter/
 
15
 
16
  WORKDIR /workspace
17
  EXPOSE 7860
18
 
19
  CMD ["streamlit", "run", "/home/appuser/geospatial_data_converter/app.py", "--server.port", "7860", "--server.address", "0.0.0.0", "--server.enableXsrfProtection=false"]
20
- #CMD ["/bin/bash"]
 
12
  RUN pip install --user --no-cache-dir --upgrade -r /home/appuser/requirements.txt
13
 
14
  COPY geospatial_data_converter/ /home/appuser/geospatial_data_converter/
15
+ COPY tests/ /home/appuser/tests/
16
 
17
  WORKDIR /workspace
18
  EXPOSE 7860
19
 
20
  CMD ["streamlit", "run", "/home/appuser/geospatial_data_converter/app.py", "--server.port", "7860", "--server.address", "0.0.0.0", "--server.enableXsrfProtection=false"]
 
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: geospatial-data-converter
3
  emoji: 🌎
4
  colorFrom: green
5
  colorTo: blue
@@ -9,21 +9,21 @@ pinned: true
9
  tags: [geospatial, streamlit, docker]
10
  ---
11
 
12
- # geospatial-data-converter
13
 
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
15
  [![python](https://img.shields.io/badge/Python-3.11-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
16
- ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/joshuasundance-swca/geospatial-data-converter)
17
 
18
- [![Push to Docker Hub](https://github.com/joshuasundance-swca/geospatial-data-converter/actions/workflows/docker-hub.yml/badge.svg)](https://github.com/joshuasundance-swca/geospatial-data-converter/actions/workflows/docker-hub.yml)
19
- [![Docker Image Size (tag)](https://img.shields.io/docker/image-size/joshuasundance/geospatial-data-converter/latest)](https://hub.docker.com/r/joshuasundance/geospatial-data-converter)
20
 
21
- [![Push to HuggingFace Space](https://github.com/joshuasundance-swca/geospatial-data-converter/actions/workflows/hf-space.yml/badge.svg)](https://github.com/joshuasundance-swca/geospatial-data-converter/actions/workflows/hf-space.yml)
22
- [![Open HuggingFace Space](https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg)](https://huggingface.co/spaces/joshuasundance/geospatial-data-converter)
23
 
24
- ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/joshuasundance-swca/geospatial-data-converter)
25
- ![Code Climate issues](https://img.shields.io/codeclimate/issues/joshuasundance-swca/geospatial-data-converter)
26
- ![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/joshuasundance-swca/geospatial-data-converter)
27
 
28
  [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
29
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)
@@ -31,7 +31,7 @@ tags: [geospatial, streamlit, docker]
31
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
32
 
33
  [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
34
- ![Known Vulnerabilities](https://snyk.io/test/github/joshuasundance-swca/geospatial-data-converter/badge.svg)
35
 
36
  This project showcases a simple geospatial data converter using [Streamlit](https://streamlit.io) and [GeoPandas](https://geopandas.org/).
37
 
@@ -45,11 +45,11 @@ This project showcases a simple geospatial data converter using [Streamlit](http
45
  - Download button for the converted data
46
 
47
  # Deployment
48
- `geospatial-data-converter` is deployed as a [Docker image](https://hub.docker.com/r/<your-dockerhub-username>/geospatial-data-converter) based on the `python:3.11-slim-bookworm` image.
49
 
50
  ## With Docker (pull from Docker Hub)
51
  1. Run in terminal:
52
- `docker run -p 7860:7860 <your-dockerhub-username>/geospatial-data-converter:latest`
53
  2. Open http://localhost:8501 in your browser
54
 
55
  ## Docker Compose (build locally)
@@ -60,7 +60,7 @@ This project showcases a simple geospatial data converter using [Streamlit](http
60
  ## Kubernetes
61
  1. Clone the repo. Navigate to cloned repo directory
62
  2. Run bash script: `/bin/bash ./kubernetes/deploy.sh`
63
- 3. Get the IP address for your new service: `kubectl get service geospatial-data-converter`
64
 
65
  # Links
66
  - [Streamlit](https://streamlit.io)
 
1
  ---
2
+ title: geospatial_data_converter
3
  emoji: 🌎
4
  colorFrom: green
5
  colorTo: blue
 
9
  tags: [geospatial, streamlit, docker]
10
  ---
11
 
12
+ # geospatial_data_converter
13
 
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
15
  [![python](https://img.shields.io/badge/Python-3.11-3776AB.svg?style=flat&logo=python&logoColor=white)](https://www.python.org)
16
+ ![GitHub tag (with filter)](https://img.shields.io/github/v/tag/joshuasundance-swca/geospatial_data_converter)
17
 
18
+ [![Push to Docker Hub](https://github.com/joshuasundance-swca/geospatial_data_converter/actions/workflows/docker-hub.yml/badge.svg)](https://github.com/joshuasundance-swca/geospatial_data_converter/actions/workflows/docker-hub.yml)
19
+ [![Docker Image Size (tag)](https://img.shields.io/docker/image-size/joshuasundance/geospatial_data_converter/latest)](https://hub.docker.com/r/joshuasundance/geospatial_data_converter)
20
 
21
+ [![Push to HuggingFace Space](https://github.com/joshuasundance-swca/geospatial_data_converter/actions/workflows/hf-space.yml/badge.svg)](https://github.com/joshuasundance-swca/geospatial_data_converter/actions/workflows/hf-space.yml)
22
+ [![Open HuggingFace Space](https://huggingface.co/datasets/huggingface/badges/raw/main/open-in-hf-spaces-sm.svg)](https://huggingface.co/spaces/joshuasundance/geospatial_data_converter)
23
 
24
+ ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/joshuasundance-swca/geospatial_data_converter)
25
+ ![Code Climate issues](https://img.shields.io/codeclimate/issues/joshuasundance-swca/geospatial_data_converter)
26
+ ![Code Climate technical debt](https://img.shields.io/codeclimate/tech-debt/joshuasundance-swca/geospatial_data_converter)
27
 
28
  [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
29
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)
 
31
  [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
32
 
33
  [![security: bandit](https://img.shields.io/badge/security-bandit-yellow.svg)](https://github.com/PyCQA/bandit)
34
+ ![Known Vulnerabilities](https://snyk.io/test/github/joshuasundance-swca/geospatial_data_converter/badge.svg)
35
 
36
  This project showcases a simple geospatial data converter using [Streamlit](https://streamlit.io) and [GeoPandas](https://geopandas.org/).
37
 
 
45
  - Download button for the converted data
46
 
47
  # Deployment
48
+ `geospatial_data_converter` is deployed as a [Docker image](https://hub.docker.com/r/<your-dockerhub-username>/geospatial_data_converter) based on the `python:3.11-slim-bookworm` image.
49
 
50
  ## With Docker (pull from Docker Hub)
51
  1. Run in terminal:
52
+ `docker run -p 7860:7860 <your-dockerhub-username>/geospatial_data_converter:latest`
53
  2. Open http://localhost:8501 in your browser
54
 
55
  ## Docker Compose (build locally)
 
60
  ## Kubernetes
61
  1. Clone the repo. Navigate to cloned repo directory
62
  2. Run bash script: `/bin/bash ./kubernetes/deploy.sh`
63
+ 3. Get the IP address for your new service: `kubectl get service geospatial_data_converter`
64
 
65
  # Links
66
  - [Streamlit](https://streamlit.io)
bumpver.toml CHANGED
@@ -14,5 +14,5 @@ push = true
14
  "bumpver.toml" = [
15
  'current_version = "{version}"',
16
  ]
17
- "geospatial-data-converter/app.py" = ['__version__ = "{version}"']
18
- "kubernetes/resources.yaml" = [' image: joshuasundance/geospatial-data-converter:{version}']
 
14
  "bumpver.toml" = [
15
  'current_version = "{version}"',
16
  ]
17
+ "geospatial_data_converter/app.py" = ['__version__ = "{version}"']
18
+ "kubernetes/resources.yaml" = [' image: joshuasundance/geospatial_data_converter:{version}']
docker-compose.yml CHANGED
@@ -1,8 +1,8 @@
1
  version: '3.8'
2
 
3
  services:
4
- geospatial-data-converter:
5
- image: geospatial-data-converter:latest
6
  build: .
7
  ports:
8
  - "${APP_PORT:-7860}:${APP_PORT:-7860}"
@@ -14,3 +14,8 @@ services:
14
  "--server.address", "0.0.0.0",
15
  "--server.enableXsrfProtection=false"
16
  ]
 
 
 
 
 
 
1
  version: '3.8'
2
 
3
  services:
4
+ geospatial_data_converter:
5
+ image: geospatial_data_converter:latest
6
  build: .
7
  ports:
8
  - "${APP_PORT:-7860}:${APP_PORT:-7860}"
 
14
  "--server.address", "0.0.0.0",
15
  "--server.enableXsrfProtection=false"
16
  ]
17
+ test:
18
+ image: geospatial_data_converter:latest # Use the same image as your main service
19
+ build: .
20
+ working_dir: /home/appuser
21
+ command: ["/bin/bash"]
geospatial_data_converter/app.py CHANGED
@@ -6,7 +6,10 @@ import streamlit as st
6
  from aiohttp import ClientSession
7
  from restgdf import FeatureLayer
8
 
9
- from utils import read_file, convert, output_format_dict
 
 
 
10
 
11
  __version__ = "0.1.1"
12
 
 
6
  from aiohttp import ClientSession
7
  from restgdf import FeatureLayer
8
 
9
+ try:
10
+ from utils import read_file, convert, output_format_dict
11
+ except ImportError:
12
+ from .utils import read_file, convert, output_format_dict
13
 
14
  __version__ = "0.1.1"
15
 
geospatial_data_converter/kml_tricks.py CHANGED
@@ -131,7 +131,7 @@ def extract_data_from_kml_code(kml_code: str) -> pd.DataFrame:
131
  """Extracts data from KML code into a DataFrame using SimpleData tags, excluding embedded tables in feature descriptions"""
132
 
133
  # Parse the KML source code
134
- soup = bs4.BeautifulSoup(kml_code, "html.parser")
135
 
136
  # Find all SchemaData tags (representing rows)
137
  schema_data_tags = soup.find_all("schemadata")
 
131
  """Extracts data from KML code into a DataFrame using SimpleData tags, excluding embedded tables in feature descriptions"""
132
 
133
  # Parse the KML source code
134
+ soup = bs4.BeautifulSoup(kml_code, features="xml")
135
 
136
  # Find all SchemaData tags (representing rows)
137
  schema_data_tags = soup.find_all("schemadata")
geospatial_data_converter/tests/test_kml.py DELETED
@@ -1,16 +0,0 @@
1
- import sys
2
-
3
- sys.path.append("..")
4
- from utils import convert, read_file
5
-
6
-
7
- def test_kml_coversion():
8
- with open("boundary.kml", "rb") as f:
9
- kml = read_file(f)
10
- converted_data = convert(kml, "boundary_converted.kml", "kml")
11
- with open("boundary_converted.kml", "wb") as f:
12
- f.write(converted_data)
13
-
14
-
15
- def test_kmz_conversion():
16
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
geospatial_data_converter/utils.py CHANGED
@@ -5,7 +5,10 @@ from tempfile import TemporaryDirectory
5
  from typing import BinaryIO
6
  import geopandas as gpd
7
 
8
- from kml_tricks import load_ge_data
 
 
 
9
 
10
  output_format_dict = {
11
  "ESRI Shapefile": ("shp", "zip", "application/zip"), # must be zipped
@@ -22,7 +25,7 @@ def read_file(file: BinaryIO, *args, **kwargs) -> gpd.GeoDataFrame:
22
  ext = ext.lower().strip(".")
23
  if ext == "zip":
24
  with TemporaryDirectory() as tmp_dir:
25
- tmp_file_path = os.path.join(tmp_dir, file.name)
26
  with open(tmp_file_path, "wb") as tmp_file:
27
  tmp_file.write(file.read())
28
  return gpd.read_file(
@@ -33,7 +36,10 @@ def read_file(file: BinaryIO, *args, **kwargs) -> gpd.GeoDataFrame:
33
  )
34
  elif ext in ("kml", "kmz"):
35
  with TemporaryDirectory() as tmp_dir:
36
- tmp_file_path = os.path.join(tmp_dir, file.name)
 
 
 
37
  with open(tmp_file_path, "wb") as tmp_file:
38
  tmp_file.write(file.read())
39
  return load_ge_data(tmp_file_path)
@@ -60,7 +66,6 @@ def convert(gdf: gpd.GeoDataFrame, output_name: str, output_format: str) -> byte
60
  """Convert a GeoDataFrame to the specified format"""
61
  with TemporaryDirectory() as tmpdir:
62
  out_path = os.path.join(tmpdir, output_name)
63
-
64
  if output_format == "CSV":
65
  gdf.to_csv(out_path)
66
  else:
 
5
  from typing import BinaryIO
6
  import geopandas as gpd
7
 
8
+ try:
9
+ from kml_tricks import load_ge_data
10
+ except ImportError:
11
+ from .kml_tricks import load_ge_data
12
 
13
  output_format_dict = {
14
  "ESRI Shapefile": ("shp", "zip", "application/zip"), # must be zipped
 
25
  ext = ext.lower().strip(".")
26
  if ext == "zip":
27
  with TemporaryDirectory() as tmp_dir:
28
+ tmp_file_path = os.path.join(tmp_dir, f"{basename}.{ext}")
29
  with open(tmp_file_path, "wb") as tmp_file:
30
  tmp_file.write(file.read())
31
  return gpd.read_file(
 
36
  )
37
  elif ext in ("kml", "kmz"):
38
  with TemporaryDirectory() as tmp_dir:
39
+ tmp_file_path = os.path.join(tmp_dir, f"{basename}.{ext}")
40
+ print(file.name)
41
+ print(basename)
42
+ print(tmp_file_path)
43
  with open(tmp_file_path, "wb") as tmp_file:
44
  tmp_file.write(file.read())
45
  return load_ge_data(tmp_file_path)
 
66
  """Convert a GeoDataFrame to the specified format"""
67
  with TemporaryDirectory() as tmpdir:
68
  out_path = os.path.join(tmpdir, output_name)
 
69
  if output_format == "CSV":
70
  gdf.to_csv(out_path)
71
  else:
kubernetes/resources.yaml CHANGED
@@ -1,20 +1,20 @@
1
  apiVersion: apps/v1
2
  kind: Deployment
3
  metadata:
4
- name: geospatial-data-converter-deployment
5
  spec:
6
  replicas: 1
7
  selector:
8
  matchLabels:
9
- app: geospatial-data-converter
10
  template:
11
  metadata:
12
  labels:
13
- app: geospatial-data-converter
14
  spec:
15
  containers:
16
- - name: geospatial-data-converter
17
- image: joshuasundance/geospatial-data-converter:0.1.1
18
  imagePullPolicy: Always
19
  resources:
20
  requests:
@@ -29,15 +29,15 @@ spec:
29
  apiVersion: v1
30
  kind: Service
31
  metadata:
32
- name: geospatial-data-converter-service
33
  # configure on Azure and uncomment below to use a vnet
34
  # annotations:
35
  # service.beta.kubernetes.io/azure-load-balancer-internal: "true"
36
  # service.beta.kubernetes.io/azure-load-balancer-ipv4: vnet.ip.goes.here
37
- # service.beta.kubernetes.io/azure-dns-label-name: "geospatial-data-converter"
38
  spec:
39
  selector:
40
- app: geospatial-data-converter
41
  ports:
42
  - protocol: TCP
43
  port: 80
@@ -47,11 +47,11 @@ spec:
47
  apiVersion: networking.k8s.io/v1
48
  kind: NetworkPolicy
49
  metadata:
50
- name: geospatial-data-converter-network-policy
51
  spec:
52
  podSelector:
53
  matchLabels:
54
- app: geospatial-data-converter
55
  policyTypes:
56
  - Ingress
57
  ingress:
 
1
  apiVersion: apps/v1
2
  kind: Deployment
3
  metadata:
4
+ name: geospatial_data_converter-deployment
5
  spec:
6
  replicas: 1
7
  selector:
8
  matchLabels:
9
+ app: geospatial_data_converter
10
  template:
11
  metadata:
12
  labels:
13
+ app: geospatial_data_converter
14
  spec:
15
  containers:
16
+ - name: geospatial_data_converter
17
+ image: joshuasundance/geospatial_data_converter:0.1.1
18
  imagePullPolicy: Always
19
  resources:
20
  requests:
 
29
  apiVersion: v1
30
  kind: Service
31
  metadata:
32
+ name: geospatial_data_converter-service
33
  # configure on Azure and uncomment below to use a vnet
34
  # annotations:
35
  # service.beta.kubernetes.io/azure-load-balancer-internal: "true"
36
  # service.beta.kubernetes.io/azure-load-balancer-ipv4: vnet.ip.goes.here
37
+ # service.beta.kubernetes.io/azure-dns-label-name: "geospatial_data_converter"
38
  spec:
39
  selector:
40
+ app: geospatial_data_converter
41
  ports:
42
  - protocol: TCP
43
  port: 80
 
47
  apiVersion: networking.k8s.io/v1
48
  kind: NetworkPolicy
49
  metadata:
50
+ name: geospatial_data_converter-network-policy
51
  spec:
52
  podSelector:
53
  matchLabels:
54
+ app: geospatial_data_converter
55
  policyTypes:
56
  - Ingress
57
  ingress:
requirements.txt CHANGED
@@ -3,5 +3,6 @@ beautifulsoup4==4.12.2
3
  geopandas==0.14.1
4
  lxml==4.9.3
5
  pyogrio==0.7.2
 
6
  restgdf==0.9.7
7
  streamlit==1.28.2
 
3
  geopandas==0.14.1
4
  lxml==4.9.3
5
  pyogrio==0.7.2
6
+ pytest
7
  restgdf==0.9.7
8
  streamlit==1.28.2
tests/__init__.py ADDED
File without changes
tests/test_conversions.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pytest
3
+ from geospatial_data_converter.utils import convert, read_file
4
+
5
+ input_exts = ["kml", "kmz", "geojson", "zip"]
6
+ output_exts = ["KML", "ESRI Shapefile", "GeoJSON", "CSV", "OpenFileGDB"]
7
+ output_format_dict = {
8
+ "ESRI Shapefile": "shp",
9
+ "GeoJSON": "geojson",
10
+ "CSV": "csv",
11
+ "KML": "kml",
12
+ "OpenFileGDB": "gdb",
13
+ }
14
+
15
+
16
+ @pytest.mark.parametrize("in_ext", input_exts)
17
+ @pytest.mark.parametrize("out_ext", output_exts)
18
+ def test_kml_coversion(in_ext: str, out_ext: str) -> None:
19
+ test_file = f"test.{in_ext}"
20
+ test_file_path = os.path.join(os.getcwd(), "tests", "test_data", test_file)
21
+ with open(test_file_path, "rb") as f:
22
+ kml = read_file(f)
23
+ out_file = f"test.{output_format_dict[out_ext]}"
24
+ converted_data = convert(kml, out_file, out_ext)
25
+ with open("test.kml", "wb") as f:
26
+ f.write(converted_data)
tests/test_data/test.geojson ADDED
The diff for this file is too large to render. See raw diff
 
geospatial_data_converter/tests/boundary.kml β†’ tests/test_data/test.kml RENAMED
The diff for this file is too large to render. See raw diff
 
geospatial_data_converter/tests/testpoint_ocean.kmz β†’ tests/test_data/test.kmz RENAMED
File without changes
tests/test_data/test.zip ADDED
Binary file (96.7 kB). View file