Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.

Commit 4f022b6

Browse files
committed
Add very basic GithubAlgorithmRepository
1 parent 55ac49b commit 4f022b6

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

src/esa_apex_toolbox/algorithms.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import dataclasses
44
import json
55
from pathlib import Path
6-
from typing import Optional, Union
6+
from typing import List, Optional, Union
77

88
import requests
99

@@ -102,3 +102,38 @@ def from_ogc_api_record(cls, src: Union[dict, str, Path]) -> Algorithm:
102102
description=properties.get("description"),
103103
udp_link=udp_link,
104104
)
105+
106+
107+
class GithubAlgorithmRepository:
108+
"""
109+
GitHub based algorithm repository.
110+
"""
111+
112+
# TODO: caching
113+
114+
def __init__(self, owner: str, repo: str, folder: str = "", branch: str = "main"):
115+
self.owner = owner
116+
self.repo = repo
117+
self.folder = folder
118+
self.branch = branch
119+
self._session = requests.Session()
120+
121+
def _list_files(self):
122+
url = f"https://api.github.com/repos/{self.owner}/{self.repo}/contents/{self.folder}".strip("/")
123+
resp = self._session.get(url, headers={"Accept": "application/vnd.github.object+json"})
124+
resp.raise_for_status()
125+
listing = resp.json()
126+
assert listing["type"] == "dir"
127+
for item in listing["entries"]:
128+
if item["type"] == "file":
129+
yield item
130+
131+
def list_algorithms(self) -> List[str]:
132+
# TODO: method to list names vs method to list parsed Algorithm objects?
133+
return [item["name"] for item in self._list_files()]
134+
135+
def get_algorithm(self, name: str) -> Algorithm:
136+
# TODO: get url from listing from API request, instead of hardcoding this raw url?
137+
url = f"https://raw.githubusercontent.com/{self.owner}/{self.repo}/{self.branch}/{self.folder}/{name}"
138+
# TODO: how to make sure GitHub URL is requested with additional headers?
139+
return Algorithm.from_ogc_api_record(url)

tests/test_algorithms.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33

44
import pytest
55

6-
from esa_apex_toolbox.algorithms import Algorithm, InvalidMetadataError, UdpLink
6+
from esa_apex_toolbox.algorithms import (
7+
Algorithm,
8+
GithubAlgorithmRepository,
9+
InvalidMetadataError,
10+
UdpLink,
11+
)
712

813
DATA_ROOT = Path(__file__).parent / "data"
914

@@ -174,3 +179,32 @@ def test_from_ogc_api_record_url(self, requests_mock):
174179
href="https://esa-apex.test/udp/algorithm01.json",
175180
title="UDP One",
176181
)
182+
183+
184+
class TestGithubAlgorithmRepository:
185+
@pytest.fixture
186+
def repo(self) -> GithubAlgorithmRepository:
187+
# TODO: avoid depending on an actual GitHub repository. Mock it instead?
188+
# Or run this as an integration test?
189+
return GithubAlgorithmRepository(
190+
owner="ESA-APEx",
191+
repo="apex_algorithms",
192+
folder="algorithm_catalog",
193+
)
194+
195+
def test_list_algorithms(self, repo):
196+
assert repo.list_algorithms() == [
197+
"worldcereal.json",
198+
]
199+
200+
def test_get_algorithm(self, repo):
201+
algorithm = repo.get_algorithm("worldcereal.json")
202+
assert algorithm == Algorithm(
203+
id="worldcereal_maize",
204+
title="ESA worldcereal global maize detector",
205+
description="A maize detection algorithm.",
206+
udp_link=UdpLink(
207+
href="https://github.com/ESA-APEX/apex_algorithms/blob/main/openeo_udp/worldcereal_inference.json",
208+
title="openEO UDP",
209+
),
210+
)

0 commit comments

Comments
 (0)