Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e756f55c19 | ||
|
|
64ae3cd521 | ||
|
|
7976d07b2e | ||
|
|
9ed7cf5444 | ||
|
|
0e90e4c5f9 | ||
|
|
80f82ed12d | ||
|
|
c2d618b0e5 | ||
|
|
f5b4f08db5 | ||
|
|
4ad814edf4 | ||
|
|
63a57675f8 | ||
|
|
d8e50b79a5 | ||
|
|
8cb02ba558 | ||
|
|
c13dea824c | ||
|
|
a4c0820cd3 | ||
|
|
05de44a11c | ||
|
|
197a530f81 | ||
|
|
827f5f3fa9 | ||
|
|
43c947ab9d | ||
|
|
4373c31b67 | ||
|
|
de4825859c | ||
|
|
d05e5a30c3 | ||
|
|
3e9945f024 | ||
|
|
77eb3cdac5 | ||
|
|
bbffe05bdb | ||
|
|
d1bbe3bf35 | ||
|
|
32c92a6b7f | ||
|
|
35a15c0040 | ||
|
|
4f8d84e898 | ||
|
|
abe89fdbba | ||
|
|
c886fd1e8a | ||
|
|
f50aedc5fc | ||
|
|
4d1b013d38 | ||
|
|
c40b4d68b5 | ||
|
|
b406fd7a06 | ||
|
|
74180ae7ea | ||
|
|
9bf1c8861c |
@@ -0,0 +1,3 @@
|
||||
Makefile
|
||||
.gitignore
|
||||
.env
|
||||
@@ -0,0 +1,7 @@
|
||||
liberapay: marcpartensky
|
||||
patreon: marcpartensky
|
||||
github: marcpartensky
|
||||
custom:
|
||||
# - monero:43J2XmRtjDPRLD35UsYGH2AZamFBQz7dt4VdvZXVm3SuadTrvcoXUXBRUNHqukFte23Q5TWXqhbwaLJJgoVQzzbe2AJDANQ
|
||||
- https://paypal.me/marcpartensky
|
||||
- https://buymeacoffee.com/marcpartensky
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# Publish `v1.2.3` tags as releases.
|
||||
tags:
|
||||
- v*
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
IMAGE_NAME: time-assistant
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build the tag
|
||||
id: tag
|
||||
run: echo "::set-output name=tag::$(git rev-parse --short HEAD).$(date +'%Y-%m-%d')"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push with tag
|
||||
id: tag-build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: marcpartensky/time-assistant:v${{ github.run_number }}.${{ steps.tag.outputs.tag }}
|
||||
- name: Build and push latest
|
||||
id: latest-build
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
push: true
|
||||
tags: marcpartensky/time-assistant:latest
|
||||
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
steps:
|
||||
- name: executing remote ssh commands using password
|
||||
uses: appleboy/ssh-action@v1.0.3
|
||||
with:
|
||||
host: ${{ secrets.HOST }}
|
||||
username: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.KEY}}
|
||||
port: ${{ secrets.PORT }}
|
||||
script: |
|
||||
cd ~/git/docker/services
|
||||
docker compose -f time-assistant/compose.yml pull
|
||||
docker compose -f time-assistant/compose.yml up -d --force-recreate time-assistant
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Test python
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
# Publish `v1.2.3` tags as releases.
|
||||
tags:
|
||||
- v*
|
||||
paths:
|
||||
- 'app/**' # Trigger only when files in the app folder change
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
test-python-versions:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
python-version: ['3.12']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: pip
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ hashFiles('setup.py') }}-${{ hashFiles('requirements.txt') }}
|
||||
- name: Install requirements
|
||||
run: pip install -r requirements.txt
|
||||
- name: Display Python version
|
||||
run: python -c "import sys; print(sys.version)"
|
||||
@@ -1,3 +1,4 @@
|
||||
.venv
|
||||
__pycache__/
|
||||
.pytest_cache
|
||||
.env
|
||||
|
||||
+7
-3
@@ -1,4 +1,4 @@
|
||||
FROM python:3.12-slim as builder
|
||||
FROM python:3.12-slim AS builder
|
||||
|
||||
RUN apt-get update && apt-get install -y curl && \
|
||||
curl -LsSf https://astral.sh/uv/install.sh | sh && \
|
||||
@@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y curl && \
|
||||
ENV PATH="/root/.cargo/bin:$PATH"
|
||||
|
||||
WORKDIR /app
|
||||
RUn pip install uv granian
|
||||
RUN pip install uv granian
|
||||
COPY requirements.txt .
|
||||
RUN uv pip install -r requirements.txt --target /opt/dependencies
|
||||
|
||||
@@ -19,5 +19,9 @@ ENV PYTHONPATH="/opt/dependencies:$PYTHONPATH"
|
||||
COPY --from=builder /opt/dependencies /opt/dependencies
|
||||
RUN pip install granian
|
||||
|
||||
COPY ./tests/health.py /tests/health.py
|
||||
HEALTHCHECK CMD python /tests/health.py
|
||||
|
||||
COPY ./app /app
|
||||
ENTRYPOINT ["granian", "--interface", "asgi", "--host", "0.0.0.0", "--port", "8000", "--http", "auto", "app.main:app"]
|
||||
# ENTRYPOINT ["granian", "--interface", "asgi", "--host", "0.0.0.0", "--port", "8000", "--http", "auto", "app.main:app"]
|
||||
ENTRYPOINT ["python", "-m", "app.main"]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# include .env
|
||||
# export
|
||||
|
||||
# uv run python ./time-assistant/server.py
|
||||
# uv run uvicorn app.main:app --reload
|
||||
# hypercorn app.main --bind 0.0.0.0 --reload --log-level debug
|
||||
# hypercorn -c hypercorn.toml --reload app.main
|
||||
# granian app.main --interface asgi --host 0.0.0.0 --access-log --reload
|
||||
run:
|
||||
python -m app.main
|
||||
test:
|
||||
uv run pytest tests/
|
||||
push:
|
||||
uv export -o requirements.txt
|
||||
docker compose build
|
||||
docker compose push
|
||||
@@ -1,9 +1,16 @@
|
||||
# include .env
|
||||
# export
|
||||
|
||||
# uv run python ./time-assistant/server.py
|
||||
# uv run uvicorn app.main:app --reload
|
||||
# hypercorn app.main --bind 0.0.0.0 --reload --log-level debug
|
||||
# hypercorn -c hypercorn.toml --reload app.main
|
||||
# granian app.main --interface asgi --host 0.0.0.0 --access-log --reload
|
||||
run:
|
||||
# uv run python ./time-assistant/server.py
|
||||
# uv run uvicorn app.main:app --reload
|
||||
# hypercorn app.main --bind 0.0.0.0 --reload --log-level debug
|
||||
hypercorn -c hypercorn.toml --reload app.main
|
||||
python -m app.main
|
||||
test:
|
||||
uv run pytest tests/
|
||||
export:
|
||||
poetry export -o requirements.txt
|
||||
push:
|
||||
uv export -o requirements.txt
|
||||
docker compose build
|
||||
docker compose push
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
NEXTCLOUD_URL = os.getenv("NEXTCLOUD_URL")
|
||||
NEXTCLOUD_USER = os.getenv("NEXTCLOUD_USER")
|
||||
NEXTCLOUD_PASSWORD = os.getenv("NEXTCLOUD_PASSWORD")
|
||||
NEXTCLOUD_BOARD = os.getenv("NEXTCLOUD_BOARD")
|
||||
|
||||
if not all([NEXTCLOUD_URL, NEXTCLOUD_USER, NEXTCLOUD_PASSWORD, NEXTCLOUD_BOARD]):
|
||||
raise Exception("Missing required environment variables.")
|
||||
+15
-2
@@ -16,6 +16,19 @@ app.include_router(websocket_router)
|
||||
|
||||
# Point d'entrée principal
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
import os
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000, log_level="info")
|
||||
from granian import Granian
|
||||
from granian.constants import Interfaces
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
Granian(
|
||||
target="app.main:app",
|
||||
interface=Interfaces.ASGI,
|
||||
address=os.environ["GRANIAN_HOST"],
|
||||
port=int(os.environ["GRANIAN_PORT"]),
|
||||
log_access=bool(os.environ["GRANIAN_LOG_ACCESS_ENABLED"]),
|
||||
).serve()
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
from datetime import datetime
|
||||
import portion as P
|
||||
from zoneinfo import ZoneInfo
|
||||
from app.settings import settings
|
||||
|
||||
# from typing import Iterable
|
||||
|
||||
|
||||
class Interval(P.Interval):
|
||||
"""Intervalle personnalisé avec un affichage lisible et gestion du timezone."""
|
||||
|
||||
def __init__(self, *args, tz: ZoneInfo = settings.zoneinfo, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.tz = tz
|
||||
|
||||
# %% Méthodes critiques à surcharger %%#
|
||||
def _convert(self, other):
|
||||
"""Convertit les autres intervalles en instances de notre classe si nécessaire"""
|
||||
if isinstance(other, P.Interval) and not isinstance(other, Interval):
|
||||
return Interval(other, tz=self.tz)
|
||||
return other
|
||||
|
||||
def __or__(self, other):
|
||||
return Interval(super().__or__(self._convert(other)), tz=self.tz)
|
||||
|
||||
def __and__(self, other):
|
||||
return Interval(super().__and__(self._convert(other)), tz=self.tz)
|
||||
|
||||
def __sub__(self, other):
|
||||
return Interval(super().__sub__(self._convert(other)), tz=self.tz)
|
||||
|
||||
def __invert__(self):
|
||||
return Interval(super().__invert__(), tz=self.tz)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.format_intervals()
|
||||
|
||||
def format_intervals(self, date_format: str = "%a %d %b %Y") -> str:
|
||||
if self.empty:
|
||||
return "∅ (empty interval)"
|
||||
|
||||
days = {}
|
||||
for atomic in self:
|
||||
start = (
|
||||
atomic.lower.astimezone(self.tz)
|
||||
if isinstance(atomic.lower, datetime)
|
||||
else atomic.lower
|
||||
)
|
||||
end = (
|
||||
atomic.upper.astimezone(self.tz)
|
||||
if isinstance(atomic.upper, datetime)
|
||||
else atomic.upper
|
||||
)
|
||||
|
||||
# Groupement par date
|
||||
if isinstance(start, datetime):
|
||||
day_key = start.strftime("%Y-%m-%d")
|
||||
time_slot = (
|
||||
start.strftime("%H:%M"),
|
||||
end.strftime("%H:%M") if isinstance(end, datetime) else str(end),
|
||||
)
|
||||
|
||||
if day_key not in days:
|
||||
days[day_key] = {"date": start, "slots": []}
|
||||
days[day_key]["slots"].append(time_slot)
|
||||
|
||||
# Construction de la sortie
|
||||
output = []
|
||||
for day in sorted(days.values(), key=lambda d: d["date"]):
|
||||
date_str = day["date"].strftime(date_format)
|
||||
slots = []
|
||||
for start, end in day["slots"]:
|
||||
slots.append(f"{start} - {end}")
|
||||
output.append(f"{date_str}: {', '.join(slots)}")
|
||||
|
||||
return "\n".join(output) or "∅ (no datetime intervals)"
|
||||
|
||||
@classmethod
|
||||
def closed(cls, lower, upper):
|
||||
tz = None
|
||||
if isinstance(lower, datetime) and lower.tzinfo:
|
||||
tz = lower.tzinfo
|
||||
return cls(P.closed(lower, upper), tz)
|
||||
|
||||
def to_json(self) -> list[tuple[datetime]]:
|
||||
"""Sérialise l'intervalle en format JSON exploitable."""
|
||||
output = []
|
||||
|
||||
for atomic in self:
|
||||
start = atomic.lower
|
||||
end = atomic.upper
|
||||
|
||||
# Conversion des datetime en ISO format
|
||||
def convert(value):
|
||||
if isinstance(value, datetime):
|
||||
return value.astimezone(self.tz).isoformat()
|
||||
return value
|
||||
|
||||
output.append((convert(start), convert(end)))
|
||||
|
||||
return output
|
||||
@@ -0,0 +1,203 @@
|
||||
from ics import Event
|
||||
import caldav
|
||||
from typing import Optional
|
||||
import vobject
|
||||
from datetime import datetime
|
||||
|
||||
from app.settings import settings
|
||||
|
||||
|
||||
def get_calendar(name: str) -> Optional[caldav.Calendar]:
|
||||
"""Get calendar by name using list comprehension"""
|
||||
return next((c for c in settings.calendars if c.name == name), None)
|
||||
|
||||
|
||||
class SmartEvent(Event):
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
rrule=None,
|
||||
exdates=None,
|
||||
sequence=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
rrule_obj : un objet dateutil.rrule (optionnel).
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if rrule is not None:
|
||||
self._rrule = rrule
|
||||
self.exdates = exdates
|
||||
self.sequence = sequence
|
||||
|
||||
@classmethod
|
||||
def from_icsevent(cls, event: Event):
|
||||
return cls(
|
||||
uid=event.uid,
|
||||
created=event.created,
|
||||
last_modified=event.last_modified,
|
||||
name=event.name,
|
||||
begin=event.begin,
|
||||
end=event.end,
|
||||
# duration=event.duration,
|
||||
# all_day=event.all_day,
|
||||
description=event.description,
|
||||
location=event.location,
|
||||
url=event.url,
|
||||
status=event.status,
|
||||
classification=event.classification,
|
||||
organizer=event.organizer,
|
||||
attendees=event.attendees,
|
||||
alarms=event.alarms,
|
||||
categories=event.categories,
|
||||
)
|
||||
|
||||
@property
|
||||
def rrule(self):
|
||||
# Pour simplifier, on retourne la chaîne RRULE actuelle
|
||||
return convert_rrule_to_str(self._rrule)
|
||||
|
||||
# @rrule.setter
|
||||
def set_rrule(self, rrule):
|
||||
self._rrule = rrule
|
||||
|
||||
def save_to_calendar(self, calendar_name: str):
|
||||
"""Sauvegarde dans un calendrier"""
|
||||
calendar = get_calendar(calendar_name)
|
||||
if not calendar:
|
||||
return False
|
||||
vcal = self.to_vobject()
|
||||
data = vcal.serialize()
|
||||
calendar.add_event(data)
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
t = "SmartEvent("
|
||||
t += self.begin.strftime("%m-%d %H:%M") + ", "
|
||||
t += self.end.strftime("%m-%d %H:%M") + ", "
|
||||
t += self.name
|
||||
t += ")"
|
||||
return t
|
||||
|
||||
def to_vobject(self) -> vobject.base.Component:
|
||||
"""Convertit un événement ICS.py en VCALENDAR avec gestion des types"""
|
||||
vcal = vobject.iCalendar()
|
||||
vevent = vcal.add("vevent")
|
||||
|
||||
# Propriétés obligatoires avec conversion explicite
|
||||
vevent.add("uid").value = str(self.uid)
|
||||
vevent.add("dtstamp").value = datetime.now(settings.zoneinfo)
|
||||
vevent.add("summary").value = str(self.name)
|
||||
|
||||
vevent.add("dtstart").value = self.begin.datetime
|
||||
if self.end:
|
||||
vevent.add("dtend").value = self.end.datetime
|
||||
|
||||
if self.sequence:
|
||||
vevent.add("sequence").value = str(self.sequence)
|
||||
|
||||
if self.description:
|
||||
vevent.add("description").value = str(self.description)
|
||||
|
||||
if self.location:
|
||||
vevent.add("location").value = str(self.location)
|
||||
|
||||
if self.categories:
|
||||
# print("categories:")
|
||||
# print(self.categories)
|
||||
# print(type(self.categories))
|
||||
# vevent.add("categories").value = ",".join(map(str, tuple(self.categories)))
|
||||
vevent.add("categories").value = self.categories
|
||||
|
||||
if hasattr(self, "status"):
|
||||
if self.status:
|
||||
vevent.add("status").value = self.status
|
||||
|
||||
if hasattr(self, "transparent"):
|
||||
vevent.add("transp").value = "TRANSPARENT" if self.transparent else "OPAQUE"
|
||||
|
||||
return vcal
|
||||
|
||||
# # Géolocalisation avec formatage contrôlé
|
||||
if hasattr(self, "geo"):
|
||||
if self.geo:
|
||||
if self.geo.latitude and self.geo.longitude:
|
||||
geo_value = f"{float(self.geo.latitude):.6f};{float(self.geo.longitude):.6f}"
|
||||
vevent.add("geo").value = geo_value
|
||||
|
||||
if hasattr(self, "rrule"):
|
||||
rrule_str = str(self.rrule).replace("RRULE:", "")
|
||||
vevent.add("rrule").value = rrule_str
|
||||
|
||||
if hasattr(self, "exdates"):
|
||||
if self.exdates:
|
||||
for exdate in self.exdates:
|
||||
exdate_prop = vevent.add("exdate")
|
||||
exdate_prop.value = exdate.astimezone(gettz("UTC"))
|
||||
|
||||
# # Alarmes avec vérification de type
|
||||
for alarm in self.alarms:
|
||||
valarm = vevent.add("valarm")
|
||||
valarm.add("action").value = "DISPLAY"
|
||||
valarm.add("trigger").value = alarm.trigger
|
||||
|
||||
if hasattr(alarm, "display_text"):
|
||||
valarm.add("description").value = str(alarm.display_text)
|
||||
|
||||
extended_props = {
|
||||
"url": "url",
|
||||
"priority": "priority",
|
||||
"class": "class",
|
||||
"created": "created",
|
||||
"last_modified": "last-modified",
|
||||
}
|
||||
|
||||
for attr, prop in extended_props.items():
|
||||
if hasattr(self, attr):
|
||||
value = getattr(self, attr)
|
||||
if isinstance(value, list):
|
||||
value = ",".join(map(str, value))
|
||||
vevent.add(prop).value = str(value)
|
||||
|
||||
# Validation avant sauvegarde
|
||||
if len(vevent.contents.get("dtstart", [])) > 1:
|
||||
raise ValueError("Multiple DTSTART properties detected")
|
||||
|
||||
return vcal
|
||||
|
||||
|
||||
def convert_rrule_to_str(rule):
|
||||
"""
|
||||
Convertit un objet dateutil.rrule en chaîne conforme à RFC5545.
|
||||
Attention : ici on utilise des attributs internes de l'objet rrule.
|
||||
"""
|
||||
freq_map = {
|
||||
0: "SECONDLY",
|
||||
1: "MINUTELY",
|
||||
2: "HOURLY",
|
||||
3: "DAILY",
|
||||
4: "WEEKLY",
|
||||
5: "MONTHLY",
|
||||
6: "YEARLY",
|
||||
}
|
||||
parts = []
|
||||
freq = rule._freq
|
||||
parts.append("FREQ=" + freq_map.get(freq, "DAILY"))
|
||||
|
||||
if rule._interval != 1:
|
||||
parts.append("INTERVAL=" + str(rule._interval))
|
||||
|
||||
if rule._until:
|
||||
parts.append("UNTIL=" + rule._until.strftime("%Y%m%dT%H%M%SZ"))
|
||||
|
||||
if rule._count:
|
||||
parts.append("COUNT=" + str(rule._count))
|
||||
|
||||
if rule._byweekday:
|
||||
day_map = {0: "MO", 1: "TU", 2: "WE", 3: "TH", 4: "FR", 5: "SA", 6: "SU"}
|
||||
weekdays = []
|
||||
for wd in rule._byweekday:
|
||||
weekdays.append(day_map.get(wd.weekday, "MO"))
|
||||
parts.append("BYDAY=" + ",".join(weekdays))
|
||||
|
||||
return ";".join(parts)
|
||||
@@ -0,0 +1,14 @@
|
||||
from fastapi import Security, HTTPException, status
|
||||
from fastapi.security import APIKeyHeader
|
||||
from db import check_api_key, get_user_from_api_key
|
||||
|
||||
api_key_header = APIKeyHeader(name="X-API-Key")
|
||||
|
||||
|
||||
def get_user(api_key_header: str = Security(api_key_header)):
|
||||
if check_api_key(api_key_header):
|
||||
user = get_user_from_api_key(api_key_header)
|
||||
return user
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED, detail="Missing or invalid API key"
|
||||
)
|
||||
+101
-22
@@ -1,4 +1,6 @@
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi import APIRouter, HTTPException, Depends
|
||||
from fastapi.security import APIKeyHeader
|
||||
|
||||
from app.models.schemas import Message
|
||||
from pydantic import BaseModel
|
||||
from collections import OrderedDict
|
||||
@@ -10,13 +12,31 @@ from app.services.logger import logger
|
||||
from app.services.deck import nc
|
||||
import app.services.deck as deck
|
||||
import app.services.calendar as calendar
|
||||
|
||||
from app.services.scheduler import Scheduler
|
||||
|
||||
from app.models.interval import Interval
|
||||
|
||||
import datetime
|
||||
import portion as P
|
||||
|
||||
from app.settings import settings
|
||||
|
||||
router = APIRouter()
|
||||
ZONEINFO = ZoneInfo(settings.timezone)
|
||||
|
||||
# Configuration de la sécurité
|
||||
api_key_header = APIKeyHeader(name="X-API-Key")
|
||||
|
||||
|
||||
async def get_api_key(api_key: str = Depends(api_key_header)):
|
||||
if api_key != settings.api_key:
|
||||
raise HTTPException(status_code=401, detail="Invalid API Key")
|
||||
return api_key
|
||||
|
||||
|
||||
if settings.debug: # pas de check en debug
|
||||
router = APIRouter()
|
||||
else:
|
||||
router = APIRouter(dependencies=[Depends(get_api_key)])
|
||||
|
||||
|
||||
def serialize_interval(interval: P.Interval) -> Tuple[str]:
|
||||
@@ -28,18 +48,13 @@ async def root():
|
||||
return {"message": "Bienvenue dans mon projet FastAPI !"}
|
||||
|
||||
|
||||
@router.get("/live")
|
||||
@router.get("/live", dependencies=[])
|
||||
async def health():
|
||||
return "OK"
|
||||
|
||||
|
||||
@router.post("/echo")
|
||||
async def echo_message(message: Message):
|
||||
return {"message": message.text}
|
||||
return {"message": "OK"}
|
||||
|
||||
|
||||
@router.get("/boards")
|
||||
async def get_all_boards():
|
||||
async def get_boards():
|
||||
return deck.get_boards()
|
||||
|
||||
|
||||
@@ -78,7 +93,7 @@ async def get_cards_by_stack_id(stack_id: str):
|
||||
|
||||
@router.get("/board/{board_name}/labels")
|
||||
async def get_labels(board_name: str):
|
||||
return deck.get_board_labels(board_name)
|
||||
return deck.get_labels_by_board_name(board_name)
|
||||
|
||||
|
||||
class Color(BaseModel):
|
||||
@@ -95,18 +110,30 @@ async def show_color(color: Color):
|
||||
return color
|
||||
|
||||
|
||||
@router.get("/events")
|
||||
async def get_events():
|
||||
@router.get("/all-events")
|
||||
async def get_all_events():
|
||||
events = calendar.get_events()
|
||||
for event in events:
|
||||
print(event)
|
||||
return events
|
||||
|
||||
|
||||
@router.get("/events/{days}")
|
||||
async def get_events(days: int = 1):
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
scheduler = Scheduler(events)
|
||||
events = scheduler.get_today_events()
|
||||
print(type(events[0]))
|
||||
return events
|
||||
|
||||
|
||||
@router.get("/gaps")
|
||||
@router.get("/gaps/{days}")
|
||||
async def get_gaps(days: int = 1):
|
||||
t1 = datetime.datetime.now(ZoneInfo("Europe/Paris"))
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
@@ -118,7 +145,7 @@ async def get_gaps(days: int = 1):
|
||||
@router.get("/trimed-gaps")
|
||||
@router.get("/trimed-gaps/{days}")
|
||||
async def get_trimed_gaps(days: int = 1):
|
||||
t1 = datetime.datetime.now(ZoneInfo("Europe/Paris"))
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
@@ -134,9 +161,41 @@ async def get_trimed_gaps(days: int = 1):
|
||||
return [serialize_interval(gap) for gap in gaps]
|
||||
|
||||
|
||||
@router.get("/eat-gaps/{days}")
|
||||
async def eat_gaps(days: int = 1):
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
scheduler = Scheduler(events)
|
||||
|
||||
lunch_interval = scheduler.create_daily_events(
|
||||
t1, t2, settings.lunch_start, settings.lunch_end
|
||||
)
|
||||
dinner_interval = scheduler.create_daily_events(
|
||||
t1, t2, settings.dinner_start, settings.dinner_end
|
||||
)
|
||||
interval = Interval(lunch_interval | dinner_interval)
|
||||
print(interval)
|
||||
return interval.to_json()
|
||||
|
||||
|
||||
@router.get("/availability/{days}")
|
||||
async def availability(days: int = 1):
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
scheduler = Scheduler(events)
|
||||
interval = scheduler.get_availability(t1, t2)
|
||||
print(type(interval))
|
||||
|
||||
return interval.to_json()
|
||||
|
||||
|
||||
@router.get("/schedule/{board_name}/{days}")
|
||||
async def schedule(board_name: str, days: int = 1):
|
||||
t1 = datetime.datetime.now(ZoneInfo("Europe/Paris"))
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
board = deck.get_board_by_name(board_name)
|
||||
@@ -146,9 +205,6 @@ async def schedule(board_name: str, days: int = 1):
|
||||
scheduler.hydrate(board)
|
||||
events = scheduler.schedule(t1, t2)
|
||||
|
||||
gaps = scheduler.get_gaps(t1, t2)
|
||||
gaps = scheduler.trim_timegaps(gaps)
|
||||
|
||||
return events
|
||||
|
||||
|
||||
@@ -160,7 +216,7 @@ async def modify():
|
||||
@router.get("/sleep")
|
||||
async def sleep():
|
||||
days = 1
|
||||
t1 = datetime.datetime.now(ZoneInfo("Europe/Paris"))
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
|
||||
events = calendar.get_events(t1, t2)
|
||||
@@ -185,6 +241,29 @@ async def sort_by_priority(board_name: str):
|
||||
async def clean(days: int = 1):
|
||||
events = calendar.get_events()
|
||||
scheduler = Scheduler(events)
|
||||
t1 = datetime.datetime.now(ZoneInfo("Europe/Paris"))
|
||||
t1 = datetime.datetime.now(ZONEINFO)
|
||||
t2 = t1 + datetime.timedelta(days)
|
||||
return scheduler.clean_deck_events(t1, t2)
|
||||
|
||||
|
||||
@router.get("/top-priority/{board_name}")
|
||||
async def get_top_priority(board_name: str):
|
||||
"""Return the top priority card to do."""
|
||||
return deck.get_top_priority(board_name)
|
||||
|
||||
|
||||
@router.get("/current-events")
|
||||
async def get_current_event():
|
||||
t = datetime.datetime.now(ZONEINFO)
|
||||
return calendar.get_events_at(t)
|
||||
|
||||
|
||||
@router.get("/focus-event")
|
||||
async def get_focus():
|
||||
t = datetime.datetime.now(ZONEINFO)
|
||||
return calendar.get_focus_event_at(t)
|
||||
|
||||
|
||||
# @router.get("/archive-done-tasks/{board_name}")
|
||||
# async def archive_done_tasks(board_name: str):
|
||||
# return deck.archive_done_tasks(board_name)
|
||||
|
||||
+155
-232
@@ -1,32 +1,26 @@
|
||||
import caldav
|
||||
import vobject
|
||||
|
||||
# from itertools import filter
|
||||
# import itertools
|
||||
|
||||
from dateutil.rrule import rrule, DAILY, WEEKLY, MO, TU, WE, TH, FR, SA, SU
|
||||
from dateutil.tz import gettz
|
||||
from datetime import datetime, timedelta
|
||||
from ics import Calendar, Event
|
||||
from ics import Calendar
|
||||
from ics.alarm import DisplayAlarm
|
||||
from ics.grammar.parse import ContentLine
|
||||
|
||||
from collections import OrderedDict
|
||||
from typing import List, Optional, Dict
|
||||
|
||||
from nextcloud import NextCloud
|
||||
|
||||
# import requests
|
||||
from typing import List, Optional, Dict, Iterator
|
||||
|
||||
# from ics.grammar.parse import ContentLine
|
||||
# from collections import OrderedDict
|
||||
# from nextcloud import NextCloud
|
||||
# from app.services.logger import logger
|
||||
from app.env import *
|
||||
|
||||
CALENDAR_NAME = "Personal"
|
||||
CALENDAR_URL = f"{NEXTCLOUD_URL}/remote.php/dav/calendars/{NEXTCLOUD_USER}"
|
||||
from app.settings import settings
|
||||
from app.models.smartevent import SmartEvent
|
||||
|
||||
print(CALENDAR_URL)
|
||||
|
||||
client = caldav.DAVClient(
|
||||
CALENDAR_URL, username=NEXTCLOUD_USER, password=NEXTCLOUD_PASSWORD
|
||||
)
|
||||
|
||||
client = settings.caldav_client
|
||||
principal = client.principal()
|
||||
calendars = principal.calendars()
|
||||
|
||||
@@ -39,235 +33,132 @@ end_of_day = datetime.combine(today, datetime.max.time()) # 23:59:59
|
||||
# )
|
||||
|
||||
|
||||
class SmartEvent(Event):
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
rrule=None,
|
||||
exdates=None,
|
||||
sequence=None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
rrule_obj : un objet dateutil.rrule (optionnel).
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
if rrule is not None:
|
||||
self._rrule = rrule
|
||||
self.exdates = exdates
|
||||
self.sequence = sequence
|
||||
|
||||
@classmethod
|
||||
def from_icsevent(cls, event: Event):
|
||||
return cls(
|
||||
uid=event.uid,
|
||||
created=event.created,
|
||||
last_modified=event.last_modified,
|
||||
name=event.name,
|
||||
begin=event.begin,
|
||||
end=event.end,
|
||||
# duration=event.duration,
|
||||
# all_day=event.all_day,
|
||||
description=event.description,
|
||||
location=event.location,
|
||||
url=event.url,
|
||||
status=event.status,
|
||||
classification=event.classification,
|
||||
organizer=event.organizer,
|
||||
attendees=event.attendees,
|
||||
alarms=event.alarms,
|
||||
categories=event.categories,
|
||||
)
|
||||
|
||||
@property
|
||||
def rrule(self):
|
||||
# Pour simplifier, on retourne la chaîne RRULE actuelle
|
||||
return convert_rrule_to_str(self._rrule)
|
||||
|
||||
def set_rrule(self, rrule):
|
||||
self._rrule = rrule
|
||||
|
||||
def save_to_calendar(self, calendar_name: str):
|
||||
"""Sauvegarde dans un calendrier"""
|
||||
calendar = get_calendar(calendar_name)
|
||||
if not calendar:
|
||||
return False
|
||||
|
||||
vcal = ics_to_vobject(self)
|
||||
data = vcal.serialize()
|
||||
calendar.add_event(data)
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
t = "SmartEvent("
|
||||
t += self.begin.strftime("%Y-%m-%d %H:%M") + ", "
|
||||
t += self.end.strftime("%Y-%m-%d %H:%M") + ", "
|
||||
t += self.name
|
||||
t += ")"
|
||||
return t
|
||||
|
||||
|
||||
def convert_rrule_to_str(rule):
|
||||
"""
|
||||
Convertit un objet dateutil.rrule en chaîne conforme à RFC5545.
|
||||
Attention : ici on utilise des attributs internes de l'objet rrule.
|
||||
"""
|
||||
freq_map = {
|
||||
0: "SECONDLY",
|
||||
1: "MINUTELY",
|
||||
2: "HOURLY",
|
||||
3: "DAILY",
|
||||
4: "WEEKLY",
|
||||
5: "MONTHLY",
|
||||
6: "YEARLY",
|
||||
}
|
||||
parts = []
|
||||
freq = rule._freq
|
||||
parts.append("FREQ=" + freq_map.get(freq, "DAILY"))
|
||||
|
||||
if rule._interval != 1:
|
||||
parts.append("INTERVAL=" + str(rule._interval))
|
||||
|
||||
if rule._until:
|
||||
parts.append("UNTIL=" + rule._until.strftime("%Y%m%dT%H%M%SZ"))
|
||||
|
||||
if rule._count:
|
||||
parts.append("COUNT=" + str(rule._count))
|
||||
|
||||
if rule._byweekday:
|
||||
day_map = {0: "MO", 1: "TU", 2: "WE", 3: "TH", 4: "FR", 5: "SA", 6: "SU"}
|
||||
weekdays = []
|
||||
for wd in rule._byweekday:
|
||||
weekdays.append(day_map.get(wd.weekday, "MO"))
|
||||
parts.append("BYDAY=" + ",".join(weekdays))
|
||||
|
||||
return ";".join(parts)
|
||||
|
||||
|
||||
def get_calendar(name: str) -> Optional[caldav.Calendar]:
|
||||
"""Get calendar by name using list comprehension"""
|
||||
return next((c for c in client.principal().calendars() if c.name == name), None)
|
||||
return next((c for c in settings.calendars if c.name == name), None)
|
||||
|
||||
|
||||
def modify_event(
|
||||
event_uid: str, new_event: SmartEvent, calendar_name: str = CALENDAR_NAME
|
||||
) -> bool:
|
||||
"""
|
||||
Modifie un événement en utilisant un objet ics.Event
|
||||
"""
|
||||
calendar = get_calendar(calendar_name)
|
||||
if not calendar:
|
||||
return False
|
||||
# def modify_event(
|
||||
# event_uid: str,
|
||||
# new_event: SmartEvent,
|
||||
# calendar_name: str = settings.personal_calendar,
|
||||
# ) -> bool:
|
||||
# """
|
||||
# Modifie un événement en utilisant un objet ics.Event
|
||||
# """
|
||||
# calendar = get_calendar(calendar_name)
|
||||
# if not calendar:
|
||||
# return False
|
||||
|
||||
# Récupérer l'événement existant
|
||||
dav_event = calendar.event_by_uid(event_uid)
|
||||
if not dav_event:
|
||||
return False
|
||||
# # Récupérer l'événement existant
|
||||
# dav_event = calendar.event_by_uid(event_uid)
|
||||
# if not dav_event:
|
||||
# return False
|
||||
|
||||
# Convertir l'événement ics en vobject
|
||||
# ics_cal = Calendar(events=[new_event])
|
||||
vcal = ics_to_vobject(new_event)
|
||||
vevent = vcal.vevent
|
||||
# # Convertir l'événement ics en vobject
|
||||
# # ics_cal = Calendar(events=[new_event])
|
||||
# vcal = ics_to_vobject(new_event)
|
||||
# vevent = vcal.vevent
|
||||
|
||||
vevent.uid.value = event_uid
|
||||
# vevent.uid.value = event_uid
|
||||
|
||||
data = vcal.serialize()
|
||||
dav_event.data = data
|
||||
dav_event.save()
|
||||
return True
|
||||
# data = vcal.serialize()
|
||||
# dav_event.data = data
|
||||
# dav_event.save()
|
||||
# return True
|
||||
|
||||
|
||||
def ics_to_vobject(ics_event: SmartEvent) -> vobject.base.Component:
|
||||
"""Convertit un événement ICS.py en VCALENDAR avec gestion des types"""
|
||||
vcal = vobject.iCalendar()
|
||||
vevent = vcal.add("vevent")
|
||||
# def ics_to_vobject(ics_event: SmartEvent) -> vobject.base.Component:
|
||||
# """Convertit un événement ICS.py en VCALENDAR avec gestion des types"""
|
||||
# vcal = vobject.iCalendar()
|
||||
# vevent = vcal.add("vevent")
|
||||
|
||||
# Propriétés obligatoires avec conversion explicite
|
||||
vevent.add("uid").value = str(ics_event.uid)
|
||||
vevent.add("dtstamp").value = datetime.now(gettz("UTC"))
|
||||
vevent.add("summary").value = str(ics_event.name)
|
||||
# # Propriétés obligatoires avec conversion explicite
|
||||
# vevent.add("uid").value = str(ics_event.uid)
|
||||
# vevent.add("dtstamp").value = datetime.now(gettz("UTC"))
|
||||
# vevent.add("summary").value = str(ics_event.name)
|
||||
|
||||
vevent.add("dtstart").value = ics_event.begin.datetime
|
||||
if ics_event.end:
|
||||
vevent.add("dtend").value = ics_event.end.datetime
|
||||
# vevent.add("dtstart").value = ics_event.begin.datetime
|
||||
# if ics_event.end:
|
||||
# vevent.add("dtend").value = ics_event.end.datetime
|
||||
|
||||
if ics_event.sequence:
|
||||
vevent.add("sequence").value = str(ics_event.sequence)
|
||||
# if ics_event.sequence:
|
||||
# vevent.add("sequence").value = str(ics_event.sequence)
|
||||
|
||||
if ics_event.description:
|
||||
vevent.add("description").value = str(ics_event.description)
|
||||
# if ics_event.description:
|
||||
# vevent.add("description").value = str(ics_event.description)
|
||||
|
||||
if ics_event.location:
|
||||
vevent.add("location").value = str(ics_event.location)
|
||||
# if ics_event.location:
|
||||
# vevent.add("location").value = str(ics_event.location)
|
||||
|
||||
if ics_event.categories:
|
||||
# print("categories:")
|
||||
# print(ics_event.categories)
|
||||
# print(type(ics_event.categories))
|
||||
# vevent.add("categories").value = ",".join(map(str, tuple(ics_event.categories)))
|
||||
vevent.add("categories").value = ics_event.categories
|
||||
# if ics_event.categories:
|
||||
# # print("categories:")
|
||||
# # print(ics_event.categories)
|
||||
# # print(type(ics_event.categories))
|
||||
# # vevent.add("categories").value = ",".join(map(str, tuple(ics_event.categories)))
|
||||
# vevent.add("categories").value = ics_event.categories
|
||||
|
||||
if hasattr(ics_event, "status"):
|
||||
if ics_event.status:
|
||||
vevent.add("status").value = ics_event.status
|
||||
# if hasattr(ics_event, "status"):
|
||||
# if ics_event.status:
|
||||
# vevent.add("status").value = ics_event.status
|
||||
|
||||
if hasattr(ics_event, "transparent"):
|
||||
vevent.add("transp").value = (
|
||||
"TRANSPARENT" if ics_event.transparent else "OPAQUE"
|
||||
)
|
||||
# if hasattr(ics_event, "transparent"):
|
||||
# vevent.add("transp").value = (
|
||||
# "TRANSPARENT" if ics_event.transparent else "OPAQUE"
|
||||
# )
|
||||
|
||||
return vcal
|
||||
# return vcal
|
||||
|
||||
# # Géolocalisation avec formatage contrôlé
|
||||
if hasattr(ics_event, "geo"):
|
||||
if ics_event.geo:
|
||||
if ics_event.geo.latitude and ics_event.geo.longitude:
|
||||
geo_value = f"{float(ics_event.geo.latitude):.6f};{float(ics_event.geo.longitude):.6f}"
|
||||
vevent.add("geo").value = geo_value
|
||||
# # # Géolocalisation avec formatage contrôlé
|
||||
# if hasattr(ics_event, "geo"):
|
||||
# if ics_event.geo:
|
||||
# if ics_event.geo.latitude and ics_event.geo.longitude:
|
||||
# geo_value = f"{float(ics_event.geo.latitude):.6f};{float(ics_event.geo.longitude):.6f}"
|
||||
# vevent.add("geo").value = geo_value
|
||||
|
||||
if hasattr(ics_event, "rrule"):
|
||||
rrule_str = str(ics_event.rrule).replace("RRULE:", "")
|
||||
vevent.add("rrule").value = rrule_str
|
||||
# if hasattr(ics_event, "rrule"):
|
||||
# rrule_str = str(ics_event.rrule).replace("RRULE:", "")
|
||||
# vevent.add("rrule").value = rrule_str
|
||||
|
||||
if hasattr(ics_event, "exdates"):
|
||||
if ics_event.exdates:
|
||||
for exdate in ics_event.exdates:
|
||||
exdate_prop = vevent.add("exdate")
|
||||
exdate_prop.value = exdate.astimezone(gettz("UTC"))
|
||||
# if hasattr(ics_event, "exdates"):
|
||||
# if ics_event.exdates:
|
||||
# for exdate in ics_event.exdates:
|
||||
# exdate_prop = vevent.add("exdate")
|
||||
# exdate_prop.value = exdate.astimezone(gettz("UTC"))
|
||||
|
||||
# # Alarmes avec vérification de type
|
||||
for alarm in ics_event.alarms:
|
||||
valarm = vevent.add("valarm")
|
||||
valarm.add("action").value = "DISPLAY"
|
||||
valarm.add("trigger").value = alarm.trigger
|
||||
# # # Alarmes avec vérification de type
|
||||
# for alarm in ics_event.alarms:
|
||||
# valarm = vevent.add("valarm")
|
||||
# valarm.add("action").value = "DISPLAY"
|
||||
# valarm.add("trigger").value = alarm.trigger
|
||||
|
||||
if hasattr(alarm, "display_text"):
|
||||
valarm.add("description").value = str(alarm.display_text)
|
||||
# if hasattr(alarm, "display_text"):
|
||||
# valarm.add("description").value = str(alarm.display_text)
|
||||
|
||||
extended_props = {
|
||||
"url": "url",
|
||||
"priority": "priority",
|
||||
"class": "class",
|
||||
"created": "created",
|
||||
"last_modified": "last-modified",
|
||||
}
|
||||
# extended_props = {
|
||||
# "url": "url",
|
||||
# "priority": "priority",
|
||||
# "class": "class",
|
||||
# "created": "created",
|
||||
# "last_modified": "last-modified",
|
||||
# }
|
||||
|
||||
for attr, prop in extended_props.items():
|
||||
if hasattr(ics_event, attr):
|
||||
value = getattr(ics_event, attr)
|
||||
if isinstance(value, list):
|
||||
value = ",".join(map(str, value))
|
||||
vevent.add(prop).value = str(value)
|
||||
# for attr, prop in extended_props.items():
|
||||
# if hasattr(ics_event, attr):
|
||||
# value = getattr(ics_event, attr)
|
||||
# if isinstance(value, list):
|
||||
# value = ",".join(map(str, value))
|
||||
# vevent.add(prop).value = str(value)
|
||||
|
||||
# Validation avant sauvegarde
|
||||
if len(vevent.contents.get("dtstart", [])) > 1:
|
||||
raise ValueError("Multiple DTSTART properties detected")
|
||||
# # Validation avant sauvegarde
|
||||
# if len(vevent.contents.get("dtstart", [])) > 1:
|
||||
# raise ValueError("Multiple DTSTART properties detected")
|
||||
|
||||
return vcal
|
||||
# return vcal
|
||||
|
||||
|
||||
def get_events(
|
||||
# calendar_name: str = CALENDAR_NAME,
|
||||
# calendar_name: str = PERSONAL_CALENDAR,
|
||||
start: datetime = start_of_day,
|
||||
end: datetime = end_of_day,
|
||||
) -> list[SmartEvent]:
|
||||
@@ -283,25 +174,47 @@ def get_events(
|
||||
return events
|
||||
|
||||
|
||||
def get_events_at(t: datetime) -> Iterator[SmartEvent]:
|
||||
for event in get_events():
|
||||
if event.begin < t < event.end:
|
||||
yield event
|
||||
|
||||
|
||||
def get_focus_event_at(t: datetime) -> Optional[SmartEvent]:
|
||||
day = timedelta(days=1)
|
||||
coming_events = (
|
||||
e
|
||||
for e in get_events()
|
||||
if e.end >= t
|
||||
and ( # not a full day
|
||||
((e.end - e.begin) % day).seconds != 0
|
||||
and e.begin == e.begin.replace(minute=0, second=0, microsecond=0)
|
||||
)
|
||||
)
|
||||
for e in sorted(coming_events, key=lambda e: (e.begin, e.end)):
|
||||
return e
|
||||
|
||||
|
||||
def modify_description():
|
||||
events = get_events()
|
||||
if not events:
|
||||
return []
|
||||
|
||||
# Convertir l'événement caldav en ics.Event
|
||||
original_event = events[0]
|
||||
event = SmartEvent(
|
||||
name="Nouveau nom d'événement",
|
||||
begin=original_event.begin.datetime,
|
||||
end=original_event.end.datetime,
|
||||
description="Nouvelle description",
|
||||
location="Nouveau lieu",
|
||||
# status=None,
|
||||
# status="CONFIRMED",
|
||||
alarms=[
|
||||
DisplayAlarm(trigger=timedelta(minutes=-15), display_text="allumer le feu"),
|
||||
],
|
||||
)
|
||||
# original_event = events[0]
|
||||
# event = SmartEvent(
|
||||
# name="Nouveau nom d'événement",
|
||||
# begin=original_event.begin.datetime,
|
||||
# end=original_event.end.datetime,
|
||||
# description="Nouvelle description",
|
||||
# location="Nouveau lieu",
|
||||
# # status=None,
|
||||
# # status="CONFIRMED",
|
||||
# alarms=[
|
||||
# DisplayAlarm(trigger=timedelta(minutes=-15), display_text="allumer le feu"),
|
||||
# ],
|
||||
# )
|
||||
|
||||
# event.rrule = convert_rrule_to_str(
|
||||
# rrule(
|
||||
# freq=DAILY,
|
||||
@@ -321,10 +234,20 @@ def modify_description():
|
||||
# "until": datetime(2025, 3, 31, 21, 0, tzinfo=gettz("UTC")),
|
||||
# }
|
||||
|
||||
event = events[0]
|
||||
# event.name = "Nouveau nom d'événement"
|
||||
event.description = "Nouvelle description"
|
||||
# event.location = "Nouveau lieu"
|
||||
# event.alarms=[
|
||||
# DisplayAlarm(trigger=timedelta(minutes=-15), display_text="allumer le feu"),
|
||||
# ],
|
||||
# )
|
||||
|
||||
# Appliquer les modifications
|
||||
success = modify_event(original_event.uid, event)
|
||||
return {"success": success, "events": get_events()}
|
||||
# success = modify_event(original_event.uid, event)
|
||||
# return {"success": success, "events": get_events()}
|
||||
return event.save_to_calendar("Personal")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(principal)
|
||||
def get_current_event_and_time_left():
|
||||
"""Get the current and event and how much time is left to do the task."""
|
||||
|
||||
+25
-11
@@ -5,12 +5,13 @@ from deck.models import Card, Board, Stack, Label
|
||||
from requests.auth import HTTPBasicAuth
|
||||
from app.services.logger import logger
|
||||
from typing import List
|
||||
from app.env import *
|
||||
|
||||
auth = HTTPBasicAuth(os.environ["NEXTCLOUD_USER"], os.environ["NEXTCLOUD_PASSWORD"])
|
||||
from app.settings import settings
|
||||
|
||||
auth = HTTPBasicAuth(settings.nextcloud_user, settings.nextcloud_password)
|
||||
|
||||
nc = NextCloudDeckAPI(
|
||||
NEXTCLOUD_URL,
|
||||
settings.nextcloud_url,
|
||||
auth,
|
||||
ssl_verify=True,
|
||||
)
|
||||
@@ -55,9 +56,6 @@ def get_label_id_by_name(board_id: str, label_name: str):
|
||||
raise LogException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
NEXTCLOUD_BOARD_ID = get_board_id_by_name(str(NEXTCLOUD_BOARD))
|
||||
|
||||
|
||||
def get_boards():
|
||||
return nc.get_boards()
|
||||
|
||||
@@ -111,11 +109,27 @@ def reorder_card(*args, **kwargs):
|
||||
return nc.reorder_card(*args, **kwargs)
|
||||
|
||||
|
||||
# def update_card(card_id: int, order: int, title: str, owner: str):
|
||||
# card_id=card.id,
|
||||
# order=50,
|
||||
# title=card.title,
|
||||
# owner=card.owner,
|
||||
def get_top_priority(board_name: str) -> Card:
|
||||
"""Return the top priority card."""
|
||||
board = get_board_by_name(board_name)
|
||||
stacks = get_stacks_by_board(board)
|
||||
stacks.sort(key=lambda stack: stack.order)
|
||||
cards = stacks[0].cards
|
||||
cards.sort(key=lambda card: card.order)
|
||||
return cards[0]
|
||||
|
||||
|
||||
# def archive_done_tasks(board_name: str) -> List[Card]:
|
||||
# """Read all the cards from the board and find the tasks that are done.
|
||||
# Then archive them and return them."""
|
||||
# # L'API nextcloud deck ne permet pas de manipuler le champs done des cartes.
|
||||
# done_cards = []
|
||||
# board = get_board_by_name(board_name)
|
||||
# stacks = get_stacks_by_board(board)
|
||||
# for stack in stacks:
|
||||
# card = stack.cards[0]
|
||||
|
||||
# return done_cards
|
||||
|
||||
|
||||
# def archive_completed(board_name, str, stack_name: str):
|
||||
|
||||
+134
-204
@@ -1,10 +1,7 @@
|
||||
from .calendar import SmartEvent, Event
|
||||
from deck.models import Card, Board, Stack, Label
|
||||
from typing import List, Tuple, NamedTuple
|
||||
import portion as P
|
||||
|
||||
import vobject
|
||||
|
||||
import app.services.deck as deck
|
||||
import app.services.calendar as calendar
|
||||
|
||||
@@ -15,26 +12,20 @@ from datetime import datetime, timedelta, time
|
||||
from zoneinfo import ZoneInfo
|
||||
import ast
|
||||
|
||||
from app.models.interval import Interval
|
||||
from app.models.smartevent import SmartEvent
|
||||
|
||||
from app.settings import settings
|
||||
|
||||
# planification uniquement dans ces zones horaires
|
||||
DEFAULT_WORK_START = 9
|
||||
DEFAULT_WORK_END = 20
|
||||
DEFAULT_WORK_START = settings.work_start
|
||||
DEFAULT_WORK_END = settings.work_end
|
||||
|
||||
PLAN_DAYS_LIMIT = 1 # Nombre de jours à planifier
|
||||
PLAN_DAYS_LIMIT = settings.plan_day_limit # Nombre de jours à planifier
|
||||
|
||||
DEFAULT_TASK_DURATION = timedelta(hours=2, minutes=45) # en heure
|
||||
|
||||
|
||||
class TimeGap(NamedTuple):
|
||||
t1: datetime
|
||||
t2: datetime
|
||||
|
||||
def __str__(self):
|
||||
return "TG" + str((self.t1, self.t2))
|
||||
|
||||
|
||||
# Class TimeGaps(List(TimeGap)):
|
||||
|
||||
# def __
|
||||
DEFAULT_TASK_DURATION = timedelta(
|
||||
hours=settings.task_duration_hours, minutes=settings.task_duration_minutes
|
||||
) # en heure
|
||||
|
||||
|
||||
class Scheduler:
|
||||
@@ -48,14 +39,21 @@ class Scheduler:
|
||||
calendars: List[str] = [],
|
||||
):
|
||||
self.events = events
|
||||
self.calendars = calendars
|
||||
|
||||
self.work_start = work_start
|
||||
self.work_stop = work_stop
|
||||
self.calendars = calendars
|
||||
self.work_calendar = "Taff"
|
||||
self.personal_calendar = "Personal"
|
||||
self.zoneinfo = ZoneInfo("Europe/Paris")
|
||||
self.priorities = ["Top priority", "High priority"]
|
||||
self.category = "Deck" # Tag given on all created events
|
||||
self.lunch_start = settings.lunch_start
|
||||
self.lunch_end = settings.lunch_end
|
||||
self.dinner_start = settings.dinner_start
|
||||
self.dinner_end = settings.dinner_end
|
||||
|
||||
self.work_calendar = settings.work_calendar
|
||||
self.personal_calendar = settings.personal_calendar
|
||||
self.zoneinfo = settings.zoneinfo
|
||||
|
||||
self.priorities = settings.priorities
|
||||
self.category = settings.deck_category
|
||||
|
||||
def hydrate(self, board: Board):
|
||||
self.board = board
|
||||
@@ -73,59 +71,16 @@ class Scheduler:
|
||||
sorted_cards.extend(cards)
|
||||
self.cards = sorted_cards
|
||||
|
||||
# def get_gaps(self, t1: datetime, t2: datetime) -> List[TimeGap]:
|
||||
# if t1 >= t2:
|
||||
# return []
|
||||
# events = [
|
||||
# (event.begin.astimezone(self.zoneinfo), event.end.astimezone(self.zoneinfo))
|
||||
# for event in self.events
|
||||
# ]
|
||||
|
||||
# valid_events = [(s, e) for (s, e) in events if s < e]
|
||||
# sorted_events = sorted(valid_events, key=lambda x: x[0])
|
||||
|
||||
# # Merging overlapping events
|
||||
# merged = []
|
||||
# for event in sorted_events:
|
||||
# if not merged:
|
||||
# merged.append(event)
|
||||
# else:
|
||||
# last_start, last_end = merged[-1]
|
||||
# current_start, current_end = event
|
||||
# if current_start <= last_end:
|
||||
# merged[-1] = (last_start, max(last_end, current_end))
|
||||
# else:
|
||||
# merged.append(event)
|
||||
|
||||
# # Clip the events starting before t1 and after t2
|
||||
# clipped = []
|
||||
# for start, end in merged:
|
||||
# clipped_start = max(start, t1)
|
||||
# clipped_end = min(end, t2)
|
||||
# if clipped_start < clipped_end:
|
||||
# clipped.append((clipped_start, clipped_end))
|
||||
|
||||
# gaps = []
|
||||
# prev_end = t1
|
||||
# for start, end in clipped:
|
||||
# if start > prev_end:
|
||||
# gaps.append(TimeGap(prev_end, start))
|
||||
# prev_end = max(prev_end, end)
|
||||
# if prev_end < t2:
|
||||
# gaps.append(TimeGap(prev_end, t2))
|
||||
|
||||
# return gaps
|
||||
|
||||
def get_gaps(self, t1: datetime, t2: datetime) -> List[P.Interval]:
|
||||
def get_gaps(self, t1: datetime, t2: datetime) -> Interval:
|
||||
"""Trouve les disponibilités entre les événements calendrier."""
|
||||
if t1 >= t2:
|
||||
return []
|
||||
return Interval(P.empty())
|
||||
|
||||
# Créer un intervalle global [t1, t2]
|
||||
total_interval = P.closed(t1, t2)
|
||||
|
||||
# Créer l'union de tous les événements
|
||||
events_union = P.Interval(
|
||||
events_union = Interval(
|
||||
*[
|
||||
P.closed(
|
||||
event.begin.astimezone(self.zoneinfo),
|
||||
@@ -135,49 +90,7 @@ class Scheduler:
|
||||
if event.begin < event.end
|
||||
]
|
||||
)
|
||||
|
||||
# Calculer les gaps = total_interval - events_union
|
||||
gaps = total_interval - events_union
|
||||
|
||||
return list(gaps) if not gaps.empty else []
|
||||
|
||||
def create_sleep_events(self):
|
||||
"""Create sleep events"""
|
||||
events = []
|
||||
|
||||
today = datetime.today().date()
|
||||
begin_of_sleep = datetime.combine(today, datetime.min.time()).astimezone(
|
||||
self.zoneinfo
|
||||
) # 00:00:00
|
||||
end_of_sleep = begin_of_sleep + timedelta(hours=9)
|
||||
gaps = self.get_gaps(begin_of_sleep, end_of_sleep)
|
||||
|
||||
print(end_of_sleep)
|
||||
# print(gaps[0][1] - timed)
|
||||
first_gap = gaps[0]
|
||||
if first_gap[1] - timedelta(hours=1) < end_of_sleep:
|
||||
end_of_sleep = first_gap[1] - timedelta(hours=1)
|
||||
print(end_of_sleep)
|
||||
|
||||
event = Event(name="Sleep", begin=begin_of_sleep, end=end_of_sleep)
|
||||
|
||||
target_calendar = calendar.get_calendar(self.personal_calendar)
|
||||
|
||||
if not target_calendar:
|
||||
raise ValueError(f"Calendar '{self.work_calendar}' not found")
|
||||
|
||||
events.append(event)
|
||||
print(event.__dict__)
|
||||
|
||||
vcal = calendar.ics_to_vobject(event)
|
||||
ical_data = vcal.serialize()
|
||||
target_calendar.add_event(ical_data)
|
||||
|
||||
return events
|
||||
|
||||
def create_deck_events(self):
|
||||
events = []
|
||||
return events
|
||||
return total_interval - events_union
|
||||
|
||||
def sort_card_by_priority(self) -> List[Card]:
|
||||
"""Tri par ordre de priorité"""
|
||||
@@ -223,7 +136,7 @@ class Scheduler:
|
||||
"""Return time estimation of card in seconds."""
|
||||
return DEFAULT_TASK_DURATION
|
||||
|
||||
def create_event(self, card: Card, interval: P.Interval) -> SmartEvent:
|
||||
def create_event(self, card: Card, interval: Interval) -> SmartEvent:
|
||||
"""Create new event using a card, t1 and t2."""
|
||||
categories = [self.category + str(card.id)]
|
||||
if card.labels:
|
||||
@@ -267,95 +180,31 @@ class Scheduler:
|
||||
dav_event.delete()
|
||||
return removed_events
|
||||
|
||||
def trim_timegaps(self, gaps: List[P.Interval]) -> List[P.Interval]:
|
||||
"""Filtre les gaps pour ne garder que les heures de travail."""
|
||||
if not gaps:
|
||||
return []
|
||||
# def clean_all_deck_events(self) -> int:
|
||||
# """Remove all vcal events that have a Category Deck* (for instance Deck1234)"""
|
||||
# calendar_obj = calendar.get_calendar(self.work_calendar)
|
||||
# if not calendar_obj:
|
||||
# return 0
|
||||
|
||||
# Créer l'union de tous les gaps
|
||||
gaps_union = P.Interval(*gaps)
|
||||
# # Récupérer les événements existants
|
||||
# removed_events = 0
|
||||
|
||||
# Générer les plages horaires de travail
|
||||
work_hours = P.empty()
|
||||
start_date = min(gap.lower.date() for gap in gaps)
|
||||
end_date = max(gap.upper.date() for gap in gaps)
|
||||
|
||||
current_date = start_date
|
||||
while current_date <= end_date:
|
||||
work_start = datetime.combine(
|
||||
current_date, time(hour=self.work_start)
|
||||
).replace(tzinfo=self.zoneinfo)
|
||||
|
||||
work_end = datetime.combine(
|
||||
current_date, time(hour=self.work_stop)
|
||||
).replace(tzinfo=self.zoneinfo)
|
||||
|
||||
work_hours |= P.closed(work_start, work_end)
|
||||
current_date += timedelta(days=1)
|
||||
|
||||
# Intersection entre les gaps et les heures de travail
|
||||
trimmed = gaps_union & work_hours
|
||||
|
||||
return list(trimmed) if not trimmed.empty else []
|
||||
|
||||
# def schedule(self, t1: datetime, t2: datetime) -> List[SmartEvent]:
|
||||
# self.clean_deck_events(t1, t2)
|
||||
# gaps = self.get_gaps(t1, t2)
|
||||
# gaps = self.trim_timegaps(gaps)
|
||||
|
||||
# if not gaps or not self.cards:
|
||||
# return []
|
||||
|
||||
# # Aplatir les intervalles composés
|
||||
# atomic_gaps = []
|
||||
# for gap in gaps:
|
||||
# atomic_gaps.extend(gap)
|
||||
|
||||
# events = []
|
||||
# card_iter = iter(self.cards)
|
||||
|
||||
# try:
|
||||
# current_card = next(card_iter)
|
||||
# current_duration = self.estimate_duration(current_card)
|
||||
|
||||
# for gap in atomic_gaps:
|
||||
# remaining_gap = gap.upper - gap.lower
|
||||
# while remaining_gap > timedelta(0):
|
||||
# if current_duration <= remaining_gap:
|
||||
# # Créer l'événement complet
|
||||
# events.append(
|
||||
# self.create_event(
|
||||
# current_card, gap.lower, gap.lower + current_duration
|
||||
# )
|
||||
# )
|
||||
# gap = P.closed(gap.lower + current_duration, gap.upper)
|
||||
# current_card = next(card_iter)
|
||||
# current_duration = self.estimate_duration(current_card)
|
||||
# remaining_gap = gap.upper - gap.lower
|
||||
# else:
|
||||
# # Utiliser une partie du gap
|
||||
# events.append(
|
||||
# self.create_event(
|
||||
# current_card, gap.lower, gap.lower + remaining_gap
|
||||
# )
|
||||
# )
|
||||
# current_duration -= remaining_gap
|
||||
# break
|
||||
|
||||
# except StopIteration:
|
||||
# pass
|
||||
|
||||
# for event in events:
|
||||
# event.save_to_calendar(self.work_calendar)
|
||||
|
||||
# return events
|
||||
# for dav_event in self.events:
|
||||
# vevent = dav_event.vobject_instance.vevent
|
||||
# if hasattr(vevent, "categories"):
|
||||
# categories = vevent.categories.value
|
||||
# # Vérifier les catégories commençant par 'Deck'
|
||||
# if any(cat.strip().startswith(self.category) for cat in categories):
|
||||
# removed_events += 1
|
||||
# dav_event.delete()
|
||||
# return removed_events
|
||||
|
||||
def add_work_breaks(
|
||||
self,
|
||||
gaps: List[P.Interval],
|
||||
gaps: List[Interval],
|
||||
work_duration: timedelta = DEFAULT_TASK_DURATION,
|
||||
break_duration: timedelta = timedelta(minutes=15),
|
||||
) -> List[P.Interval]:
|
||||
) -> List[Interval]:
|
||||
"""Découpe les plages horaires en ajoutant des pauses régulières."""
|
||||
new_gaps = []
|
||||
|
||||
@@ -379,12 +228,35 @@ class Scheduler:
|
||||
|
||||
return new_gaps
|
||||
|
||||
def schedule(self, t1: datetime, t2: datetime) -> List[SmartEvent]:
|
||||
self.clean_deck_events(t1, t2)
|
||||
gaps = self.get_gaps(t1, t2)
|
||||
gaps = self.trim_timegaps(gaps)
|
||||
gaps = self.add_work_breaks(gaps)
|
||||
def get_availability(self, t1: datetime, t2: datetime) -> Interval:
|
||||
"""Consider all parameters to get my availibility interval."""
|
||||
availability = self.get_gaps(t1, t2)
|
||||
|
||||
# eat intervals
|
||||
lunch_interval = self.create_daily_events(
|
||||
t1, t2, settings.lunch_start, settings.lunch_end
|
||||
)
|
||||
dinner_interval = self.create_daily_events(
|
||||
t1, t2, settings.dinner_start, settings.dinner_end
|
||||
)
|
||||
eat_interval = lunch_interval | dinner_interval
|
||||
availability -= eat_interval
|
||||
|
||||
# sleep
|
||||
sleep_interval = self.create_sleep_events(t1, t2)
|
||||
availability -= sleep_interval
|
||||
|
||||
# gaps |= self.add_work_breaks(gaps)
|
||||
return Interval(availability)
|
||||
|
||||
def schedule(
|
||||
self,
|
||||
t1: datetime,
|
||||
t2: datetime,
|
||||
) -> List[SmartEvent]:
|
||||
"""Schedule my calendar between t1 and t2."""
|
||||
self.clean_deck_events(t1, t2)
|
||||
gaps = self.get_availability(t1, t2)
|
||||
if not gaps or not self.cards:
|
||||
return []
|
||||
|
||||
@@ -431,5 +303,63 @@ class Scheduler:
|
||||
|
||||
for event in events:
|
||||
event.save_to_calendar(self.work_calendar)
|
||||
|
||||
return events
|
||||
|
||||
def create_daily_events(
|
||||
self,
|
||||
t1: datetime,
|
||||
t2: datetime,
|
||||
g1: int,
|
||||
g2: int,
|
||||
) -> Interval:
|
||||
"""Create daily events in the schedule."""
|
||||
daily_events = P.empty()
|
||||
current_date = t1.date()
|
||||
end_date = t2.date()
|
||||
|
||||
while current_date <= end_date:
|
||||
# Create daily start and end times for the current date
|
||||
daily_start_time = time(hour=g1)
|
||||
daily_end_time = time(hour=g2)
|
||||
daily_start_dt = datetime.combine(current_date, daily_start_time).replace(
|
||||
tzinfo=self.zoneinfo
|
||||
)
|
||||
daily_end_dt = datetime.combine(current_date, daily_end_time).replace(
|
||||
tzinfo=self.zoneinfo
|
||||
)
|
||||
|
||||
# Adjust daily_end_dt if it's on the next day
|
||||
if daily_end_dt <= daily_start_dt:
|
||||
daily_end_dt += timedelta(days=1)
|
||||
|
||||
# Create the interval for the current day's daily
|
||||
day_daily_interval = P.closed(daily_start_dt, daily_end_dt)
|
||||
|
||||
# Intersect with the overall time range [t1, t2]
|
||||
valid_interval = day_daily_interval & P.closed(t1, t2)
|
||||
|
||||
if not valid_interval.empty:
|
||||
daily_events |= valid_interval
|
||||
|
||||
current_date += timedelta(days=1)
|
||||
|
||||
return daily_events
|
||||
|
||||
def create_sleep_events(self, t1: datetime, t2: datetime) -> Interval:
|
||||
"""Create sleep events"""
|
||||
sleep_events = self.create_daily_events(t1, t2, 0, settings.work_start)
|
||||
# events = self.create_daily_events(t1, t2, settings.work_end, 24)
|
||||
return sleep_events
|
||||
|
||||
def get_today_events(self) -> List[SmartEvent]:
|
||||
"""Renvoie tous les événements prévus aujourd'hui dans le calendrier."""
|
||||
now = datetime.now(self.zoneinfo)
|
||||
start_of_day = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = start_of_day + timedelta(days=1)
|
||||
|
||||
return [
|
||||
event
|
||||
for event in self.events
|
||||
if event.begin.astimezone(self.zoneinfo) < end_of_day
|
||||
and event.end.astimezone(self.zoneinfo) > start_of_day
|
||||
]
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from typing import List
|
||||
from dotenv import load_dotenv
|
||||
from zoneinfo import ZoneInfo
|
||||
import caldav
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="TIMEASSISTANT_")
|
||||
|
||||
# Nextcloud parameters are mandatory
|
||||
nextcloud_user: str
|
||||
nextcloud_password: str
|
||||
nextcloud_board: str
|
||||
nextcloud_url: str
|
||||
|
||||
app_name: str = "Time Assistant"
|
||||
debug: bool = False
|
||||
log_level: str = "Info"
|
||||
timezone: str = "Europe/Paris"
|
||||
board_name: str = "-Todolist"
|
||||
deck_category: str = "Deck"
|
||||
|
||||
personal_calendar: str = "Personal"
|
||||
work_calendar: str = "Taff"
|
||||
fitness_calendar: str = "Fitness"
|
||||
|
||||
plan_day_limit: int = 1
|
||||
task_duration_hours: int = 3
|
||||
task_duration_minutes: int = 0
|
||||
|
||||
work_start: int = 9
|
||||
work_end: int = 20
|
||||
lunch_start: int = 12
|
||||
lunch_end: int = 13
|
||||
dinner_start: int = 20
|
||||
dinner_end: int = 21
|
||||
|
||||
api_key: str = "osef"
|
||||
|
||||
priorities: List[str] = [
|
||||
"Top priority",
|
||||
"High priority",
|
||||
"Action needed",
|
||||
"Low priority",
|
||||
]
|
||||
|
||||
@property
|
||||
def zoneinfo(self) -> ZoneInfo:
|
||||
return ZoneInfo(self.timezone)
|
||||
|
||||
@property
|
||||
def calendar_url(self):
|
||||
return f"{self.nextcloud_url}/remote.php/dav/calendars/{self.nextcloud_user}"
|
||||
|
||||
@property
|
||||
def caldav_client(self) -> caldav.DAVClient:
|
||||
"""Return caldav client"""
|
||||
return caldav.DAVClient(
|
||||
self.calendar_url,
|
||||
username=self.nextcloud_user,
|
||||
password=self.nextcloud_password,
|
||||
)
|
||||
|
||||
@property
|
||||
def principal(self) -> caldav.Principal:
|
||||
"""Return principal"""
|
||||
return self.caldav_client.principal()
|
||||
|
||||
@property
|
||||
def calendars(self) -> List[caldav.Calendar]:
|
||||
"""Return calendars"""
|
||||
return self.principal.calendars()
|
||||
|
||||
|
||||
settings = Settings()
|
||||
|
||||
from pprint import pp
|
||||
|
||||
pp(settings)
|
||||
@@ -3,3 +3,5 @@ services:
|
||||
image: marcpartensky/time-assistant
|
||||
build: .
|
||||
env_file: .env
|
||||
ports:
|
||||
- "8000:8000"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
# hypercorn.toml
|
||||
bind = "0.0.0.0:8000"
|
||||
workers = 1
|
||||
http2 = true
|
||||
accesslog = "-"
|
||||
# reload = true
|
||||
|
||||
[logging]
|
||||
log_level = "debug"
|
||||
access_log_format = "%(h)s %(r)s %(s)s %(b)s %(D)s"
|
||||
Generated
-1213
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -7,7 +7,6 @@ requires-python = ">=3.12,<4"
|
||||
dependencies = [
|
||||
"caldav>=1.4.0",
|
||||
"fastapi>=0.115.6",
|
||||
"icalendar>=6.1.0",
|
||||
"ics>=0.7.2",
|
||||
"nextcloud-api-wrapper>=0.2.3",
|
||||
"nextcloud-deck>=0.2.1",
|
||||
@@ -15,17 +14,19 @@ dependencies = [
|
||||
"termcolor>=2.5.0",
|
||||
"websockets>=14.1",
|
||||
"portion (>=2.6.0,<3.0.0)",
|
||||
"hypercorn (>=0.17.3,<0.18.0)",
|
||||
"granian>=2.0.0",
|
||||
"pydantic-settings>=2.8.1",
|
||||
]
|
||||
|
||||
[tool.uv]
|
||||
dev-dependencies = [
|
||||
"black>=24.10.0",
|
||||
"granian[reload]>=2.0.0",
|
||||
"httpx>=0.28.1",
|
||||
"pyright>=1.1.390",
|
||||
"pytest>=8.3.4",
|
||||
]
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
granian = "^2.0.0"
|
||||
granian = {extras = ["reload"], version = "^2.0.0"}
|
||||
|
||||
|
||||
+306
-490
@@ -1,515 +1,331 @@
|
||||
annotated-types==0.7.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \
|
||||
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89
|
||||
anyio==4.8.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a \
|
||||
--hash=sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a
|
||||
arrow==1.3.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80 \
|
||||
--hash=sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85
|
||||
attrs==21.4.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4 \
|
||||
--hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd
|
||||
caldav==1.4.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
# This file was autogenerated by uv via the following command:
|
||||
# uv export -o requirements.txt
|
||||
annotated-types==0.7.0 \
|
||||
--hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 \
|
||||
--hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53
|
||||
anyio==4.7.0 \
|
||||
--hash=sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48 \
|
||||
--hash=sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352
|
||||
arrow==1.3.0 \
|
||||
--hash=sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85 \
|
||||
--hash=sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80
|
||||
attrs==21.4.0 \
|
||||
--hash=sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd \
|
||||
--hash=sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4
|
||||
black==24.10.0 \
|
||||
--hash=sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875 \
|
||||
--hash=sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3 \
|
||||
--hash=sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65 \
|
||||
--hash=sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f \
|
||||
--hash=sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8 \
|
||||
--hash=sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981 \
|
||||
--hash=sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b \
|
||||
--hash=sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2 \
|
||||
--hash=sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b \
|
||||
--hash=sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d
|
||||
caldav==1.4.0 \
|
||||
--hash=sha256:4317131127d8793f740cff2fd256f369321fa49ad750f83d6f31780f7c16c67b \
|
||||
--hash=sha256:e75e84824092e33a9e03ac693de3d01133a3e044fd50a1c542c7f78d1aff0cb2
|
||||
cattrs==1.10.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
cattrs==1.10.0 \
|
||||
--hash=sha256:211800f725cdecedcbcf4c753bbd22d248312b37d130f06045434acb7d9b34e1 \
|
||||
--hash=sha256:35dd9063244263e63bd0bd24ea61e3015b00272cead084b2c40d788b0f857c46
|
||||
certifi==2025.1.31 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651 \
|
||||
--hash=sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe
|
||||
charset-normalizer==3.4.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537 \
|
||||
--hash=sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa \
|
||||
--hash=sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a \
|
||||
--hash=sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294 \
|
||||
--hash=sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b \
|
||||
--hash=sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd \
|
||||
--hash=sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601 \
|
||||
--hash=sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd \
|
||||
--hash=sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4 \
|
||||
--hash=sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d \
|
||||
--hash=sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2 \
|
||||
--hash=sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313 \
|
||||
--hash=sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd \
|
||||
--hash=sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa \
|
||||
--hash=sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8 \
|
||||
--hash=sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1 \
|
||||
--hash=sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2 \
|
||||
--hash=sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496 \
|
||||
--hash=sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d \
|
||||
--hash=sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b \
|
||||
--hash=sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e \
|
||||
--hash=sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a \
|
||||
--hash=sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4 \
|
||||
--hash=sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca \
|
||||
--hash=sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78 \
|
||||
--hash=sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408 \
|
||||
--hash=sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5 \
|
||||
--hash=sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3 \
|
||||
--hash=sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f \
|
||||
--hash=sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a \
|
||||
--hash=sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765 \
|
||||
--hash=sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6 \
|
||||
--hash=sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146 \
|
||||
--hash=sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6 \
|
||||
--hash=sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9 \
|
||||
--hash=sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd \
|
||||
--hash=sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c \
|
||||
--hash=sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f \
|
||||
--hash=sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545 \
|
||||
--hash=sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176 \
|
||||
--hash=sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770 \
|
||||
--hash=sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824 \
|
||||
--hash=sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f \
|
||||
--hash=sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf \
|
||||
--hash=sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487 \
|
||||
--hash=sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d \
|
||||
--hash=sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd \
|
||||
--hash=sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b \
|
||||
--hash=sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534 \
|
||||
--hash=sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f \
|
||||
--hash=sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b \
|
||||
--hash=sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9 \
|
||||
--hash=sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd \
|
||||
--hash=sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125 \
|
||||
--hash=sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9 \
|
||||
--hash=sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de \
|
||||
--hash=sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11 \
|
||||
--hash=sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d \
|
||||
--hash=sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35 \
|
||||
--hash=sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f \
|
||||
--hash=sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda \
|
||||
--hash=sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7 \
|
||||
--hash=sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a \
|
||||
--hash=sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971 \
|
||||
--hash=sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8 \
|
||||
--hash=sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41 \
|
||||
--hash=sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d \
|
||||
--hash=sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f \
|
||||
--hash=sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757 \
|
||||
--hash=sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a \
|
||||
--hash=sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886 \
|
||||
--hash=sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77 \
|
||||
--hash=sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76 \
|
||||
--hash=sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247 \
|
||||
--hash=sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85 \
|
||||
--hash=sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb \
|
||||
--hash=sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7 \
|
||||
--hash=sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e \
|
||||
--hash=sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6 \
|
||||
--hash=sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037 \
|
||||
--hash=sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1 \
|
||||
--hash=sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e \
|
||||
--hash=sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807 \
|
||||
--hash=sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407 \
|
||||
--hash=sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c \
|
||||
--hash=sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12 \
|
||||
--hash=sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3 \
|
||||
--hash=sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089 \
|
||||
--hash=sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd \
|
||||
--hash=sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e \
|
||||
--hash=sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00 \
|
||||
--hash=sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616
|
||||
click==8.1.8 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2 \
|
||||
--hash=sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a
|
||||
colorama==0.4.6 ; python_version >= "3.12" and python_version < "4" and platform_system == "Windows" \
|
||||
certifi==2024.12.14 \
|
||||
--hash=sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db \
|
||||
--hash=sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56
|
||||
charset-normalizer==3.4.0 \
|
||||
--hash=sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e \
|
||||
--hash=sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6 \
|
||||
--hash=sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf \
|
||||
--hash=sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db \
|
||||
--hash=sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1 \
|
||||
--hash=sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03 \
|
||||
--hash=sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284 \
|
||||
--hash=sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15 \
|
||||
--hash=sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8 \
|
||||
--hash=sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2 \
|
||||
--hash=sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719 \
|
||||
--hash=sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631 \
|
||||
--hash=sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b \
|
||||
--hash=sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565 \
|
||||
--hash=sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7 \
|
||||
--hash=sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9 \
|
||||
--hash=sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114 \
|
||||
--hash=sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed \
|
||||
--hash=sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250 \
|
||||
--hash=sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920 \
|
||||
--hash=sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64 \
|
||||
--hash=sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23 \
|
||||
--hash=sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc \
|
||||
--hash=sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d \
|
||||
--hash=sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88 \
|
||||
--hash=sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90 \
|
||||
--hash=sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b \
|
||||
--hash=sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d \
|
||||
--hash=sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482 \
|
||||
--hash=sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67 \
|
||||
--hash=sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b \
|
||||
--hash=sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079
|
||||
click==8.1.7 \
|
||||
--hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de \
|
||||
--hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28
|
||||
colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows' \
|
||||
--hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
|
||||
--hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
|
||||
dateutils==0.6.12 ; python_version >= "3.12" and python_version < "4" \
|
||||
dateutils==0.6.12 \
|
||||
--hash=sha256:03dd90bcb21541bd4eb4b013637e4f1b5f944881c46cc6e4b67a6059e370e3f1 \
|
||||
--hash=sha256:f33b6ab430fa4166e7e9cb8b21ee9f6c9843c48df1a964466f52c79b2a8d53b3
|
||||
fastapi==0.115.11 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:32e1541b7b74602e4ef4a0260ecaf3aadf9d4f19590bba3e1bf2ac4666aa2c64 \
|
||||
--hash=sha256:cc81f03f688678b92600a65a5e618b93592c65005db37157147204d8924bf94f
|
||||
h11==0.14.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
fastapi==0.115.6 \
|
||||
--hash=sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654 \
|
||||
--hash=sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305
|
||||
granian==2.0.0 \
|
||||
--hash=sha256:d040c63e069ce195607530afa53fc1fd50b8e99284184f051e5bd6b4c3742081 \
|
||||
--hash=sha256:16b71f2e5d0c7e2059cc5902dc61908c4dbb54088c09acc14457c89f1778936d \
|
||||
--hash=sha256:4c58be99fb385ee219111cdaf6f5b069c668a3345551b4209073a1db83125b94 \
|
||||
--hash=sha256:6f4695bcf6e2c3842e212dc42253be0400ee68e7b2245555d9d04ffb44e85919 \
|
||||
--hash=sha256:a7122062532355af2c87048f859f6006a452073af5e88dd4b0b8f8eafb4107b5 \
|
||||
--hash=sha256:0618855fcf40a0c768839031f5c55cf0debe0b4fc123c129e5a6f0166e341fc5 \
|
||||
--hash=sha256:6a2d7c51d20cb0fe831d481c980f4ff2c400a4c7f84abad66d8c1ac94f468921 \
|
||||
--hash=sha256:54bf94c4bbfb2630b33b1a075b4571fa2b1e870cbf06f4145c29e13122aec8ff \
|
||||
--hash=sha256:f98a6d604b0dbdd226b0a0c823242706bd99070abd6977d4903c4249d231aab4 \
|
||||
--hash=sha256:3f83644dace8acb07a4bd36c5fdf6731eea785ef243a8c5ee77361eec09c2c35 \
|
||||
--hash=sha256:f65c7d4b6d5280a622a3e0ed899654221d748cf7bde801d701f2d7266ec0cb8e \
|
||||
--hash=sha256:b87b851f38e1cf3d48bb49069efb7e4b18db46c664512e3dd6d0fea5ce6806e1 \
|
||||
--hash=sha256:a71872e7f619f7b8e543fdfcdb0a5025395cd36326f53558474f4c9c243219de \
|
||||
--hash=sha256:70cdb93c63711f3da3e4b3a465affb514cb933635fdbdbfd0349c4f2920069ab \
|
||||
--hash=sha256:4d3fc2895de84a15ded59ecc69a100ff5e53cfb6ac9eb96f6db00d571b604b1c \
|
||||
--hash=sha256:565c90932189b759534096d260837a6af3dac492490fa4858339f5d251181e94 \
|
||||
--hash=sha256:5769cf26bd27bdfce2932570ec3cf1e6703b493ef9d64acbba7740800299982a \
|
||||
--hash=sha256:88be218f5daad58b2984aeec27720b35082d47bbb1cd1b6f910168431a94c181 \
|
||||
--hash=sha256:de8ce3356f8016aababf946caec88d3423102d428e6c988538de83e1345d419a \
|
||||
--hash=sha256:1598cc112b15b78915a3231d916c1db14c6d00e1b3802118a9610ac4e171dea1 \
|
||||
--hash=sha256:035f765b7a918c8b197917b3451dd4653db3478b9c8221c2a7f0532472a203a9 \
|
||||
--hash=sha256:b80e1acdd3ec632abec6ddce9c549351ecf0bd7a24914f338920375a28e35f8b
|
||||
h11==0.14.0 \
|
||||
--hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \
|
||||
--hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761
|
||||
icalendar==6.1.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:2c44355a8f006de5ae73fa3f022a1cbe2a0de6b1607ce8879739eb887c4f3471 \
|
||||
--hash=sha256:accf3a4be9a1f89bad00e0bf14103b02cd9b02dcb9b4258eb717f39d24cf58e9
|
||||
ics==0.7.2 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:5fcf4d29ec6e7dfcb84120abd617bbba632eb77b097722b7df70e48dbcf26103 \
|
||||
--hash=sha256:6743539bca10391635249b87d74fcd1094af20b82098bebf7c7521df91209f05
|
||||
idna==3.10 ; python_version >= "3.12" and python_version < "4" \
|
||||
httpcore==1.0.7 \
|
||||
--hash=sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c \
|
||||
--hash=sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd
|
||||
httpx==0.28.1 \
|
||||
--hash=sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc \
|
||||
--hash=sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad
|
||||
icalendar==6.1.0 \
|
||||
--hash=sha256:43c2db8632959d634f4e48f6e6131e706bf2cdddad488cf0b72fda079b796bad \
|
||||
--hash=sha256:46c09b774a6e6948495dafcb166dc15135c8259d0ae25491f154cbc822714b69
|
||||
ics==0.7.2 \
|
||||
--hash=sha256:6743539bca10391635249b87d74fcd1094af20b82098bebf7c7521df91209f05 \
|
||||
--hash=sha256:5fcf4d29ec6e7dfcb84120abd617bbba632eb77b097722b7df70e48dbcf26103
|
||||
idna==3.10 \
|
||||
--hash=sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9 \
|
||||
--hash=sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3
|
||||
lxml==5.3.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:016b96c58e9a4528219bb563acf1aaaa8bc5452e7651004894a973f03b84ba81 \
|
||||
--hash=sha256:05123fad495a429f123307ac6d8fd6f977b71e9a0b6d9aeeb8f80c017cb17131 \
|
||||
--hash=sha256:057e30d0012439bc54ca427a83d458752ccda725c1c161cc283db07bcad43cf9 \
|
||||
--hash=sha256:06a20d607a86fccab2fc15a77aa445f2bdef7b49ec0520a842c5c5afd8381576 \
|
||||
--hash=sha256:094b28ed8a8a072b9e9e2113a81fda668d2053f2ca9f2d202c2c8c7c2d6516b1 \
|
||||
--hash=sha256:0bcfadea3cdc68e678d2b20cb16a16716887dd00a881e16f7d806c2138b8ff0c \
|
||||
--hash=sha256:0d6b2fa86becfa81f0a0271ccb9eb127ad45fb597733a77b92e8a35e53414914 \
|
||||
--hash=sha256:0f2cfae0688fd01f7056a17367e3b84f37c545fb447d7282cf2c242b16262607 \
|
||||
--hash=sha256:106b7b5d2977b339f1e97efe2778e2ab20e99994cbb0ec5e55771ed0795920c8 \
|
||||
--hash=sha256:133f3493253a00db2c870d3740bc458ebb7d937bd0a6a4f9328373e0db305709 \
|
||||
--hash=sha256:136bf638d92848a939fd8f0e06fcf92d9f2e4b57969d94faae27c55f3d85c05b \
|
||||
--hash=sha256:155e1a5693cf4b55af652f5c0f78ef36596c7f680ff3ec6eb4d7d85367259b2c \
|
||||
--hash=sha256:1637fa31ec682cd5760092adfabe86d9b718a75d43e65e211d5931809bc111e7 \
|
||||
--hash=sha256:172d65f7c72a35a6879217bcdb4bb11bc88d55fb4879e7569f55616062d387c2 \
|
||||
--hash=sha256:17b5d7f8acf809465086d498d62a981fa6a56d2718135bb0e4aa48c502055f5c \
|
||||
--hash=sha256:198bb4b4dd888e8390afa4f170d4fa28467a7eaf857f1952589f16cfbb67af27 \
|
||||
--hash=sha256:1b6f92e35e2658a5ed51c6634ceb5ddae32053182851d8cad2a5bc102a359b33 \
|
||||
--hash=sha256:1b92fe86e04f680b848fff594a908edfa72b31bfc3499ef7433790c11d4c8cd8 \
|
||||
--hash=sha256:1bcc211542f7af6f2dfb705f5f8b74e865592778e6cafdfd19c792c244ccce19 \
|
||||
--hash=sha256:1c93ed3c998ea8472be98fb55aed65b5198740bfceaec07b2eba551e55b7b9ae \
|
||||
--hash=sha256:203b1d3eaebd34277be06a3eb880050f18a4e4d60861efba4fb946e31071a295 \
|
||||
--hash=sha256:22ec2b3c191f43ed21f9545e9df94c37c6b49a5af0a874008ddc9132d49a2d9c \
|
||||
--hash=sha256:231cf4d140b22a923b1d0a0a4e0b4f972e5893efcdec188934cc65888fd0227b \
|
||||
--hash=sha256:236610b77589faf462337b3305a1be91756c8abc5a45ff7ca8f245a71c5dab70 \
|
||||
--hash=sha256:29bfc8d3d88e56ea0a27e7c4897b642706840247f59f4377d81be8f32aa0cfbf \
|
||||
--hash=sha256:2b8969dbc8d09d9cd2ae06362c3bad27d03f433252601ef658a49bd9f2b22d79 \
|
||||
--hash=sha256:2dd0b80ac2d8f13ffc906123a6f20b459cb50a99222d0da492360512f3e50f84 \
|
||||
--hash=sha256:2df7ed5edeb6bd5590914cd61df76eb6cce9d590ed04ec7c183cf5509f73530d \
|
||||
--hash=sha256:2e4a570f6a99e96c457f7bec5ad459c9c420ee80b99eb04cbfcfe3fc18ec6423 \
|
||||
--hash=sha256:2f1be45d4c15f237209bbf123a0e05b5d630c8717c42f59f31ea9eae2ad89394 \
|
||||
--hash=sha256:2f23cf50eccb3255b6e913188291af0150d89dab44137a69e14e4dcb7be981f1 \
|
||||
--hash=sha256:3031e4c16b59424e8d78522c69b062d301d951dc55ad8685736c3335a97fc270 \
|
||||
--hash=sha256:33e06717c00c788ab4e79bc4726ecc50c54b9bfb55355eae21473c145d83c2d2 \
|
||||
--hash=sha256:364de8f57d6eda0c16dcfb999af902da31396949efa0e583e12675d09709881b \
|
||||
--hash=sha256:3715cdf0dd31b836433af9ee9197af10e3df41d273c19bb249230043667a5dfd \
|
||||
--hash=sha256:3bb8149840daf2c3f97cebf00e4ed4a65a0baff888bf2605a8d0135ff5cf764e \
|
||||
--hash=sha256:3c3c8b55c7fc7b7e8877b9366568cc73d68b82da7fe33d8b98527b73857a225f \
|
||||
--hash=sha256:3d68eeef7b4d08a25e51897dac29bcb62aba830e9ac6c4e3297ee7c6a0cf6439 \
|
||||
--hash=sha256:3dddf0fb832486cc1ea71d189cb92eb887826e8deebe128884e15020bb6e3f61 \
|
||||
--hash=sha256:3edbb9c9130bac05d8c3fe150c51c337a471cc7fdb6d2a0a7d3a88e88a829314 \
|
||||
--hash=sha256:3effe081b3135237da6e4c4530ff2a868d3f80be0bda027e118a5971285d42d0 \
|
||||
--hash=sha256:422c179022ecdedbe58b0e242607198580804253da220e9454ffe848daa1cfd2 \
|
||||
--hash=sha256:42978a68d3825eaac55399eb37a4d52012a205c0c6262199b8b44fcc6fd686e8 \
|
||||
--hash=sha256:4399b4226c4785575fb20998dc571bc48125dc92c367ce2602d0d70e0c455eb0 \
|
||||
--hash=sha256:45fbb70ccbc8683f2fb58bea89498a7274af1d9ec7995e9f4af5604e028233fc \
|
||||
--hash=sha256:4867361c049761a56bd21de507cab2c2a608c55102311d142ade7dab67b34f32 \
|
||||
--hash=sha256:48fd46bf7155def2e15287c6f2b133a2f78e2d22cdf55647269977b873c65499 \
|
||||
--hash=sha256:4b0d5cdba1b655d5b18042ac9c9ff50bda33568eb80feaaca4fc237b9c4fbfde \
|
||||
--hash=sha256:4df0ec814b50275ad6a99bc82a38b59f90e10e47714ac9871e1b223895825468 \
|
||||
--hash=sha256:4e52e1b148867b01c05e21837586ee307a01e793b94072d7c7b91d2c2da02ffe \
|
||||
--hash=sha256:514fe78fc4b87e7a7601c92492210b20a1b0c6ab20e71e81307d9c2e377c64de \
|
||||
--hash=sha256:524ccfded8989a6595dbdda80d779fb977dbc9a7bc458864fc9a0c2fc15dc877 \
|
||||
--hash=sha256:528f3a0498a8edc69af0559bdcf8a9f5a8bf7c00051a6ef3141fdcf27017bbf5 \
|
||||
--hash=sha256:52d82b0d436edd6a1d22d94a344b9a58abd6c68c357ed44f22d4ba8179b37629 \
|
||||
--hash=sha256:5412500e0dc5481b1ee9cf6b38bb3b473f6e411eb62b83dc9b62699c3b7b79f7 \
|
||||
--hash=sha256:585c4dc429deebc4307187d2b71ebe914843185ae16a4d582ee030e6cfbb4d8a \
|
||||
--hash=sha256:5865b270b420eda7b68928d70bb517ccbe045e53b1a428129bb44372bf3d7dd5 \
|
||||
--hash=sha256:5881aaa4bf3a2d086c5f20371d3a5856199a0d8ac72dd8d0dbd7a2ecfc26ab73 \
|
||||
--hash=sha256:5885bc586f1edb48e5d68e7a4b4757b5feb2a496b64f462b4d65950f5af3364f \
|
||||
--hash=sha256:5a11b16a33656ffc43c92a5343a28dc71eefe460bcc2a4923a96f292692709f6 \
|
||||
--hash=sha256:5a997b784a639e05b9d4053ef3b20c7e447ea80814a762f25b8ed5a89d261eac \
|
||||
--hash=sha256:5be8f5e4044146a69c96077c7e08f0709c13a314aa5315981185c1f00235fe65 \
|
||||
--hash=sha256:63d57fc94eb0bbb4735e45517afc21ef262991d8758a8f2f05dd6e4174944519 \
|
||||
--hash=sha256:673b9d8e780f455091200bba8534d5f4f465944cbdd61f31dc832d70e29064a5 \
|
||||
--hash=sha256:67d2f8ad9dcc3a9e826bdc7802ed541a44e124c29b7d95a679eeb58c1c14ade8 \
|
||||
--hash=sha256:67f5e80adf0aafc7b5454f2c1cb0cde920c9b1f2cbd0485f07cc1d0497c35c5d \
|
||||
--hash=sha256:68018c4c67d7e89951a91fbd371e2e34cd8cfc71f0bb43b5332db38497025d51 \
|
||||
--hash=sha256:6c4dd3bfd0c82400060896717dd261137398edb7e524527438c54a8c34f736bf \
|
||||
--hash=sha256:71f31eda4e370f46af42fc9f264fafa1b09f46ba07bdbee98f25689a04b81c20 \
|
||||
--hash=sha256:7512b4d0fc5339d5abbb14d1843f70499cab90d0b864f790e73f780f041615d7 \
|
||||
--hash=sha256:75fa3d6946d317ffc7016a6fcc44f42db6d514b7fdb8b4b28cbe058303cb6e53 \
|
||||
--hash=sha256:779e851fd0e19795ccc8a9bb4d705d6baa0ef475329fe44a13cf1e962f18ff1e \
|
||||
--hash=sha256:796520afa499732191e39fc95b56a3b07f95256f2d22b1c26e217fb69a9db5b5 \
|
||||
--hash=sha256:7aae7a3d63b935babfdc6864b31196afd5145878ddd22f5200729006366bc4d5 \
|
||||
--hash=sha256:7b82e67c5feb682dbb559c3e6b78355f234943053af61606af126df2183b9ef9 \
|
||||
--hash=sha256:7c0536bd9178f754b277a3e53f90f9c9454a3bd108b1531ffff720e082d824f2 \
|
||||
--hash=sha256:7eda194dd46e40ec745bf76795a7cccb02a6a41f445ad49d3cf66518b0bd9cff \
|
||||
--hash=sha256:82a4bb10b0beef1434fb23a09f001ab5ca87895596b4581fd53f1e5145a8934a \
|
||||
--hash=sha256:85c4f11be9cf08917ac2a5a8b6e1ef63b2f8e3799cec194417e76826e5f1de9c \
|
||||
--hash=sha256:88b72eb7222d918c967202024812c2bfb4048deeb69ca328363fb8e15254c549 \
|
||||
--hash=sha256:89934f9f791566e54c1d92cdc8f8fd0009447a5ecdb1ec6b810d5f8c4955f6be \
|
||||
--hash=sha256:8b1942b3e4ed9ed551ed3083a2e6e0772de1e5e3aca872d955e2e86385fb7ff9 \
|
||||
--hash=sha256:8ffb141361108e864ab5f1813f66e4e1164181227f9b1f105b042729b6c15125 \
|
||||
--hash=sha256:8fffc08de02071c37865a155e5ea5fce0282e1546fd5bde7f6149fcaa32558ac \
|
||||
--hash=sha256:91fb6a43d72b4f8863d21f347a9163eecbf36e76e2f51068d59cd004c506f332 \
|
||||
--hash=sha256:928e75a7200a4c09e6efc7482a1337919cc61fe1ba289f297827a5b76d8969c2 \
|
||||
--hash=sha256:96eef5b9f336f623ffc555ab47a775495e7e8846dde88de5f941e2906453a1ce \
|
||||
--hash=sha256:a0611da6b07dd3720f492db1b463a4d1175b096b49438761cc9f35f0d9eaaef5 \
|
||||
--hash=sha256:a091026c3bf7519ab1e64655a3f52a59ad4a4e019a6f830c24d6430695b1cf6a \
|
||||
--hash=sha256:a22f66270bd6d0804b02cd49dae2b33d4341015545d17f8426f2c4e22f557a23 \
|
||||
--hash=sha256:a243132767150a44e6a93cd1dde41010036e1cbc63cc3e9fe1712b277d926ce3 \
|
||||
--hash=sha256:a31fa7536ec1fb7155a0cd3a4e3d956c835ad0a43e3610ca32384d01f079ea1c \
|
||||
--hash=sha256:a364e8e944d92dcbf33b6b494d4e0fb3499dcc3bd9485beb701aa4b4201fa414 \
|
||||
--hash=sha256:a4058f16cee694577f7e4dd410263cd0ef75644b43802a689c2b3c2a7e69453b \
|
||||
--hash=sha256:a4b382e0e636ed54cd278791d93fe2c4f370772743f02bcbe431a160089025c9 \
|
||||
--hash=sha256:a83d3adea1e0ee36dac34627f78ddd7f093bb9cfc0a8e97f1572a949b695cb98 \
|
||||
--hash=sha256:a8ade0363f776f87f982572c2860cc43c65ace208db49c76df0a21dde4ddd16e \
|
||||
--hash=sha256:aa59974880ab5ad8ef3afaa26f9bda148c5f39e06b11a8ada4660ecc9fb2feb3 \
|
||||
--hash=sha256:aa826340a609d0c954ba52fd831f0fba2a4165659ab0ee1a15e4aac21f302406 \
|
||||
--hash=sha256:aaca5a812f050ab55426c32177091130b1e49329b3f002a32934cd0245571307 \
|
||||
--hash=sha256:ae82fce1d964f065c32c9517309f0c7be588772352d2f40b1574a214bd6e6098 \
|
||||
--hash=sha256:aed57b541b589fa05ac248f4cb1c46cbb432ab82cbd467d1c4f6a2bdc18aecf9 \
|
||||
--hash=sha256:afa578b6524ff85fb365f454cf61683771d0170470c48ad9d170c48075f86725 \
|
||||
--hash=sha256:b0884e3f22d87c30694e625b1e62e6f30d39782c806287450d9dc2fdf07692fd \
|
||||
--hash=sha256:b2aca14c235c7a08558fe0a4786a1a05873a01e86b474dfa8f6df49101853a4e \
|
||||
--hash=sha256:b450d7cabcd49aa7ab46a3c6aa3ac7e1593600a1a0605ba536ec0f1b99a04322 \
|
||||
--hash=sha256:b725e70d15906d24615201e650d5b0388b08a5187a55f119f25874d0103f90dd \
|
||||
--hash=sha256:bfbbab9316330cf81656fed435311386610f78b6c93cc5db4bebbce8dd146675 \
|
||||
--hash=sha256:c093c7088b40d8266f57ed71d93112bd64c6724d31f0794c1e52cc4857c28e0e \
|
||||
--hash=sha256:c2e49dc23a10a1296b04ca9db200c44d3eb32c8d8ec532e8c1fd24792276522a \
|
||||
--hash=sha256:c4393600915c308e546dc7003d74371744234e8444a28622d76fe19b98fa59d1 \
|
||||
--hash=sha256:c5ae125276f254b01daa73e2c103363d3e99e3e10505686ac7d9d2442dd4627a \
|
||||
--hash=sha256:c6aacf00d05b38a5069826e50ae72751cb5bc27bdc4d5746203988e429b385bb \
|
||||
--hash=sha256:c76722b5ed4a31ba103e0dc77ab869222ec36efe1a614e42e9bcea88a36186fe \
|
||||
--hash=sha256:c809eef167bf4a57af4b03007004896f5c60bd38dc3852fcd97a26eae3d4c9e6 \
|
||||
--hash=sha256:c92ea6d9dd84a750b2bae72ff5e8cf5fdd13e58dda79c33e057862c29a8d5b50 \
|
||||
--hash=sha256:cb659702a45136c743bc130760c6f137870d4df3a9e14386478b8a0511abcfca \
|
||||
--hash=sha256:ce0930a963ff593e8bb6fda49a503911accc67dee7e5445eec972668e672a0f0 \
|
||||
--hash=sha256:d0751528b97d2b19a388b302be2a0ee05817097bab46ff0ed76feeec24951f78 \
|
||||
--hash=sha256:d184f85ad2bb1f261eac55cddfcf62a70dee89982c978e92b9a74a1bfef2e367 \
|
||||
--hash=sha256:d2a3e412ce1849be34b45922bfef03df32d1410a06d1cdeb793a343c2f1fd666 \
|
||||
--hash=sha256:d61ec60945d694df806a9aec88e8f29a27293c6e424f8ff91c80416e3c617645 \
|
||||
--hash=sha256:db0c742aad702fd5d0c6611a73f9602f20aec2007c102630c06d7633d9c8f09a \
|
||||
--hash=sha256:db4743e30d6f5f92b6d2b7c86b3ad250e0bad8dee4b7ad8a0c44bfb276af89a3 \
|
||||
--hash=sha256:dbf7bebc2275016cddf3c997bf8a0f7044160714c64a9b83975670a04e6d2252 \
|
||||
--hash=sha256:de1fc314c3ad6bc2f6bd5b5a5b9357b8c6896333d27fdbb7049aea8bd5af2d79 \
|
||||
--hash=sha256:df7e5edac4778127f2bf452e0721a58a1cfa4d1d9eac63bdd650535eb8543615 \
|
||||
--hash=sha256:e220f7b3e8656ab063d2eb0cd536fafef396829cafe04cb314e734f87649058f \
|
||||
--hash=sha256:e3c623923967f3e5961d272718655946e5322b8d058e094764180cdee7bab1af \
|
||||
--hash=sha256:e69add9b6b7b08c60d7ff0152c7c9a6c45b4a71a919be5abde6f98f1ea16421c \
|
||||
--hash=sha256:e8e0d177b1fe251c3b1b914ab64135475c5273c8cfd2857964b2e3bb0fe196a7 \
|
||||
--hash=sha256:ef45f31aec9be01379fc6c10f1d9c677f032f2bac9383c827d44f620e8a88407 \
|
||||
--hash=sha256:f1208c1c67ec9e151d78aa3435aa9b08a488b53d9cfac9b699f15255a3461ef2 \
|
||||
--hash=sha256:f12582b8d3b4c6be1d298c49cb7ae64a3a73efaf4c2ab4e37db182e3545815ac \
|
||||
--hash=sha256:f1de541a9893cf8a1b1db9bf0bf670a2decab42e3e82233d36a74eda7822b4c9 \
|
||||
--hash=sha256:f4eac0584cdc3285ef2e74eee1513a6001681fd9753b259e8159421ed28a72e5 \
|
||||
--hash=sha256:f7b64fcd670bca8800bc10ced36620c6bbb321e7bc1214b9c0c0df269c1dddc2 \
|
||||
--hash=sha256:fb7c61d4be18e930f75948705e9718618862e6fc2ed0d7159b2262be73f167a2
|
||||
nextcloud-api-wrapper==0.2.3 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:1899650073f1428cb12837bd61b0accd95ca54612752c42f901d7bd49deeea75 \
|
||||
iniconfig==2.0.0 \
|
||||
--hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
|
||||
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
|
||||
lxml==5.3.0 \
|
||||
--hash=sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f \
|
||||
--hash=sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859 \
|
||||
--hash=sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e \
|
||||
--hash=sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f \
|
||||
--hash=sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e \
|
||||
--hash=sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179 \
|
||||
--hash=sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a \
|
||||
--hash=sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3 \
|
||||
--hash=sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1 \
|
||||
--hash=sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d \
|
||||
--hash=sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c \
|
||||
--hash=sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99 \
|
||||
--hash=sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff \
|
||||
--hash=sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a \
|
||||
--hash=sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8 \
|
||||
--hash=sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d \
|
||||
--hash=sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30 \
|
||||
--hash=sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f \
|
||||
--hash=sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a \
|
||||
--hash=sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd \
|
||||
--hash=sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51 \
|
||||
--hash=sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b \
|
||||
--hash=sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002 \
|
||||
--hash=sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4 \
|
||||
--hash=sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492 \
|
||||
--hash=sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3 \
|
||||
--hash=sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4 \
|
||||
--hash=sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367 \
|
||||
--hash=sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832 \
|
||||
--hash=sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff \
|
||||
--hash=sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd \
|
||||
--hash=sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb \
|
||||
--hash=sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b \
|
||||
--hash=sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957 \
|
||||
--hash=sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d
|
||||
mypy-extensions==1.0.0 \
|
||||
--hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782 \
|
||||
--hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d
|
||||
nextcloud-api-wrapper==0.2.3 \
|
||||
--hash=sha256:30ac51f86a7a00506a376034f804cc1fe70385496443fb75de6064d7c1f97a6e \
|
||||
--hash=sha256:a30e66e4fbe451692c050c15b2dc6130df5547bd53e29db548d8141303c03e48
|
||||
nextcloud-deck==0.2.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:39ec92c51e7b85081a88697f4e7d8167213b1c7782e1a60ca89169caca4c61ba \
|
||||
--hash=sha256:b1f49c8cb1833a612c2db176d1ed77ef27dde4894f61c0999cf05c9f513f8f46
|
||||
portion==2.6.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:1899650073f1428cb12837bd61b0accd95ca54612752c42f901d7bd49deeea75
|
||||
nextcloud-deck==0.2.1 \
|
||||
--hash=sha256:b1f49c8cb1833a612c2db176d1ed77ef27dde4894f61c0999cf05c9f513f8f46 \
|
||||
--hash=sha256:39ec92c51e7b85081a88697f4e7d8167213b1c7782e1a60ca89169caca4c61ba
|
||||
nodeenv==1.9.1 \
|
||||
--hash=sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f \
|
||||
--hash=sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9
|
||||
packaging==24.2 \
|
||||
--hash=sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f \
|
||||
--hash=sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759
|
||||
pathspec==0.12.1 \
|
||||
--hash=sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712 \
|
||||
--hash=sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08
|
||||
platformdirs==4.3.6 \
|
||||
--hash=sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907 \
|
||||
--hash=sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb
|
||||
pluggy==1.5.0 \
|
||||
--hash=sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1 \
|
||||
--hash=sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669
|
||||
portion==2.6.0 \
|
||||
--hash=sha256:6fb538b57a92058f0edd360667694448aa3fc028ab97e41e3091359d14ba4dd5 \
|
||||
--hash=sha256:9132580df989b1985dae1caa3070b768f2a3360b40c89c59daa4acc7e6674dc4
|
||||
pydantic-core==2.27.2 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278 \
|
||||
--hash=sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50 \
|
||||
--hash=sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9 \
|
||||
--hash=sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f \
|
||||
--hash=sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6 \
|
||||
--hash=sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc \
|
||||
--hash=sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54 \
|
||||
--hash=sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630 \
|
||||
--hash=sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9 \
|
||||
--hash=sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236 \
|
||||
--hash=sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7 \
|
||||
--hash=sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee \
|
||||
--hash=sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b \
|
||||
--hash=sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048 \
|
||||
--hash=sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc \
|
||||
--hash=sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130 \
|
||||
--hash=sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4 \
|
||||
--hash=sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd \
|
||||
--hash=sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4 \
|
||||
--hash=sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7 \
|
||||
--hash=sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7 \
|
||||
--hash=sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4 \
|
||||
--hash=sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e \
|
||||
--hash=sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa \
|
||||
--hash=sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6 \
|
||||
--hash=sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962 \
|
||||
--hash=sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b \
|
||||
--hash=sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f \
|
||||
--hash=sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474 \
|
||||
--hash=sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5 \
|
||||
--hash=sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459 \
|
||||
--hash=sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf \
|
||||
--hash=sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a \
|
||||
--hash=sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c \
|
||||
--hash=sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76 \
|
||||
--hash=sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362 \
|
||||
--hash=sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4 \
|
||||
--hash=sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934 \
|
||||
--hash=sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320 \
|
||||
--hash=sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118 \
|
||||
--hash=sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96 \
|
||||
--hash=sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306 \
|
||||
--hash=sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046 \
|
||||
--hash=sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3 \
|
||||
--hash=sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2 \
|
||||
--hash=sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af \
|
||||
--hash=sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9 \
|
||||
--hash=sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67 \
|
||||
--hash=sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a \
|
||||
--hash=sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27 \
|
||||
--hash=sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35 \
|
||||
--hash=sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b \
|
||||
--hash=sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151 \
|
||||
--hash=sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b \
|
||||
--hash=sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154 \
|
||||
--hash=sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133 \
|
||||
--hash=sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef \
|
||||
--hash=sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145 \
|
||||
--hash=sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15 \
|
||||
--hash=sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4 \
|
||||
--hash=sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc \
|
||||
--hash=sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee \
|
||||
--hash=sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c \
|
||||
--hash=sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0 \
|
||||
--hash=sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5 \
|
||||
--hash=sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57 \
|
||||
--hash=sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b \
|
||||
--hash=sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8 \
|
||||
--hash=sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1 \
|
||||
--hash=sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da \
|
||||
--hash=sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e \
|
||||
--hash=sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc \
|
||||
--hash=sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993 \
|
||||
--hash=sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656 \
|
||||
--hash=sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4 \
|
||||
--hash=sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c \
|
||||
--hash=sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb \
|
||||
--hash=sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d \
|
||||
--hash=sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9 \
|
||||
--hash=sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e \
|
||||
--hash=sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1 \
|
||||
--hash=sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc \
|
||||
--hash=sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a \
|
||||
--hash=sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9 \
|
||||
--hash=sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506 \
|
||||
--hash=sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b \
|
||||
--hash=sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1 \
|
||||
--hash=sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d \
|
||||
--hash=sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99 \
|
||||
--hash=sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3 \
|
||||
--hash=sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31 \
|
||||
--hash=sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c \
|
||||
--hash=sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39 \
|
||||
--hash=sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a \
|
||||
--hash=sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308 \
|
||||
--hash=sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2 \
|
||||
--hash=sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228 \
|
||||
--hash=sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b \
|
||||
--hash=sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9 \
|
||||
--hash=sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad
|
||||
pydantic==2.10.6 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584 \
|
||||
--hash=sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236
|
||||
python-dateutil==2.9.0.post0 ; python_version >= "3.12" and python_version < "4" \
|
||||
pydantic==2.10.3 \
|
||||
--hash=sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9 \
|
||||
--hash=sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d
|
||||
pydantic-core==2.27.1 \
|
||||
--hash=sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235 \
|
||||
--hash=sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f \
|
||||
--hash=sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02 \
|
||||
--hash=sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c \
|
||||
--hash=sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac \
|
||||
--hash=sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb \
|
||||
--hash=sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529 \
|
||||
--hash=sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35 \
|
||||
--hash=sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089 \
|
||||
--hash=sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381 \
|
||||
--hash=sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb \
|
||||
--hash=sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae \
|
||||
--hash=sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c \
|
||||
--hash=sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16 \
|
||||
--hash=sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e \
|
||||
--hash=sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073 \
|
||||
--hash=sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08 \
|
||||
--hash=sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf \
|
||||
--hash=sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737 \
|
||||
--hash=sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2 \
|
||||
--hash=sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107 \
|
||||
--hash=sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51 \
|
||||
--hash=sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a \
|
||||
--hash=sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc \
|
||||
--hash=sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960 \
|
||||
--hash=sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23 \
|
||||
--hash=sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05 \
|
||||
--hash=sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337 \
|
||||
--hash=sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5
|
||||
pydantic-settings==2.8.1 \
|
||||
--hash=sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585 \
|
||||
--hash=sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c
|
||||
pyright==1.1.390 \
|
||||
--hash=sha256:aad7f160c49e0fbf8209507a15e17b781f63a86a1facb69ca877c71ef2e9538d \
|
||||
--hash=sha256:ecebfba5b6b50af7c1a44c2ba144ba2ab542c227eb49bc1f16984ff714e0e110
|
||||
pytest==8.3.4 \
|
||||
--hash=sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761 \
|
||||
--hash=sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6
|
||||
python-dateutil==2.9.0.post0 \
|
||||
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
|
||||
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
|
||||
python-dotenv==1.0.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
python-dotenv==1.0.1 \
|
||||
--hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \
|
||||
--hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a
|
||||
pytz==2025.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57 \
|
||||
--hash=sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e
|
||||
recurring-ical-events==3.5.2 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:8622383ceb03d1b30159c2a6beb4cc3623646bfc52e349858e9315df149e52a2 \
|
||||
--hash=sha256:8eb7be7a3ebd322b942fde74dbfb3c230010ab49e1d9eea1e9ea2201c1e4da93
|
||||
requests==2.32.3 ; python_version >= "3.12" and python_version < "4" \
|
||||
pytz==2024.2 \
|
||||
--hash=sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a \
|
||||
--hash=sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725
|
||||
recurring-ical-events==3.3.4 \
|
||||
--hash=sha256:8e7f4fb03d1a180445d618327f0698f48cd2cabf40cf37f852cb5799c8883990 \
|
||||
--hash=sha256:24f8c11687e480ed90e84ee8dc8dc8920241476abe0450bd55606bac894414b1
|
||||
requests==2.32.3 \
|
||||
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
|
||||
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
|
||||
six==1.17.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 \
|
||||
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81
|
||||
sniffio==1.3.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||
sortedcontainers==2.4.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
six==1.17.0 \
|
||||
--hash=sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81 \
|
||||
--hash=sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274
|
||||
sniffio==1.3.1 \
|
||||
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc \
|
||||
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2
|
||||
sortedcontainers==2.4.0 \
|
||||
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
|
||||
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
|
||||
starlette==0.46.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038 \
|
||||
--hash=sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50
|
||||
tatsu==5.13.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:941e12df8798f444957600346c52a5ff3455f485de5b4570a3a71a00ed896a14 \
|
||||
--hash=sha256:df8c216eecd09af9b5b308624f1bfda1e4b5af3d3df1bdc4f331a1e8ea6bc012
|
||||
termcolor==2.5.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8 \
|
||||
--hash=sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f
|
||||
types-python-dateutil==2.9.0.20241206 ; python_version >= "3.12" and python_version < "4" \
|
||||
starlette==0.41.3 \
|
||||
--hash=sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835 \
|
||||
--hash=sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7
|
||||
tatsu==5.12.2 \
|
||||
--hash=sha256:5894dc7ddba9a1886a95ff2f06cef1be2b3d3a37c776eba8177ef4dcd80ccb03 \
|
||||
--hash=sha256:9c313186ae5262662cb3fbec52c9a12db1ef752e615f46cac3eb568cb91eacf9
|
||||
termcolor==2.5.0 \
|
||||
--hash=sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f \
|
||||
--hash=sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8
|
||||
types-python-dateutil==2.9.0.20241206 \
|
||||
--hash=sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb \
|
||||
--hash=sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53
|
||||
typing-extensions==4.12.2 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
|
||||
tzdata==2025.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694 \
|
||||
--hash=sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639
|
||||
urllib3==2.3.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df \
|
||||
--hash=sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d
|
||||
uvicorn==0.34.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4 \
|
||||
--hash=sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9
|
||||
vobject==0.9.9 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:0fbdb982065cf4d1843a5d5950c88510041c6de026bda49c3502721de1c6ac3d \
|
||||
--hash=sha256:ac44e5d7e2079d84c1d52c50a615b9bec4b1ba958608c4c7fe40cbf33247b38e
|
||||
websockets==15.0 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:0e389efe46ccb25a1f93d08c7a74e8123a2517f7b7458f043bd7529d1a63ffeb \
|
||||
--hash=sha256:0f2205cdb444a42a7919690238fb5979a05439b9dbb73dd47c863d39640d85ab \
|
||||
--hash=sha256:10552fed076757a70ba2c18edcbc601c7637b30cdfe8c24b65171e824c7d6081 \
|
||||
--hash=sha256:110a847085246ab8d4d119632145224d6b49e406c64f1bbeed45c6f05097b680 \
|
||||
--hash=sha256:1206432cc6c644f6fc03374b264c5ff805d980311563202ed7fef91a38906276 \
|
||||
--hash=sha256:1657a9eecb29d7838e3b415458cc494e6d1b194f7ac73a34aa55c6fb6c72d1f3 \
|
||||
--hash=sha256:17f2854c6bd9ee008c4b270f7010fe2da6c16eac5724a175e75010aacd905b31 \
|
||||
--hash=sha256:190bc6ef8690cd88232a038d1b15714c258f79653abad62f7048249b09438af3 \
|
||||
--hash=sha256:1caf951110ca757b8ad9c4974f5cac7b8413004d2f29707e4d03a65d54cedf2b \
|
||||
--hash=sha256:24d5333a9b2343330f0f4eb88546e2c32a7f5c280f8dd7d3cc079beb0901781b \
|
||||
--hash=sha256:26ba70fed190708551c19a360f9d7eca8e8c0f615d19a574292b7229e0ae324c \
|
||||
--hash=sha256:2bd8ef197c87afe0a9009f7a28b5dc613bfc585d329f80b7af404e766aa9e8c7 \
|
||||
--hash=sha256:2ea4f210422b912ebe58ef0ad33088bc8e5c5ff9655a8822500690abc3b1232d \
|
||||
--hash=sha256:30cff3ef329682b6182c01c568f551481774c476722020b8f7d0daacbed07a17 \
|
||||
--hash=sha256:327adab7671f3726b0ba69be9e865bba23b37a605b585e65895c428f6e47e766 \
|
||||
--hash=sha256:32e02a2d83f4954aa8c17e03fe8ec6962432c39aca4be7e8ee346b05a3476904 \
|
||||
--hash=sha256:37d66646f929ae7c22c79bc73ec4074d6db45e6384500ee3e0d476daf55482a9 \
|
||||
--hash=sha256:3a302241fbe825a3e4fe07666a2ab513edfdc6d43ce24b79691b45115273b5e7 \
|
||||
--hash=sha256:3abd670ca7ce230d5a624fd3d55e055215d8d9b723adee0a348352f5d8d12ff4 \
|
||||
--hash=sha256:4095a1f2093002c2208becf6f9a178b336b7572512ee0a1179731acb7788e8ad \
|
||||
--hash=sha256:45535fead66e873f411c1d3cf0d3e175e66f4dd83c4f59d707d5b3e4c56541c4 \
|
||||
--hash=sha256:45d464622314973d78f364689d5dbb9144e559f93dca11b11af3f2480b5034e1 \
|
||||
--hash=sha256:4f7290295794b5dec470867c7baa4a14182b9732603fd0caf2a5bf1dc3ccabf3 \
|
||||
--hash=sha256:4ff380aabd7a74a42a760ee76c68826a8f417ceb6ea415bd574a035a111fd133 \
|
||||
--hash=sha256:51ffd53c53c4442415b613497a34ba0aa7b99ac07f1e4a62db5dcd640ae6c3c3 \
|
||||
--hash=sha256:5294fcb410ed0a45d5d1cdedc4e51a60aab5b2b3193999028ea94afc2f554b05 \
|
||||
--hash=sha256:56e3efe356416bc67a8e093607315951d76910f03d2b3ad49c4ade9207bf710d \
|
||||
--hash=sha256:5d3cc75ef3e17490042c47e0523aee1bcc4eacd2482796107fd59dd1100a44bc \
|
||||
--hash=sha256:5e6ee18a53dd5743e6155b8ff7e8e477c25b29b440f87f65be8165275c87fef0 \
|
||||
--hash=sha256:67a04754d121ea5ca39ddedc3f77071651fb5b0bc6b973c71c515415b44ed9c5 \
|
||||
--hash=sha256:7394c0b7d460569c9285fa089a429f58465db930012566c03046f9e3ab0ed181 \
|
||||
--hash=sha256:789c43bf4a10cd067c24c321238e800b8b2716c863ddb2294d2fed886fa5a689 \
|
||||
--hash=sha256:7ac67b542505186b3bbdaffbc303292e1ee9c8729e5d5df243c1f20f4bb9057e \
|
||||
--hash=sha256:8561c48b0090993e3b2a54db480cab1d23eb2c5735067213bb90f402806339f5 \
|
||||
--hash=sha256:86bfb52a9cfbcc09aba2b71388b0a20ea5c52b6517c0b2e316222435a8cdab72 \
|
||||
--hash=sha256:8711682a629bbcaf492f5e0af72d378e976ea1d127a2d47584fa1c2c080b436b \
|
||||
--hash=sha256:89da58e4005e153b03fe8b8794330e3f6a9774ee9e1c3bd5bc52eb098c3b0c4f \
|
||||
--hash=sha256:89f72524033abbfde880ad338fd3c2c16e31ae232323ebdfbc745cbb1b3dcc03 \
|
||||
--hash=sha256:8bf1ab71f9f23b0a1d52ec1682a3907e0c208c12fef9c3e99d2b80166b17905f \
|
||||
--hash=sha256:8d7bbbe2cd6ed80aceef2a14e9f1c1b61683194c216472ed5ff33b700e784e37 \
|
||||
--hash=sha256:94c4a9b01eede952442c088d415861b0cf2053cbd696b863f6d5022d4e4e2453 \
|
||||
--hash=sha256:98dcf978d4c6048965d1762abd534c9d53bae981a035bfe486690ba11f49bbbb \
|
||||
--hash=sha256:a4cc73a6ae0a6751b76e69cece9d0311f054da9b22df6a12f2c53111735657c8 \
|
||||
--hash=sha256:a9f8e33747b1332db11cf7fcf4a9512bef9748cb5eb4d3f7fbc8c30d75dc6ffc \
|
||||
--hash=sha256:ace960769d60037ca9625b4c578a6f28a14301bd2a1ff13bb00e824ac9f73e55 \
|
||||
--hash=sha256:ae721bcc8e69846af00b7a77a220614d9b2ec57d25017a6bbde3a99473e41ce8 \
|
||||
--hash=sha256:aea01f40995fa0945c020228ab919b8dfc93fc8a9f2d3d705ab5b793f32d9e99 \
|
||||
--hash=sha256:b499caef4bca9cbd0bd23cd3386f5113ee7378094a3cb613a2fa543260fe9506 \
|
||||
--hash=sha256:b89504227a5311610e4be16071465885a0a3d6b0e82e305ef46d9b064ce5fb72 \
|
||||
--hash=sha256:bd66b4865c8b853b8cca7379afb692fc7f52cf898786537dfb5e5e2d64f0a47f \
|
||||
--hash=sha256:bfcd3acc1a81f106abac6afd42327d2cf1e77ec905ae11dc1d9142a006a496b6 \
|
||||
--hash=sha256:c24ba103ecf45861e2e1f933d40b2d93f5d52d8228870c3e7bf1299cd1cb8ff1 \
|
||||
--hash=sha256:c348abc5924caa02a62896300e32ea80a81521f91d6db2e853e6b1994017c9f6 \
|
||||
--hash=sha256:c53f97032b87a406044a1c33d1e9290cc38b117a8062e8a8b285175d7e2f99c9 \
|
||||
--hash=sha256:c7cd4b1015d2f60dfe539ee6c95bc968d5d5fad92ab01bb5501a77393da4f596 \
|
||||
--hash=sha256:c86dc2068f1c5ca2065aca34f257bbf4f78caf566eb230f692ad347da191f0a1 \
|
||||
--hash=sha256:c8c5c8e1bac05ef3c23722e591ef4f688f528235e2480f157a9cfe0a19081375 \
|
||||
--hash=sha256:ca36151289a15b39d8d683fd8b7abbe26fc50be311066c5f8dcf3cb8cee107ab \
|
||||
--hash=sha256:cc8821a03bcfb36e4e4705316f6b66af28450357af8a575dc8f4b09bf02a3dee \
|
||||
--hash=sha256:cccc18077acd34c8072578394ec79563664b1c205f7a86a62e94fafc7b59001f \
|
||||
--hash=sha256:d2244d8ab24374bed366f9ff206e2619345f9cd7fe79aad5225f53faac28b6b1 \
|
||||
--hash=sha256:d4c22992e24f12de340ca5f824121a5b3e1a37ad4360b4e1aaf15e9d1c42582d \
|
||||
--hash=sha256:dd24c4d256558429aeeb8d6c24ebad4e982ac52c50bc3670ae8646c181263965 \
|
||||
--hash=sha256:e413352a921f5ad5d66f9e2869b977e88d5103fc528b6deb8423028a2befd842 \
|
||||
--hash=sha256:ee06405ea2e67366a661ed313e14cf2a86e84142a3462852eb96348f7219cee3 \
|
||||
--hash=sha256:f83eca8cbfd168e424dfa3b3b5c955d6c281e8fc09feb9d870886ff8d03683c7 \
|
||||
--hash=sha256:fb915101dfbf318486364ce85662bb7b020840f68138014972c08331458d41f3 \
|
||||
--hash=sha256:ffc02b159b65c05f2ed9ec176b715b66918a674bd4daed48a9a7a590dd4be1aa \
|
||||
--hash=sha256:ffc5ae23ada6515f31604f700009e2df90b091b67d463a8401c1d8a37f76c1d7
|
||||
x-wr-timezone==2.0.1 ; python_version >= "3.12" and python_version < "4" \
|
||||
--hash=sha256:9166c40e6ffd4c0edebabc354e1a1e2cffc1bb473f88007694793757685cc8c3 \
|
||||
--hash=sha256:e74a53b9f4f7def8138455c240e65e47c224778bce3c024fcd6da2cbe91ca038
|
||||
typing-extensions==4.12.2 \
|
||||
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 \
|
||||
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d
|
||||
tzdata==2024.2 \
|
||||
--hash=sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc \
|
||||
--hash=sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd
|
||||
urllib3==2.2.3 \
|
||||
--hash=sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9 \
|
||||
--hash=sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac
|
||||
vobject==0.9.9 \
|
||||
--hash=sha256:ac44e5d7e2079d84c1d52c50a615b9bec4b1ba958608c4c7fe40cbf33247b38e \
|
||||
--hash=sha256:0fbdb982065cf4d1843a5d5950c88510041c6de026bda49c3502721de1c6ac3d
|
||||
watchfiles==1.0.4 \
|
||||
--hash=sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205 \
|
||||
--hash=sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2 \
|
||||
--hash=sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9 \
|
||||
--hash=sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712 \
|
||||
--hash=sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12 \
|
||||
--hash=sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844 \
|
||||
--hash=sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733 \
|
||||
--hash=sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af \
|
||||
--hash=sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a \
|
||||
--hash=sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff \
|
||||
--hash=sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e \
|
||||
--hash=sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94 \
|
||||
--hash=sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c \
|
||||
--hash=sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90 \
|
||||
--hash=sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9 \
|
||||
--hash=sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60 \
|
||||
--hash=sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407 \
|
||||
--hash=sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d \
|
||||
--hash=sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d \
|
||||
--hash=sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b \
|
||||
--hash=sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590 \
|
||||
--hash=sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902 \
|
||||
--hash=sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1 \
|
||||
--hash=sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303 \
|
||||
--hash=sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80 \
|
||||
--hash=sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc
|
||||
websockets==14.1 \
|
||||
--hash=sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8 \
|
||||
--hash=sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed \
|
||||
--hash=sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d \
|
||||
--hash=sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707 \
|
||||
--hash=sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a \
|
||||
--hash=sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45 \
|
||||
--hash=sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58 \
|
||||
--hash=sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058 \
|
||||
--hash=sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4 \
|
||||
--hash=sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05 \
|
||||
--hash=sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0 \
|
||||
--hash=sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f \
|
||||
--hash=sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9 \
|
||||
--hash=sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b \
|
||||
--hash=sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3 \
|
||||
--hash=sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59 \
|
||||
--hash=sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2 \
|
||||
--hash=sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da \
|
||||
--hash=sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9 \
|
||||
--hash=sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7 \
|
||||
--hash=sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a \
|
||||
--hash=sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6 \
|
||||
--hash=sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0 \
|
||||
--hash=sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e
|
||||
x-wr-timezone==2.0.0 \
|
||||
--hash=sha256:1e475d2dedcd2128550cd88b4b5773a224e936e1c9f22ff50104622180455265 \
|
||||
--hash=sha256:37f3927ddc32970c330af97773dc12bf8e0d2deb9ede5c3c676dc13ed62feb11
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import requests
|
||||
import os
|
||||
|
||||
print(requests.get("http://localhost:" + os.environ["GRANIAN_PORT"] + "/live"))
|
||||
+67
-8
@@ -10,6 +10,9 @@ from fastapi.websockets import WebSocket
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
BOARD_NAME = "Infra"
|
||||
BOARD_STACK = "Doing"
|
||||
|
||||
|
||||
def test_websocket():
|
||||
with client.websocket_connect("/ws/test-client") as websocket:
|
||||
@@ -18,13 +21,6 @@ def test_websocket():
|
||||
assert "Client #test-client dit: Hello WebSocket" in data
|
||||
|
||||
|
||||
def test_echo_message():
|
||||
test_message = {"text": "Hello FastAPI!"}
|
||||
response = client.post("/echo", json=test_message)
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": test_message["text"]}
|
||||
|
||||
|
||||
def test_websocket_echo():
|
||||
with client.websocket_connect("/ws/test-client") as websocket:
|
||||
websocket.send_text("Hello WebSocket")
|
||||
@@ -32,4 +28,67 @@ def test_websocket_echo():
|
||||
assert response == "Client #test-client dit: Hello WebSocket"
|
||||
|
||||
|
||||
def create_event_and_delete():
|
||||
def test_healthcheck():
|
||||
response = client.get("/live")
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"message": "OK"}
|
||||
|
||||
|
||||
def test_get_boards():
|
||||
response = client.get("/boards")
|
||||
assert response.status_code == 200
|
||||
# assert response.json() == {"message": "OK"}
|
||||
|
||||
|
||||
def test_get_board():
|
||||
response = client.get(f"/board/{BOARD_NAME}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_cards():
|
||||
response = client.get(f"/cards/{BOARD_NAME}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_stack():
|
||||
response = client.get(f"/board/{BOARD_NAME}/stack/{BOARD_STACK}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
# def test_get_stack_by_id():
|
||||
# response = client.get(f"/board/{BOARD_NAME}/stack/{BOARD_STACK}")
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_labels():
|
||||
response = client.get(f"/board/{BOARD_NAME}/labels")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_events():
|
||||
response = client.get(f"/events")
|
||||
assert response.status_code == 200
|
||||
assert response.json()
|
||||
|
||||
|
||||
def test_get_gaps():
|
||||
response = client.get(f"/gaps")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_gaps_2days():
|
||||
response = client.get(f"/gaps/2")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_sort_by_priority():
|
||||
response = client.get(f"/sort-by-priority/{BOARD_NAME}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_get_top_priority():
|
||||
response = client.get(f"/top-priority/{BOARD_NAME}")
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
# def create_event_and_delete():
|
||||
|
||||
@@ -194,6 +194,43 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/52/b3/7e4df40e585df024fac2f80d1a2d579c854ac37109675db2b0cc22c0bb9e/fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305", size = 94843 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "granian"
|
||||
version = "2.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/24/18/58f706aa9f79bd31dd68ac144e88c2a82afcc1aab84f33947559c8a7bc37/granian-2.0.0.tar.gz", hash = "sha256:d040c63e069ce195607530afa53fc1fd50b8e99284184f051e5bd6b4c3742081", size = 92342 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/43/25/dec6bf5fa6bac1ae4ecdba3a0097065faf80f3bf2032944bd2b0f79e2c10/granian-2.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:16b71f2e5d0c7e2059cc5902dc61908c4dbb54088c09acc14457c89f1778936d", size = 2864444 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/f7/7934eb3552eedeb918f6f59c6e9f4334f83d358befa743eaf62a08ded7d3/granian-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4c58be99fb385ee219111cdaf6f5b069c668a3345551b4209073a1db83125b94", size = 2640479 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/3f/ea56204a5e7f89f4de3b1f16ee51c5c538f92c864af1736293442343dad7/granian-2.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4695bcf6e2c3842e212dc42253be0400ee68e7b2245555d9d04ffb44e85919", size = 2999510 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7c/3d/a3e3ca28fcc9a7d4ac23c95535eb3bc25d1b174227525069d9e05ed4c82b/granian-2.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7122062532355af2c87048f859f6006a452073af5e88dd4b0b8f8eafb4107b5", size = 3071416 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/8b/9865bfd80fc06ec249347b3a0e89a6059870a8e235591bda3ed3f502a92d/granian-2.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0618855fcf40a0c768839031f5c55cf0debe0b4fc123c129e5a6f0166e341fc5", size = 3000238 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/2c/72337748b7f9b06adff13a31ae6559b960da54398d533fbb64d83c390899/granian-2.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6a2d7c51d20cb0fe831d481c980f4ff2c400a4c7f84abad66d8c1ac94f468921", size = 3109544 },
|
||||
{ url = "https://files.pythonhosted.org/packages/57/66/678f01ccdfb32c58d9d89797982c363110e33049929b874f2b2d4c40b7b7/granian-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:54bf94c4bbfb2630b33b1a075b4571fa2b1e870cbf06f4145c29e13122aec8ff", size = 2609526 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/16/992b68e535313103e66f8667d3af0f68362f85131d192239ff304418ca3a/granian-2.0.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f98a6d604b0dbdd226b0a0c823242706bd99070abd6977d4903c4249d231aab4", size = 2863592 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/b4/5d1d055512b398c0423100a3a07a9a1cf3af4558aba207f05dfd2e2fa8a6/granian-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f83644dace8acb07a4bd36c5fdf6731eea785ef243a8c5ee77361eec09c2c35", size = 2640150 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/64/0edb5188db312ed32037626adefb042a4a08d728b67657fe9295dd80798a/granian-2.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f65c7d4b6d5280a622a3e0ed899654221d748cf7bde801d701f2d7266ec0cb8e", size = 3002636 },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/96/6afac3a929a45ceed1b2317bfa48ea425607ed5badf82e5aee0bd771302d/granian-2.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b87b851f38e1cf3d48bb49069efb7e4b18db46c664512e3dd6d0fea5ce6806e1", size = 3070948 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/8b/f49b53e321d3786aa5f3b06edd7083253e4f809dd6e1bc0d3bcf21bea8f5/granian-2.0.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a71872e7f619f7b8e543fdfcdb0a5025395cd36326f53558474f4c9c243219de", size = 3000279 },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/54/447887eacc6b359384d992783f9504606d838004f6a53c1292b10f8ee4f3/granian-2.0.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:70cdb93c63711f3da3e4b3a465affb514cb933635fdbdbfd0349c4f2920069ab", size = 3109187 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/de/b0b98e19839b54b9dfd873984b3535ff73ec8cf5d0fe02d5a027a2e8333c/granian-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4d3fc2895de84a15ded59ecc69a100ff5e53cfb6ac9eb96f6db00d571b604b1c", size = 2609375 },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/13/6a5aaf474640c9129a2b7715bcbf9a779693d97833220f47dc566629baca/granian-2.0.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:565c90932189b759534096d260837a6af3dac492490fa4858339f5d251181e94", size = 2813729 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/41/29271bd7707c40d5630514ff8e41d58ae9e230d0f0f50321a61910825577/granian-2.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5769cf26bd27bdfce2932570ec3cf1e6703b493ef9d64acbba7740800299982a", size = 2556192 },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/5d/415fbe74ffcf0a290aa616082bbdbc45182f1350429d3ebdab8f0b83aea1/granian-2.0.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88be218f5daad58b2984aeec27720b35082d47bbb1cd1b6f910168431a94c181", size = 2832816 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/e2/7002c59900f6c433bda4d9598a7f2920d1f4248f20f866240601cde75400/granian-2.0.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de8ce3356f8016aababf946caec88d3423102d428e6c988538de83e1345d419a", size = 2922955 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/1b/55f5a17ce60e552a5828819e6b07e330f8118225d6db7f118e2beb928ca5/granian-2.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:1598cc112b15b78915a3231d916c1db14c6d00e1b3802118a9610ac4e171dea1", size = 2991128 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/e5/4eab530da2ce4b38e745dde3a5401c5419deb24e2930202244ae1f11c82d/granian-2.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:035f765b7a918c8b197917b3451dd4653db3478b9c8221c2a7f0532472a203a9", size = 3096132 },
|
||||
{ url = "https://files.pythonhosted.org/packages/79/87/e4f94b4ae21d81bf258358bac0c173cf830aede6aa679627eae538114196/granian-2.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:b80e1acdd3ec632abec6ddce9c549351ecf0bd7a24914f338920375a28e35f8b", size = 2612935 },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
reload = [
|
||||
{ name = "watchfiles" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.14.0"
|
||||
@@ -467,6 +504,19 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c3/b15fb833926d91d982fde29c0624c9f225da743c7af801dace0d4e187e71/pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5", size = 1882983 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pydantic-settings"
|
||||
version = "2.8.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "python-dotenv" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/88/82/c79424d7d8c29b994fb01d277da57b0a9b09cc03c3ff875f9bd8a86b2145/pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585", size = 83550 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/53/a64f03044927dc47aafe029c42a5b7aabc38dfb813475e0e1bf71c4a59d0/pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c", size = 30839 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyright"
|
||||
version = "1.1.390"
|
||||
@@ -619,20 +669,21 @@ source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "caldav" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "icalendar" },
|
||||
{ name = "granian" },
|
||||
{ name = "ics" },
|
||||
{ name = "nextcloud-api-wrapper" },
|
||||
{ name = "nextcloud-deck" },
|
||||
{ name = "portion" },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "python-dotenv" },
|
||||
{ name = "termcolor" },
|
||||
{ name = "uvicorn" },
|
||||
{ name = "websockets" },
|
||||
]
|
||||
|
||||
[package.dev-dependencies]
|
||||
dev = [
|
||||
{ name = "black" },
|
||||
{ name = "granian", extra = ["reload"] },
|
||||
{ name = "httpx" },
|
||||
{ name = "pyright" },
|
||||
{ name = "pytest" },
|
||||
@@ -642,20 +693,21 @@ dev = [
|
||||
requires-dist = [
|
||||
{ name = "caldav", specifier = ">=1.4.0" },
|
||||
{ name = "fastapi", specifier = ">=0.115.6" },
|
||||
{ name = "icalendar", specifier = ">=6.1.0" },
|
||||
{ name = "granian", specifier = ">=2.0.0" },
|
||||
{ name = "ics", specifier = ">=0.7.2" },
|
||||
{ name = "nextcloud-api-wrapper", specifier = ">=0.2.3" },
|
||||
{ name = "nextcloud-deck", specifier = ">=0.2.1" },
|
||||
{ name = "portion", specifier = ">=2.6.0,<3.0.0" },
|
||||
{ name = "pydantic-settings", specifier = ">=2.8.1" },
|
||||
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
||||
{ name = "termcolor", specifier = ">=2.5.0" },
|
||||
{ name = "uvicorn", specifier = ">=0.34.0" },
|
||||
{ name = "websockets", specifier = ">=14.1" },
|
||||
]
|
||||
|
||||
[package.metadata.requires-dev]
|
||||
dev = [
|
||||
{ name = "black", specifier = ">=24.10.0" },
|
||||
{ name = "granian", extras = ["reload"], specifier = ">=2.0.0" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "pyright", specifier = ">=1.1.390" },
|
||||
{ name = "pytest", specifier = ">=8.3.4" },
|
||||
@@ -697,19 +749,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uvicorn"
|
||||
version = "0.34.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "click" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vobject"
|
||||
version = "0.9.9"
|
||||
@@ -724,6 +763,42 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/68/20/6bba813bbd498c28edbbcf8253a6398cf4266ecf7bfa6129835c0a2bfbb1/vobject-0.9.9-py2.py3-none-any.whl", hash = "sha256:0fbdb982065cf4d1843a5d5950c88510041c6de026bda49c3502721de1c6ac3d", size = 47526 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "watchfiles"
|
||||
version = "1.0.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f5/26/c705fc77d0a9ecdb9b66f1e2976d95b81df3cae518967431e7dbf9b5e219/watchfiles-1.0.4.tar.gz", hash = "sha256:6ba473efd11062d73e4f00c2b730255f9c1bdd73cd5f9fe5b5da8dbd4a717205", size = 94625 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5b/1a/8f4d9a1461709756ace48c98f07772bc6d4519b1e48b5fa24a4061216256/watchfiles-1.0.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:229e6ec880eca20e0ba2f7e2249c85bae1999d330161f45c78d160832e026ee2", size = 391345 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/d2/6750b7b3527b1cdaa33731438432e7238a6c6c40a9924049e4cebfa40805/watchfiles-1.0.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5717021b199e8353782dce03bd8a8f64438832b84e2885c4a645f9723bf656d9", size = 381515 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4e/17/80500e42363deef1e4b4818729ed939aaddc56f82f4e72b2508729dd3c6b/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0799ae68dfa95136dde7c472525700bd48777875a4abb2ee454e3ab18e9fc712", size = 449767 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/37/1427fa4cfa09adbe04b1e97bced19a29a3462cc64c78630787b613a23f18/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43b168bba889886b62edb0397cab5b6490ffb656ee2fcb22dec8bfeb371a9e12", size = 455677 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c5/7a/39e9397f3a19cb549a7d380412fd9e507d4854eddc0700bfad10ef6d4dba/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb2c46e275fbb9f0c92e7654b231543c7bbfa1df07cdc4b99fa73bedfde5c844", size = 482219 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/2d/7113931a77e2ea4436cad0c1690c09a40a7f31d366f79c6f0a5bc7a4f6d5/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:857f5fc3aa027ff5e57047da93f96e908a35fe602d24f5e5d8ce64bf1f2fc733", size = 518830 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f9/1b/50733b1980fa81ef3c70388a546481ae5fa4c2080040100cd7bf3bf7b321/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55ccfd27c497b228581e2838d4386301227fc0cb47f5a12923ec2fe4f97b95af", size = 497997 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/b4/9396cc61b948ef18943e7c85ecfa64cf940c88977d882da57147f62b34b1/watchfiles-1.0.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c11ea22304d17d4385067588123658e9f23159225a27b983f343fcffc3e796a", size = 452249 },
|
||||
{ url = "https://files.pythonhosted.org/packages/fb/69/0c65a5a29e057ad0dc691c2fa6c23b2983c7dabaa190ba553b29ac84c3cc/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:74cb3ca19a740be4caa18f238298b9d472c850f7b2ed89f396c00a4c97e2d9ff", size = 614412 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/b9/319fcba6eba5fad34327d7ce16a6b163b39741016b1996f4a3c96b8dd0e1/watchfiles-1.0.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c7cce76c138a91e720d1df54014a047e680b652336e1b73b8e3ff3158e05061e", size = 611982 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/47/143c92418e30cb9348a4387bfa149c8e0e404a7c5b0585d46d2f7031b4b9/watchfiles-1.0.4-cp312-cp312-win32.whl", hash = "sha256:b045c800d55bc7e2cadd47f45a97c7b29f70f08a7c2fa13241905010a5493f94", size = 271822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/94/b0165481bff99a64b29e46e07ac2e0df9f7a957ef13bec4ceab8515f44e3/watchfiles-1.0.4-cp312-cp312-win_amd64.whl", hash = "sha256:c2acfa49dd0ad0bf2a9c0bb9a985af02e89345a7189be1efc6baa085e0f72d7c", size = 285441 },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/de/09fe56317d582742d7ca8c2ca7b52a85927ebb50678d9b0fa8194658f536/watchfiles-1.0.4-cp312-cp312-win_arm64.whl", hash = "sha256:22bb55a7c9e564e763ea06c7acea24fc5d2ee5dfc5dafc5cfbedfe58505e9f90", size = 277141 },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/98/f03efabec64b5b1fa58c0daab25c68ef815b0f320e54adcacd0d6847c339/watchfiles-1.0.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8012bd820c380c3d3db8435e8cf7592260257b378b649154a7948a663b5f84e9", size = 390954 },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/09/4dd49ba0a32a45813debe5fb3897955541351ee8142f586303b271a02b40/watchfiles-1.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:aa216f87594f951c17511efe5912808dfcc4befa464ab17c98d387830ce07b60", size = 381133 },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/59/5aa6fc93553cd8d8ee75c6247763d77c02631aed21551a97d94998bf1dae/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c9953cf85529c05b24705639ffa390f78c26449e15ec34d5339e8108c7c407", size = 449516 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4c/aa/df4b6fe14b6317290b91335b23c96b488d365d65549587434817e06895ea/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cf684aa9bba4cd95ecb62c822a56de54e3ae0598c1a7f2065d51e24637a3c5d", size = 454820 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/71/185f8672f1094ce48af33252c73e39b48be93b761273872d9312087245f6/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f44a39aee3cbb9b825285ff979ab887a25c5d336e5ec3574f1506a4671556a8d", size = 481550 },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/d7/50ebba2c426ef1a5cb17f02158222911a2e005d401caf5d911bfca58f4c4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38320582736922be8c865d46520c043bff350956dfc9fbaee3b2df4e1740a4b", size = 518647 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/7a/4c009342e393c545d68987e8010b937f72f47937731225b2b29b7231428f/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39f4914548b818540ef21fd22447a63e7be6e24b43a70f7642d21f1e73371590", size = 497547 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/7c/1cf50b35412d5c72d63b2bf9a4fffee2e1549a245924960dd087eb6a6de4/watchfiles-1.0.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f12969a3765909cf5dc1e50b2436eb2c0e676a3c75773ab8cc3aa6175c16e902", size = 452179 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/a9/3db1410e1c1413735a9a472380e4f431ad9a9e81711cda2aaf02b7f62693/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0986902677a1a5e6212d0c49b319aad9cc48da4bd967f86a11bde96ad9676ca1", size = 614125 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f2/e1/0025d365cf6248c4d1ee4c3d2e3d373bdd3f6aff78ba4298f97b4fad2740/watchfiles-1.0.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:308ac265c56f936636e3b0e3f59e059a40003c655228c131e1ad439957592303", size = 611911 },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/55/035838277d8c98fc8c917ac9beeb0cd6c59d675dc2421df5f9fcf44a0070/watchfiles-1.0.4-cp313-cp313-win32.whl", hash = "sha256:aee397456a29b492c20fda2d8961e1ffb266223625346ace14e4b6d861ba9c80", size = 271152 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/e5/96b8e55271685ddbadc50ce8bc53aa2dff278fb7ac4c2e473df890def2dc/watchfiles-1.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:d6097538b0ae5c1b88c3b55afa245a66793a8fec7ada6755322e465fb1a0e8cc", size = 285216 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "14.1"
|
||||
|
||||
Reference in New Issue
Block a user