Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3369b92b11 |
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
out/
|
.env
|
||||||
.venv
|
.venv
|
||||||
.idea/
|
|
||||||
.vscode/
|
.vscode/
|
||||||
|
config.toml
|
||||||
|
out/
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
[odoo]
|
||||||
|
session_id = "your-odoo-session-id-here"
|
||||||
|
|
||||||
|
[team]
|
||||||
|
members = [
|
||||||
|
"gram_1",
|
||||||
|
"gram_2",
|
||||||
|
"gram_3",
|
||||||
|
"gram_4",
|
||||||
|
"gram_5"
|
||||||
|
]
|
||||||
@@ -6,6 +6,8 @@ readme = "README.md"
|
|||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"click>=8.1.8",
|
"click>=8.1.8",
|
||||||
|
"pytest>=8.3.5",
|
||||||
|
"python-dotenv>=1.1.0",
|
||||||
"requests>=2.32.3",
|
"requests>=2.32.3",
|
||||||
"rich>=13.9.4",
|
"rich>=13.9.4",
|
||||||
]
|
]
|
||||||
|
|||||||
+53
-20
@@ -1,9 +1,18 @@
|
|||||||
import random
|
import os
|
||||||
from datetime import datetime
|
from dataclasses import dataclass, field
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
vfyd_tags = [
|
import tomllib
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Config:
|
||||||
|
config_path: Path = Path("config.toml")
|
||||||
|
vfyd_tags: list[str] = field(default_factory=lambda: [
|
||||||
"vfyd accounting",
|
"vfyd accounting",
|
||||||
"vfyd discuss",
|
"vfyd discuss",
|
||||||
|
"vfyd easy",
|
||||||
"vfyd editor",
|
"vfyd editor",
|
||||||
"vfyd js",
|
"vfyd js",
|
||||||
"vfyd marketing",
|
"vfyd marketing",
|
||||||
@@ -17,23 +26,47 @@ vfyd_tags = [
|
|||||||
"vfyd website",
|
"vfyd website",
|
||||||
"vfyd industry",
|
"vfyd industry",
|
||||||
"vfyd voip",
|
"vfyd voip",
|
||||||
"vfyd ai",
|
])
|
||||||
"vfyd iot",
|
_config: dict = field(init=False, repr=False)
|
||||||
]
|
|
||||||
|
|
||||||
type TeamMember = str
|
def __post_init__(self):
|
||||||
|
load_dotenv()
|
||||||
|
self._config = self._load_config()
|
||||||
|
self.session_id = self._get_session_id()
|
||||||
|
self.team = self._validate_team()
|
||||||
|
|
||||||
team: list[TeamMember] = [
|
def _load_config(self) -> dict:
|
||||||
"abz",
|
"""Load the configuration file."""
|
||||||
"hael",
|
if not self.config_path.exists():
|
||||||
"roen",
|
raise FileNotFoundError(f"Configuration file '{self.config_path}' not found.")
|
||||||
"sbel",
|
|
||||||
"wasa",
|
|
||||||
"huvw",
|
|
||||||
"artn",
|
|
||||||
"gvar",
|
|
||||||
"sben",
|
|
||||||
"zavan",
|
|
||||||
]
|
|
||||||
|
|
||||||
random.shuffle(team)
|
try:
|
||||||
|
with self.config_path.open("rb") as f:
|
||||||
|
return tomllib.load(f)
|
||||||
|
except tomllib.TOMLDecodeError as e:
|
||||||
|
raise ValueError(f"Error decoding TOML file '{self.config_path}': {e}")
|
||||||
|
|
||||||
|
def _get_session_id(self) -> str:
|
||||||
|
"""Prioritize configuration sources for session ID."""
|
||||||
|
session_id = self._get("odoo", "session_id") or os.getenv("ODOO_SESSION_ID")
|
||||||
|
|
||||||
|
if not session_id:
|
||||||
|
raise ValueError("No valid session ID found in config or environment")
|
||||||
|
|
||||||
|
return session_id
|
||||||
|
|
||||||
|
def _validate_team(self) -> list[str]:
|
||||||
|
"""Validate and sanitize team members."""
|
||||||
|
team = self._get("team", "members")
|
||||||
|
|
||||||
|
if not team:
|
||||||
|
raise ValueError("No team members defined in configuration.")
|
||||||
|
|
||||||
|
return list({member.lower().strip() for member in team})
|
||||||
|
|
||||||
|
def _get(self, section: str, key: str, default=None):
|
||||||
|
"""Retrieve a configuration value."""
|
||||||
|
return self._config.get(section, {}).get(key, default)
|
||||||
|
|
||||||
|
|
||||||
|
config = Config()
|
||||||
|
|||||||
+2
-1
@@ -1,7 +1,8 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from model import OdooModel
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
from model import OdooModel
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Leave(OdooModel):
|
class Leave(OdooModel):
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
import odoo_client
|
|
||||||
from datetime import datetime
|
|
||||||
from leave import Leave
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
class LeaveService:
|
|
||||||
def __init__(self, client: odoo_client.OdooClient):
|
|
||||||
self.client = client
|
|
||||||
|
|
||||||
def fetch_available_employees(self, team: list[str]) -> list:
|
|
||||||
today = datetime.now().strftime("%Y-%m-%d")
|
|
||||||
domain = [
|
|
||||||
("start_datetime", "<=", today),
|
|
||||||
("stop_datetime", ">=", today),
|
|
||||||
]
|
|
||||||
domain.extend(["|"] * (len(team) - 1))
|
|
||||||
domain.extend(
|
|
||||||
[("employee_id.name", "=ilike", f"%({employee})") for employee in team]
|
|
||||||
)
|
|
||||||
leaves = self.client.web_search_read(Leave, domain)
|
|
||||||
|
|
||||||
team_availability = {employee: (True, True) for employee in team}
|
|
||||||
|
|
||||||
for leave in leaves:
|
|
||||||
employee = re.search(r"\((.*?)\)", leave.employee_id).group(1).lower()
|
|
||||||
is_morning = leave.start_datetime.hour < 11
|
|
||||||
is_afternoon = leave.stop_datetime.hour >= 14
|
|
||||||
if is_morning:
|
|
||||||
team_availability[employee] = (False, team_availability[employee][1])
|
|
||||||
if is_afternoon:
|
|
||||||
team_availability[employee] = (team_availability[employee][0], False)
|
|
||||||
|
|
||||||
available_employees = [
|
|
||||||
employee
|
|
||||||
for employee, (morning, afternoon) in team_availability.items()
|
|
||||||
if morning or afternoon
|
|
||||||
]
|
|
||||||
return available_employees
|
|
||||||
+131
-54
@@ -1,19 +1,21 @@
|
|||||||
import click
|
|
||||||
import random
|
import random
|
||||||
import odoo_client
|
import re
|
||||||
from rich import print, console
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from config import vfyd_tags, team, TeamMember
|
from datetime import datetime
|
||||||
from leave_service import LeaveService
|
from itertools import cycle
|
||||||
from task_repository import TaskRepository
|
|
||||||
from storage import JsonDispatchStorage, DispatchStorage
|
import click
|
||||||
|
import odoo_client
|
||||||
|
from config import config
|
||||||
|
from leave import Leave
|
||||||
|
from rich import console, print
|
||||||
|
from storage import DispatchStorage
|
||||||
from tasks import Task
|
from tasks import Task
|
||||||
|
|
||||||
console = console.Console()
|
console = console.Console()
|
||||||
storage: DispatchStorage = JsonDispatchStorage()
|
|
||||||
client = odoo_client.OdooClient()
|
team = config.team
|
||||||
leave_service = LeaveService(client)
|
vfyd_tags = config.vfyd_tags
|
||||||
task_repository = TaskRepository(client, vfyd_tags)
|
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@@ -25,57 +27,121 @@ def cli() -> None:
|
|||||||
def dispatch():
|
def dispatch():
|
||||||
"""Randomly distribute unassigned tasks among team members."""
|
"""Randomly distribute unassigned tasks among team members."""
|
||||||
|
|
||||||
if storage.load():
|
client = odoo_client.OdooClient()
|
||||||
print("[red]Tickets were already dispatched.[/red]")
|
today = datetime.now().strftime("%Y-%m-%d")
|
||||||
return
|
|
||||||
|
|
||||||
available_employees = leave_service.fetch_available_employees(team)
|
domain = [
|
||||||
|
("start_datetime", "<=", today),
|
||||||
|
("stop_datetime", ">=", today),
|
||||||
|
]
|
||||||
|
domain.extend(["|"] * (len(team) - 1))
|
||||||
|
domain.extend(
|
||||||
|
[("employee_id.name", "=ilike", f"%({employee})") for employee in team]
|
||||||
|
)
|
||||||
|
|
||||||
|
leaves = client.web_search_read(Leave, domain)
|
||||||
|
|
||||||
|
team_availability = {employee: (True, True) for employee in team}
|
||||||
|
|
||||||
|
for leave in leaves:
|
||||||
|
employee = re.search(r"\((.*?)\)", leave.employee_id).group(1).lower()
|
||||||
|
is_morning = leave.start_datetime.hour < 11
|
||||||
|
is_afternoon = leave.stop_datetime.hour >= 14
|
||||||
|
morning, afternoon = team_availability.get(employee, (True, True))
|
||||||
|
team_availability[employee] = (
|
||||||
|
morning and not is_morning,
|
||||||
|
afternoon and not is_afternoon,
|
||||||
|
)
|
||||||
|
|
||||||
|
available_employees = [
|
||||||
|
employee
|
||||||
|
for employee, (morning, afternoon) in team_availability.items()
|
||||||
|
if morning or afternoon
|
||||||
|
]
|
||||||
|
|
||||||
if not available_employees:
|
if not available_employees:
|
||||||
print("[red]No available employees for dispatch.[/red]")
|
print("[red]No available employees for dispatch.[/red]")
|
||||||
return
|
return
|
||||||
|
|
||||||
previous_dispatch = storage.load_week_to_date()
|
previous_dispatch = DispatchStorage.load_week_to_date()
|
||||||
previous_dispatch_ids = [
|
previous_dispatch_ids = [
|
||||||
task_id for tasks in previous_dispatch.values() for task_id in tasks
|
task_id for tasks in previous_dispatch.values() for task_id in tasks
|
||||||
]
|
]
|
||||||
|
|
||||||
# Find unsorted tasks that have not been dispatched on previous days,
|
domain = [
|
||||||
# Assign them to team members and save the result
|
("stage_id", "=", 194),
|
||||||
new_unsorted_tasks = task_repository.get_unsorted_excluding(previous_dispatch_ids)
|
("user_ids", "=", False),
|
||||||
random.shuffle(new_unsorted_tasks)
|
("project_id", "=", 49),
|
||||||
new_dispatch: dict[TeamMember, list[int]] = defaultdict(list)
|
("tag_ids", "not in", vfyd_tags),
|
||||||
out: dict[TeamMember, list[Task]] = defaultdict(list)
|
("id", "not in", previous_dispatch_ids),
|
||||||
for idx, task in enumerate(new_unsorted_tasks):
|
]
|
||||||
member = available_employees[idx % len(available_employees)]
|
records = client.web_search_read(Task, domain)
|
||||||
new_dispatch[member].append(task.id)
|
|
||||||
out[member].append(task)
|
|
||||||
storage.save(new_dispatch)
|
|
||||||
|
|
||||||
# Find unsorted tasks that have been dispatched on previous days.
|
random.shuffle(records)
|
||||||
# Add them back to the previously assign team members
|
random.shuffle(available_employees)
|
||||||
old_unsorted_tasks = task_repository.get_unsorted_within(previous_dispatch_ids)
|
employee_cycle = cycle(available_employees)
|
||||||
combined_tasks_to_sort: dict[TeamMember, list[Task]] = defaultdict(list)
|
|
||||||
for member, task_ids in previous_dispatch.items():
|
|
||||||
for task_id in task_ids:
|
|
||||||
task = next((t for t in old_unsorted_tasks if t.id == task_id), None)
|
|
||||||
if task:
|
|
||||||
combined_tasks_to_sort[member].append(task)
|
|
||||||
|
|
||||||
# Merge the new unsorted tasks with the old ones
|
new_dispatch = defaultdict(list)
|
||||||
for member, tasks in out.items():
|
|
||||||
combined_tasks_to_sort[member].extend(tasks)
|
|
||||||
|
|
||||||
for key, tasks in combined_tasks_to_sort.items():
|
for task in records:
|
||||||
print(f"**{key}:**")
|
employee = next(employee_cycle)
|
||||||
|
new_dispatch[employee].append(task)
|
||||||
|
|
||||||
|
merged_dispatch = defaultdict(list)
|
||||||
|
|
||||||
|
if previous_dispatch_ids:
|
||||||
|
domain = [
|
||||||
|
("stage_id", "=", 194),
|
||||||
|
("user_ids", "=", False),
|
||||||
|
("project_id", "=", 49),
|
||||||
|
("tag_ids", "not in", vfyd_tags),
|
||||||
|
("id", "in", previous_dispatch_ids),
|
||||||
|
]
|
||||||
|
old_tasks = client.web_search_read(Task, domain)
|
||||||
|
old_tasks_by_id = {task.id: task for task in old_tasks}
|
||||||
|
else:
|
||||||
|
old_tasks_by_id = {}
|
||||||
|
|
||||||
|
for employee, task_ids in previous_dispatch.items():
|
||||||
|
merged_dispatch[employee].extend(
|
||||||
|
old_tasks_by_id[tid] for tid in task_ids if tid in old_tasks_by_id
|
||||||
|
)
|
||||||
|
|
||||||
|
for employee, tasks in new_dispatch.items():
|
||||||
|
merged_dispatch[employee].extend(tasks)
|
||||||
|
|
||||||
|
dispatch_to_save = {
|
||||||
|
employee: [task.id for task in tasks]
|
||||||
|
for employee, tasks in merged_dispatch.items()
|
||||||
|
}
|
||||||
|
DispatchStorage.save(dispatch_to_save)
|
||||||
|
|
||||||
|
for employee, tasks in sorted(merged_dispatch.items()):
|
||||||
|
if not tasks:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print(f"**{employee}:**")
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
print(task.url)
|
print(task.url)
|
||||||
|
print("\n")
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
def status():
|
def status():
|
||||||
"""Show all unassigned tasks in the backlog."""
|
"""Show all unassigned tasks in the backlog."""
|
||||||
tasks = task_repository.get_unassigned_tasks()
|
|
||||||
|
client = odoo_client.OdooClient()
|
||||||
|
domain = [
|
||||||
|
("stage_id", "=", 194),
|
||||||
|
("user_ids", "=", False),
|
||||||
|
("project_id", "=", 49),
|
||||||
|
("tag_ids", "not in", vfyd_tags),
|
||||||
|
]
|
||||||
|
tasks = client.web_search_read(Task, domain)
|
||||||
|
|
||||||
|
if not tasks:
|
||||||
|
print("[yellow]No unassigned tasks in the backlog.[/yellow]")
|
||||||
|
else:
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
print(task)
|
print(task)
|
||||||
|
|
||||||
@@ -83,22 +149,33 @@ def status():
|
|||||||
@cli.command()
|
@cli.command()
|
||||||
def check():
|
def check():
|
||||||
"""Check which previously dispatched tasks are still not completed."""
|
"""Check which previously dispatched tasks are still not completed."""
|
||||||
dispatch = storage.load()
|
|
||||||
reverse_dispatch = {v: k for k, vs in dispatch.items() for v in vs}
|
dispatch = DispatchStorage.load_week_to_date()
|
||||||
tasks = task_repository.get_tasks_with_ids(reverse_dispatch.keys())
|
task_id_to_employee = {
|
||||||
|
task_id: employee
|
||||||
|
for employee, task_ids in dispatch.items()
|
||||||
|
for task_id in task_ids
|
||||||
|
}
|
||||||
|
client = odoo_client.OdooClient()
|
||||||
|
domain = [
|
||||||
|
("id", "in", list(task_id_to_employee.keys())),
|
||||||
|
("tag_ids", "not in", vfyd_tags),
|
||||||
|
("stage_id", "=", 194),
|
||||||
|
("project_id", "=", 49),
|
||||||
|
]
|
||||||
|
tasks = client.web_search_read(Task, domain)
|
||||||
|
|
||||||
|
if not tasks:
|
||||||
|
print("[green]All dispatched tasks have been completed![/green]")
|
||||||
|
return
|
||||||
|
|
||||||
not_done = defaultdict(list)
|
not_done = defaultdict(list)
|
||||||
for task in tasks:
|
for task in tasks:
|
||||||
not_done[reverse_dispatch[task.id]].append(task)
|
not_done[task_id_to_employee[task.id]].append(task)
|
||||||
|
|
||||||
if not not_done:
|
for employee, pending_tasks in not_done.items():
|
||||||
print("[green]All dispatched tasks are done![/green]")
|
console.rule(f"[red]{employee}[/red]")
|
||||||
return
|
for task in pending_tasks:
|
||||||
|
|
||||||
for key, value in not_done.items():
|
|
||||||
if value:
|
|
||||||
console.rule(f"[red]{key}[/red]")
|
|
||||||
for task in value:
|
|
||||||
print(task)
|
print(task)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
from typing import Self, Any, get_origin
|
from dataclasses import Field, fields
|
||||||
from dataclasses import fields, Field
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from typing import Any, Self, get_origin
|
||||||
|
|
||||||
|
|
||||||
class OdooModel:
|
class OdooModel:
|
||||||
|
|||||||
+11
-47
@@ -1,48 +1,21 @@
|
|||||||
import subprocess
|
|
||||||
import requests
|
|
||||||
import random
|
import random
|
||||||
from typing import Any, TypeVar
|
from typing import Any, TypeVar
|
||||||
from urllib.parse import urlparse
|
|
||||||
|
import requests
|
||||||
|
from config import config
|
||||||
from model import OdooModel
|
from model import OdooModel
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=OdooModel)
|
T = TypeVar("T", bound=OdooModel)
|
||||||
|
|
||||||
|
|
||||||
def _get_odoo_session_id() -> str:
|
|
||||||
result = subprocess.run(
|
|
||||||
["actions", "auth", "session", "get", "odoo"],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
lines = result.stdout.splitlines()
|
|
||||||
if len(lines) > 1:
|
|
||||||
lines = lines[1:]
|
|
||||||
session_id = lines[0].strip() if lines else ""
|
|
||||||
if not session_id:
|
|
||||||
raise Exception(
|
|
||||||
"Failed to retrieve odoo session_id via `actions auth session get odoo`"
|
|
||||||
)
|
|
||||||
return session_id
|
|
||||||
|
|
||||||
|
|
||||||
def _set_odoo_session_id(session_id: str) -> None:
|
|
||||||
subprocess.run(
|
|
||||||
["actions", "auth", "session", "set", "odoo", session_id],
|
|
||||||
capture_output=True,
|
|
||||||
text=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OdooClient:
|
class OdooClient:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.base_url = "https://www.odoo.com"
|
self.base_url = "https://www.odoo.com"
|
||||||
self._domain = urlparse(self.base_url).hostname
|
|
||||||
self.session = requests.Session()
|
self.session = requests.Session()
|
||||||
self._session_id = _get_odoo_session_id()
|
|
||||||
self.session.cookies.set("session_id", self._session_id, domain=self._domain)
|
self.session.cookies.set("session_id", config.session_id)
|
||||||
self.session.cookies.set("cids", "1", domain=self._domain)
|
self.session.cookies.set("cids", "1")
|
||||||
self.session.cookies.set("frontend_lang", "en_US", domain=self._domain)
|
self.session.cookies.set("frontend_lang", "en_US")
|
||||||
|
|
||||||
def rpc(
|
def rpc(
|
||||||
self, model: str, method: str, *args: Any, **kwargs: Any
|
self, model: str, method: str, *args: Any, **kwargs: Any
|
||||||
@@ -64,14 +37,6 @@ class OdooClient:
|
|||||||
json=body,
|
json=body,
|
||||||
)
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
|
||||||
new_session_id = self.session.cookies.get(
|
|
||||||
"session_id", domain=self._domain
|
|
||||||
)
|
|
||||||
if new_session_id and new_session_id != self._session_id:
|
|
||||||
_set_odoo_session_id(new_session_id)
|
|
||||||
self._session_id = new_session_id
|
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def web_search_read(
|
def web_search_read(
|
||||||
@@ -82,12 +47,11 @@ class OdooClient:
|
|||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> list[T]:
|
) -> list[T]:
|
||||||
args = [domain, model.specification(), *args]
|
args = [domain, model.specification(), *args]
|
||||||
res = self.rpc(model._name, "web_search_read", *args, **kwargs)
|
response = self.rpc(model._name, "web_search_read", *args, **kwargs)
|
||||||
res.raise_for_status()
|
json_data = response.json()
|
||||||
json = res.json()
|
|
||||||
|
|
||||||
if error := json.get("error"):
|
if error := json_data.get("error"):
|
||||||
raise RuntimeError(f"Odoo RPC Error: {error}")
|
raise RuntimeError(f"Odoo RPC Error: {error}")
|
||||||
|
|
||||||
records = json.get("result", {}).get("records", [])
|
records = json_data.get("result", {}).get("records", [])
|
||||||
return [model.from_record(record) for record in records]
|
return [model.from_record(record) for record in records]
|
||||||
|
|||||||
+28
-44
@@ -1,57 +1,41 @@
|
|||||||
from typing import Optional
|
import json
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import json
|
from typing import Optional
|
||||||
from config import TeamMember
|
|
||||||
|
|
||||||
|
|
||||||
class DispatchStorage:
|
class DispatchStorage:
|
||||||
def load(self, date: Optional[str] = None) -> dict[TeamMember, list[int]]: ...
|
|
||||||
|
|
||||||
def save(
|
|
||||||
self, dispatch: dict[TeamMember, list[int]], date: Optional[str] = None
|
|
||||||
) -> None: ...
|
|
||||||
|
|
||||||
def load_week_to_date(self) -> dict[TeamMember, list[int]]:
|
|
||||||
"""
|
|
||||||
Loads and combines dispatch data from the start of the current week up to today.
|
|
||||||
"""
|
|
||||||
today = datetime.now()
|
|
||||||
days_since_monday = today.weekday()
|
|
||||||
current_date = today - timedelta(days=days_since_monday)
|
|
||||||
|
|
||||||
combined_dispatch: dict[str, list[int]] = {}
|
|
||||||
while current_date.date() < today.date():
|
|
||||||
date_str = current_date.strftime("%Y-%m-%d")
|
|
||||||
daily_dispatch = self.load(date_str)
|
|
||||||
for key, values in daily_dispatch.items():
|
|
||||||
if key in combined_dispatch:
|
|
||||||
combined_dispatch[key].extend(values)
|
|
||||||
else:
|
|
||||||
combined_dispatch[key] = values.copy()
|
|
||||||
current_date += timedelta(days=1)
|
|
||||||
|
|
||||||
return combined_dispatch
|
|
||||||
|
|
||||||
|
|
||||||
class JsonDispatchStorage(DispatchStorage):
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def path(date: Optional[str] = None) -> Path:
|
def path(date: Optional[str] = None) -> Path:
|
||||||
if date is None:
|
if date is None:
|
||||||
date = datetime.now().strftime("%Y-%m-%d")
|
date = datetime.now().strftime("%Y-%m-%d")
|
||||||
return Path("out", f"{date}_dispatch.json")
|
return Path(f"out/{date}_dispatch.json")
|
||||||
|
|
||||||
def load(self, date: Optional[str] = None) -> dict[TeamMember, list[int]]:
|
@staticmethod
|
||||||
try:
|
def load(date: Optional[str] = None) -> dict[str, list[int]]:
|
||||||
with open(self.path(date), "r") as f:
|
path = DispatchStorage.path(date)
|
||||||
return json.load(f)
|
if not path.exists():
|
||||||
except FileNotFoundError:
|
|
||||||
return {}
|
return {}
|
||||||
|
with path.open("r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
def save(
|
@staticmethod
|
||||||
self, dispatch: dict[TeamMember, list[int]], date: Optional[str] = None
|
def save(dispatch: dict[str, list[int]], date: Optional[str] = None) -> None:
|
||||||
) -> None:
|
path = DispatchStorage.path(date)
|
||||||
path = self.path(date)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
path.parent.mkdir(exist_ok=True)
|
with path.open("w", encoding="utf-8") as f:
|
||||||
with open(path, "w") as f:
|
|
||||||
json.dump(dispatch, f)
|
json.dump(dispatch, f)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_week_to_date() -> dict[str, list[int]]:
|
||||||
|
today = datetime.now()
|
||||||
|
start_of_week = today - timedelta(days=today.weekday()) # Previous Monday
|
||||||
|
|
||||||
|
combined_dispatch: dict[str, list[int]] = {}
|
||||||
|
for i in range((today - start_of_week).days + 1):
|
||||||
|
date_str = (start_of_week + timedelta(days=i)).strftime("%Y-%m-%d")
|
||||||
|
daily_dispatch = DispatchStorage.load(date_str)
|
||||||
|
for key, values in daily_dispatch.items():
|
||||||
|
combined_dispatch.setdefault(key, []).extend(values)
|
||||||
|
|
||||||
|
return combined_dispatch
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from tasks import Task
|
|
||||||
|
|
||||||
STAGE_BACKLOG = 194
|
|
||||||
PROJECT_ID = 49
|
|
||||||
|
|
||||||
|
|
||||||
class TaskRepository:
|
|
||||||
def __init__(self, client, vfyd_tags: Iterable[int]):
|
|
||||||
self.client = client
|
|
||||||
self._base_domain = [
|
|
||||||
("stage_id", "=", STAGE_BACKLOG),
|
|
||||||
("project_id", "=", PROJECT_ID),
|
|
||||||
("tag_ids", "not in", list(vfyd_tags)),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _search(self, domain: list) -> list[Task]:
|
|
||||||
return self.client.web_search_read(Task, domain)
|
|
||||||
|
|
||||||
def get_unassigned_tasks(self) -> list[Task]:
|
|
||||||
return self._search(
|
|
||||||
[
|
|
||||||
*self._base_domain,
|
|
||||||
("user_ids", "=", False),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_tasks_with_ids(self, ids: Iterable[int]) -> list[Task]:
|
|
||||||
ids = list(ids)
|
|
||||||
if not ids:
|
|
||||||
return []
|
|
||||||
|
|
||||||
domain = [
|
|
||||||
*self._base_domain,
|
|
||||||
("id", "in", ids),
|
|
||||||
]
|
|
||||||
return self._search(domain)
|
|
||||||
|
|
||||||
def get_unsorted_excluding(self, exclude_ids: Iterable[int]) -> list[Task]:
|
|
||||||
exclude_ids = list(exclude_ids)
|
|
||||||
|
|
||||||
domain = [
|
|
||||||
*self._base_domain,
|
|
||||||
("user_ids", "=", False),
|
|
||||||
]
|
|
||||||
|
|
||||||
if exclude_ids:
|
|
||||||
domain.append(("id", "not in", exclude_ids))
|
|
||||||
|
|
||||||
return self._search(domain)
|
|
||||||
|
|
||||||
def get_unsorted_within(self, ids: Iterable[int]) -> list[Task]:
|
|
||||||
included_ids = list(ids)
|
|
||||||
if not included_ids:
|
|
||||||
return []
|
|
||||||
|
|
||||||
domain = [
|
|
||||||
*self._base_domain,
|
|
||||||
("user_ids", "=", False),
|
|
||||||
("id", "in", included_ids),
|
|
||||||
]
|
|
||||||
|
|
||||||
return self._search(domain)
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
from model import OdooModel
|
from model import OdooModel
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,53 +1,49 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 3
|
revision = 1
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
[options]
|
|
||||||
exclude-newer = "2026-04-17T09:38:00.577575651Z"
|
|
||||||
exclude-newer-span = "P7D"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2025.1.31"
|
version = "2025.1.31"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" },
|
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "charset-normalizer"
|
name = "charset-normalizer"
|
||||||
version = "3.4.1"
|
version = "3.4.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" },
|
{ url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" },
|
{ url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" },
|
{ url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" },
|
{ url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" },
|
{ url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" },
|
{ url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" },
|
{ url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" },
|
{ url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" },
|
{ url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" },
|
{ url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" },
|
{ url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" },
|
{ url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" },
|
{ url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" },
|
{ url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" },
|
{ url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" },
|
{ url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" },
|
{ url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" },
|
{ url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" },
|
{ url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" },
|
{ url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" },
|
{ url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" },
|
{ url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" },
|
{ url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" },
|
{ url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" },
|
{ url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" },
|
{ url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
|
||||||
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" },
|
{ url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -57,27 +53,36 @@ source = { registry = "https://pypi.org/simple" }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload-time = "2024-12-21T18:38:44.339Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" },
|
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "3.10"
|
version = "3.10"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" },
|
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -87,27 +92,69 @@ source = { registry = "https://pypi.org/simple" }
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "mdurl" },
|
{ name = "mdurl" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" },
|
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdurl"
|
name = "mdurl"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packaging"
|
||||||
|
version = "24.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pluggy"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pygments"
|
name = "pygments"
|
||||||
version = "2.19.1"
|
version = "2.19.1"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581, upload-time = "2025-01-06T17:26:30.443Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293, upload-time = "2025-01-06T17:26:25.553Z" },
|
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "8.3.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dotenv"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -120,9 +167,9 @@ dependencies = [
|
|||||||
{ name = "idna" },
|
{ name = "idna" },
|
||||||
{ name = "urllib3" },
|
{ name = "urllib3" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" },
|
{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -133,9 +180,9 @@ dependencies = [
|
|||||||
{ name = "markdown-it-py" },
|
{ name = "markdown-it-py" },
|
||||||
{ name = "pygments" },
|
{ name = "pygments" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149, upload-time = "2024-11-01T16:43:57.873Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424, upload-time = "2024-11-01T16:43:55.817Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -144,6 +191,8 @@ version = "0.1.0"
|
|||||||
source = { virtual = "." }
|
source = { virtual = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "python-dotenv" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
{ name = "rich" },
|
{ name = "rich" },
|
||||||
]
|
]
|
||||||
@@ -151,6 +200,8 @@ dependencies = [
|
|||||||
[package.metadata]
|
[package.metadata]
|
||||||
requires-dist = [
|
requires-dist = [
|
||||||
{ name = "click", specifier = ">=8.1.8" },
|
{ name = "click", specifier = ">=8.1.8" },
|
||||||
|
{ name = "pytest", specifier = ">=8.3.5" },
|
||||||
|
{ name = "python-dotenv", specifier = ">=1.1.0" },
|
||||||
{ name = "requests", specifier = ">=2.32.3" },
|
{ name = "requests", specifier = ">=2.32.3" },
|
||||||
{ name = "rich", specifier = ">=13.9.4" },
|
{ name = "rich", specifier = ">=13.9.4" },
|
||||||
]
|
]
|
||||||
@@ -159,7 +210,7 @@ requires-dist = [
|
|||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" },
|
{ url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user