Compare commits

...

5 commits

17 changed files with 201 additions and 143 deletions

View file

@ -42,12 +42,11 @@ On your WebvDAV account, create a resource (directory) named `ovdashboard`.
The intended installation method is as follows: The intended installation method is as follows:
1. Have a Raspberry Pi (3 or later) connected to a HDMI screen 1. Have a Raspberry Pi (3 or later) connected to a HDMI screen
1. Boot up "Raspberry Pi OS" (64 bit, lite) and connect the Pi to your local network 1. Boot up "Raspberry Pi OS" (64 bit) and connect the Pi to your local network
1. Download (and review) the [OVDashboard installer](TODO), then run it from a terminal 1. Download (and review) the [OVDashboard installer](TODO), then run it from a terminal
> If you feel adventurous and want to skip the script review, run `curl --proto '=https' --tlsv1.2 -sSf 'TODO' | sh` instead.
1. Reboot the Raspberry Pi 1. Reboot the Raspberry Pi
If you feel adventurous and want to skip the script review, run `curl --proto '=https' --tlsv1.2 -sSf 'TODO' | sh` instead.
For a better understanding of your newly created OVDashboard, refer to the [about section](TODO). For a better understanding of your newly created OVDashboard, refer to the [about section](TODO).

View file

@ -1,11 +1,20 @@
# See here for image contents: https://github.com/devcontainers/images/blob/main/src/python/.devcontainer/Dockerfile # See here for image contents: https://github.com/devcontainers/images/blob/main/src/python/.devcontainer/Dockerfile
# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): # [Choice] Python version (use -bookworm or -bullseye variants on local arm64/Apple Silicon):
# - 3, 3.11, 3.10, 3.9, 3.8, 3.7, 3.6 # - 3, 3.12, 3.11, 3.10, 3.9, 3.8
# - 3-bullseye, 3.11-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye # - 3-bookworm, 3.12-bookworm, 3.11-bookworm, 3.10-bookworm, 3.9-bookworm, 3.8-bookworm
# - 3-buster, 3.11-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster # - 3-bullseye, 3.12-bullseye, 3.11-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye
ARG VARIANT="3.11-bullseye" # - 3-buster, 3.12-buster, 3.11-buster, 3.10-buster, 3.9-buster, 3.8-buster
FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} ARG VARIANT="3.12-bookworm"
FROM mcr.microsoft.com/vscode/devcontainers/python:1-${VARIANT}
# Add "Poetry": https://python-poetry.org
ARG POETRY_HOME="/usr/local"
ENV POETRY_HOME="${POETRY_HOME}"
RUN set -ex; \
\
curl -sSL https://install.python-poetry.org | python3 -; \
poetry self add poetry-plugin-up;
# [Choice] Node.js version: none, lts/*, 18, 16, 14, 12, 10 # [Choice] Node.js version: none, lts/*, 18, 16, 14, 12, 10
ARG NODE_VERSION="none" ARG NODE_VERSION="none"
@ -34,5 +43,3 @@ RUN set -ex; \
# [Optional] Uncomment this line to install global node packages. # [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1 # RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
RUN su vscode -c "curl -sSL https://install.python-poetry.org | python3 -" 2>&1

4
api/.flake8 Normal file
View file

@ -0,0 +1,4 @@
[flake8]
max-line-length = 80
select = C,E,F,I,W,B,B950
extend-ignore = E203, E501

View file

@ -18,5 +18,4 @@
], ],
"python.testing.unittestEnabled": false, "python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true, "python.testing.pytestEnabled": true,
"python.formatting.provider": "none",
} }

View file

@ -43,4 +43,4 @@ class LogConfig(BaseModel):
} }
dictConfig(LogConfig().dict()) dictConfig(LogConfig().model_dump())

View file

@ -9,9 +9,7 @@ from typing import Awaitable, Callable, TypeVar
RT = TypeVar("RT") RT = TypeVar("RT")
def run_in_executor( def run_in_executor(function: Callable[..., RT]) -> Callable[..., Awaitable[RT]]:
function: Callable[..., RT]
) -> Callable[..., Awaitable[RT]]:
""" """
Decorator to make blocking a function call asyncio compatible. Decorator to make blocking a function call asyncio compatible.
https://stackoverflow.com/questions/41063331/how-to-use-asyncio-with-existing-blocking-library/ https://stackoverflow.com/questions/41063331/how-to-use-asyncio-with-existing-blocking-library/

