Compare commits
1 Commits
feat-email
...
fix-issues
| Author | SHA1 | Date | |
|---|---|---|---|
|
d316601e9a
|
29
README.md
29
README.md
@@ -373,26 +373,31 @@ CREATE TABLE IF NOT EXISTS user_preferences (
|
|||||||
2. Convert: `pyside6-uic dialog.ui -o dialog_ui.py`
|
2. Convert: `pyside6-uic dialog.ui -o dialog_ui.py`
|
||||||
3. Create dialog class in `src/ui/dialogs/`
|
3. Create dialog class in `src/ui/dialogs/`
|
||||||
4. Connect signals to business logic
|
4. Connect signals to business logic
|
||||||
### Building Documentation
|
|
||||||
|
|
||||||
```bash
|
## 📚 Documentation
|
||||||
# Using uv
|
|
||||||
uv run mkdocs build
|
|
||||||
uv run mkdocs serve # View at http://localhost:8000
|
|
||||||
|
|
||||||
# Or with activated venv
|
- **[User Manual](docs/)**: Complete user guide built with Zola and the Tanuki theme
|
||||||
mkdocs build
|
- View documentation at `http://localhost:8000` when running the application
|
||||||
mkdocs serve
|
|
||||||
```*[API Documentation](docs/)**: Detailed module documentation
|
|
||||||
- **[User Manual](docs/index.md)**: Complete user guide (MkDocs)
|
|
||||||
|
|
||||||
### Building Documentation
|
### Building Documentation
|
||||||
|
|
||||||
|
The documentation is built using [Zola](https://www.getzola.org/) with the Tanuki theme.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mkdocs build
|
# Build documentation using the provided script
|
||||||
mkdocs serve # View at http://localhost:8000
|
.\build_docs.ps1
|
||||||
|
|
||||||
|
# Or manually:
|
||||||
|
cd docs
|
||||||
|
zola build
|
||||||
|
|
||||||
|
# Serve documentation locally for development
|
||||||
|
cd docs
|
||||||
|
zola serve # View at http://127.0.0.1:1111
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The built documentation is served automatically when you run the application and access the documentation menu.
|
||||||
|
|
||||||
## 🤝 Contributing
|
## 🤝 Contributing
|
||||||
|
|
||||||
Contributions are welcome! Please follow these guidelines:
|
Contributions are welcome! Please follow these guidelines:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from typing import Optional, Any, Union
|
from typing import Optional, Any, Union
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from omegaconf import OmegaConf, DictConfig
|
from omegaconf import OmegaConf, DictConfig, ListConfig
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class Config:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_config: Optional[DictConfig] = None
|
_config: Optional[Union[DictConfig, ListConfig]] = None
|
||||||
config_exists: bool = True
|
config_exists: bool = True
|
||||||
|
|
||||||
def __init__(self, config_path: str):
|
def __init__(self, config_path: str):
|
||||||
@@ -183,22 +183,25 @@ class Config:
|
|||||||
"""
|
"""
|
||||||
Reloads the configuration from the file.
|
Reloads the configuration from the file.
|
||||||
"""
|
"""
|
||||||
self._config = OmegaConf.load(self.config_path)
|
if self.config_path is not None:
|
||||||
|
self._config = OmegaConf.load(self.config_path)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def zotero(self):
|
def zotero(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
return Zotero(**self._config.zotero)
|
return Zotero(**self._config.zotero)
|
||||||
|
|
||||||
@property
|
def get_zotero_attr(self, name: str):
|
||||||
def zotero_attr(self, name: str):
|
|
||||||
return getattr(self.zotero, name)
|
return getattr(self.zotero, name)
|
||||||
|
|
||||||
@zotero_attr.setter
|
def set_zotero_attr(self, name: str, value: Any):
|
||||||
def zotero_attr(self, name: str, value: Any):
|
|
||||||
self.zotero._setattr(name, value)
|
self.zotero._setattr(name, value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def database(self):
|
def database(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
return Database(**self._config.database)
|
return Database(**self._config.database)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -211,43 +214,57 @@ class Config:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def openAI(self):
|
def openAI(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
return OpenAI(**self._config.openAI)
|
return OpenAI(**self._config.openAI)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mail(self):
|
def mail(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
return Mail(**self._config.mail)
|
return Mail(**self._config.mail)
|
||||||
|
|
||||||
def mail_attr(self, name: str):
|
def mail_attr(self, name: str):
|
||||||
return getattr(self.mail, name)
|
return getattr(self.mail, name)
|
||||||
|
|
||||||
def set_mail_attr(self, name: str, value: Any):
|
def set_mail_attr(self, name: str, value: Any):
|
||||||
OmegaConf.update(self._config, f"mail.{name}", value)
|
if self._config is not None:
|
||||||
|
OmegaConf.update(self._config, f"mail.{name}", value)
|
||||||
|
|
||||||
def set_database_attr(self, name: str, value: Any):
|
def set_database_attr(self, name: str, value: Any):
|
||||||
OmegaConf.update(self._config, f"database.{name}", value)
|
if self._config is not None:
|
||||||
|
OmegaConf.update(self._config, f"database.{name}", value)
|
||||||
|
|
||||||
def set_zotero_attr(self, name: str, value: Any):
|
def set_zotero_attr(self, name: str, value: Any):
|
||||||
OmegaConf.update(self._config, f"zotero.{name}", value)
|
if self._config is not None:
|
||||||
|
OmegaConf.update(self._config, f"zotero.{name}", value)
|
||||||
|
|
||||||
def set_openai_attr(self, name: str, value: Any):
|
def set_openai_attr(self, name: str, value: Any):
|
||||||
OmegaConf.update(self._config, f"openAI.{name}", value)
|
if self._config is not None:
|
||||||
|
OmegaConf.update(self._config, f"openAI.{name}", value)
|
||||||
|
|
||||||
def set_icon_attr(self, name: str, value: Any):
|
def set_icon_attr(self, name: str, value: Any):
|
||||||
OmegaConf.update(self._config, f"icons.{name}", value)
|
if self._config is not None:
|
||||||
|
OmegaConf.update(self._config, f"icons.{name}", value)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def save_path(self):
|
def save_path(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
return self._config.save_path
|
return self._config.save_path
|
||||||
|
|
||||||
@save_path.setter
|
@save_path.setter
|
||||||
def save_path(self, value: str):
|
def save_path(self, value: str):
|
||||||
self._config.save_path = value
|
if self._config is not None:
|
||||||
|
self._config.save_path = value
|
||||||
|
|
||||||
def load_config(self, path, filename):
|
def load_config(self, path, filename):
|
||||||
return OmegaConf.load(os.path.join(path, filename))
|
return OmegaConf.load(os.path.join(path, filename))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def icons(self):
|
def icons(self):
|
||||||
|
if self._config is None:
|
||||||
|
raise RuntimeError("Configuration not loaded")
|
||||||
icons = Icons()
|
icons = Icons()
|
||||||
icons.assign("path", self._config.icon_path)
|
icons.assign("path", self._config.icon_path)
|
||||||
icons.assign("colors", self._config.colors)
|
icons.assign("colors", self._config.colors)
|
||||||
|
|||||||
1
docs/themes/tanuki
vendored
1
docs/themes/tanuki
vendored
Submodule docs/themes/tanuki deleted from f81db54c4e
@@ -41,34 +41,14 @@ dev = [
|
|||||||
"pytest",
|
"pytest",
|
||||||
"pytest-cov",
|
"pytest-cov",
|
||||||
"pyinstaller>=6.17.0",
|
"pyinstaller>=6.17.0",
|
||||||
|
"ty>=0.0.15",
|
||||||
]
|
]
|
||||||
swbtest = ["alive-progress>=3.3.0"]
|
swbtest = ["alive-progress>=3.3.0"]
|
||||||
|
|
||||||
[tool.bumpversion]
|
[tool.ruff]
|
||||||
current_version = "1.0.2"
|
line-length = 88
|
||||||
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
|
target-version = "py313"
|
||||||
serialize = ["{major}.{minor}.{patch}"]
|
|
||||||
search = "{current_version}"
|
|
||||||
replace = "{new_version}"
|
|
||||||
regex = false
|
|
||||||
ignore_missing_version = false
|
|
||||||
ignore_missing_files = false
|
|
||||||
tag = true
|
|
||||||
sign_tags = false
|
|
||||||
tag_name = "v{new_version}"
|
|
||||||
tag_message = "Bump version: {current_version} → {new_version}"
|
|
||||||
allow_dirty = true
|
|
||||||
commit = true
|
|
||||||
message = "Bump version: {current_version} → {new_version}"
|
|
||||||
moveable_tags = []
|
|
||||||
commit_args = ""
|
|
||||||
setup_hooks = []
|
|
||||||
pre_commit_hooks = []
|
|
||||||
post_commit_hooks = []
|
|
||||||
[[tool.bumpversion.files]]
|
|
||||||
filename = "src/__init__.py"
|
|
||||||
[[tool.bumpversion.files]]
|
|
||||||
filename = ".version"
|
|
||||||
|
|
||||||
[[tool.uv.index]]
|
[[tool.uv.index]]
|
||||||
name = "gitea"
|
name = "gitea"
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if not _user_log_dir:
|
|||||||
if not _user_config_dir:
|
if not _user_config_dir:
|
||||||
_user_config_dir = str(get_app_base_path() / "config")
|
_user_config_dir = str(get_app_base_path() / "config")
|
||||||
|
|
||||||
|
from config import Config # noqa: E402
|
||||||
|
|
||||||
LOG_DIR: str = _user_log_dir
|
LOG_DIR: str = _user_log_dir
|
||||||
CONFIG_DIR: str = _user_config_dir
|
CONFIG_DIR: str = _user_config_dir
|
||||||
|
|
||||||
@@ -49,8 +51,6 @@ except Exception:
|
|||||||
Path(LOG_DIR).mkdir(parents=True, exist_ok=True)
|
Path(LOG_DIR).mkdir(parents=True, exist_ok=True)
|
||||||
Path(CONFIG_DIR).mkdir(parents=True, exist_ok=True)
|
Path(CONFIG_DIR).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
from config import Config
|
|
||||||
|
|
||||||
|
|
||||||
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
settings = Config(f"{CONFIG_DIR}/config.yaml")
|
||||||
DATABASE_DIR: Union[Path, str] = ( # type: ignore
|
DATABASE_DIR: Union[Path, str] = ( # type: ignore
|
||||||
|
|||||||
@@ -1216,9 +1216,9 @@ class Database:
|
|||||||
Optional[int]: the id of the apparat
|
Optional[int]: the id of the apparat
|
||||||
|
|
||||||
"""
|
"""
|
||||||
log.debug("Creating apparat: {} - {}", app.appnr, app.name)
|
|
||||||
app = apparat.apparat
|
app = apparat.apparat
|
||||||
prof = apparat.prof
|
prof = apparat.prof
|
||||||
|
log.debug("Creating apparat: {} - {}", app.appnr, app.name)
|
||||||
present_prof = self.getProfByName(prof.name())
|
present_prof = self.getProfByName(prof.name())
|
||||||
prof_id = present_prof.id
|
prof_id = present_prof.id
|
||||||
log.debug("Present prof: {}", preview(present_prof, 300))
|
log.debug("Present prof: {}", preview(present_prof, 300))
|
||||||
|
|||||||
@@ -24,5 +24,5 @@ class DocumentationThread(QThread):
|
|||||||
self._process.terminate() # terminate the subprocess
|
self._process.terminate() # terminate the subprocess
|
||||||
try:
|
try:
|
||||||
self._process.wait(timeout=5) # wait up to 5 seconds
|
self._process.wait(timeout=5) # wait up to 5 seconds
|
||||||
except:
|
except Exception:
|
||||||
self._process.kill() # force kill if it doesn't stop
|
self._process.kill() # force kill if it doesn't stop
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from .models import (
|
|||||||
Subjects,
|
Subjects,
|
||||||
XMLMailSubmission,
|
XMLMailSubmission,
|
||||||
)
|
)
|
||||||
from .constants import *
|
from .constants import * # noqa: F403
|
||||||
from .semester import Semester
|
from .semester import Semester
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class Prof:
|
|||||||
telnr: str | None = None
|
telnr: str | None = None
|
||||||
|
|
||||||
# add function that sets the data based on a dict
|
# add function that sets the data based on a dict
|
||||||
def from_dict(self, data: dict[str, Union[str, int]]):
|
def from_dict(self, data: dict[str, Union[str, int]]) -> 'Prof':
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if hasattr(self, key):
|
if hasattr(self, key):
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
@@ -38,27 +38,40 @@ class Prof:
|
|||||||
self._title = value
|
self._title = value
|
||||||
|
|
||||||
# add function that sets the data from a tuple
|
# add function that sets the data from a tuple
|
||||||
def from_tuple(self, data: tuple[Union[str, int], ...]) -> Prof:
|
def from_tuple(self, data: tuple[Union[int, str, None], ...]) -> 'Prof':
|
||||||
self.id = data[0]
|
self.id = data[0] if data[0] is not None and isinstance(data[0], int) else None
|
||||||
self._title = data[1]
|
self._title = str(data[1]) if data[1] is not None else None
|
||||||
self.firstname = data[2]
|
self.firstname = str(data[2]) if data[2] is not None else None
|
||||||
self.lastname = data[3]
|
self.lastname = str(data[3]) if data[3] is not None else None
|
||||||
self.fullname = data[4]
|
self.fullname = str(data[4]) if data[4] is not None else None
|
||||||
self.mail = data[5]
|
self.mail = str(data[5]) if data[5] is not None else None
|
||||||
self.telnr = data[6]
|
self.telnr = str(data[6]) if data[6] is not None else None
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def name(self, comma: bool = False) -> Optional[str]:
|
def name(self, comma: bool = False) -> Optional[str]:
|
||||||
if self.firstname is None and self.lastname is None:
|
if self.firstname is None and self.lastname is None:
|
||||||
if "," in self.fullname:
|
if self.fullname and "," in self.fullname:
|
||||||
self.firstname = self.fullname.split(",")[1].strip()
|
parts = self.fullname.split(",")
|
||||||
self.lastname = self.fullname.split(",")[0].strip()
|
if len(parts) >= 2:
|
||||||
|
self.firstname = parts[1].strip()
|
||||||
|
self.lastname = parts[0].strip()
|
||||||
else:
|
else:
|
||||||
return self.fullname
|
return self.fullname
|
||||||
|
|
||||||
if comma:
|
if comma:
|
||||||
return f"{self.lastname}, {self.firstname}"
|
if self.lastname and self.firstname:
|
||||||
return f"{self.lastname} {self.firstname}"
|
return f"{self.lastname}, {self.firstname}"
|
||||||
|
elif self.lastname:
|
||||||
|
return self.lastname
|
||||||
|
elif self.firstname:
|
||||||
|
return f", {self.firstname}"
|
||||||
|
elif self.lastname and self.firstname:
|
||||||
|
return f"{self.lastname} {self.firstname}"
|
||||||
|
elif self.lastname:
|
||||||
|
return self.lastname
|
||||||
|
elif self.firstname:
|
||||||
|
return self.firstname
|
||||||
|
return self.fullname
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -90,10 +103,12 @@ class BookData:
|
|||||||
if isinstance(self.language, list) and self.language:
|
if isinstance(self.language, list) and self.language:
|
||||||
self.language = [lang.strip() for lang in self.language if lang.strip()]
|
self.language = [lang.strip() for lang in self.language if lang.strip()]
|
||||||
self.language = ",".join(self.language)
|
self.language = ",".join(self.language)
|
||||||
self.year = regex.sub(r"[^\d]", "", str(self.year)) if self.year else None
|
if self.year is not None:
|
||||||
|
year_str = regex.sub(r"[^\d]", "", str(self.year))
|
||||||
|
self.year = int(year_str) if year_str else None
|
||||||
self.in_library = True if self.signature else False
|
self.in_library = True if self.signature else False
|
||||||
|
|
||||||
def from_dict(self, data: dict) -> BookData:
|
def from_dict(self, data: dict[str, Any]) -> 'BookData':
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
return self
|
return self
|
||||||
@@ -132,23 +147,26 @@ class BookData:
|
|||||||
del data_dict["old_book"]
|
del data_dict["old_book"]
|
||||||
return json.dumps(data_dict, ensure_ascii=False)
|
return json.dumps(data_dict, ensure_ascii=False)
|
||||||
|
|
||||||
def from_dataclass(self, dataclass: Optional[Any]) -> None:
|
def from_dataclass(self, data_obj: Optional[Any]) -> None:
|
||||||
if dataclass is None:
|
if data_obj is None:
|
||||||
return
|
return
|
||||||
for key, value in dataclass.__dict__.items():
|
for key, value in data_obj.__dict__.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
|
||||||
def get_book_type(self) -> str:
|
def get_book_type(self) -> str:
|
||||||
if "Online" in self.pages:
|
if self.pages and "Online" in self.pages:
|
||||||
return "eBook"
|
return "eBook"
|
||||||
return "Druckausgabe"
|
return "Druckausgabe"
|
||||||
|
|
||||||
def from_string(self, data: str) -> BookData:
|
def from_string(self, data: str) -> 'BookData':
|
||||||
ndata = json.loads(data)
|
ndata = json.loads(data)
|
||||||
|
# Create a new BookData instance and set its attributes
|
||||||
|
book_data = BookData()
|
||||||
|
for key, value in ndata.items():
|
||||||
|
setattr(book_data, key, value)
|
||||||
|
return book_data
|
||||||
|
|
||||||
return BookData(**ndata)
|
def from_LehmannsSearchResult(self, result: Any) -> 'BookData':
|
||||||
|
|
||||||
def from_LehmannsSearchResult(self, result: Any) -> BookData:
|
|
||||||
self.title = result.title
|
self.title = result.title
|
||||||
self.author = "; ".join(result.authors) if result.authors else None
|
self.author = "; ".join(result.authors) if result.authors else None
|
||||||
self.edition = str(result.edition) if result.edition else None
|
self.edition = str(result.edition) if result.edition else None
|
||||||
@@ -170,7 +188,7 @@ class BookData:
|
|||||||
def edition_number(self) -> Optional[int]:
|
def edition_number(self) -> Optional[int]:
|
||||||
if self.edition is None:
|
if self.edition is None:
|
||||||
return 0
|
return 0
|
||||||
match = regex.search(r"(\d+)", self.edition)
|
match = regex.search(r"(\d+)", self.edition or "")
|
||||||
if match:
|
if match:
|
||||||
return int(match.group(1))
|
return int(match.group(1))
|
||||||
return 0
|
return 0
|
||||||
@@ -215,13 +233,13 @@ class Subjects(Enum):
|
|||||||
return self.value[0]
|
return self.value[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def subject_name(self) -> str:
|
||||||
return self.value[1]
|
return self.value[1]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_index(cls, name: str) -> Optional[int]:
|
def get_index(cls, name: str) -> Optional[int]:
|
||||||
for i in cls:
|
for i in cls:
|
||||||
if i.name == name:
|
if i.subject_name == name:
|
||||||
return i.id - 1
|
return i.id - 1
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -355,25 +373,24 @@ class Book:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SemapDocument:
|
class SemapDocument:
|
||||||
subject: str | None
|
subject: str | None = None
|
||||||
phoneNumber: int | None
|
phoneNumber: int | None = None
|
||||||
mail: str | None
|
mail: str | None = None
|
||||||
title: str | None
|
title: str | None = None
|
||||||
personName: str | None
|
personName: str | None = None
|
||||||
personTitle: str | None
|
personTitle: str | None = None
|
||||||
title_suggestions: list[str] = None
|
title_suggestions: list[str] = field(default_factory=list)
|
||||||
semester: Union[str, Semester] = None
|
semester: Union[str, 'Semester', None] = None
|
||||||
books: list[Book] = None
|
books: list[Book] = field(default_factory=list)
|
||||||
eternal: bool = False
|
eternal: bool = False
|
||||||
title_length: int = 0
|
title_length: int = 0
|
||||||
title_max_length: int = 0
|
title_max_length: int = 0
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
def __post_init__(self) -> None:
|
||||||
"""."""
|
"""."""
|
||||||
self.title_suggestions = []
|
if self.phoneNumber is not None:
|
||||||
self.phoneNumber = int(
|
phone_str = regex.sub(r"[^\d]", "", str(self.phoneNumber))
|
||||||
regex.sub(r"[^\d]", "", str(self.phoneNumber)),
|
self.phoneNumber = int(phone_str) if phone_str else None
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nameSetter(self):
|
def nameSetter(self):
|
||||||
@@ -399,7 +416,7 @@ class SemapDocument:
|
|||||||
def renameSemester(self) -> None:
|
def renameSemester(self) -> None:
|
||||||
from src.services.openai import semester_converter
|
from src.services.openai import semester_converter
|
||||||
|
|
||||||
if self.semester:
|
if self.semester and isinstance(self.semester, str):
|
||||||
if ", Dauer" in self.semester:
|
if ", Dauer" in self.semester:
|
||||||
self.semester = self.semester.split(",")[0]
|
self.semester = self.semester.split(",")[0]
|
||||||
self.eternal = True
|
self.eternal = True
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Semester helper class
|
"""Semester helper class.
|
||||||
|
|
||||||
A small utility around the *German* academic calendar that distinguishes
|
A small utility around the *German* academic calendar that distinguishes
|
||||||
between *Wintersemester* (WiSe) and *Sommersemester* (SoSe).
|
between *Wintersemester* (WiSe) and *Sommersemester* (SoSe).
|
||||||
@@ -7,7 +7,7 @@ Key points
|
|||||||
----------
|
----------
|
||||||
* A **`Semester`** is identified by a *term* ("SoSe" or "WiSe") and the last two
|
* A **`Semester`** is identified by a *term* ("SoSe" or "WiSe") and the last two
|
||||||
digits of the calendar year in which the term *starts*.
|
digits of the calendar year in which the term *starts*.
|
||||||
* Formatting **never** pads the year with a leading zero – so ``6`` stays ``6``.
|
* Formatting **never** pads the year with a leading zero - so ``6`` stays ``6``.
|
||||||
* ``offset(n)`` and the static ``generate_missing`` reliably walk the timeline
|
* ``offset(n)`` and the static ``generate_missing`` reliably walk the timeline
|
||||||
one semester at a time with correct year transitions:
|
one semester at a time with correct year transitions:
|
||||||
|
|
||||||
@@ -26,13 +26,13 @@ class Semester:
|
|||||||
"""Represents a German university semester (WiSe or SoSe)."""
|
"""Represents a German university semester (WiSe or SoSe)."""
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Class‑level defaults – will be *copied* to each instance and then
|
# Class-level defaults - will be *copied* to each instance and then
|
||||||
# potentially overwritten in ``__init__``.
|
# potentially overwritten in ``__init__``.
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
_year: int | None = None # Will be set in __post_init__
|
_year: int | None = None # Will be set in __post_init__
|
||||||
_semester: str | None = None # "WiSe" or "SoSe" – set later
|
_semester: str | None = None # "WiSe" or "SoSe" - set later
|
||||||
_month: int | None = None # Will be set in __post_init__
|
_month: int | None = None # Will be set in __post_init__
|
||||||
value: str | None = None # Human‑readable label, e.g. "WiSe 23/24"
|
value: str | None = None # Human-readable label, e.g. "WiSe 23/24"
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Construction helpers
|
# Construction helpers
|
||||||
@@ -89,17 +89,22 @@ class Semester:
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def _generate_semester_from_month(self) -> None:
|
def _generate_semester_from_month(self) -> None:
|
||||||
"""Infer *WiSe* / *SoSe* from the month attribute."""
|
"""Infer *WiSe* / *SoSe* from the month attribute."""
|
||||||
self._semester = "WiSe" if (self._month <= 3 or self._month > 9) else "SoSe"
|
if self._month is not None:
|
||||||
|
self._semester = "WiSe" if (self._month <= 3 or self._month > 9) else "SoSe"
|
||||||
|
else:
|
||||||
|
self._semester = "WiSe" # Default value if month is None
|
||||||
|
|
||||||
def _compute_value(self) -> None:
|
def _compute_value(self) -> None:
|
||||||
"""Human‑readable semester label – e.g. ``WiSe 23/24`` or ``SoSe 24``."""
|
"""Human-readable semester label - e.g. ``WiSe 23/24`` or ``SoSe 24``."""
|
||||||
year = self._year
|
if self._year is not None:
|
||||||
if self._semester == "WiSe":
|
year = self._year
|
||||||
next_year = (year + 1) % 100 # wrap 99 → 0
|
if self._semester == "WiSe":
|
||||||
|
next_year = (year + 1) % 100 # wrap 99 → 0
|
||||||
self.value = f"WiSe {year}/{next_year}"
|
self.value = f"WiSe {year}/{next_year}"
|
||||||
else: # SoSe
|
else: # SoSe
|
||||||
self.value = f"SoSe {year}"
|
self.value = f"SoSe {year}"
|
||||||
|
else:
|
||||||
|
self.value = "<invalid Semester>"
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# Public API
|
# Public API
|
||||||
@@ -117,10 +122,12 @@ class Semester:
|
|||||||
if value == 0:
|
if value == 0:
|
||||||
return Semester(self._year, self._semester)
|
return Semester(self._year, self._semester)
|
||||||
|
|
||||||
|
if self._year is None:
|
||||||
|
raise ValueError("Cannot offset from a semester with no year")
|
||||||
current_idx = self._year * 2 + (0 if self._semester == "SoSe" else 1)
|
current_idx = self._year * 2 + (0 if self._semester == "SoSe" else 1)
|
||||||
target_idx = current_idx + value
|
target_idx = current_idx + value
|
||||||
if target_idx < 0:
|
if target_idx < 0:
|
||||||
raise ValueError("offset would result in a negative year – not supported")
|
raise ValueError("offset would result in a negative year - not supported")
|
||||||
|
|
||||||
new_year, semester_bit = divmod(target_idx, 2)
|
new_year, semester_bit = divmod(target_idx, 2)
|
||||||
new_semester = "SoSe" if semester_bit == 0 else "WiSe"
|
new_semester = "SoSe" if semester_bit == 0 else "WiSe"
|
||||||
@@ -164,10 +171,14 @@ class Semester:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def year(self) -> int:
|
def year(self) -> int:
|
||||||
|
if self._year is None:
|
||||||
|
raise ValueError("Year is not set for this semester")
|
||||||
return self._year
|
return self._year
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def semester(self) -> str:
|
def semester(self) -> str:
|
||||||
|
if self._semester is None:
|
||||||
|
raise ValueError("Semester is not set for this semester")
|
||||||
return self._semester
|
return self._semester
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@@ -178,14 +189,14 @@ class Semester:
|
|||||||
"""Return all consecutive semesters from *start* to *end* (inclusive)."""
|
"""Return all consecutive semesters from *start* to *end* (inclusive)."""
|
||||||
if not isinstance(start, Semester) or not isinstance(end, Semester):
|
if not isinstance(start, Semester) or not isinstance(end, Semester):
|
||||||
raise TypeError("start and end must be Semester instances")
|
raise TypeError("start and end must be Semester instances")
|
||||||
if start.is_future_semester(end) and not start.isMatch(end):
|
if start.is_future_semester(end) and not start.is_match(end):
|
||||||
raise ValueError("'start' must not be after 'end'")
|
raise ValueError("'start' must not be after 'end'")
|
||||||
|
|
||||||
chain: list[Semester] = [start.value]
|
chain: list[str] = [str(start)]
|
||||||
current = start
|
current = start
|
||||||
while not current.isMatch(end):
|
while not current.is_match(end):
|
||||||
current = current.next
|
current = current.next
|
||||||
chain.append(current.value)
|
chain.append(str(current))
|
||||||
if len(chain) > 1000: # sanity guard
|
if len(chain) > 1000: # sanity guard
|
||||||
raise RuntimeError("generate_missing exceeded sane iteration limit")
|
raise RuntimeError("generate_missing exceeded sane iteration limit")
|
||||||
return chain
|
return chain
|
||||||
@@ -195,9 +206,9 @@ class Semester:
|
|||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_string(cls, s: str) -> Semester:
|
def from_string(cls, s: str) -> Semester:
|
||||||
"""Parse a human‑readable semester label and return a :class:`Semester`.
|
"""Parse a human-readable semester label and return a :class:`Semester`.
|
||||||
|
|
||||||
Accepted formats (case‑insensitive)::
|
Accepted formats (case-insensitive)::
|
||||||
|
|
||||||
"SoSe <YY>" → SoSe of year YY
|
"SoSe <YY>" → SoSe of year YY
|
||||||
"WiSe <YY>/<YY+1>" → Winter term starting in YY
|
"WiSe <YY>/<YY+1>" → Winter term starting in YY
|
||||||
@@ -212,7 +223,7 @@ class Semester:
|
|||||||
m = re.fullmatch(pattern, s, flags=re.IGNORECASE)
|
m = re.fullmatch(pattern, s, flags=re.IGNORECASE)
|
||||||
if not m:
|
if not m:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
"invalid semester string format – expected 'SoSe YY' or 'WiSe YY/YY' (spacing flexible)",
|
"invalid semester string format - expected 'SoSe YY' or 'WiSe YY/YY' (spacing flexible)",
|
||||||
)
|
)
|
||||||
|
|
||||||
term_raw, y1_str, y2_str = m.groups()
|
term_raw, y1_str, y2_str = m.groups()
|
||||||
@@ -236,7 +247,7 @@ class Semester:
|
|||||||
return cls(year, "WiSe")
|
return cls(year, "WiSe")
|
||||||
|
|
||||||
|
|
||||||
# ------------------------- quick self‑test -------------------------
|
# ------------------------- quick self-test -------------------------
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Chain generation demo ------------------------------------------------
|
# Chain generation demo ------------------------------------------------
|
||||||
s_start = Semester(6, "SoSe") # SoSe 6
|
s_start = Semester(6, "SoSe") # SoSe 6
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ class Database:
|
|||||||
try:
|
try:
|
||||||
if self.db_path is not None:
|
if self.db_path is not None:
|
||||||
self.run_migrations()
|
self.run_migrations()
|
||||||
except Exception as e:
|
except (sql.Error, OSError, IOError) as e:
|
||||||
log.error(f"Error while running migrations: {e}")
|
log.error(f"Error while running migrations: {e}")
|
||||||
|
|
||||||
# --- Migration helpers integrated into Database ---
|
# --- Migration helpers integrated into Database ---
|
||||||
@@ -212,9 +212,9 @@ class Database:
|
|||||||
).__str__()
|
).__str__()
|
||||||
return result[0]
|
return result[0]
|
||||||
|
|
||||||
def getElsaMediaType(self, id):
|
def getElsaMediaType(self, media_id):
|
||||||
query = "SELECT type FROM elsa_media WHERE id=?"
|
query = "SELECT type FROM elsa_media WHERE id=?"
|
||||||
return self.query_db(query, (id,), one=True)[0]
|
return self.query_db(query, (media_id,), one=True)[0]
|
||||||
|
|
||||||
def get_db_contents(self) -> Union[List[Tuple[Any]], None]:
|
def get_db_contents(self) -> Union[List[Tuple[Any]], None]:
|
||||||
"""
|
"""
|
||||||
@@ -736,7 +736,7 @@ class Database:
|
|||||||
try:
|
try:
|
||||||
bloat.debug("Recreated file blob size: {} bytes", len(blob))
|
bloat.debug("Recreated file blob size: {} bytes", len(blob))
|
||||||
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
||||||
except Exception:
|
except (TypeError, UnicodeDecodeError, ValueError):
|
||||||
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
bloat.debug("Recreated file blob (preview): {}", preview(blob, 2000))
|
||||||
tempdir = settings.database.temp.expanduser()
|
tempdir = settings.database.temp.expanduser()
|
||||||
if not tempdir.exists():
|
if not tempdir.exists():
|
||||||
@@ -990,16 +990,16 @@ class Database:
|
|||||||
person = Prof()
|
person = Prof()
|
||||||
return person.from_tuple(data)
|
return person.from_tuple(data)
|
||||||
|
|
||||||
def getProf(self, id) -> Prof:
|
def getProf(self, prof_id) -> Prof:
|
||||||
"""Get a professor based on the id
|
"""Get a professor based on the id
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
id ([type]): the id of the professor
|
prof_id ([type]): the id of the professor
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Prof: a Prof object containing the data of the professor
|
Prof: a Prof object containing the data of the professor
|
||||||
"""
|
"""
|
||||||
data = self.query_db("SELECT * FROM prof WHERE id=?", (id,), one=True)
|
data = self.query_db("SELECT * FROM prof WHERE id=?", (prof_id,), one=True)
|
||||||
return Prof().from_tuple(data)
|
return Prof().from_tuple(data)
|
||||||
|
|
||||||
def getProfs(self) -> list[Prof]:
|
def getProfs(self) -> list[Prof]:
|
||||||
@@ -1278,17 +1278,17 @@ class Database:
|
|||||||
# print(apparat_nr, app_id)
|
# print(apparat_nr, app_id)
|
||||||
self.query_db("UPDATE media SET deleted=1 WHERE app_id=?", (app_id,))
|
self.query_db("UPDATE media SET deleted=1 WHERE app_id=?", (app_id,))
|
||||||
|
|
||||||
def isEternal(self, id):
|
def isEternal(self, apparat_id):
|
||||||
"""check if the apparat is eternal (dauerapparat)
|
"""check if the apparat is eternal (dauerapparat)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
id (int): the id of the apparat to be checked
|
apparat_id (int): the id of the apparat to be checked
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
int: the state of the apparat
|
int: the state of the apparat
|
||||||
"""
|
"""
|
||||||
return self.query_db(
|
return self.query_db(
|
||||||
"SELECT dauer FROM semesterapparat WHERE appnr=?", (id,), one=True
|
"SELECT dauer FROM semesterapparat WHERE appnr=?", (apparat_id,), one=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def getApparatName(self, app_id: Union[str, int], prof_id: Union[str, int]):
|
def getApparatName(self, app_id: Union[str, int], prof_id: Union[str, int]):
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from docx import Document
|
from docx import Document
|
||||||
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
||||||
@@ -69,20 +70,20 @@ class SemesterDocument:
|
|||||||
full: bool = False,
|
full: bool = False,
|
||||||
):
|
):
|
||||||
assert isinstance(apparats, list), SemesterError(
|
assert isinstance(apparats, list), SemesterError(
|
||||||
"Apparats must be a list of tuples"
|
"Apparats must be a list of tuples",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
||||||
"Apparats must be a list of tuples"
|
"Apparats must be a list of tuples",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
||||||
"Apparat numbers must be integers"
|
"Apparat numbers must be integers",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
||||||
"Apparat names must be strings"
|
"Apparat names must be strings",
|
||||||
)
|
)
|
||||||
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
||||||
assert "." not in filename and isinstance(filename, str), SemesterError(
|
assert "." not in filename and isinstance(filename, str), SemesterError(
|
||||||
"Filename must be a string and not contain an extension"
|
"Filename must be a string and not contain an extension",
|
||||||
)
|
)
|
||||||
self.doc = Document()
|
self.doc = Document()
|
||||||
self.apparats = apparats
|
self.apparats = apparats
|
||||||
@@ -108,8 +109,7 @@ class SemesterDocument:
|
|||||||
log.info("Document printed")
|
log.info("Document printed")
|
||||||
|
|
||||||
def set_table_border(self, table):
|
def set_table_border(self, table):
|
||||||
"""
|
"""Adds a full border to the table.
|
||||||
Adds a full border to the table.
|
|
||||||
|
|
||||||
:param table: Table object to which the border will be applied.
|
:param table: Table object to which the border will be applied.
|
||||||
"""
|
"""
|
||||||
@@ -150,7 +150,8 @@ class SemesterDocument:
|
|||||||
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
||||||
trHeight = OxmlElement("w:trHeight")
|
trHeight = OxmlElement("w:trHeight")
|
||||||
trHeight.set(
|
trHeight.set(
|
||||||
qn("w:val"), str(int(Pt(15).pt * 20))
|
qn("w:val"),
|
||||||
|
str(int(Pt(15).pt * 20)),
|
||||||
) # Convert points to twips
|
) # Convert points to twips
|
||||||
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
||||||
trPr.append(trHeight)
|
trPr.append(trHeight)
|
||||||
@@ -233,7 +234,7 @@ class SemesterDocument:
|
|||||||
self.save_document(self.filename + ".docx")
|
self.save_document(self.filename + ".docx")
|
||||||
docpath = os.path.abspath(self.filename + ".docx")
|
docpath = os.path.abspath(self.filename + ".docx")
|
||||||
doc = word.Documents.Open(docpath)
|
doc = word.Documents.Open(docpath)
|
||||||
curdir = os.getcwd()
|
curdir = Path.cwd()
|
||||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||||
doc.Close()
|
doc.Close()
|
||||||
word.Quit()
|
word.Quit()
|
||||||
@@ -317,7 +318,7 @@ class SemapSchilder:
|
|||||||
self.save_document()
|
self.save_document()
|
||||||
docpath = os.path.abspath(f"{self.filename}.docx")
|
docpath = os.path.abspath(f"{self.filename}.docx")
|
||||||
doc = word.Documents.Open(docpath)
|
doc = word.Documents.Open(docpath)
|
||||||
curdir = os.getcwd()
|
curdir = Path.cwd()
|
||||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||||
doc.Close()
|
doc.Close()
|
||||||
word.Quit()
|
word.Quit()
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
# import basic error classes
|
# import basic error classes
|
||||||
from .DatabaseErrors import *
|
from .DatabaseErrors import * # noqa: F403
|
||||||
|
|||||||
@@ -6,3 +6,11 @@ from .transformers import (
|
|||||||
RDSData,
|
RDSData,
|
||||||
RISData,
|
RISData,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Explicit re-exports to avoid F401 warnings
|
||||||
|
RDS_AVAIL_DATA = RDS_AVAIL_DATA
|
||||||
|
ARRAYData = ARRAYData
|
||||||
|
BibTeXData = BibTeXData
|
||||||
|
COinSData = COinSData
|
||||||
|
RDSData = RDSData
|
||||||
|
RISData = RISData
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class ARRAYData:
|
|||||||
source = source.replace("\t", "").replace("\r", "")
|
source = source.replace("\t", "").replace("\r", "")
|
||||||
source = source.split(search)[1].split(")")[0]
|
source = source.split(search)[1].split(")")[0]
|
||||||
return _get_line(source, entry).replace("=>", "").strip()
|
return _get_line(source, entry).replace("=>", "").strip()
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _get_isbn(source: str) -> list:
|
def _get_isbn(source: str) -> list:
|
||||||
@@ -157,7 +157,7 @@ class ARRAYData:
|
|||||||
continue
|
continue
|
||||||
ret.append(isb) if isb not in ret else None
|
ret.append(isb) if isb not in ret else None
|
||||||
return ret
|
return ret
|
||||||
except:
|
except Exception:
|
||||||
isbn = []
|
isbn = []
|
||||||
return isbn
|
return isbn
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ class COinSData:
|
|||||||
try:
|
try:
|
||||||
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
||||||
return data.split("rft")[0].strip() if "rft" in data else data
|
return data.split("rft")[0].strip() if "rft" in data else data
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
@@ -319,7 +319,7 @@ class RISData:
|
|||||||
try:
|
try:
|
||||||
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
||||||
return data.split("\n")[0].strip() if "\n" in data else data
|
return data.split("\n")[0].strip() if "\n" in data else data
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
@@ -356,7 +356,7 @@ class BibTeXData:
|
|||||||
.replace("[", "")
|
.replace("[", "")
|
||||||
.replace("];", "")
|
.replace("];", "")
|
||||||
)
|
)
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ from ratelimit import limits, sleep_and_retry
|
|||||||
|
|
||||||
from src.core.models import BookData
|
from src.core.models import BookData
|
||||||
from src.shared.logging import log, get_bloat_logger, preview
|
from src.shared.logging import log, get_bloat_logger, preview
|
||||||
|
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
||||||
|
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
||||||
|
|
||||||
# bloat logger for large/raw HTTP responses
|
# bloat logger for large/raw HTTP responses
|
||||||
bloat = get_bloat_logger()
|
bloat = get_bloat_logger()
|
||||||
from src.transformers import ARRAYData, BibTeXData, COinSData, RDSData, RISData
|
|
||||||
from src.transformers.transformers import RDS_AVAIL_DATA, RDS_GENERIC_DATA
|
|
||||||
|
|
||||||
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
# logger.add(sys.stderr, format="{time} {level} {message}", level="INFO")
|
||||||
|
|
||||||
|
|||||||
@@ -6,3 +6,11 @@ from .transformers import (
|
|||||||
RDSData,
|
RDSData,
|
||||||
RISData,
|
RISData,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Explicit re-exports to avoid F401 warnings
|
||||||
|
RDS_AVAIL_DATA = RDS_AVAIL_DATA
|
||||||
|
ARRAYData = ARRAYData
|
||||||
|
BibTeXData = BibTeXData
|
||||||
|
COinSData = COinSData
|
||||||
|
RDSData = RDSData
|
||||||
|
RISData = RISData
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ class ARRAYData:
|
|||||||
source = source.replace("\t", "").replace("\r", "")
|
source = source.replace("\t", "").replace("\r", "")
|
||||||
source = source.split(search)[1].split(")")[0]
|
source = source.split(search)[1].split(")")[0]
|
||||||
return _get_line(source, entry).replace("=>", "").strip()
|
return _get_line(source, entry).replace("=>", "").strip()
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _get_isbn(source: str) -> list:
|
def _get_isbn(source: str) -> list:
|
||||||
@@ -157,7 +157,7 @@ class ARRAYData:
|
|||||||
continue
|
continue
|
||||||
ret.append(isb) if isb not in ret else None
|
ret.append(isb) if isb not in ret else None
|
||||||
return ret
|
return ret
|
||||||
except:
|
except Exception:
|
||||||
isbn = []
|
isbn = []
|
||||||
return isbn
|
return isbn
|
||||||
|
|
||||||
@@ -294,7 +294,7 @@ class COinSData:
|
|||||||
try:
|
try:
|
||||||
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
data = source.split(f"{search}=")[1] # .split("")[0].strip()
|
||||||
return data.split("rft")[0].strip() if "rft" in data else data
|
return data.split("rft")[0].strip() if "rft" in data else data
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
@@ -319,7 +319,7 @@ class RISData:
|
|||||||
try:
|
try:
|
||||||
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
data = source.split(f"{search} - ")[1] # .split("")[0].strip()
|
||||||
return data.split("\n")[0].strip() if "\n" in data else data
|
return data.split("\n")[0].strip() if "\n" in data else data
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
@@ -356,7 +356,7 @@ class BibTeXData:
|
|||||||
.replace("[", "")
|
.replace("[", "")
|
||||||
.replace("];", "")
|
.replace("];", "")
|
||||||
)
|
)
|
||||||
except:
|
except Exception:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
return BookData(
|
return BookData(
|
||||||
|
|||||||
@@ -1,32 +1,35 @@
|
|||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
from .semesterapparat_ui_ui import Ui_MainWindow as Ui_Semesterapparat
|
from .semesterapparat_ui_ui import Ui_MainWindow as Ui_Semesterapparat
|
||||||
|
|
||||||
# from .dialogs import (
|
# Explicit re-export to avoid F401 warnings
|
||||||
# ApparatExtendDialog,
|
Ui_Semesterapparat = Ui_Semesterapparat
|
||||||
# Mail_Dialog,
|
|
||||||
# Settings,
|
# from .dialogs import (
|
||||||
# edit_bookdata_ui,
|
# ApparatExtendDialog,
|
||||||
# login_ui,
|
# Mail_Dialog,
|
||||||
# medienadder_ui,
|
# Settings,
|
||||||
# parsed_titles_ui,
|
# edit_bookdata_ui,
|
||||||
# popus_confirm,
|
# login_ui,
|
||||||
# reminder_ui,
|
# medienadder_ui,
|
||||||
# About,
|
# parsed_titles_ui,
|
||||||
# ElsaAddEntry,
|
# popus_confirm,
|
||||||
# )
|
# reminder_ui,
|
||||||
# from .widgets import (
|
# About,
|
||||||
# FilePicker,
|
# ElsaAddEntry,
|
||||||
# StatusWidget,
|
# )
|
||||||
# CalendarEntry,
|
# from .widgets import (
|
||||||
# MessageCalendar,
|
# FilePicker,
|
||||||
# SearchStatisticPage, #
|
# StatusWidget,
|
||||||
# DataGraph,
|
# CalendarEntry,
|
||||||
# ElsaDialog,
|
# MessageCalendar,
|
||||||
# UserCreate,
|
# SearchStatisticPage, #
|
||||||
# EditUser,
|
# DataGraph,
|
||||||
# EditProf,
|
# ElsaDialog,
|
||||||
# )
|
# UserCreate,
|
||||||
path = pathlib.Path(__file__).parent.absolute()
|
# EditUser,
|
||||||
# from .mainwindow import Ui_MainWindow as Ui_MainWindow
|
# EditProf,
|
||||||
# from .sap import Ui_MainWindow as MainWindow_SAP
|
# )
|
||||||
|
path = pathlib.Path(__file__).parent.absolute()
|
||||||
|
# from .mainwindow import Ui_MainWindow as Ui_MainWindow
|
||||||
|
# from .sap import Ui_MainWindow as MainWindow_SAP
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
from .newMailTemplateDesigner_ui import Ui_Dialog as NewMailTemplateDesignerDialog
|
from .newMailTemplateDesigner_ui import Ui_Dialog as NewMailTemplateDesignerDialog
|
||||||
|
|
||||||
|
# Explicit re-export to avoid F401 warnings
|
||||||
|
NewMailTemplateDesignerDialog = NewMailTemplateDesignerDialog
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ from src.services.lehmanns import LehmannsClient
|
|||||||
from src.services.sru import SWB
|
from src.services.sru import SWB
|
||||||
|
|
||||||
|
|
||||||
|
def filter_prefer_swb(response):
|
||||||
|
"""Filter function to prefer SWB results when available."""
|
||||||
|
# This is a placeholder implementation - adjust based on actual requirements
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
class CheckThread(QtCore.QThread):
|
class CheckThread(QtCore.QThread):
|
||||||
updateSignal = QtCore.Signal()
|
updateSignal = QtCore.Signal()
|
||||||
total_entries_signal = QtCore.Signal(int)
|
total_entries_signal = QtCore.Signal(int)
|
||||||
|
|||||||
@@ -180,7 +180,8 @@ class Ui(QtWidgets.QMainWindow, Ui_Semesterapparat):
|
|||||||
# self.update_app_media_list()
|
# self.update_app_media_list()
|
||||||
self.populate_prof_dropdown()
|
self.populate_prof_dropdown()
|
||||||
self.populate_appfach_dropdown()
|
self.populate_appfach_dropdown()
|
||||||
# if the focus is changed from the prof name dropdown, set the prof data if the prof exists in the database, otherwise show a message
|
# if the focus is changed from the prof name dropdown, set the prof data if the
|
||||||
|
# prof exists in the database, otherwise show a message
|
||||||
self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data) # type:ignore
|
self.drpdwn_prof_name.currentIndexChanged.connect(self.set_prof_data) # type:ignore
|
||||||
self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection) # type:ignore
|
self.cancel_active_selection.clicked.connect(self.btn_cancel_active_selection) # type:ignore
|
||||||
self.check_eternal_app.stateChanged.connect(self.set_state) # type:ignore
|
self.check_eternal_app.stateChanged.connect(self.set_state) # type:ignore
|
||||||
|
|||||||
@@ -4,15 +4,36 @@ from src.database import Database
|
|||||||
from src.utils.icon import Icon
|
from src.utils.icon import Icon
|
||||||
|
|
||||||
|
|
||||||
class AdminQueryWidget(QtWidgets.QWidget, Ui_Form):
|
class AdminQueryWidget(QtWidgets.QWidget):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setupUi(self)
|
self.setupUi()
|
||||||
self.setWindowIcon(Icon("db_search").icon)
|
self.setWindowIcon(Icon("db_search").icon)
|
||||||
self.db = Database()
|
self.db = Database()
|
||||||
# Connect the button click to the method
|
# Connect the button click to the method
|
||||||
self.sendquery.clicked.connect(self.on_pushButton_clicked)
|
self.sendquery.clicked.connect(self.on_pushButton_clicked)
|
||||||
|
|
||||||
|
def setupUi(self):
|
||||||
|
# Create the layout and widgets
|
||||||
|
layout = QtWidgets.QVBoxLayout(self)
|
||||||
|
|
||||||
|
# Create SQL query input area
|
||||||
|
self.sqlquery = QtWidgets.QTextEdit()
|
||||||
|
self.sqlquery.setPlaceholderText("Enter SQL query here...")
|
||||||
|
|
||||||
|
# Create execute button
|
||||||
|
self.sendquery = QtWidgets.QPushButton("Execute Query")
|
||||||
|
|
||||||
|
# Create results table
|
||||||
|
self.queryResult = QtWidgets.QTableWidget()
|
||||||
|
self.queryResult.setColumnCount(5) # Adjust as needed
|
||||||
|
self.queryResult.setHorizontalHeaderLabels(["Column 1", "Column 2", "Column 3", "Column 4", "Column 5"])
|
||||||
|
|
||||||
|
# Add widgets to layout
|
||||||
|
layout.addWidget(self.sqlquery)
|
||||||
|
layout.addWidget(self.sendquery)
|
||||||
|
layout.addWidget(self.queryResult)
|
||||||
|
|
||||||
def on_pushButton_clicked(self):
|
def on_pushButton_clicked(self):
|
||||||
# Handle button click event
|
# Handle button click event
|
||||||
self.queryResult.setRowCount(0) # Clear previous results
|
self.queryResult.setRowCount(0) # Clear previous results
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
|
"""Utilities for managing documentation server functionality."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from src import LOG_DIR
|
from src import LOG_DIR
|
||||||
|
|
||||||
log_path = os.path.join(LOG_DIR, "web_documentation.log")
|
log_path = Path(LOG_DIR) / "web_documentation.log"
|
||||||
|
|
||||||
# Replace the default StreamHandler with a FileHandler
|
# Replace the default StreamHandler with a FileHandler
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -19,13 +21,12 @@ logger = logging.getLogger(__name__) # inherits the same file handler
|
|||||||
docport = 8000
|
docport = 8000
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def start_documentation_server():
|
def start_documentation_server():
|
||||||
"""
|
"""Start the Zensical documentation server as a subprocess.
|
||||||
Start the Zensical documentation server as a subprocess.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
subprocess.Popen: The subprocess object, or None if startup failed.
|
subprocess.Popen: The subprocess object, or None if startup failed.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Prepare subprocess arguments
|
# Prepare subprocess arguments
|
||||||
@@ -33,7 +34,7 @@ def start_documentation_server():
|
|||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
# Hide console window on Windows
|
# Hide console window on Windows
|
||||||
creationflags = subprocess.CREATE_NO_WINDOW
|
creationflags = subprocess.CREATE_NO_WINDOW
|
||||||
|
|
||||||
# Start subprocess with all output suppressed
|
# Start subprocess with all output suppressed
|
||||||
process = subprocess.Popen(
|
process = subprocess.Popen(
|
||||||
["uv", "run", "zensical", "serve"],
|
["uv", "run", "zensical", "serve"],
|
||||||
@@ -41,12 +42,12 @@ def start_documentation_server():
|
|||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL,
|
||||||
stdin=subprocess.DEVNULL,
|
stdin=subprocess.DEVNULL,
|
||||||
creationflags=creationflags,
|
creationflags=creationflags,
|
||||||
cwd=os.getcwd(),
|
cwd=Path.cwd(),
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"Documentation server started with PID {process.pid}")
|
logger.info(f"Documentation server started with PID {process.pid}")
|
||||||
return process
|
return process
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to start documentation server: {e}")
|
logger.error(f"Failed to start documentation server: {e}")
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from os.path import basename
|
from os.path import basename
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from docx import Document
|
from docx import Document
|
||||||
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
||||||
@@ -69,20 +70,20 @@ class SemesterDocument:
|
|||||||
full: bool = False,
|
full: bool = False,
|
||||||
):
|
):
|
||||||
assert isinstance(apparats, list), SemesterError(
|
assert isinstance(apparats, list), SemesterError(
|
||||||
"Apparats must be a list of tuples"
|
"Apparats must be a list of tuples",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat, tuple) for apparat in apparats), SemesterError(
|
||||||
"Apparats must be a list of tuples"
|
"Apparats must be a list of tuples",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat[0], int) for apparat in apparats), SemesterError(
|
||||||
"Apparat numbers must be integers"
|
"Apparat numbers must be integers",
|
||||||
)
|
)
|
||||||
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
assert all(isinstance(apparat[1], str) for apparat in apparats), SemesterError(
|
||||||
"Apparat names must be strings"
|
"Apparat names must be strings",
|
||||||
)
|
)
|
||||||
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
assert isinstance(semester, str), SemesterError("Semester must be a string")
|
||||||
assert "." not in filename and isinstance(filename, str), SemesterError(
|
assert "." not in filename and isinstance(filename, str), SemesterError(
|
||||||
"Filename must be a string and not contain an extension"
|
"Filename must be a string and not contain an extension",
|
||||||
)
|
)
|
||||||
self.doc = Document()
|
self.doc = Document()
|
||||||
self.apparats = apparats
|
self.apparats = apparats
|
||||||
@@ -108,8 +109,7 @@ class SemesterDocument:
|
|||||||
log.info("Document printed")
|
log.info("Document printed")
|
||||||
|
|
||||||
def set_table_border(self, table):
|
def set_table_border(self, table):
|
||||||
"""
|
"""Adds a full border to the table.
|
||||||
Adds a full border to the table.
|
|
||||||
|
|
||||||
:param table: Table object to which the border will be applied.
|
:param table: Table object to which the border will be applied.
|
||||||
"""
|
"""
|
||||||
@@ -150,7 +150,8 @@ class SemesterDocument:
|
|||||||
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
trPr = row._tr.get_or_add_trPr() # Get or add the <w:trPr> element
|
||||||
trHeight = OxmlElement("w:trHeight")
|
trHeight = OxmlElement("w:trHeight")
|
||||||
trHeight.set(
|
trHeight.set(
|
||||||
qn("w:val"), str(int(Pt(15).pt * 20))
|
qn("w:val"),
|
||||||
|
str(int(Pt(15).pt * 20)),
|
||||||
) # Convert points to twips
|
) # Convert points to twips
|
||||||
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
trHeight.set(qn("w:hRule"), "exact") # Use "exact" for fixed height
|
||||||
trPr.append(trHeight)
|
trPr.append(trHeight)
|
||||||
@@ -233,7 +234,7 @@ class SemesterDocument:
|
|||||||
self.save_document(self.filename + ".docx")
|
self.save_document(self.filename + ".docx")
|
||||||
docpath = os.path.abspath(self.filename + ".docx")
|
docpath = os.path.abspath(self.filename + ".docx")
|
||||||
doc = word.Documents.Open(docpath)
|
doc = word.Documents.Open(docpath)
|
||||||
curdir = os.getcwd()
|
curdir = Path.cwd()
|
||||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||||
doc.Close()
|
doc.Close()
|
||||||
word.Quit()
|
word.Quit()
|
||||||
@@ -317,7 +318,7 @@ class SemapSchilder:
|
|||||||
self.save_document()
|
self.save_document()
|
||||||
docpath = os.path.abspath(f"{self.filename}.docx")
|
docpath = os.path.abspath(f"{self.filename}.docx")
|
||||||
doc = word.Documents.Open(docpath)
|
doc = word.Documents.Open(docpath)
|
||||||
curdir = os.getcwd()
|
curdir = Path.cwd()
|
||||||
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
doc.SaveAs(f"{curdir}/{self.filename}.pdf", FileFormat=17)
|
||||||
doc.Close()
|
doc.Close()
|
||||||
word.Quit()
|
word.Quit()
|
||||||
|
|||||||
14
test.py
14
test.py
@@ -1,10 +1,12 @@
|
|||||||
from src.services.webrequest import BibTextTransformer, TransformerType, WebRequest
|
# from src.services.webrequest import BibTextTransformer, TransformerType, WebRequest
|
||||||
|
|
||||||
transformer = BibTextTransformer(TransformerType.RDS)
|
# transformer = BibTextTransformer(TransformerType.RDS)
|
||||||
|
|
||||||
data = WebRequest().set_apparat(71)
|
# data = WebRequest().set_apparat(71)
|
||||||
data = data.get_ppn("CU 3700 R244 (2)").get_data()
|
# data = data.get_ppn("CU 3700 R244 (2)").get_data()
|
||||||
|
|
||||||
rds = transformer.get_data(data).return_data("rds_availability")
|
# rds = transformer.get_data(data).return_data("rds_availability")
|
||||||
|
|
||||||
|
# print(rds)
|
||||||
|
from src.parsers.xml_parser import eml_parser, eml_to_semap
|
||||||
|
|
||||||
print(rds)
|
|
||||||
|
|||||||
163
uv.lock
generated
163
uv.lock
generated
@@ -307,6 +307,75 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/03/2f/ca9029d5da14b5a3a103d6061149a4a94a54ab848f56c7d2809dbb36f48c/comtypes-1.4.15-py3-none-any.whl", hash = "sha256:cda90486de8762ec57d7ce04e68721920911f3f03415cb29afdf7609c427c7e3", size = 274650, upload-time = "2026-01-19T23:45:44.34Z" },
|
{ url = "https://files.pythonhosted.org/packages/03/2f/ca9029d5da14b5a3a103d6061149a4a94a54ab848f56c7d2809dbb36f48c/comtypes-1.4.15-py3-none-any.whl", hash = "sha256:cda90486de8762ec57d7ce04e68721920911f3f03415cb29afdf7609c427c7e3", size = 274650, upload-time = "2026-01-19T23:45:44.34Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coverage"
|
||||||
|
version = "7.13.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/24/56/95b7e30fa389756cb56630faa728da46a27b8c6eb46f9d557c68fff12b65/coverage-7.13.4.tar.gz", hash = "sha256:e5c8f6ed1e61a8b2dcdf31eb0b9bbf0130750ca79c1c49eb898e2ad86f5ccc91", size = 827239, upload-time = "2026-02-09T12:59:03.86Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/23/aad45061a31677d68e47499197a131eea55da4875d16c1f42021ab963503/coverage-7.13.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b66a2da594b6068b48b2692f043f35d4d3693fb639d5ea8b39533c2ad9ac3ab9", size = 219474, upload-time = "2026-02-09T12:57:19.332Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/70/9b8b67a0945f3dfec1fd896c5cefb7c19d5a3a6d74630b99a895170999ae/coverage-7.13.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3599eb3992d814d23b35c536c28df1a882caa950f8f507cef23d1cbf334995ac", size = 219844, upload-time = "2026-02-09T12:57:20.66Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/fd/7e859f8fab324cef6c4ad7cff156ca7c489fef9179d5749b0c8d321281c2/coverage-7.13.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:93550784d9281e374fb5a12bf1324cc8a963fd63b2d2f223503ef0fd4aa339ea", size = 250832, upload-time = "2026-02-09T12:57:22.007Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/dc/b2442d10020c2f52617828862d8b6ee337859cd8f3a1f13d607dddda9cf7/coverage-7.13.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b720ce6a88a2755f7c697c23268ddc47a571b88052e6b155224347389fdf6a3b", size = 253434, upload-time = "2026-02-09T12:57:23.339Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/88/6728a7ad17428b18d836540630487231f5470fb82454871149502f5e5aa2/coverage-7.13.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7b322db1284a2ed3aa28ffd8ebe3db91c929b7a333c0820abec3d838ef5b3525", size = 254676, upload-time = "2026-02-09T12:57:24.774Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7c/bc/21244b1b8cedf0dff0a2b53b208015fe798d5f2a8d5348dbfece04224fff/coverage-7.13.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4594c67d8a7c89cf922d9df0438c7c7bb022ad506eddb0fdb2863359ff78242", size = 256807, upload-time = "2026-02-09T12:57:26.125Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/97/a0/ddba7ed3251cff51006737a727d84e05b61517d1784a9988a846ba508877/coverage-7.13.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:53d133df809c743eb8bce33b24bcababb371f4441340578cd406e084d94a6148", size = 251058, upload-time = "2026-02-09T12:57:27.614Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/55/e289addf7ff54d3a540526f33751951bf0878f3809b47f6dfb3def69c6f7/coverage-7.13.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76451d1978b95ba6507a039090ba076105c87cc76fc3efd5d35d72093964d49a", size = 252805, upload-time = "2026-02-09T12:57:29.066Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/4e/cc276b1fa4a59be56d96f1dabddbdc30f4ba22e3b1cd42504c37b3313255/coverage-7.13.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7f57b33491e281e962021de110b451ab8a24182589be17e12a22c79047935e23", size = 250766, upload-time = "2026-02-09T12:57:30.522Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/44/1093b8f93018f8b41a8cf29636c9292502f05e4a113d4d107d14a3acd044/coverage-7.13.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1731dc33dc276dafc410a885cbf5992f1ff171393e48a21453b78727d090de80", size = 254923, upload-time = "2026-02-09T12:57:31.946Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/55/ea2796da2d42257f37dbea1aab239ba9263b31bd91d5527cdd6db5efe174/coverage-7.13.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:bd60d4fe2f6fa7dff9223ca1bbc9f05d2b6697bc5961072e5d3b952d46e1b1ea", size = 250591, upload-time = "2026-02-09T12:57:33.842Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/fa/7c4bb72aacf8af5020675aa633e59c1fbe296d22aed191b6a5b711eb2bc7/coverage-7.13.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9181a3ccead280b828fae232df12b16652702b49d41e99d657f46cc7b1f6ec7a", size = 252364, upload-time = "2026-02-09T12:57:35.743Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/38/a8d2ec0146479c20bbaa7181b5b455a0c41101eed57f10dd19a78ab44c80/coverage-7.13.4-cp313-cp313-win32.whl", hash = "sha256:f53d492307962561ac7de4cd1de3e363589b000ab69617c6156a16ba7237998d", size = 222010, upload-time = "2026-02-09T12:57:37.25Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e2/0c/dbfafbe90a185943dcfbc766fe0e1909f658811492d79b741523a414a6cc/coverage-7.13.4-cp313-cp313-win_amd64.whl", hash = "sha256:e6f70dec1cc557e52df5306d051ef56003f74d56e9c4dd7ddb07e07ef32a84dd", size = 222818, upload-time = "2026-02-09T12:57:38.734Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/d1/934918a138c932c90d78301f45f677fb05c39a3112b96fd2c8e60503cdc7/coverage-7.13.4-cp313-cp313-win_arm64.whl", hash = "sha256:fb07dc5da7e849e2ad31a5d74e9bece81f30ecf5a42909d0a695f8bd1874d6af", size = 221438, upload-time = "2026-02-09T12:57:40.223Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/57/ee93ced533bcb3e6df961c0c6e42da2fc6addae53fb95b94a89b1e33ebd7/coverage-7.13.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:40d74da8e6c4b9ac18b15331c4b5ebc35a17069410cad462ad4f40dcd2d50c0d", size = 220165, upload-time = "2026-02-09T12:57:41.639Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/e0/969fc285a6fbdda49d91af278488d904dcd7651b2693872f0ff94e40e84a/coverage-7.13.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4223b4230a376138939a9173f1bdd6521994f2aff8047fae100d6d94d50c5a12", size = 220516, upload-time = "2026-02-09T12:57:44.215Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/b8/9531944e16267e2735a30a9641ff49671f07e8138ecf1ca13db9fd2560c7/coverage-7.13.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1d4be36a5114c499f9f1f9195e95ebf979460dbe2d88e6816ea202010ba1c34b", size = 261804, upload-time = "2026-02-09T12:57:45.989Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/f3/e63df6d500314a2a60390d1989240d5f27318a7a68fa30ad3806e2a9323e/coverage-7.13.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:200dea7d1e8095cc6e98cdabe3fd1d21ab17d3cee6dab00cadbb2fe35d9c15b9", size = 263885, upload-time = "2026-02-09T12:57:47.42Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f3/67/7654810de580e14b37670b60a09c599fa348e48312db5b216d730857ffe6/coverage-7.13.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8eb931ee8e6d8243e253e5ed7336deea6904369d2fd8ae6e43f68abbf167092", size = 266308, upload-time = "2026-02-09T12:57:49.345Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/37/6f/39d41eca0eab3cc82115953ad41c4e77935286c930e8fad15eaed1389d83/coverage-7.13.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:75eab1ebe4f2f64d9509b984f9314d4aa788540368218b858dad56dc8f3e5eb9", size = 267452, upload-time = "2026-02-09T12:57:50.811Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/6d/39c0fbb8fc5cd4d2090811e553c2108cf5112e882f82505ee7495349a6bf/coverage-7.13.4-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c35eb28c1d085eb7d8c9b3296567a1bebe03ce72962e932431b9a61f28facf26", size = 261057, upload-time = "2026-02-09T12:57:52.447Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a4/a2/60010c669df5fa603bb5a97fb75407e191a846510da70ac657eb696b7fce/coverage-7.13.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb88b316ec33760714a4720feb2816a3a59180fd58c1985012054fa7aebee4c2", size = 263875, upload-time = "2026-02-09T12:57:53.938Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/d9/63b22a6bdbd17f1f96e9ed58604c2a6b0e72a9133e37d663bef185877cf6/coverage-7.13.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7d41eead3cc673cbd38a4417deb7fd0b4ca26954ff7dc6078e33f6ff97bed940", size = 261500, upload-time = "2026-02-09T12:57:56.012Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/70/bf/69f86ba1ad85bc3ad240e4c0e57a2e620fbc0e1645a47b5c62f0e941ad7f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:fb26a934946a6afe0e326aebe0730cdff393a8bc0bbb65a2f41e30feddca399c", size = 265212, upload-time = "2026-02-09T12:57:57.5Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/f2/5f65a278a8c2148731831574c73e42f57204243d33bedaaf18fa79c5958f/coverage-7.13.4-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:dae88bc0fc77edaa65c14be099bd57ee140cf507e6bfdeea7938457ab387efb0", size = 260398, upload-time = "2026-02-09T12:57:59.027Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ef/80/6e8280a350ee9fea92f14b8357448a242dcaa243cb2c72ab0ca591f66c8c/coverage-7.13.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:845f352911777a8e722bfce168958214951e07e47e5d5d9744109fa5fe77f79b", size = 262584, upload-time = "2026-02-09T12:58:01.129Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/63/01ff182fc95f260b539590fb12c11ad3e21332c15f9799cb5e2386f71d9f/coverage-7.13.4-cp313-cp313t-win32.whl", hash = "sha256:2fa8d5f8de70688a28240de9e139fa16b153cc3cbb01c5f16d88d6505ebdadf9", size = 222688, upload-time = "2026-02-09T12:58:02.736Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/43/89de4ef5d3cd53b886afa114065f7e9d3707bdb3e5efae13535b46ae483d/coverage-7.13.4-cp313-cp313t-win_amd64.whl", hash = "sha256:9351229c8c8407645840edcc277f4a2d44814d1bc34a2128c11c2a031d45a5dd", size = 223746, upload-time = "2026-02-09T12:58:05.362Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/39/7cf0aa9a10d470a5309b38b289b9bb07ddeac5d61af9b664fe9775a4cb3e/coverage-7.13.4-cp313-cp313t-win_arm64.whl", hash = "sha256:30b8d0512f2dc8c8747557e8fb459d6176a2c9e5731e2b74d311c03b78451997", size = 222003, upload-time = "2026-02-09T12:58:06.952Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/11/a9cf762bb83386467737d32187756a42094927150c3e107df4cb078e8590/coverage-7.13.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:300deaee342f90696ed186e3a00c71b5b3d27bffe9e827677954f4ee56969601", size = 219522, upload-time = "2026-02-09T12:58:08.623Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d3/28/56e6d892b7b052236d67c95f1936b6a7cf7c3e2634bf27610b8cbd7f9c60/coverage-7.13.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:29e3220258d682b6226a9b0925bc563ed9a1ebcff3cad30f043eceea7eaf2689", size = 219855, upload-time = "2026-02-09T12:58:10.176Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/69/233459ee9eb0c0d10fcc2fe425a029b3fa5ce0f040c966ebce851d030c70/coverage-7.13.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:391ee8f19bef69210978363ca930f7328081c6a0152f1166c91f0b5fdd2a773c", size = 250887, upload-time = "2026-02-09T12:58:12.503Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/06/90/2cdab0974b9b5bbc1623f7876b73603aecac11b8d95b85b5b86b32de5eab/coverage-7.13.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0dd7ab8278f0d58a0128ba2fca25824321f05d059c1441800e934ff2efa52129", size = 253396, upload-time = "2026-02-09T12:58:14.615Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/15/ea4da0f85bf7d7b27635039e649e99deb8173fe551096ea15017f7053537/coverage-7.13.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78cdf0d578b15148b009ccf18c686aa4f719d887e76e6b40c38ffb61d264a552", size = 254745, upload-time = "2026-02-09T12:58:16.162Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/11/bb356e86920c655ca4d61daee4e2bbc7258f0a37de0be32d233b561134ff/coverage-7.13.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:48685fee12c2eb3b27c62f2658e7ea21e9c3239cba5a8a242801a0a3f6a8c62a", size = 257055, upload-time = "2026-02-09T12:58:17.892Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/0f/9ae1f8cb17029e09da06ca4e28c9e1d5c1c0a511c7074592e37e0836c915/coverage-7.13.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4e83efc079eb39480e6346a15a1bcb3e9b04759c5202d157e1dd4303cd619356", size = 250911, upload-time = "2026-02-09T12:58:19.495Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/89/3a/adfb68558fa815cbc29747b553bc833d2150228f251b127f1ce97e48547c/coverage-7.13.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ecae9737b72408d6a950f7e525f30aca12d4bd8dd95e37342e5beb3a2a8c4f71", size = 252754, upload-time = "2026-02-09T12:58:21.064Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/32/b1/540d0c27c4e748bd3cd0bd001076ee416eda993c2bae47a73b7cc9357931/coverage-7.13.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ae4578f8528569d3cf303fef2ea569c7f4c4059a38c8667ccef15c6e1f118aa5", size = 250720, upload-time = "2026-02-09T12:58:22.622Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/95/383609462b3ffb1fe133014a7c84fc0dd01ed55ac6140fa1093b5af7ebb1/coverage-7.13.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:6fdef321fdfbb30a197efa02d48fcd9981f0d8ad2ae8903ac318adc653f5df98", size = 254994, upload-time = "2026-02-09T12:58:24.548Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f7/ba/1761138e86c81680bfc3c49579d66312865457f9fe405b033184e5793cb3/coverage-7.13.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b0f6ccf3dbe577170bebfce1318707d0e8c3650003cb4b3a9dd744575daa8b5", size = 250531, upload-time = "2026-02-09T12:58:26.271Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/8e/05900df797a9c11837ab59c4d6fe94094e029582aab75c3309a93e6fb4e3/coverage-7.13.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75fcd519f2a5765db3f0e391eb3b7d150cce1a771bf4c9f861aeab86c767a3c0", size = 252189, upload-time = "2026-02-09T12:58:27.807Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/bd/29c9f2db9ea4ed2738b8a9508c35626eb205d51af4ab7bf56a21a2e49926/coverage-7.13.4-cp314-cp314-win32.whl", hash = "sha256:8e798c266c378da2bd819b0677df41ab46d78065fb2a399558f3f6cae78b2fbb", size = 222258, upload-time = "2026-02-09T12:58:29.441Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/4d/1f8e723f6829977410efeb88f73673d794075091c8c7c18848d273dc9d73/coverage-7.13.4-cp314-cp314-win_amd64.whl", hash = "sha256:245e37f664d89861cf2329c9afa2c1fe9e6d4e1a09d872c947e70718aeeac505", size = 223073, upload-time = "2026-02-09T12:58:31.026Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/51/5b/84100025be913b44e082ea32abcf1afbf4e872f5120b7a1cab1d331b1e13/coverage-7.13.4-cp314-cp314-win_arm64.whl", hash = "sha256:ad27098a189e5838900ce4c2a99f2fe42a0bf0c2093c17c69b45a71579e8d4a2", size = 221638, upload-time = "2026-02-09T12:58:32.599Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/e4/c884a405d6ead1370433dad1e3720216b4f9fd8ef5b64bfd984a2a60a11a/coverage-7.13.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:85480adfb35ffc32d40918aad81b89c69c9cc5661a9b8a81476d3e645321a056", size = 220246, upload-time = "2026-02-09T12:58:34.181Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/5c/4d7ed8b23b233b0fffbc9dfec53c232be2e695468523242ea9fd30f97ad2/coverage-7.13.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:79be69cf7f3bf9b0deeeb062eab7ac7f36cd4cc4c4dd694bd28921ba4d8596cc", size = 220514, upload-time = "2026-02-09T12:58:35.704Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/6f/3284d4203fd2f28edd73034968398cd2d4cb04ab192abc8cff007ea35679/coverage-7.13.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:caa421e2684e382c5d8973ac55e4f36bed6821a9bad5c953494de960c74595c9", size = 261877, upload-time = "2026-02-09T12:58:37.864Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/aa/b672a647bbe1556a85337dc95bfd40d146e9965ead9cc2fe81bde1e5cbce/coverage-7.13.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14375934243ee05f56c45393fe2ce81fe5cc503c07cee2bdf1725fb8bef3ffaf", size = 264004, upload-time = "2026-02-09T12:58:39.492Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/a1/aa384dbe9181f98bba87dd23dda436f0c6cf2e148aecbb4e50fc51c1a656/coverage-7.13.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:25a41c3104d08edb094d9db0d905ca54d0cd41c928bb6be3c4c799a54753af55", size = 266408, upload-time = "2026-02-09T12:58:41.852Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/5e/5150bf17b4019bc600799f376bb9606941e55bd5a775dc1e096b6ffea952/coverage-7.13.4-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f01afcff62bf9a08fb32b2c1d6e924236c0383c02c790732b6537269e466a72", size = 267544, upload-time = "2026-02-09T12:58:44.093Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/ed/f1de5c675987a4a7a672250d2c5c9d73d289dbf13410f00ed7181d8017dd/coverage-7.13.4-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eb9078108fbf0bcdde37c3f4779303673c2fa1fe8f7956e68d447d0dd426d38a", size = 260980, upload-time = "2026-02-09T12:58:45.721Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/e3/fe758d01850aa172419a6743fe76ba8b92c29d181d4f676ffe2dae2ba631/coverage-7.13.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0e086334e8537ddd17e5f16a344777c1ab8194986ec533711cbe6c41cde841b6", size = 263871, upload-time = "2026-02-09T12:58:47.334Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/76/b829869d464115e22499541def9796b25312b8cf235d3bb00b39f1675395/coverage-7.13.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:725d985c5ab621268b2edb8e50dfe57633dc69bda071abc470fed55a14935fd3", size = 261472, upload-time = "2026-02-09T12:58:48.995Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/9e/caedb1679e73e2f6ad240173f55218488bfe043e38da577c4ec977489915/coverage-7.13.4-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:3c06f0f1337c667b971ca2f975523347e63ec5e500b9aa5882d91931cd3ef750", size = 265210, upload-time = "2026-02-09T12:58:51.178Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3a/10/0dd02cb009b16ede425b49ec344aba13a6ae1dc39600840ea6abcb085ac4/coverage-7.13.4-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:590c0ed4bf8e85f745e6b805b2e1c457b2e33d5255dd9729743165253bc9ad39", size = 260319, upload-time = "2026-02-09T12:58:53.081Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/8e/234d2c927af27c6d7a5ffad5bd2cf31634c46a477b4c7adfbfa66baf7ebb/coverage-7.13.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eb30bf180de3f632cd043322dad5751390e5385108b2807368997d1a92a509d0", size = 262638, upload-time = "2026-02-09T12:58:55.258Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/64/e5547c8ff6964e5965c35a480855911b61509cce544f4d442caa759a0702/coverage-7.13.4-cp314-cp314t-win32.whl", hash = "sha256:c4240e7eded42d131a2d2c4dec70374b781b043ddc79a9de4d55ca71f8e98aea", size = 223040, upload-time = "2026-02-09T12:58:56.936Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/96/38086d58a181aac86d503dfa9c47eb20715a79c3e3acbdf786e92e5c09a8/coverage-7.13.4-cp314-cp314t-win_amd64.whl", hash = "sha256:4c7d3cc01e7350f2f0f6f7036caaf5673fb56b6998889ccfe9e1c1fe75a9c932", size = 224148, upload-time = "2026-02-09T12:58:58.645Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/72/8d10abd3740a0beb98c305e0c3faf454366221c0f37a8bcf8f60020bb65a/coverage-7.13.4-cp314-cp314t-win_arm64.whl", hash = "sha256:23e3f687cf945070d1c90f85db66d11e3025665d8dafa831301a0e0038f3db9b", size = 222172, upload-time = "2026-02-09T12:59:00.396Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/4a/331fe2caf6799d591109bb9c08083080f6de90a823695d412a935622abb2/coverage-7.13.4-py3-none-any.whl", hash = "sha256:1af1641e57cf7ba1bd67d677c9abdbcd6cc2ab7da3bca7fa1e2b7e50e65f2ad0", size = 211242, upload-time = "2026-02-09T12:59:02.032Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cryptography"
|
name = "cryptography"
|
||||||
version = "46.0.4"
|
version = "46.0.4"
|
||||||
@@ -552,6 +621,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
{ url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iniconfig"
|
||||||
|
version = "2.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itsdangerous"
|
name = "itsdangerous"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
@@ -999,27 +1077,12 @@ wheels = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prek"
|
name = "pluggy"
|
||||||
version = "0.3.2"
|
version = "1.6.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/d3/f5/ee52def928dd1355c20bcfcf765e1e61434635c33f3075e848e7b83a157b/prek-0.3.2.tar.gz", hash = "sha256:dce0074ff1a21290748ca567b4bda7553ee305a8c7b14d737e6c58364a499364", size = 334229, upload-time = "2026-02-06T13:49:47.539Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/76/69/70a5fc881290a63910494df2677c0fb241d27cfaa435bbcd0de5cd2e2443/prek-0.3.2-py3-none-linux_armv6l.whl", hash = "sha256:4f352f9c3fc98aeed4c8b2ec4dbf16fc386e45eea163c44d67e5571489bd8e6f", size = 4614960, upload-time = "2026-02-06T13:50:05.818Z" },
|
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||||
{ url = "https://files.pythonhosted.org/packages/c0/15/a82d5d32a2207ccae5d86ea9e44f2b93531ed000faf83a253e8d1108e026/prek-0.3.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4a000cfbc3a6ec7d424f8be3c3e69ccd595448197f92daac8652382d0acc2593", size = 4622889, upload-time = "2026-02-06T13:49:53.662Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/89/75/ea833b58a12741397017baef9b66a6e443bfa8286ecbd645d14111446280/prek-0.3.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5436bdc2702cbd7bcf9e355564ae66f8131211e65fefae54665a94a07c3d450a", size = 4239653, upload-time = "2026-02-06T13:50:02.88Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/10/b4/d9c3885987afac6e20df4cb7db14e3b0d5a08a77ae4916488254ebac4d0b/prek-0.3.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:0161b5f584f9e7f416d6cf40a17b98f17953050ff8d8350ec60f20fe966b86b6", size = 4595101, upload-time = "2026-02-06T13:49:49.813Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/21/a6/1a06473ed83dbc898de22838abdb13954e2583ce229f857f61828384634c/prek-0.3.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4e641e8533bca38797eebb49aa89ed0e8db0e61225943b27008c257e3af4d631", size = 4521978, upload-time = "2026-02-06T13:49:41.266Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/0c/5e/c38390d5612e6d86b32151c1d2fdab74a57913473193591f0eb00c894c21/prek-0.3.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfca1810d49d3f9ef37599c958c4e716bc19a1d78a7e88cbdcb332e0b008994f", size = 4829108, upload-time = "2026-02-06T13:49:44.598Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/80/a6/cecce2ab623747ff65ed990bb0d95fa38449ee19b348234862acf9392fff/prek-0.3.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d69d754299a95a85dc20196f633232f306bee7e7c8cba61791f49ce70404ec", size = 5357520, upload-time = "2026-02-06T13:49:48.512Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a5/18/d6bcb29501514023c76d55d5cd03bdbc037737c8de8b6bc41cdebfb1682c/prek-0.3.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:539dcb90ad9b20837968539855df6a29493b328a1ae87641560768eed4f313b0", size = 4852635, upload-time = "2026-02-06T13:49:58.347Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1b/0a/ae46f34ba27ba87aea5c9ad4ac9cd3e07e014fd5079ae079c84198f62118/prek-0.3.2-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1998db3d0cbe243984736c82232be51318f9192e2433919a6b1c5790f600b5fd", size = 4599484, upload-time = "2026-02-06T13:49:43.296Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/1a/a9/73bfb5b3f7c3583f9b0d431924873928705cdef6abb3d0461c37254a681b/prek-0.3.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:07ab237a5415a3e8c0db54de9d63899bcd947624bdd8820d26f12e65f8d19eb7", size = 4657694, upload-time = "2026-02-06T13:50:01.074Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/a7/bc/0994bc176e1a80110fad3babce2c98b0ac4007630774c9e18fc200a34781/prek-0.3.2-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:0ced19701d69c14a08125f14a5dd03945982edf59e793c73a95caf4697a7ac30", size = 4509337, upload-time = "2026-02-06T13:49:54.891Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/f9/13/e73f85f65ba8f626468e5d1694ab3763111513da08e0074517f40238c061/prek-0.3.2-py3-none-musllinux_1_1_i686.whl", hash = "sha256:ffb28189f976fa111e770ee94e4f298add307714568fb7d610c8a7095cb1ce59", size = 4697350, upload-time = "2026-02-06T13:50:04.526Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/14/47/98c46dcd580305b9960252a4eb966f1a7b1035c55c363f378d85662ba400/prek-0.3.2-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f63134b3eea14421789a7335d86f99aee277cb520427196f2923b9260c60e5c5", size = 4955860, upload-time = "2026-02-06T13:49:56.581Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/73/42/1bb4bba3ff47897df11e9dfd774027cdfa135482c961a54e079af0faf45a/prek-0.3.2-py3-none-win32.whl", hash = "sha256:58c806bd1344becd480ef5a5ba348846cc000af0e1fbe854fef91181a2e06461", size = 4267619, upload-time = "2026-02-06T13:49:39.503Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/97/11/6665f47a7c350d83de17403c90bbf7a762ef50876ece456a86f64f46fbfb/prek-0.3.2-py3-none-win_amd64.whl", hash = "sha256:70114b48e9eb8048b2c11b4c7715ce618529c6af71acc84dd8877871a2ef71a6", size = 4624324, upload-time = "2026-02-06T13:49:45.922Z" },
|
|
||||||
{ url = "https://files.pythonhosted.org/packages/22/e7/740997ca82574d03426f897fd88afe3fc8a7306b8c7ea342a8bc1c538488/prek-0.3.2-py3-none-win_arm64.whl", hash = "sha256:9144d176d0daa2469a25c303ef6f6fa95a8df015eb275232f5cb53551ecefef0", size = 4336008, upload-time = "2026-02-06T13:49:52.27Z" },
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1285,6 +1348,36 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b9/f9/c9757a984c4ffb6d12fab69e966d95dfc862a5d44e12b7900f3a03780b76/pyside6_essentials-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:db5f4913648bb6afddb8b347edae151ee2378f12bceb03c8b2515a530a4b38d9", size = 55258626, upload-time = "2026-02-02T08:46:36.788Z" },
|
{ url = "https://files.pythonhosted.org/packages/b9/f9/c9757a984c4ffb6d12fab69e966d95dfc862a5d44e12b7900f3a03780b76/pyside6_essentials-6.10.2-cp39-abi3-win_arm64.whl", hash = "sha256:db5f4913648bb6afddb8b347edae151ee2378f12bceb03c8b2515a530a4b38d9", size = 55258626, upload-time = "2026-02-02T08:46:36.788Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest"
|
||||||
|
version = "9.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
{ name = "iniconfig" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pygments" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytest-cov"
|
||||||
|
version = "7.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "coverage" },
|
||||||
|
{ name = "pluggy" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.9.0.post0"
|
version = "2.9.0.post0"
|
||||||
@@ -1571,8 +1664,10 @@ dev = [
|
|||||||
{ name = "bump-my-version" },
|
{ name = "bump-my-version" },
|
||||||
{ name = "icecream" },
|
{ name = "icecream" },
|
||||||
{ name = "nuitka" },
|
{ name = "nuitka" },
|
||||||
{ name = "prek" },
|
|
||||||
{ name = "pyinstaller" },
|
{ name = "pyinstaller" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "ty" },
|
||||||
]
|
]
|
||||||
swbtest = [
|
swbtest = [
|
||||||
{ name = "alive-progress" },
|
{ name = "alive-progress" },
|
||||||
@@ -1613,8 +1708,10 @@ dev = [
|
|||||||
{ name = "bump-my-version", specifier = ">=0.29.0" },
|
{ name = "bump-my-version", specifier = ">=0.29.0" },
|
||||||
{ name = "icecream", specifier = ">=2.1.4" },
|
{ name = "icecream", specifier = ">=2.1.4" },
|
||||||
{ name = "nuitka", specifier = ">=2.5.9" },
|
{ name = "nuitka", specifier = ">=2.5.9" },
|
||||||
{ name = "prek", specifier = ">=0.3.2" },
|
|
||||||
{ name = "pyinstaller", specifier = ">=6.17.0" },
|
{ name = "pyinstaller", specifier = ">=6.17.0" },
|
||||||
|
{ name = "pytest" },
|
||||||
|
{ name = "pytest-cov" },
|
||||||
|
{ name = "ty", specifier = ">=0.0.15" },
|
||||||
]
|
]
|
||||||
swbtest = [{ name = "alive-progress", specifier = ">=3.3.0" }]
|
swbtest = [{ name = "alive-progress", specifier = ">=3.3.0" }]
|
||||||
|
|
||||||
@@ -1693,6 +1790,30 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
|
{ url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ty"
|
||||||
|
version = "0.0.15"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4e/25/257602d316b9333089b688a7a11b33ebc660b74e8dacf400dc3dfdea1594/ty-0.0.15.tar.gz", hash = "sha256:4f9a5b8df208c62dba56e91b93bed8b5bb714839691b8cff16d12c983bfa1174", size = 5101936, upload-time = "2026-02-05T01:06:34.922Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/c5/35626e732b79bf0e6213de9f79aff59b5f247c0a1e3ce0d93e675ab9b728/ty-0.0.15-py3-none-linux_armv6l.whl", hash = "sha256:68e092458516c61512dac541cde0a5e4e5842df00b4e81881ead8f745ddec794", size = 10138374, upload-time = "2026-02-05T01:07:03.804Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d5/8a/48fd81664604848f79d03879b3ca3633762d457a069b07e09fb1b87edd6e/ty-0.0.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:79f2e75289eae3cece94c51118b730211af4ba5762906f52a878041b67e54959", size = 9947858, upload-time = "2026-02-05T01:06:47.453Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/85/c1ac8e97bcd930946f4c94db85b675561d590b4e72703bf3733419fc3973/ty-0.0.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:112a7b26e63e48cc72c8c5b03227d1db280cfa57a45f2df0e264c3a016aa8c3c", size = 9443220, upload-time = "2026-02-05T01:06:44.98Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/d9/244bc02599d950f7a4298fbc0c1b25cc808646b9577bdf7a83470b2d1cec/ty-0.0.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71f62a2644972975a657d9dc867bf901235cde51e8d24c20311067e7afd44a56", size = 9949976, upload-time = "2026-02-05T01:07:01.515Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/ab/3a0daad66798c91a33867a3ececf17d314ac65d4ae2bbbd28cbfde94da63/ty-0.0.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e48b42be2d257317c85b78559233273b655dd636fc61e7e1d69abd90fd3cba4", size = 9965918, upload-time = "2026-02-05T01:06:54.283Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/39/4e/e62b01338f653059a7c0cd09d1a326e9a9eedc351a0f0de9db0601658c3d/ty-0.0.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27dd5b52a421e6871c5bfe9841160331b60866ed2040250cb161886478ab3e4f", size = 10424943, upload-time = "2026-02-05T01:07:08.777Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/b5/7aa06655ce69c0d4f3e845d2d85e79c12994b6d84c71699cfb437e0bc8cf/ty-0.0.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76b85c9ec2219e11c358a7db8e21b7e5c6674a1fb9b6f633836949de98d12286", size = 10964692, upload-time = "2026-02-05T01:06:37.103Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/04/36fdfe1f3c908b471e246e37ce3d011175584c26d3853e6c5d9a0364564c/ty-0.0.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9e8204c61d8ede4f21f2975dce74efdb80fafb2fae1915c666cceb33ea3c90b", size = 10692225, upload-time = "2026-02-05T01:06:49.714Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/13/41/5bf882649bd8b64ded5fbce7fb8d77fb3b868de1a3b1a6c4796402b47308/ty-0.0.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af87c3be7c944bb4d6609d6c63e4594944b0028c7bd490a525a82b88fe010d6d", size = 10516776, upload-time = "2026-02-05T01:06:52.047Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/75/66852d7e004f859839c17ffe1d16513c1e7cc04bcc810edb80ca022a9124/ty-0.0.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:50dccf7398505e5966847d366c9e4c650b8c225411c2a68c32040a63b9521eea", size = 9928828, upload-time = "2026-02-05T01:06:56.647Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/72/96bc16c7b337a3ef358fd227b3c8ef0c77405f3bfbbfb59ee5915f0d9d71/ty-0.0.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:bd797b8f231a4f4715110259ad1ad5340a87b802307f3e06d92bfb37b858a8f3", size = 9978960, upload-time = "2026-02-05T01:06:29.567Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/18/d2e316a35b626de2227f832cd36d21205e4f5d96fd036a8af84c72ecec1b/ty-0.0.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9deb7f20e18b25440a9aa4884f934ba5628ef456dbde91819d5af1a73da48af3", size = 10135903, upload-time = "2026-02-05T01:06:59.256Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/02/d3/b617a79c9dad10c888d7c15cd78859e0160b8772273637b9c4241a049491/ty-0.0.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7b31b3de031255b90a5f4d9cb3d050feae246067c87130e5a6861a8061c71754", size = 10615879, upload-time = "2026-02-05T01:07:06.661Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/b0/2652a73c71c77296a6343217063f05745da60c67b7e8a8e25f2064167fce/ty-0.0.15-py3-none-win32.whl", hash = "sha256:9362c528ceb62c89d65c216336d28d500bc9f4c10418413f63ebc16886e16cc1", size = 9578058, upload-time = "2026-02-05T01:06:42.928Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/6e/08a4aedebd2a6ce2784b5bc3760e43d1861f1a184734a78215c2d397c1df/ty-0.0.15-py3-none-win_amd64.whl", hash = "sha256:4db040695ae67c5524f59cb8179a8fa277112e69042d7dfdac862caa7e3b0d9c", size = 10457112, upload-time = "2026-02-05T01:06:39.885Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/be/1991f2bc12847ae2d4f1e3ac5dcff8bb7bc1261390645c0755bb55616355/ty-0.0.15-py3-none-win_arm64.whl", hash = "sha256:e5a98d4119e77d6136461e16ae505f8f8069002874ab073de03fbcb1a5e8bf25", size = 9937490, upload-time = "2026-02-05T01:06:32.388Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typing-extensions"
|
name = "typing-extensions"
|
||||||
version = "4.15.0"
|
version = "4.15.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user