Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b0387f0
compute cone during seq creation
MateoLostanlen Dec 22, 2025
05c2930
round values
MateoLostanlen Dec 22, 2025
212bac9
drop seq with cones
MateoLostanlen Dec 22, 2025
2be61dd
create alerts
MateoLostanlen Dec 22, 2025
d504b35
add overlap from triangulation pr
MateoLostanlen Dec 22, 2025
c508900
new alerts strat
MateoLostanlen Dec 22, 2025
756038b
missing init
MateoLostanlen Dec 22, 2025
f6cf578
missings deps
MateoLostanlen Dec 22, 2025
086cd7d
use preset variable
MateoLostanlen Dec 22, 2025
b9e3faf
updates routes
MateoLostanlen Dec 22, 2025
2d8d6d0
update poetry
MateoLostanlen Dec 22, 2025
45e5cbb
update loc
MateoLostanlen Dec 22, 2025
f8523c4
error management
MateoLostanlen Dec 23, 2025
4fe12f8
fix on seq case
MateoLostanlen Dec 23, 2025
1bb2d20
use started_at and last_seens_at
MateoLostanlen Dec 23, 2025
2527baa
clean output
MateoLostanlen Dec 23, 2025
31a3960
missing READ
MateoLostanlen Dec 23, 2025
e5ab687
add test
MateoLostanlen Dec 23, 2025
92510a8
fix style
MateoLostanlen Dec 23, 2025
0140d29
test overlap
MateoLostanlen Dec 23, 2025
f1a4236
mypy
MateoLostanlen Dec 23, 2025
cf8a8ed
ruff on test overlap
MateoLostanlen Dec 23, 2025
0bc7de1
import issue
MateoLostanlen Dec 23, 2025
6ab2d8d
fix style
MateoLostanlen Dec 23, 2025
83626e0
fix deletions to respect fk
MateoLostanlen Dec 23, 2025
19e5762
cast
MateoLostanlen Dec 23, 2025
2bc5efb
recompyte alerts after seq annotation
MateoLostanlen Dec 23, 2025
56ad18b
fix alert update
MateoLostanlen Dec 23, 2025
761fefd
style
MateoLostanlen Dec 23, 2025
a306c0b
style
MateoLostanlen Dec 23, 2025
2ab57fd
adapt test
MateoLostanlen Dec 23, 2025
afda94e
ruff on test
MateoLostanlen Dec 23, 2025
1d9732f
add tests on detections
MateoLostanlen Dec 24, 2025
204bbb6
increase test on seq
MateoLostanlen Dec 24, 2025
662300e
ruff
MateoLostanlen Dec 24, 2025
3d3fa35
limit lat and lon
MateoLostanlen Dec 30, 2025
ea1994a
rename fonction
MateoLostanlen Dec 30, 2025
7c78792
rename to sequence_azimuth
MateoLostanlen Jan 3, 2026
a50fca8
rename sequence camera azimuth
MateoLostanlen Jan 3, 2026
420ac01
add AlertBase
MateoLostanlen Jan 3, 2026
3c67124
adapt e2e
MateoLostanlen Jan 3, 2026
318b54f
Merge branch 'main' into alerts
MateoLostanlen Jan 3, 2026
06895c0
new headers
MateoLostanlen Jan 3, 2026
e71b467
header fix
MateoLostanlen Jan 3, 2026
549ee5d
header fix
MateoLostanlen Jan 3, 2026
7a9b17a
add triangulation test
MateoLostanlen Jan 4, 2026
54c2ec5
drop dupicate
MateoLostanlen Jan 4, 2026
742fabd
imprrove test for label_sequence
MateoLostanlen Jan 7, 2026
2065489
style
MateoLostanlen Jan 7, 2026
21e2236
add seq delete test
MateoLostanlen Jan 7, 2026
de42c2d
Merge remote-tracking branch 'origin' into alerts
MateoLostanlen Jan 7, 2026
a1523bb
fix(api): resolve merge conflict and fix router
MateoLostanlen Jan 7, 2026
7cbfc91
style
MateoLostanlen Jan 7, 2026
496d280
test(api): add test coverage for organization deletion of alerts and …
MateoLostanlen Jan 7, 2026
05d4b90
put back old delete
MateoLostanlen Jan 7, 2026
52b39e5
style org
MateoLostanlen Jan 7, 2026
2014735
style test
MateoLostanlen Jan 7, 2026
5eaa731
fix test org
MateoLostanlen Jan 7, 2026
cf4c21f
reduce complexity
MateoLostanlen Jan 7, 2026
b1ee9d9
style
MateoLostanlen Jan 7, 2026
d8a4682
use pose_id
MateoLostanlen Dec 28, 2025
ba068dd
style
MateoLostanlen Dec 28, 2025
440e444
adapt test
MateoLostanlen Dec 28, 2025
3443f27
split seq based on bbox
MateoLostanlen Dec 28, 2025
29337f3
adapt test
MateoLostanlen Dec 28, 2025
ad3a625
prevent UnboundLocalError
MateoLostanlen Dec 28, 2025
c6ea4f5
fix mypy client
MateoLostanlen Dec 28, 2025
fd587e1
complete client
MateoLostanlen Dec 29, 2025
8c2aa06
style
MateoLostanlen Dec 29, 2025
c2674be
ruff
MateoLostanlen Jan 7, 2026
c8e5a38
rename camera_azimuth
MateoLostanlen Jan 7, 2026
3b53291
fix test
MateoLostanlen Jan 7, 2026
5545dc1
client/
MateoLostanlen Jan 7, 2026
256096a
fix test
MateoLostanlen Jan 7, 2026
f2e68c3
reorder router
MateoLostanlen Jan 9, 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
35 changes: 22 additions & 13 deletions client/pyroclient/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# See LICENSE or go to <https://opensource.org/licenses/Apache-2.0> for full license details.

