Compare commits

..

No commits in common. "main" and "v0.1.0" have entirely different histories.
main ... v0.1.0

5 changed files with 38 additions and 56 deletions

View File

@ -1,4 +1,4 @@
WHEREIS_API_BASE_URL=https://api.locame.duckdns.org WHEREIS_API_BASE_URL=https://api-whereis.thegux.fr
WHEREIS_API_EMAIL= WHEREIS_API_EMAIL=
WHEREIS_API_PASSWORD= WHEREIS_API_PASSWORD=

View File

@ -25,10 +25,6 @@ only-include = ["src"]
"src" = "whereis_client" "src" = "whereis_client"
[tool.ruff] [tool.ruff]
line-length = 88
target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I"] select = ["E", "F", "I"]
ignore = [] ignore = []
@ -38,7 +34,11 @@ exclude = [
"venv", "venv",
] ]
[tool.ruff.lint.mccabe] line-length = 88
target-version = "py310"
[tool.ruff.mccabe]
max-complexity = 10 max-complexity = 10
[tool.mypy] [tool.mypy]

View File

@ -1,5 +1,5 @@
mypy===1.13.0 mypy===1.13.0
ruff>=0.9.6 ruff==0.8.3
twine==6.0.1 twine==6.0.1
types-requests==2.32.0.20241016 types-requests==2.32.0.20241016
hatch==1.14.0 hatch==1.14.0

View File

@ -2,4 +2,4 @@ from .client import Client, OrderField
__all__ = ["Client", "OrderField"] __all__ = ["Client", "OrderField"]
VERSION = "0.1.2" VERSION = "0.1.0"

View File

