Compare commits

...
13 Commits
11 changed files with 378 additions and 141 deletions
+4 -4
View File
@@ -10,7 +10,7 @@ wheels/
.venv .venv
# Project specific # Project specific
*.json *.bak
*.psm1 src/output/*
*.xml originals/*
*.bak .vscode
+2
View File
@@ -6,8 +6,10 @@ readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"click>=8.1.8", "click>=8.1.8",
"httpx>=0.28.1",
"loguru>=0.7.3", "loguru>=0.7.3",
"lxml>=5.3.1", "lxml>=5.3.1",
"pydantic>=2.10.6",
] ]
[dependency-groups] [dependency-groups]
-42
View File
@@ -1,42 +0,0 @@
import json
from pathlib import Path
import click
def _get_fields(json_data: dict) -> list[str]:
"""
Extract field names from a JSON data structure.
Args:
json_data (dict): The JSON data from which to extract field names.
Returns:
list[str]: A list of field names extracted from the JSON data.
"""
namelist = []
for attr in json_data["conceptCodes"]:
for name in attr["attributes"]:
if name["attributeName"] not in namelist:
namelist.append(name["attributeName"])
return namelist
def parse_json(json_file: Path) -> list[str]:
"""
Parse a JSON file and extract field names.
Args:
json_file (Path): The path to the JSON file to parse.
Returns:
list[str]: A list of field names extracted from the JSON file.
Raises:
click.FileError: If the file is not a valid JSON file.
"""
try:
with open(json_file, "r") as f:
json_data = json.load(f)
except json.JSONDecodeError:
raise click.FileError(json_file, "Not a valid JSON file.")
return _get_fields(json_data)
+9
View File
@@ -0,0 +1,9 @@
def sanitize_name(name: str) -> str:
"""
Sanitize a name by removing all non-letter characters except hyphens and Scandinavian letters.
"""
# Replace Scandinavian letters
name = name.replace('ä', 'a').replace('ö', 'o')
name = name.replace('Ä', 'A').replace('Ö', 'O')
# Remove all non-letter characters except hyphens
return ''.join(c for c in name if c.isalpha() or c == '-')
+113 -14
View File
@@ -1,23 +1,122 @@
import click import click
from pathlib import Path from pathlib import Path
import json
from datetime import datetime
from pydantic import HttpUrl
import httpx
from urllib.parse import urlparse, urlencode
from json_parser import parse_json from models.savefile import SaveFile, CodeItem
from psm_parser import write_psm from psm_parser import write_psm
from xml_parser import get_xml_data, add_connection, write_xml_data from xml_parser import write_xml_data
def _strip_params(url: HttpUrl) -> str:
parsed_url = urlparse(str(url))
return f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
@click.command def get_attributes(url: str) -> list[str]:
@click.option('-j', '--json', type=Path, required=True, help='Path to the JSON file') """
@click.option('-x', '--xml', type=Path, required=True, help='Path to the XML file. If the file does not exist, a default configuration will be generated.') Get attributes from a URL.
@click.option('-n', '--name', type=str, required=True, help='Name of the connection')
@click.option('-d', '--description', type=str, required=True, help='Description of the connection') Args:
@click.option('-p', '--pretty', is_flag=True, default=False, help='Enable pretty output') url (str): The URL to get attributes from.
def main(json: Path, xml: Path, name: str, description: str, pretty: bool):
xml_data = get_xml_data(xml) Returns:
field_names = parse_json(json) list[str]: A list of attributes.
root = add_connection(xml_data, name, field_names, description=description) """
write_xml_data(root, xml, pretty=pretty) query_params = {"pageSize": 500}
write_psm(name, field_names)
new_url = f"{url}?{urlencode(query_params, doseq=True)}"
response = httpx.get(new_url)
if response.status_code != 200:
click.echo(f"Failed to get attributes from {url}")
return []
conseptCodes = response.json()["conceptCodes"]
retval = []
for attr in conseptCodes:
for name in attr["attributes"]:
if name["attributeName"] not in retval:
retval.append(name["attributeName"])
return retval
def write_json(json_file: Path, data: SaveFile):
with open(json_file, "w") as f:
f.write(data.model_dump_json())
@click.group
def main():
click.echo("Varha CodeServer Connector")
pass
@main.command
@click.argument('json_file', type=Path, required=True)
@click.option('--url', '-u', type=str, required=True, help="URL of the connection")
@click.option('--name', '-n', type=str, required=True, help="Name of the connection")
def add(json_file: Path, url: str, name: str):
"""
Add a new connection to the JSON file.
Args:
json_file (Path): The path to the JSON file.
url (str): The URL of the connection.
name (str): The name of the connection.
"""
server_data = SaveFile()
if json_file.exists():
with open(json_file, "r") as f:
server_data = SaveFile(**json.load(f))
# Check if name already exists
existing_names = [code.name for code in server_data.conceptCodes]
if name in existing_names:
click.echo(f"Name {name} already exists.")
return
# Get attributes from the URL
attributes = get_attributes(_strip_params(url))
server_data.conceptCodes.append(CodeItem(name=name, url=_strip_params(url), attributes=attributes))
server_data.updated_at = datetime.now()
write_json(json_file, server_data)
@main.command
@click.argument('json_file', type=Path, required=True)
def listUrls(json_file: Path):
#TODO: Format the output
if not json_file.exists():
click.echo(f"File {json_file} does not exist.")
return
with open(json_file, "r") as f:
server_data = SaveFile(**json.load(f))
click.echo(server_data.model_dump_json(indent=4))
@main.command
@click.argument('json_file', type=Path, required=True)
@click.option('--name', '-n', type=str, required=True, help="Name of the connection")
def remove(json_file: Path, name: str):
if not json_file.exists():
click.echo(f"File {json_file} does not exist.")
return
with open(json_file, "r") as f:
server_data = SaveFile(**json.load(f))
server_data.conceptCodes = [code for code in server_data.conceptCodes if code.name != name]
server_data.updated_at = datetime.now()
write_json(json_file, server_data)
@main.command
@click.argument('json_file', type=Path, required=True)
def write_output(json_file: Path):
if not json_file.exists():
click.echo(f"File {json_file} does not exist.")
return
server_data = None
with open(json_file, "r") as f:
server_data = SaveFile(**json.load(f))
write_psm(server_data)
write_xml_data(server_data)
if __name__ == "__main__": if __name__ == "__main__":
+33
View File
@@ -0,0 +1,33 @@
from datetime import datetime
from pydantic import BaseModel, HttpUrl
from enum import Enum
class LinkType(Enum):
get = "GET"
post = "POST"
put = "PUT"
delete = "DELETE"
class Attributes(BaseModel):
attributeName: str
attributeValue: list[str]
class Links(BaseModel):
href: HttpUrl
rel: str
type: LinkType
class CodeServerItem(BaseModel):
attributes: list[Attributes]
beginDate: datetime
classificationId: str
classificationName: str
conceptCodeId: str
createDate: datetime
expirationDate: datetime
lastModifiedBy: str
lastModifiedDate: datetime
links: list[Links]
status: str
versionId: str
versionName: str
+12
View File
@@ -0,0 +1,12 @@
from pydantic import BaseModel, HttpUrl, Field
from datetime import datetime
class DataLocation(BaseModel):
href: HttpUrl
name: str
description: str = "Default description"
class LocationList(BaseModel):
location: DataLocation
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
+14
View File
@@ -0,0 +1,14 @@
from pydantic import BaseModel, Field
from datetime import datetime
class CodeItem(BaseModel):
name: str
url: str
attributes: list[str]
checked_at: datetime = Field(default_factory=datetime.now)
class SaveFile(BaseModel):
version: int = 1
conceptCodes: list[CodeItem] = Field(default_factory=list)
created_at: datetime = Field(default_factory=datetime.now)
updated_at: datetime = Field(default_factory=datetime.now)
+21 -39
View File
@@ -1,8 +1,7 @@
import shutil
from pathlib import Path from pathlib import Path
from datetime import datetime
from xml_parser import sanitize_name from lib import sanitize_name
from models.savefile import SaveFile
get_all_code = """ get_all_code = """
function Get-AllCodeServerPages!NAME! { function Get-AllCodeServerPages!NAME! {
@@ -10,17 +9,12 @@ function Get-AllCodeServerPages!NAME! {
param ( param (
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$Resource, [string]$Resource,
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$RootProperty, [string]$RootProperty,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[string]$CacheKeyProperty = "conceptCodeId", [string]$CacheKeyProperty = "conceptCodeId",
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[ScriptBlock]$Converter = ${function:ConvertFrom-CodeServerObject!NAME!}, [ScriptBlock]$Converter = ${function:ConvertFrom-CodeServerObject!NAME!},
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[int]$PageSize = 500 [int]$PageSize = 500
) )
@@ -32,7 +26,6 @@ function Get-AllCodeServerPages!NAME! {
$cacheKey = $Resource.GetHashCode() $cacheKey = $Resource.GetHashCode()
$cache[$cacheKey] = @{} $cache[$cacheKey] = @{}
} }
process { process {
$currentPage = 0; $currentPage = 0;
$Global:debugOut = @() $Global:debugOut = @()
@@ -56,16 +49,12 @@ function Get-AllCodeServerPages!NAME! {
$Global:cache[$cacheKey].Add($key, $codeObject.attributes) $Global:cache[$cacheKey].Add($key, $codeObject.attributes)
} }
} }
} }
} }
write-output $cache[$cacheKey].Values
write-output $cache[$cacheKey].Values
} }
end { end {
} }
} }
""" """
@@ -76,42 +65,33 @@ function Get-CodeServerObject!NAME! {
param ( param (
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$conceptCodeId, [string]$conceptCodeId,
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$Resource, [string]$Resource,
[Parameter(Mandatory=$true)] [Parameter(Mandatory=$true)]
[string]$RootProperty, [string]$RootProperty,
[Parameter(Mandatory=$false)] [Parameter(Mandatory=$false)]
[string]$CacheKeyProperty = "conceptCodeId" [string]$CacheKeyProperty = "conceptCodeId"
) )
begin { begin {
if ($null -eq $Global:cache) { if ($null -eq $Global:cache) {
$params = @{ $params = @{
Resource = $Resource; Resource = $Resource;
RootProperty = $RootProperty; RootProperty = $RootProperty;
} }
if ($PSBoundParameters.ContainsKey("CacheKeyProperty")) { if ($PSBoundParameters.ContainsKey("CacheKeyProperty")) {
$params.Add("CacheKeyProperty", $CacheKeyProperty) $params.Add("CacheKeyProperty", $CacheKeyProperty)
} }
[void](Get-AllCodeServerPages!NAME! @params) [void](Get-AllCodeServerPages!NAME! @params)
} }
$cacheKey = $Resource.GetHashCode() $cacheKey = $Resource.GetHashCode()
} }
process { process {
Write-Debug $cacheKey Write-Debug $cacheKey
Write-Output $cache[$cacheKey][$conceptCodeId] Write-Output $cache[$cacheKey][$conceptCodeId]
} }
end { end {
} }
} }
""" """
@@ -151,9 +131,7 @@ class CodeServer!NAME! {
} }
Export-ModuleMember ConvertFrom-CodeServerObject!NAME! Export-ModuleMember ConvertFrom-CodeServerObject!NAME!, Get-AllCodeServerPages!NAME!,Get-CodeServerObject!NAME!
Export-ModuleMember Get-AllCodeServerPages!NAME!
Export-ModuleMember Get-CodeServerObject!NAME!
""" """
def define_maps(items: list[str]) -> str: def define_maps(items: list[str]) -> str:
@@ -166,15 +144,19 @@ def define_strings(items: list[str]) -> str:
return "\n".join(["\t" + "[string]$" + "{" + sanitize_name(item) + "}" for item in items]) return "\n".join(["\t" + "[string]$" + "{" + sanitize_name(item) + "}" for item in items])
def write_psm(name, items: list[str], filename: Path = Path("CodeServerConnector.psm1")) -> str: def write_psm(savefile: SaveFile) -> None:
if filename.exists():
backup_filename = str(filename) + f".{datetime.now().strftime('%Y-%m-%d.%H%M%S')}.bak"
shutil.copy2(filename, backup_filename)
output = get_all_code.replace("!NAME!", name) filename = Path("output/CodeServerConnector.psm1")
output += get_code.replace("!NAME!", name) if not filename.parent.exists():
output += convert_from.replace("!NAME!", name).replace("!MAPS!", define_maps(items)) filename.parent.mkdir(parents=True, exist_ok=True)
output += class_definition.replace("!NAME!", name).replace("!VARIABLES!", define_variables(items)).replace("!STRINGS!", define_strings(items))
with open(filename, "a", encoding="utf-8-sig") as f: with open(filename, "w", encoding="utf-8-sig") as f:
f.write(output) f.write("# ------- BEGIN CODE BLOCK -------\n")
pass for code in savefile.conceptCodes:
f.write(get_all_code.replace("!NAME!", code.name))
f.write(get_code.replace("!NAME!", code.name))
f.write(convert_from.replace("!NAME!", code.name).replace("!MAPS!", define_maps(code.attributes)))
f.write(class_definition.replace("!NAME!", code.name).replace("!VARIABLES!", define_variables(code.attributes)).replace("!STRINGS!", define_strings(code.attributes)))
f.write("# ------- END CODE BLOCK -------\n")
return
+17 -42
View File
@@ -1,28 +1,21 @@
import lxml.etree as ET import lxml.etree as ET
from pathlib import Path from pathlib import Path
import click
from datetime import datetime
def write_xml_data(root: ET.Element, file_path: Path, pretty: bool = True) -> None: from models.savefile import SaveFile
if file_path.exists(): from lib import sanitize_name
file_path.rename(str(file_path) + f".{datetime.now().strftime('%Y-%m-%d.%H%M%S')}.bak")
xml_str = ET.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=pretty)
if pretty:
# Parse the XML string to ensure proper indentation
parser = ET.XMLParser(remove_blank_text=True, strip_cdata=False)
root = ET.fromstring(xml_str, parser=parser)
xml_str = ET.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=pretty)
file_path.write_bytes(xml_str)
def sanitize_name(name: str) -> str: def write_xml_data(savefile: SaveFile) -> None:
""" filename = Path("output/CodeServerConnector.xml")
Sanitize a name by removing all non-letter characters except hyphens and Scandinavian letters. if not filename.parent.exists():
""" filename.parent.mkdir(parents=True, exist_ok=True)
# Replace Scandinavian letters
name = name.replace('ä', 'a').replace('ö', 'o') root = _generate_xml_root()
name = name.replace('Ä', 'A').replace('Ö', 'O') for code in savefile.conceptCodes:
# Remove all non-letter characters except hyphens root = add_connection(root, code.name, code.attributes)
return ''.join(c for c in name if c.isalpha() or c == '-')
xml_str = ET.tostring(root, encoding="utf-8", xml_declaration=True, pretty_print=True).decode("utf-8-sig")
with open(filename, "w", encoding="utf-8-sig") as f:
f.write(xml_str)
def add_connection(root: ET.Element, name: str, attr_list: list[str], description: str = "Default description") -> ET.Element: def add_connection(root: ET.Element, name: str, attr_list: list[str], description: str = "Default description") -> ET.Element:
""" """
@@ -31,6 +24,7 @@ def add_connection(root: ET.Element, name: str, attr_list: list[str], descriptio
Args: Args:
root (ET.Element): The root element of the XML configuration. root (ET.Element): The root element of the XML configuration.
name (str): The name of the connection parameter. name (str): The name of the connection parameter.
attr_list (list[str]): List of attributes for the connection parameter.
description (str, optional): The description of the connection parameter. Defaults to "Default description". description (str, optional): The description of the connection parameter. Defaults to "Default description".
Returns: Returns:
@@ -77,7 +71,7 @@ def add_connection(root: ET.Element, name: str, attr_list: list[str], descriptio
ET.SubElement(item, "SetParameter", Param="RootProperty", Source="FixedValue", Value="conceptCodes") ET.SubElement(item, "SetParameter", Param="RootProperty", Source="FixedValue", Value="conceptCodes")
return root return root
def _generate_xml_configuration() -> ET.Element: def _generate_xml_root() -> ET.Element:
""" """
Generate a default XML configuration for a PowerShell connector. Generate a default XML configuration for a PowerShell connector.
@@ -100,23 +94,4 @@ def _generate_xml_configuration() -> ET.Element:
ET.SubElement(command_sequence, "Item", Command="Initialize-CodeServer", Order="1") ET.SubElement(command_sequence, "Item", Command="Initialize-CodeServer", Order="1")
ET.SubElement(environment_initialization, "Disconnect") ET.SubElement(environment_initialization, "Disconnect")
ET.SubElement(root, "Schema") ET.SubElement(root, "Schema")
return root return root
def get_xml_data(xml_file: Path) -> ET.Element:
"""
Get XML data from a file. If the file does not exist, generate a default configuration.
Args:
xml_file (Path): The path to the XML file.
Returns:
ET.Element: The root element of the XML configuration.
"""
if not xml_file.exists():
return _generate_xml_configuration()
try:
parser = ET.XMLParser(strip_cdata=False)
tree = ET.parse(xml_file, parser=parser)
return tree
except ET.XMLSyntaxError as e:
raise click.FileError(xml_file, f"Not a valid XML file. {e}")
Generated
+153
View File
@@ -2,6 +2,38 @@ version = 1
revision = 1 revision = 1
requires-python = ">=3.12" requires-python = ">=3.12"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
]
[[package]]
name = "anyio"
version = "4.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
]
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
]
[[package]] [[package]]
name = "click" name = "click"
version = "8.1.8" version = "8.1.8"
@@ -23,14 +55,62 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
] ]
[[package]]
name = "h11"
version = "0.14.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
]
[[package]]
name = "httpcore"
version = "1.0.7"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]] [[package]]
name = "int" name = "int"
version = "0.1.0" version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "httpx" },
{ name = "loguru" }, { name = "loguru" },
{ name = "lxml" }, { name = "lxml" },
{ name = "pydantic" },
] ]
[package.dev-dependencies] [package.dev-dependencies]
@@ -41,8 +121,10 @@ dev = [
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "click", specifier = ">=8.1.8" }, { name = "click", specifier = ">=8.1.8" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "loguru", specifier = ">=0.7.3" }, { name = "loguru", specifier = ">=0.7.3" },
{ name = "lxml", specifier = ">=5.3.1" }, { name = "lxml", specifier = ">=5.3.1" },
{ name = "pydantic", specifier = ">=2.10.6" },
] ]
[package.metadata.requires-dev] [package.metadata.requires-dev]
@@ -124,6 +206,59 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
] ]
[[package]]
name = "pydantic"
version = "2.10.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 },
]
[[package]]
name = "pydantic-core"
version = "2.27.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 },
{ url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 },
{ url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 },
{ url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 },
{ url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 },
{ url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 },
{ url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 },
{ url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 },
{ url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 },
{ url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 },
{ url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 },
{ url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 },
{ url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 },
{ url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 },
{ url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 },
{ url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 },
{ url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 },
{ url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 },
{ url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 },
{ url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 },
{ url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 },
{ url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 },
{ url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 },
{ url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 },
{ url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 },
{ url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 },
{ url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 },
{ url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
]
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.19.1" version = "2.19.1"
@@ -146,6 +281,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
] ]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "typing-extensions"
version = "4.12.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
]
[[package]] [[package]]
name = "win32-setctime" name = "win32-setctime"
version = "1.2.0" version = "1.2.0"