from enum import Enum
from typing import Dict, List, Optional, Tuple
from typing import Dict, List, Tuple
from urllib.parse import urljoin

import requests
Expand Down Expand Up @@ -180,17 +180,27 @@ def create_pose(
timeout=self.timeout,
)

def patch_pose(
def get_current_poses(self) -> Response:
"""Fetch poses for the authenticated camera."""
params: Dict[str, int] | None = None
return requests.get(
urljoin(self._route_prefix, ClientRoute.POSES_CREATE),
headers=self.headers,
params=params,
timeout=self.timeout,
)

def update_pose(
self,
pose_id: int,
azimuth: float | None = None,
patrol_id: int | None = None,
) -> Response:
"""Update a pose

>>> api_client.patch_pose(pose_id=1, azimuth=90.0)
>>> api_client.update_pose(pose_id=1, azimuth=90.0)
"""
payload = {}
payload: Dict[str, float | int] = {}
if azimuth is not None:
payload["azimuth"] = azimuth
if patrol_id is not None:
Expand All @@ -203,6 +213,9 @@ def patch_pose(
timeout=self.timeout,
)

# Backward compatibility alias
patch_pose = update_pose

def delete_pose(self, pose_id: int) -> Response:
"""Delete a pose

Expand Down Expand Up @@ -296,34 +309,30 @@ def delete_occlusion_mask(self, mask_id: int) -> Response:
def create_detection(
self,
media: bytes,
azimuth: float,
bboxes: List[Tuple[float, float, float, float, float]],
pose_id: Optional[int] = None,
pose_id: int,
) -> Response:
"""Notify the detection of a wildfire on the picture taken by a camera.

>>> from pyroclient import Client
>>> api_client = Client("MY_CAM_TOKEN")
>>> with open("path/to/my/file.ext", "rb") as f: data = f.read()
>>> response = api_client.create_detection(data, azimuth=124.2, bboxes=[(.1,.1,.5,.8,.5)], pose_id=12)
>>> response = api_client.create_detection(data, bboxes=[(.1,.1,.5,.8,.5)], pose_id=12)

Args:
media: byte data of the picture
azimuth: the azimuth of the camera when the picture was taken
bboxes: list of tuples where each tuple is a relative coordinate in order xmin, ymin, xmax, ymax, conf
pose_id: optional, pose_id of the detection
pose_id: pose_id of the detection

Returns:
HTTP response
"""
if not isinstance(bboxes, (list, tuple)) or len(bboxes) == 0 or len(bboxes) > 5:
raise ValueError("bboxes must be a non-empty list of tuples with a maximum of 5 boxes")
data = {
"azimuth": azimuth,
data: Dict[str, str] = {
"bboxes": _dump_bbox_to_json(bboxes),
}
if pose_id is not None:
data["pose_id"] = pose_id
data["pose_id"] = str(pose_id)
return requests.post(
urljoin(self._route_prefix, ClientRoute.DETECTIONS_CREATE),
headers=self.headers,
Expand Down
17 changes: 14 additions & 3 deletions client/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def mock_img():


@pytest.fixture(scope="session")
def cam_token():
def cam_setup():
admin_headers = {"Authorization": f"Bearer {SUPERADMIN_TOKEN}"}
payload = {
"name": "pyro-camera-01",
Expand All @@ -44,11 +44,22 @@ def cam_token():
payload = {"azimuth": 359, "patrol_id": 1, "camera_id": cam_id}
response = requests.post(urljoin(API_URL, "poses"), json=payload, headers=admin_headers, timeout=5)
assert response.status_code == 201
pose_id = response.json()["id"]

# Create a cam token
return requests.post(urljoin(API_URL, f"cameras/{cam_id}/token"), headers=admin_headers, timeout=5).json()[
cam_token = requests.post(urljoin(API_URL, f"cameras/{cam_id}/token"), headers=admin_headers, timeout=5).json()[
"access_token"
]
return {"token": cam_token, "pose_id": pose_id}


@pytest.fixture(scope="session")
def cam_token(cam_setup):
return cam_setup["token"]


@pytest.fixture(scope="session")
def cam_pose_id(cam_setup):
return cam_setup["pose_id"]


@pytest.fixture(scope="session")
Expand Down
16 changes: 10 additions & 6 deletions client/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_client_constructor(token, host, timeout, expected_error):


@pytest.fixture(scope="session")
def test_cam_workflow(cam_token, mock_img):
def test_cam_workflow(cam_token, cam_pose_id, mock_img):
cam_client = Client(cam_token, "http://localhost:5050", timeout=10)
response = cam_client.heartbeat()
assert response.status_code == 200
Expand All @@ -37,14 +37,18 @@ def test_cam_workflow(cam_token, mock_img):
assert isinstance(response.json()["last_image"], str)
# Check that adding bboxes works
with pytest.raises(ValueError, match="bboxes must be a non-empty list of tuples"):
cam_client.create_detection(mock_img, 123.2, None)
cam_client.create_detection(mock_img, None, pose_id=cam_pose_id)
with pytest.raises(ValueError, match="bboxes must be a non-empty list of tuples"):
cam_client.create_detection(mock_img, 123.2, [])
response = cam_client.create_detection(mock_img, 123.2, [(0, 0, 1.0, 0.9, 0.5)], pose_id=1)
cam_client.create_detection(mock_img, [], pose_id=cam_pose_id)
response = cam_client.create_detection(mock_img, [(0, 0, 1.0, 0.9, 0.5)], pose_id=cam_pose_id)
assert response.status_code == 201, response.__dict__
response = cam_client.create_detection(mock_img, 123.2, [(0, 0, 1.0, 0.9, 0.5), (0.2, 0.2, 0.7, 0.7, 0.8)])
response = cam_client.create_detection(
mock_img,
[(0, 0, 1.0, 0.9, 0.5), (0.2, 0.2, 0.7, 0.7, 0.8)],
pose_id=cam_pose_id,
)
assert response.status_code == 201, response.__dict__
response = cam_client.create_detection(mock_img, 123.2, [(0, 0, 1.0, 0.9, 0.5)])
response = cam_client.create_detection(mock_img, [(0, 0, 1.0, 0.9, 0.5)], pose_id=cam_pose_id)
assert response.status_code == 201, response.__dict__
return response.json()["id"]

Expand Down
Loading
Loading