Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
dcbe6dc
ENH: Add toml file for configuration
scranford1 May 19, 2025
c408709
Merge pull request #4 from scranford1/feature/toml
jdw-creare May 21, 2025
fce1403
ENH: Dev Container Setup
scranford1 May 21, 2025
49b4619
ENH: add unit tests
scranford1 May 21, 2025
5b99c3d
Update unit tests with url
scranford1 May 21, 2025
cdc4373
Setup with non-root user
scranford1 May 22, 2025
9e798ba
Merge pull request #5 from creare-com/feature/dev_container
jdw-creare May 22, 2025
78c89b9
Fix try catches and remove random usage
scranford1 May 22, 2025
e37e53b
Merge pull request #6 from creare-com/feature/unit_test
jdw-creare May 22, 2025
55cad74
constantized
jdw-creare May 17, 2025
1571892
Removed unreachable code that had duplicate constants
jdw-creare May 19, 2025
0d2d4e2
Fix a default parameter
jdw-creare May 20, 2025
dfced51
Specified exception class
jdw-creare May 20, 2025
6dc95e6
Removed unused arguments
jdw-creare May 20, 2025
85fddd9
Constantized
jdw-creare May 20, 2025
2c0dae6
Fixed sonarqube findings that can be fixed without refactoring
jdw-creare May 20, 2025
6bb3380
Merge pull request #7 from creare-com/feature/sq_findings
jdw-creare May 23, 2025
4dc6edc
Add workflows and adjust configuration
scranford1 May 23, 2025
22ab882
Change setuptools version
scranford1 May 23, 2025
93e303f
Fix versioning in workflow
scranford1 May 23, 2025
aad1db1
Merge pull request #8 from creare-com/feature/ci_unit_test
jdw-creare May 28, 2025
efd36c6
Fix SonarQube Findings
scranford1 Jun 5, 2025
0880db6
Merge pull request #9 from creare-com/chore/linting
jdw-creare Jun 6, 2025
3daba3b
Feature/sonarqube coverage report (#10)
scranford1 Sep 25, 2025
aa077ca
Add environmental data retrieval (EDR) capabilities
scranford1 Nov 26, 2025
4822f13
Improve EDR instance generation and minor cleanup
scranford1 Dec 8, 2025
08b7e3e
Fix EDR routing
scranford1 Dec 8, 2025
2c572cf
Resolve sonarqube findings
scranford1 Dec 8, 2025
5aa995b
Remove unneeded schema setting
scranford1 Dec 8, 2025
0daf606
Fix strict slashes
scranford1 Dec 11, 2025
927a936
Improve CRS error handling
scranford1 Dec 11, 2025
2a83f74
Fix multi-provider scenarios and limit api creation
scranford1 Dec 11, 2025
2189057
Fix name shadowing
scranford1 Dec 11, 2025
9e8d328
Fix cache issues with multiple EDR configurations
scranford1 Dec 12, 2025
bcc22a0
Merge pull request #11 from creare-com/feature/edr_server
jdw-creare Dec 12, 2025
3922eed
Allow format specification regardless of case
scranford1 Dec 18, 2025
46c760e
Fix configuration errors due to pickling failure
scranford1 Dec 18, 2025
ad24726
Fix numpy array errors in JSON encoding
scranford1 Dec 18, 2025
58d5775
Remove time offsets in output data
scranford1 Dec 19, 2025
e8d7eb3
Fix error messages not reaching user
scranford1 Dec 19, 2025
da5a637
Resolve sonarqube findings
scranford1 Dec 19, 2025
cbc450c
Merge pull request #12 from creare-com:bugfix/layer_configuration
jdw-creare Jan 2, 2026
3910e9b
Update valid CRS formats for EDR
scranford1 Jan 5, 2026
afeabcd
Add EDR links to default home endpoint
scranford1 Jan 6, 2026
8abf700
Update the EDR configuration server url with request urls
scranford1 Jan 7, 2026
6a4708a
Update default python version to 3.12
scranford1 Jan 8, 2026
47ca81d
Fix mapping and add default css
scranford1 Jan 8, 2026
77226db
Add URL configuration change test case
scranford1 Jan 8, 2026
63c99a7
Convert additional formats to lowercase
scranford1 Jan 8, 2026
0c26f7b
Merge pull request #13 from creare-com/feature/edr_crs
jdw-creare Jan 9, 2026
2353f69
Feature/edr compliance (#14)
scranford1 Feb 6, 2026
75ad465
Feature/edr conformance (#15)
scranford1 Feb 18, 2026
cf62944
Feature/query formats (#16)
scranford1 Mar 6, 2026
1db90ef
Bugfix/extent generation (#17)
scranford1 Mar 23, 2026
199b7c4
Convert coverage report to absolute paths
jdw-creare Apr 7, 2026
0b4c4c9
Added comment linking to PR with explanation
jdw-creare Apr 7, 2026
5d9f1bc
Merge pull request #18 from creare-com/feature/coverage_abs_path
jdw-creare Apr 7, 2026
c2ad77d
Removed unused imports
jdw-creare Apr 14, 2026
38bee03
Merge pull request #19 from creare-com/feature/linting_and_sq
jdw-creare Apr 15, 2026
9d8c86a
ENH: Allow OGC formats to be optionally supported
scranford1 Apr 17, 2026
355034b
FIX: Black formatting fixes
scranford1 Apr 21, 2026
19092f0
FIX: E741 ambiguous single-letter variable names
scranford1 Apr 21, 2026
12140ec
FIX: E266 too many leading '#' for block comment
scranford1 Apr 21, 2026
fff9a48
FIX: E401 multiple imports on one line
scranford1 Apr 21, 2026
d38bea7
CHORE: Remove passing formatting checks on failure
scranford1 Apr 21, 2026
ef72d7b
FIX: Sonarqube remove list comprehension with any
scranford1 Apr 21, 2026
ff8a77d
Add testing for WCS/WMS supported formats
scranford1 Apr 21, 2026
a39220e
Merge pull request #22 from creare-com/feature/ogc-format-options
jdw-creare Apr 21, 2026
e74fce3
Improve linting and formatting checks
scranford1 Apr 24, 2026
4cef1c2
Add flake8 bugbear and builtins
scranford1 Apr 24, 2026
b875cc7
FIX: B006 Remove mutable data structures as argument defaults
scranford1 Apr 24, 2026
ced82ee
FIX: A002 Name shadowing
scranford1 Apr 24, 2026
118f539
FIX: B023 Function definition does not bind loop variable
scranford1 Apr 24, 2026
3b3e7f0
FIX: A002 Name shadowing
scranford1 Apr 24, 2026
a823a1b
FIX: B042 Exception class with should pass all args...
scranford1 Apr 24, 2026
885a4b5
FIX: B018 Found useless Tuple expression
scranford1 Apr 24, 2026
e449f0e
Merge pull request #23 from creare-com/chore/formatting
jdw-creare Apr 24, 2026
e5001b1
checking input characters validity and escaping html
BAyotte Apr 29, 2026
ba7e14b
updating black formatting
BAyotte Apr 29, 2026
569f48a
Feature/time dimension (#21)
scranford1 Apr 29, 2026
ee40985
Merge pull request #25 from creare-com/feature/prevent-overflow
jdw-creare Apr 29, 2026
52e8ba1
Release v0.5.0
scranford1 Apr 29, 2026
96a6eff
Increment version for release 0.5.0
scranford1 Apr 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .devcontainer/dev_container.dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Base image for the development container
ARG BASE_URL=python:3.12-slim
FROM ${BASE_URL}

USER root

# We'll be running as a non-root user in a container and may want root permissions
RUN apt update && apt -y install nano ssh sudo && apt clean

# Install setup tools and dependencies
WORKDIR /app
COPY . /app
RUN pip install --upgrade pip setuptools && pip install .[dev]
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

# Set up user to match the host OS (https://stackoverflow.com/a/78621662/415551)
ARG HOST_USER
ARG HOST_UID
ARG HOST_GID

RUN addgroup --gid ${HOST_GID} ${HOST_USER} \

Check warning on line 20 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyg&open=AZ3aHgPGguP0YtWdsRyg&pullRequest=26

Check warning on line 20 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyh&open=AZ3aHgPGguP0YtWdsRyh&pullRequest=26
&& adduser --gecos "" --disabled-password --uid ${HOST_UID} --gid ${HOST_GID} ${HOST_USER} \

Check warning on line 21 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyj&open=AZ3aHgPGguP0YtWdsRyj&pullRequest=26

Check warning on line 21 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyi&open=AZ3aHgPGguP0YtWdsRyi&pullRequest=26

Check warning on line 21 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyk&open=AZ3aHgPGguP0YtWdsRyk&pullRequest=26
&& usermod -aG sudo ${HOST_USER} \

Check warning on line 22 in .devcontainer/dev_container.dockerfile

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Surround this variable with double quotes; otherwise, it can lead to unexpected behavior.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPGguP0YtWdsRyl&open=AZ3aHgPGguP0YtWdsRyl&pullRequest=26
&& echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers

ENV HOME /home/${HOST_USER}
ENV TMPDIR=/tmp
WORKDIR /home/${HOST_USER}

USER ${HOST_USER}
51 changes: 51 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "Unit Test Image",
"remoteUser": "${localEnv:USER}",
"build": {
// Sets the build context to one level up instead of the .devcontainer folder.
"context": "..",
"dockerfile": "dev_container.dockerfile",
"args": {
"HOST_USER": "${localEnv:USER}",
"HOST_UID": "${localEnv:HOST_UID:1000}",
"HOST_GID": "${localEnv:HOST_GID:1000}"
}
},
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/git:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"donjayamanne.git-extension-pack",
"eamodio.gitlens",
"mhutchie.git-graph",
"ms-azuretools.vscode-docker",
"ms-python.python",
"ms-python.debugpy",
"ms-python.black-formatter",
"ms-python.flake8",
"ms-python.vscode-pylance",
"njpwerner.autodocstring",
"redhat.vscode-yaml",
"SonarSource.sonarlint-vscode"
],
"settings": {
"flake8.args": [
"--max-line-length=120"
]
}
}
}
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Uncomment the next line to run commands after the container is created.
// "postCreateCommand": "cat /etc/os-release",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "devcontainer"
}
105 changes: 105 additions & 0 deletions .github/workflows/github-python-workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# This workflow handles linting, testing, and SonarQube scanning for the OGC repository.
name: Python Workflow

on:
push:
branches: [ main, develop ]
pull_request:
types: [ opened, synchronize, reopened ]

jobs:
lint:
name: Lint
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.12"

- name: Install dependencies
run: |
pip install --upgrade pip setuptools && pip install .[dev]
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

- name: Run flake8 lint checks
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --ignore=E,W,D,I,N806,N815,N818,Q000,Q001,Q002,S001,B008,B028 --show-source --statistics --max-line-length=120 --output-file=flake8-report.txt

- name: Run black format checks
run: black --check --diff -l 120 ogc example > black-report.txt

- name: Artifact flake8 report
uses: actions/upload-artifact@v4
with:
name: flake8-report
path: flake8-report.txt
retention-days: 1

- name: Artifact black report
uses: actions/upload-artifact@v4
with:
name: black-report
path: black-report.txt
retention-days: 1

unit_testing:
name: Unit Testing
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.12"

- name: Install dependencies
run: |
pip install --upgrade pip setuptools && pip install .[dev]
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

- name: Run pytest with coverage
run: |
coverage run --data-file=coverage.bin --branch \
-m pytest --continue-on-collection-errors || \
{ echo "WARNING: Some tests have failed. (Temporarily ignored)"; true; }
coverage xml --data-file=coverage.bin -o coverage.xml
sed -i 's,<source>.*/ogc</source>,<source>ogc</source>,' coverage.xml
- name: Artifact coverage document
uses: actions/upload-artifact@v4
with:
name: coverage
path: coverage.xml
retention-days: 1

sonarqube_scan:
name: SonarQube Scan
needs: [ unit_testing ]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Download coverage artifact
uses: actions/download-artifact@v4
with:
name: coverage
path: .

- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@fd88b7d7ccbaefd23d8f36f73b59db7a3d246602
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/ambv/black
rev: stable
hooks:
- id: black
language_version: python3
files: (^ogc|^example)
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Changelog
## 0.5.0

### Introduction
Support for Environmental Data Retrieval (EDR) was added in this version to extend OGC capabilities.
EDR is optional and can be enabled via the OGC_SUPPORTED_FORMATS environmental variable.
The currently supported version of EDR is v1.1.0. Additionally, the repository was modernized by
adding a development container, pyproject.toml, and Github Actions for linting, formatting,
unit testing and scanning.

### Features
* Added support for EDR v1.1.0.

### Maintenance
* Added unit testing for code coverage requirements.
* Added development container and pyproject.toml file.
* Added Github Actions for linting, formatting, unit testing, and scanning.

### Bugfixes
* Fixed formatting, linting, and SonarQube errors to pass CI/CD scans.
41 changes: 35 additions & 6 deletions example/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,43 @@
Demo Flask app using PODPAC Node backed layers.
"""

from flask import Flask
from datetime import datetime
import re

from ogc import servers
from ogc import core
from ogc import podpac as pogc
from ogc.settings import EDR_TIME_INSTANCE_DIMENSION

import podpac
import numpy as np

# Setup new dimension
podpac.core.coordinates.utils.add_valid_dimension(EDR_TIME_INSTANCE_DIMENSION)

# create some podpac nodes
data = np.random.rand(11, 21)
data = np.random.default_rng(1).random((11, 21))
lat = np.linspace(90, -90, 11)
lon = np.linspace(-180, 180, 21)
coords = podpac.Coordinates([lat, lon], dims=["lat", "lon"])
node1 = podpac.data.Array(source=data, coordinates=coords)

data2 = np.random.rand(11, 21)
data2 = np.random.default_rng(1).random((11, 21))
node2 = podpac.data.Array(source=data2, coordinates=coords)

time = np.array(["2025-10-24T12:00:00"], dtype="datetime64")
instance = np.array(["2025-10-24T00:00:00"], dtype="datetime64")
coords = podpac.Coordinates([lat, lon, time, instance], dims=["lat", "lon", "time", EDR_TIME_INSTANCE_DIMENSION])
data3 = np.random.default_rng(1).random((11, 21, 1, 1))
node3 = podpac.data.Array(source=data3, coordinates=coords)

# use podpac nodes to create some OGC layers
layer1 = pogc.Layer(
node=node1,
identifier="layer1",
title="OGC/POPAC layer containing random data",
abstract="This layer contains some random data",
group="Layers",
)

layer2 = pogc.Layer(
Expand All @@ -37,9 +47,19 @@
title="FOUO: Another OGC/POPAC layer containing random data",
abstract="Marked as FOUO. This layer contains some random data. Same coordinates as layer1, but different values.",
is_fouo=True,
group="Layers",
)

layer3 = pogc.Layer(
node=node3,
identifier="layer3",
title="OGC/POPAC layer containing random data with time instances available.",
abstract="This layer contains some random data with time instances available.",
group="Layers",
valid_times=[dt.astype(datetime) for dt in time],
)

all_layers = [layer1, layer2]
all_layers = [layer1, layer2, layer3]
non_fouo_layers = [layer for layer in all_layers if not layer.is_fouo]

# create a couple of different ogc endpoints
Expand Down Expand Up @@ -69,25 +89,34 @@
<li><a href="?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&LAYER={test_layer}&STYLE=default&FORMAT=image/png">WMS GetLegend Example (PNG)</a> <i>(v1.3.0)</i></li>
</ul>
</li>
<li> EDR: Open Geospatial Consortium (OGC) Environmental Data Retrieval (EDR) <i>(v1.0.1)</i>
<ul>
<li><a href="{endpoint}/edr?f=html">EDR Landing Page (HTML)</a> <i>(v1.0.1)</i></li>
<li><a href="{endpoint}/edr/conformance?f=json">EDR Conformance (JSON)</a> <i>(v1.0.1)</i></li>
<li><a href="{endpoint}/edr/collections?f=json">EDR Collections (JSON)</a> <i>(v1.0.1)</i></li>
</ul>
</li>
</ul>
"""


app = servers.FlaskServer(__name__, ogcs=[NonFouoOGC, FouoOGC], home_func=api_home)


# add in some other endpoints.
@app.route("/")

Check warning on line 107 in example/app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Specify the HTTP methods this route should accept.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPvguP0YtWdsRyu&open=AZ3aHgPvguP0YtWdsRyu&pullRequest=26
def home():
return f'This is an example OGC flask app. See <a href="/ogc_full"> FULL </a> and <a href="/ogc"> PARTIAL </a> endpoints.'
return """This is an example OGC flask app.
See <a href="/ogc_full"> FULL </a> and <a href="/ogc"> PARTIAL </a> endpoints."""


@app.route("/layers/<layer>")

Check warning on line 113 in example/app.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Specify the HTTP methods this route should accept.

See more on https://sonarcloud.io/project/issues?id=creare-com_ogc&issues=AZ3aHgPvguP0YtWdsRyv&open=AZ3aHgPvguP0YtWdsRyv&pullRequest=26
def check_layers(layer):
match_object = re.match("[a-zA-Z0-9]+", layer)

if match_object:
clean_layer = match_object.group(0)
if clean_layer in [l.identifier for l in all_layers]:
if clean_layer in [available_layer.identifier for available_layer in all_layers]:
return "{} is an available layer id".format(clean_layer)
else:
return "No layer available with that id"
Expand Down
11 changes: 4 additions & 7 deletions ogc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
OGC WMS/WCS (v1.3.0/v1.0.0) server
OGC WMS/WCS (v1.3.0/v1.0.0) server
"""

import traitlets as tl
Expand Down Expand Up @@ -70,10 +70,9 @@ class Layer(tl.HasTraits):
identifier = tl.Unicode()
title = tl.Unicode(default_value="An OGC Layer")
abstract = tl.Unicode(default_value="This is an example OGC Layer")
group = tl.Unicode(default_value="Default")
is_fouo = tl.Bool(default_value=False)
grid_coordinates = tl.Instance(
klass=GridCoordinates, default_value=GridCoordinates()
)
grid_coordinates = tl.Instance(klass=GridCoordinates, default_value=GridCoordinates())
valid_times = tl.List(
trait=tl.Instance(datetime.datetime),
default_value=tl.Undefined,
Expand Down Expand Up @@ -101,9 +100,7 @@ def __init__(self, *args, **kwargs):
elif "title" in kwargs:
string_repr = kwargs["title"]
if "is_enumerated" in kwargs:
self._style = Style(
string_repr=string_repr, is_enumerated=kwargs["is_enumerated"]
)
self._style = Style(string_repr=string_repr, is_enumerated=kwargs["is_enumerated"])
else:
self._style = Style(string_repr=string_repr)
if self.valid_times is not tl.Undefined:
Expand Down
Loading
Loading