View file

@ -2,13 +2,13 @@
Python representation of the "config.txt" file inside the WebDAV directory. Python representation of the "config.txt" file inside the WebDAV directory.
""" """
import tomllib
from io import BytesIO from io import BytesIO
from logging import getLogger from logging import getLogger
from tomllib import loads as toml_loads
from typing import Any from typing import Any
import tomli_w
from pydantic import BaseModel from pydantic import BaseModel
from tomli_w import dump as toml_dump
from webdav3.exceptions import RemoteResourceNotFound from webdav3.exceptions import RemoteResourceNotFound
from .dav_common import caldav_list from .dav_common import caldav_list
@ -119,7 +119,7 @@ class Config(BaseModel):
dav_file = DavFile(SETTINGS.config_path) dav_file = DavFile(SETTINGS.config_path)
try: try:
cfg = cls.parse_obj(toml_loads(await dav_file.as_string)) cfg = cls.model_validate(tomllib.loads(await dav_file.as_string))
except RemoteResourceNotFound: except RemoteResourceNotFound:
_logger.warning( _logger.warning(
@ -130,7 +130,7 @@ class Config(BaseModel):
cfg.calendar.aggregates["All Events"] = list(await caldav_list()) cfg.calendar.aggregates["All Events"] = list(await caldav_list())
buffer = BytesIO() buffer = BytesIO()
toml_dump(cfg.dict(), buffer) tomli_w.dump(cfg.model_dump(), buffer)
buffer.seek(0) buffer.seek(0)
await dav_file.write(buffer.read()) await dav_file.write(buffer.read())

View file

@ -22,17 +22,6 @@ from .dav_common import caldav_principal
from .settings import SETTINGS from .settings import SETTINGS
_logger = getLogger(__name__) _logger = getLogger(__name__)
def _string_strip(in_str: str) -> str:
"""
Wrapper for str.strip().
Used to define `pydantic` validators.
"""
return in_str.strip()
StrippedStr = Annotated[str, AfterValidator(lambda s: s.strip())] StrippedStr = Annotated[str, AfterValidator(lambda s: s.strip())]
@ -67,7 +56,7 @@ class CalEvent(BaseModel):
Compare all properties. Compare all properties.
""" """
return self.dict() == other.dict() return self.model_dump() == other.model_dump()
@classmethod @classmethod
def from_vevent(cls, event: Component) -> "CalEvent": def from_vevent(cls, event: Component) -> "CalEvent":
@ -101,7 +90,7 @@ class CalEvent(BaseModel):
del data["duration"] del data["duration"]
return cls.parse_obj(data) return cls.model_validate(data)
@AsyncTTL(time_to_live=SETTINGS.cache_time, maxsize=SETTINGS.cache_size) @AsyncTTL(time_to_live=SETTINGS.cache_time, maxsize=SETTINGS.cache_size)

View file

@ -18,12 +18,14 @@ from . import __file__ as OVD_INIT
from .async_helpers import run_in_executor from .async_helpers import run_in_executor
from .settings import SETTINGS from .settings import SETTINGS
_WEBDAV_CLIENT = WebDAVclient({ _WEBDAV_CLIENT = WebDAVclient(
"webdav_hostname": SETTINGS.webdav.url, {
"webdav_login": SETTINGS.webdav.username, "webdav_hostname": SETTINGS.webdav.url,
"webdav_password": SETTINGS.webdav.password, "webdav_login": SETTINGS.webdav.username,
"disable_check": SETTINGS.webdav_disable_check, "webdav_password": SETTINGS.webdav.password,
}) "disable_check": SETTINGS.webdav_disable_check,
}
)
_logger = getLogger(__name__) _logger = getLogger(__name__)
@ -113,11 +115,13 @@ def webdav_ensure_files(remote_path: str, *file_names: str) -> None:
missing_files = ( missing_files = (
file_name file_name
for file_name in file_names for file_name in file_names
if not _WEBDAV_CLIENT.check(path.join( if not _WEBDAV_CLIENT.check(
SETTINGS.webdav_prefix, path.join(
remote_path, SETTINGS.webdav_prefix,
file_name, remote_path,
)) file_name,
)
)
) )
webdav_upload_skel( webdav_upload_skel(
@ -132,9 +136,7 @@ def webdav_resource(remote_path: Any) -> WebDAVResource:
Gets a resource using the main WebDAV client. Gets a resource using the main WebDAV client.
""" """
return _WEBDAV_CLIENT.resource( return _WEBDAV_CLIENT.resource(f"{SETTINGS.webdav_prefix}/{remote_path}")
f"{SETTINGS.webdav_prefix}/{remote_path}"
)
@run_in_executor @run_in_executor
@ -143,9 +145,7 @@ def webdav_list(remote_path: str) -> list[str]:
Asynchronously lists a WebDAV path using the main WebDAV client. Asynchronously lists a WebDAV path using the main WebDAV client.
""" """
return _WEBDAV_CLIENT.list( return _WEBDAV_CLIENT.list(f"{SETTINGS.webdav_prefix}/{remote_path}")
f"{SETTINGS.webdav_prefix}/{remote_path}"
)
_CALDAV_CLIENT = CalDAVclient( _CALDAV_CLIENT = CalDAVclient(
@ -169,7 +169,4 @@ def caldav_list() -> Iterator[str]:
Asynchronously lists all calendars using the main WebDAV client. Asynchronously lists all calendars using the main WebDAV client.
""" """
return ( return (str(cal.name) for cal in caldav_principal().calendars())
str(cal.name)
for cal in caldav_principal().calendars()
)

View file

@ -57,17 +57,13 @@ class FileNameLister:
async def remote_path(self) -> str: async def remote_path(self) -> str:
cfg = await Config.get() cfg = await Config.get()
return str(cfg.dict()[self.path_name]) return str(cfg.model_dump()[self.path_name])
async def __call__(self) -> Iterator[str]: async def __call__(self) -> Iterator[str]:
try: try:
file_names = await webdav_list(await self.remote_path) file_names = await webdav_list(await self.remote_path)
return ( return (name for name in file_names if self.re.search(name))
name
for name in file_names
if self.re.search(name)
)
except RemoteResourceNotFound: except RemoteResourceNotFound:
_logger.error( _logger.error(
@ -115,8 +111,7 @@ class PrefixFinder:
return { return {
**_RESPONSE_OK, **_RESPONSE_OK,
status.HTTP_404_NOT_FOUND: { status.HTTP_404_NOT_FOUND: {
"description": "Failure in lister " + "description": f"Failure in lister {self.lister.__class__.__name__!r}",
repr(self.lister.__class__.__name__),
"content": None, "content": None,
}, },
} }

View file

@ -10,6 +10,7 @@ from logging import getLogger
from typing import Iterator from typing import Iterator
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from ovdashboard_api.config import Config from ovdashboard_api.config import Config
from ...dav_calendar import CalEvent, DavCalendar from ...dav_calendar import CalEvent, DavCalendar
@ -52,12 +53,13 @@ async def get_aggregate_calendar(
aggregate = cfg.calendar.aggregates[name] aggregate = cfg.calendar.aggregates[name]
calendars = ( calendars = (
DavCalendar(await calendar_unique(cal_prefix)) DavCalendar(await calendar_unique(cal_prefix)) for cal_prefix in aggregate
for cal_prefix in aggregate
) )
return sorted([ return sorted(
event [
async for calendar in calendars # type: ignore event
for event in (await calendar.events) async for calendar in calendars # type: ignore
]) for event in (await calendar.events)
]
)

View file

@ -88,7 +88,5 @@ async def get_file(
return StreamingResponse( return StreamingResponse(
content=buffer, content=buffer,
media_type=mime, media_type=mime,
headers={ headers={"Content-Disposition": f"filename={prefix}"},
"Content-Disposition": f"filename={prefix}"
},
) )

View file

@ -83,11 +83,7 @@ async def get_image(
cfg = await Config.get() cfg = await Config.get()
dav_file = DavFile(f"{await image_lister.remote_path}/{name}") dav_file = DavFile(f"{await image_lister.remote_path}/{name}")
img = Image.open( img = Image.open(BytesIO(await dav_file.as_bytes)).convert(cfg.image.mode)
BytesIO(await dav_file.as_bytes)
).convert(
cfg.image.mode
)
img_buffer = BytesIO() img_buffer = BytesIO()
img.save(img_buffer, **cfg.image.save_params) img.save(img_buffer, **cfg.image.save_params)
@ -96,9 +92,7 @@ async def get_image(
return StreamingResponse( return StreamingResponse(
content=img_buffer, content=img_buffer,
media_type="image/jpeg", media_type="image/jpeg",
headers={ headers={"Content-Disposition": f"filename={prefix}.jpg"},
"Content-Disposition": f"filename={prefix}.jpg"
},
) )

View file

@ -42,11 +42,7 @@ async def get_ticker_lines() -> Iterator[str]:
f"{await text_lister.remote_path}/{file_name}", f"{await text_lister.remote_path}/{file_name}",
).as_string ).as_string
return ( return (line.strip() for line in ticker.split("\n") if line.strip())
line.strip()
for line in ticker.split("\n")
if line.strip()
)
async def get_ticker_content_lines( async def get_ticker_content_lines(
@ -55,9 +51,7 @@ async def get_ticker_content_lines(
cfg = await Config.get() cfg = await Config.get()
return ( return (
line line for line in ticker_lines if not line.startswith(cfg.ticker.comment_marker)
for line in ticker_lines
if not line.startswith(cfg.ticker.comment_marker)
) )

View file

@ -7,7 +7,7 @@ Converts per-run (environment) variables and config files into the
Pydantic models might have convenience methods attached. Pydantic models might have convenience methods attached.
""" """
from typing import Any, Optional from typing import Any
from pydantic import BaseModel, root_validator from pydantic import BaseModel, root_validator
from pydantic_settings import BaseSettings from pydantic_settings import BaseSettings
@ -18,11 +18,11 @@ class DavSettings(BaseModel):
Connection to a DAV server. Connection to a DAV server.
""" """
protocol: Optional[str] = None protocol: str | None = None
host: Optional[str] = None host: str | None = None
username: Optional[str] = None username: str | None = None
password: Optional[str] = None password: str | None = None
path: Optional[str] = None path: str | None = None
@property @property
def url(self) -> str: def url(self) -> str:
@ -57,8 +57,8 @@ class Settings(BaseSettings):
##### #####
openapi_url: str = "/openapi.json" openapi_url: str = "/openapi.json"
docs_url: Optional[str] = None if production_mode else "/docs" docs_url: str | None = None if production_mode else "/docs"
redoc_url: Optional[str] = None if production_mode else "/redoc" redoc_url: str | None = None if production_mode else "/redoc"
##### #####
# webdav settings # webdav settings
@ -94,7 +94,7 @@ class Settings(BaseSettings):
host="example.com", host="example.com",
username="ovdashboard", username="ovdashboard",
password="secret", password="secret",
).dict() ).model_dump()
for key in default_dav: for key in default_dav:
# if "webdav" value is not specified, use default # if "webdav" value is not specified, use default

162
api/poetry.lock generated
View file

@ -219,6 +219,39 @@ typing-extensions = ">=4.5.0"
[package.extras] [package.extras]
all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"]
[[package]]
name = "flake8"
version = "6.1.0"
description = "the modular source code checker: pep8 pyflakes and co"
optional = false
python-versions = ">=3.8.1"
files = [
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"},
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"},
]
[package.dependencies]
mccabe = ">=0.7.0,<0.8.0"
pycodestyle = ">=2.11.0,<2.12.0"
pyflakes = ">=3.1.0,<3.2.0"
[[package]]
name = "flake8-isort"
version = "6.1.0"
description = "flake8 plugin that integrates isort ."
optional = false
python-versions = ">=3.8"
files = [
{file = "flake8-isort-6.1.0.tar.gz", hash = "sha256:d4639343bac540194c59fb1618ac2c285b3e27609f353bef6f50904d40c1643e"},
]
[package.dependencies]
flake8 = "*"
isort = ">=5.0.0,<6"
[package.extras]
test = ["pytest"]
[[package]] [[package]]
name = "h11" name = "h11"
version = "0.14.0" version = "0.14.0"
@ -232,46 +265,47 @@ files = [
[[package]] [[package]]
name = "httptools" name = "httptools"
version = "0.6.0" version = "0.6.1"
description = "A collection of framework independent HTTP protocol utils." description = "A collection of framework independent HTTP protocol utils."
optional = false optional = false
python-versions = ">=3.5.0" python-versions = ">=3.8.0"
files = [ files = [
{file = "httptools-0.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:818325afee467d483bfab1647a72054246d29f9053fd17cc4b86cda09cc60339"}, {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"},
{file = "httptools-0.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72205730bf1be875003692ca54a4a7c35fac77b4746008966061d9d41a61b0f5"}, {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"},
{file = "httptools-0.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33eb1d4e609c835966e969a31b1dedf5ba16b38cab356c2ce4f3e33ffa94cad3"}, {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"},
{file = "httptools-0.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdc6675ec6cb79d27e0575750ac6e2b47032742e24eed011b8db73f2da9ed40"}, {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"},
{file = "httptools-0.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:463c3bc5ef64b9cf091be9ac0e0556199503f6e80456b790a917774a616aff6e"}, {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"},
{file = "httptools-0.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:82f228b88b0e8c6099a9c4757ce9fdbb8b45548074f8d0b1f0fc071e35655d1c"}, {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"},
{file = "httptools-0.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:0781fedc610293a2716bc7fa142d4c85e6776bc59d617a807ff91246a95dea35"}, {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"},
{file = "httptools-0.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:721e503245d591527cddd0f6fd771d156c509e831caa7a57929b55ac91ee2b51"}, {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"},
{file = "httptools-0.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:274bf20eeb41b0956e34f6a81f84d26ed57c84dd9253f13dcb7174b27ccd8aaf"}, {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"},
{file = "httptools-0.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:259920bbae18740a40236807915def554132ad70af5067e562f4660b62c59b90"}, {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"},
{file = "httptools-0.6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03bfd2ae8a2d532952ac54445a2fb2504c804135ed28b53fefaf03d3a93eb1fd"}, {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"},
{file = "httptools-0.6.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f959e4770b3fc8ee4dbc3578fd910fab9003e093f20ac8c621452c4d62e517cb"}, {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"},
{file = "httptools-0.6.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e22896b42b95b3237eccc42278cd72c0df6f23247d886b7ded3163452481e38"}, {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"},
{file = "httptools-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:38f3cafedd6aa20ae05f81f2e616ea6f92116c8a0f8dcb79dc798df3356836e2"}, {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"},
{file = "httptools-0.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:47043a6e0ea753f006a9d0dd076a8f8c99bc0ecae86a0888448eb3076c43d717"}, {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"},
{file = "httptools-0.6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a541579bed0270d1ac10245a3e71e5beeb1903b5fbbc8d8b4d4e728d48ff1d"}, {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"},
{file = "httptools-0.6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65d802e7b2538a9756df5acc062300c160907b02e15ed15ba035b02bce43e89c"}, {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"},
{file = "httptools-0.6.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:26326e0a8fe56829f3af483200d914a7cd16d8d398d14e36888b56de30bec81a"}, {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"},
{file = "httptools-0.6.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e41ccac9e77cd045f3e4ee0fc62cbf3d54d7d4b375431eb855561f26ee7a9ec4"}, {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"},
{file = "httptools-0.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4e748fc0d5c4a629988ef50ac1aef99dfb5e8996583a73a717fc2cac4ab89932"}, {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"},
{file = "httptools-0.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cf8169e839a0d740f3d3c9c4fa630ac1a5aaf81641a34575ca6773ed7ce041a1"}, {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"},
{file = "httptools-0.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5dcc14c090ab57b35908d4a4585ec5c0715439df07be2913405991dbb37e049d"}, {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"},
{file = "httptools-0.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d0b0571806a5168013b8c3d180d9f9d6997365a4212cb18ea20df18b938aa0b"}, {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"},
{file = "httptools-0.6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fb4a608c631f7dcbdf986f40af7a030521a10ba6bc3d36b28c1dc9e9035a3c0"}, {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"},
{file = "httptools-0.6.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:93f89975465133619aea8b1952bc6fa0e6bad22a447c6d982fc338fbb4c89649"}, {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"},
{file = "httptools-0.6.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:73e9d66a5a28b2d5d9fbd9e197a31edd02be310186db423b28e6052472dc8201"}, {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"},
{file = "httptools-0.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:22c01fcd53648162730a71c42842f73b50f989daae36534c818b3f5050b54589"}, {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"},
{file = "httptools-0.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f96d2a351b5625a9fd9133c95744e8ca06f7a4f8f0b8231e4bbaae2c485046a"}, {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"},
{file = "httptools-0.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:72ec7c70bd9f95ef1083d14a755f321d181f046ca685b6358676737a5fecd26a"}, {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"},
{file = "httptools-0.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b703d15dbe082cc23266bf5d9448e764c7cb3fcfe7cb358d79d3fd8248673ef9"}, {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"},
{file = "httptools-0.6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82c723ed5982f8ead00f8e7605c53e55ffe47c47465d878305ebe0082b6a1755"}, {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"},
{file = "httptools-0.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b0a816bb425c116a160fbc6f34cece097fd22ece15059d68932af686520966bd"}, {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"},
{file = "httptools-0.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:dea66d94e5a3f68c5e9d86e0894653b87d952e624845e0b0e3ad1c733c6cc75d"}, {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"},
{file = "httptools-0.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:23b09537086a5a611fad5696fc8963d67c7e7f98cb329d38ee114d588b0b74cd"}, {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"},
{file = "httptools-0.6.0.tar.gz", hash = "sha256:9fc6e409ad38cbd68b177cd5158fc4042c796b82ca88d99ec78f07bed6c6b796"}, {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"},
{file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"},
] ]
[package.extras] [package.extras]
@ -303,6 +337,23 @@ files = [
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
] ]
[[package]]
name = "isort"
version = "5.12.0"
description = "A Python utility / library to sort Python imports."
optional = false
python-versions = ">=3.8.0"
files = [
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
]
[package.extras]
colors = ["colorama (>=0.4.3)"]
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
plugins = ["setuptools"]
requirements-deprecated-finder = ["pip-api", "pipreqs"]
[[package]] [[package]]
name = "lxml" name = "lxml"
version = "4.9.3" version = "4.9.3"
@ -425,6 +476,17 @@ files = [
docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"]
testing = ["coverage", "pyyaml"] testing = ["coverage", "pyyaml"]
[[package]]
name = "mccabe"
version = "0.7.0"
description = "McCabe checker, plugin for flake8"
optional = false
python-versions = ">=3.6"
files = [
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
[[package]] [[package]]
name = "pillow" name = "pillow"
version = "10.1.0" version = "10.1.0"
@ -492,6 +554,17 @@ files = [
docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
[[package]]
name = "pycodestyle"
version = "2.11.1"
description = "Python style guide checker"
optional = false
python-versions = ">=3.8"
files = [
{file = "pycodestyle-2.11.1-py2.py3-none-any.whl", hash = "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"},
{file = "pycodestyle-2.11.1.tar.gz", hash = "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f"},
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.4.2" version = "2.4.2"
@ -644,6 +717,17 @@ files = [
pydantic = ">=2.0.1" pydantic = ">=2.0.1"
python-dotenv = ">=0.21.0" python-dotenv = ">=0.21.0"
[[package]]
name = "pyflakes"
version = "3.1.0"
description = "passive checker of Python programs"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"},
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"},
]
[[package]] [[package]]
name = "python-dateutil" name = "python-dateutil"
version = "2.8.2" version = "2.8.2"
@ -1182,5 +1266,5 @@ pytz = "*"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.12"
content-hash = "8f12ef0c2885507b642715d41c29593a6e93c07e5db84646023439f876302bff" content-hash = "cc77f4d7f7a03c79b6d3022c83c16bb8133638fd2cb0c06b146d03aea82ae78f"

View file

@ -2,7 +2,7 @@
authors = ["Jörn-Michael Miehe <jmm@yavook.de>"] authors = ["Jörn-Michael Miehe <jmm@yavook.de>"]
description = "" description = ""
include = ["ovdashboard_api/skel/*"] include = ["ovdashboard_api/skel/*"]
name = "ovdashboard-api" name = "ovdashboard_api"
version = "0.1.0" version = "0.1.0"
[tool.poetry.dependencies] [tool.poetry.dependencies]
@ -12,17 +12,15 @@ async-cache = "^1.1.1"
caldav = "^1.3.6" caldav = "^1.3.6"
fastapi = "^0.103.2" fastapi = "^0.103.2"
pydantic-settings = "^2.0.3" pydantic-settings = "^2.0.3"
python = "^3.11" python = "^3.12"
python-magic = "^0.4.27" python-magic = "^0.4.27"
tomli-w = "^1.0.0" tomli-w = "^1.0.0"
uvicorn = {extras = ["standard"], version = "^0.23.2"} uvicorn = {extras = ["standard"], version = "^0.23.2"}
webdavclient3 = "^3.14.6" webdavclient3 = "^3.14.6"
[tool.poetry.dev-dependencies] [tool.poetry.group.dev.dependencies]
# pytest = "^5.2" flake8 = "^6.1.0"
flake8-isort = "^6.1.0"
[tool.poetry.scripts]
ovdashboard-api = "ovdashboard_api.__main__:main"
[build-system] [build-system]
build-backend = "poetry.core.masonry.api" build-backend = "poetry.core.masonry.api"