@ -5,6 +5,7 @@ from datetime import datetime as dt
from enum import Enum from enum import Enum
from threading import Event, Thread from threading import Event, Thread
from typing import Any, Callable from typing import Any, Callable
from urllib.parse import urljoin
from uuid import UUID from uuid import UUID
import requests import requests
@ -15,7 +16,7 @@ from sseclient import SSEClient
from .exceptions import UnauthorizedException, WhereIsException from .exceptions import UnauthorizedException, WhereIsException
API_DEFAULT_URL = "https://api.locame.duckdns.org" API_DEFAULT_URL = "https://api-whereis.thegux.fr"
__all__ = ["Client", "OrderField"] __all__ = ["Client", "OrderField"]
@ -85,7 +86,7 @@ class SessionWatcher:
callback: Callable[[str], None] | None = None, callback: Callable[[str], None] | None = None,
verify: bool = True, verify: bool = True,
) -> "SessionWatcher": ) -> "SessionWatcher":
session_url = base_url + f"/sessions/{id_}/events/" session_url = urljoin(base_url, f"/sessions/{id_}/events/")
headers = {**headers, "Accept": "text/event-stream"} headers = {**headers, "Accept": "text/event-stream"}
resp = requests.get(session_url, stream=True, headers=headers, verify=verify) resp = requests.get(session_url, stream=True, headers=headers, verify=verify)
@ -133,7 +134,7 @@ class Client:
sessions = cli.get_sessions() sessions = cli.get_sessions()
""" """
_base_url: str base_url: str
email: str email: str
password: str password: str
@ -143,10 +144,6 @@ class Client:
default_factory=dict, init=False default_factory=dict, init=False
) )
@property
def base_url(self) -> str:
return self._base_url.removesuffix("/")
def _login(self) -> WhereIsException | None: def _login(self) -> WhereIsException | None:
"""Get the access token and store it in the `Session` header""" """Get the access token and store it in the `Session` header"""
data = { data = {
@ -154,7 +151,7 @@ class Client:
"password": self.password, "password": self.password,
} }
login_url = self.base_url + "/auth/token/" login_url = urljoin(self.base_url, "/auth/token/")
logging.info(f"login: {login_url}") logging.info(f"login: {login_url}")
res = self.session.post(login_url, json=data) res = self.session.post(login_url, json=data)
@ -168,7 +165,7 @@ class Client:
def _refresh(self) -> WhereIsException | None: def _refresh(self) -> WhereIsException | None:
"""Refresh the access token and store it in the `Session` header""" """Refresh the access token and store it in the `Session` header"""
refresh_url = self.base_url + "/auth/refresh/" refresh_url = urljoin(self.base_url, "/auth/refresh/")
logging.info(f"refresh: {refresh_url}") logging.info(f"refresh: {refresh_url}")
res = self.session.post(refresh_url) res = self.session.post(refresh_url)
@ -180,24 +177,6 @@ class Client:
return None return None
@staticmethod
def _init_client(
base_url: str, email: str, password: str, verify: bool = True
) -> "Client":
cli = Client(base_url, email, password)
cli.session = Session()
cli.session.headers.update({"content-type": "application/json"})
if not verify:
urllib3.disable_warnings()
cli.session.verify = verify
cli._login()
logging.info(f"client successfully initialized for user: {cli.email}")
return cli
@classmethod @classmethod
def from_env(cls) -> "Client": def from_env(cls) -> "Client":
""" """
@ -214,17 +193,24 @@ class Client:
dotenv_data = dotenv_values() dotenv_data = dotenv_values()
env_data.update(dotenv_data) # type: ignore env_data.update(dotenv_data) # type: ignore
return Client._init_client( cli = Client(
env_data.get("WHEREIS_API_BASE_URL", ""), env_data.get("WHEREIS_API_BASE_URL", ""),
env_data.get("WHEREIS_API_EMAIL", ""), env_data.get("WHEREIS_API_EMAIL", ""),
env_data.get("WHEREIS_API_PASSWORD", ""), env_data.get("WHEREIS_API_PASSWORD", ""),
) )
cli.session = Session()
cli.session.headers.update({"content-type": "application/json"})
@classmethod is_cert_verify = env_data.get("WHEREIS_CERT_VERIFY", "") != "false"
def from_creds( if not is_cert_verify:
cls, base_url: str, email: str, password: str, verify: bool = False urllib3.disable_warnings()
) -> "Client":
return Client._init_client(base_url, email, password, verify) cli.session.verify = is_cert_verify
cli._login()
logging.info(f"client successfully initialized for user: {cli.email}")
return cli
@refresh() @refresh()
def _get_sessions_page(self, url: str) -> dict[str, Any] | WhereIsException: def _get_sessions_page(self, url: str) -> dict[str, Any] | WhereIsException:
@ -250,7 +236,7 @@ class Client:
search: str, search sessions over username and description search: str, search sessions over username and description
ordering: OrderField, ordering sessions by dates ordering: OrderField, ordering sessions by dates
""" """
sessions_url = self.base_url + "/sessions/" sessions_url = urljoin(self.base_url, "/sessions/")
has_query_param = False has_query_param = False
if search is not None: if search is not None:
@ -279,7 +265,7 @@ class Client:
@refresh() @refresh()
def get_session(self, id_: UUID) -> list[dict[str, Any]] | WhereIsException: def get_session(self, id_: UUID) -> list[dict[str, Any]] | WhereIsException:
session_url = self.base_url + f"/sessions/{id_}/" session_url = urljoin(self.base_url, f"/sessions/{id_}/")
logging.info(f"get session: {session_url}") logging.info(f"get session: {session_url}")
res = self.session.get(session_url) res = self.session.get(session_url)
@ -295,7 +281,7 @@ class Client:
def create_session( def create_session(
self, name: str, description: str | None = None, is_public: bool = False self, name: str, description: str | None = None, is_public: bool = False
) -> dict[str, Any] | WhereIsException: ) -> dict[str, Any] | WhereIsException:
sessions_url = self.base_url + "/sessions/" sessions_url = urljoin(self.base_url, "/sessions/")
logging.info(f"create session: {sessions_url}") logging.info(f"create session: {sessions_url}")
data = {"name": name, "description": description, "is_public": is_public} data = {"name": name, "description": description, "is_public": is_public}
@ -327,7 +313,7 @@ class Client:
description: str | None = None, description: str | None = None,
is_public: bool | None = None, is_public: bool | None = None,
) -> dict[str, Any] | WhereIsException: ) -> dict[str, Any] | WhereIsException:
session_url = self.base_url + f"/sessions/{id_}/" session_url = urljoin(self.base_url, f"/sessions/{id_}/")
logging.info(f"update session: {session_url}") logging.info(f"update session: {session_url}")
data = {"name": name, "description": description, "is_public": is_public} data = {"name": name, "description": description, "is_public": is_public}
@ -355,7 +341,7 @@ class Client:
NOTE: The GPS positions associated to the session are not deleted ! NOTE: The GPS positions associated to the session are not deleted !
""" """
session_url = self.base_url + f"/sessions/{id_}/" session_url = urljoin(self.base_url, f"/sessions/{id_}/")
logging.info(f"delete session: {session_url}") logging.info(f"delete session: {session_url}")
res = self.session.delete(session_url) res = self.session.delete(session_url)
@ -377,7 +363,7 @@ class Client:
WARN: An empty users list parameter cleans all users. WARN: An empty users list parameter cleans all users.
""" """
session_url = self.base_url + f"/sessions/{id_}/users/" session_url = urljoin(self.base_url, f"/sessions/{id_}/users/")
logging.info(f"update session users: {session_url}") logging.info(f"update session users: {session_url}")
res = self.session.post(session_url, json={"users": users}) res = self.session.post(session_url, json={"users": users})
@ -393,7 +379,7 @@ class Client:
@refresh() @refresh()
def close_session(self, id_: UUID) -> dict[str, Any] | WhereIsException: def close_session(self, id_: UUID) -> dict[str, Any] | WhereIsException:
"""Close the DEFINITIVELY the session. Users can't be added anymore.""" """Close the DEFINITIVELY the session. Users can't be added anymore."""
session_url = self.base_url + f"/sessions/{id_}/close/" session_url = urljoin(self.base_url, f"/sessions/{id_}/close/")
logging.info(f"close session: {session_url}") logging.info(f"close session: {session_url}")
res = self.session.post(session_url) res = self.session.post(session_url)
@ -461,7 +447,7 @@ class Client:
@refresh() @refresh()
def get_wstokens(self) -> list[dict[str, Any]] | WhereIsException: def get_wstokens(self) -> list[dict[str, Any]] | WhereIsException:
wstoken_url = self.base_url + "/auth/ws-token/" wstoken_url = urljoin(self.base_url, "/auth/ws-token/")
logging.info(f"get ws token: {wstoken_url}") logging.info(f"get ws token: {wstoken_url}")
res = self.session.get(wstoken_url) res = self.session.get(wstoken_url)
@ -482,7 +468,7 @@ class Client:
NOTE: only one, and only one ws token per user is allowed. NOTE: only one, and only one ws token per user is allowed.
If it expired, delete it and create a new one. If it expired, delete it and create a new one.
""" """
wstoken_url = self.base_url + "/auth/ws-token/" wstoken_url = urljoin(self.base_url, "/auth/ws-token/")
logging.info(f"create ws token: {wstoken_url}") logging.info(f"create ws token: {wstoken_url}")
res = self.session.post(wstoken_url) res = self.session.post(wstoken_url)
@ -497,7 +483,7 @@ class Client:
@refresh() @refresh()
def delete_wstoken(self, id_: UUID) -> None | WhereIsException: def delete_wstoken(self, id_: UUID) -> None | WhereIsException:
wstoken_url = self.base_url + f"/auth/ws-token/{id_}/" wstoken_url = urljoin(self.base_url, f"/auth/ws-token/{id_}/")
logging.info(f"delete ws token: {wstoken_url}") logging.info(f"delete ws token: {wstoken_url}")
res = self.session.delete(wstoken_url) res = self.session.delete(wstoken_url)
@ -541,7 +527,7 @@ class Client:
The dates formats must be in any valid ISO 8601 format otherwise The dates formats must be in any valid ISO 8601 format otherwise
it will raise a ValueError. it will raise a ValueError.
""" """
gps_url = self.base_url + "/gps/positions/" gps_url = urljoin(self.base_url, "/gps/positions/")
lst_gps_positions: list[dict[str, Any]] = [] lst_gps_positions: list[dict[str, Any]] = []
if date_start: if date_start:
@ -555,10 +541,6 @@ class Client:
gps_url += f"?date_end={de.isoformat()}" gps_url += f"?date_end={de.isoformat()}"
while gps_url is not None: while gps_url is not None:
# pagination api next url returns http scheme instead of https
if self.base_url.startswith("https"):
gps_url = gps_url.replace("http://", "https://")
logging.info(f"get gps data from: {gps_url}") logging.info(f"get gps data from: {gps_url}")
data = self._get_paginate_gps_positions(gps_url) data = self._get_paginate_gps_positions(gps_url)