Project little revamp

This commit is contained in:
t0xa 2025-08-31 14:43:31 +03:00
parent 6eb005b203
commit 8faf0dc233
16 changed files with 1566 additions and 233 deletions

View file

@ -1,5 +0,0 @@
# Команда для генерации файлов
*Запускать надо из корня приложения*
```
python3 -m grpc_tools.protoc -Iapi/proto=api/proto --python_out=. --pyi_out=. --grpc_python_out=. api/proto/routes.proto
```

View file

View file

@ -1,14 +0,0 @@
syntax = "proto3";
service RouteApproaches {
rpc GetApproach(ApproachID) returns (Approach) {}
}
message ApproachID {
int32 approachID = 1;
}
message Approach {
int32 reps = 1;
double weight = 2;
}

View file

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: api/proto/routes.proto
# Protobuf Python Version: 5.29.0
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
5,
29,
0,
'',
'api/proto/routes.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61pi/proto/routes.proto\" \n\nApproachID\x12\x12\n\napproachID\x18\x01 \x01(\x05\"(\n\x08\x41pproach\x12\x0c\n\x04reps\x18\x01 \x01(\x05\x12\x0e\n\x06weight\x18\x02 \x01(\x01\x32:\n\x0fRouteApproaches\x12\'\n\x0bGetApproach\x12\x0b.ApproachID\x1a\t.Approach\"\x00\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'api.proto.routes_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
DESCRIPTOR._loaded_options = None
_globals['_APPROACHID']._serialized_start=26
_globals['_APPROACHID']._serialized_end=58
_globals['_APPROACH']._serialized_start=60
_globals['_APPROACH']._serialized_end=100
_globals['_ROUTEAPPROACHES']._serialized_start=102
_globals['_ROUTEAPPROACHES']._serialized_end=160
# @@protoc_insertion_point(module_scope)

View file

@ -1,19 +0,0 @@
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from typing import ClassVar as _ClassVar, Optional as _Optional
DESCRIPTOR: _descriptor.FileDescriptor
class ApproachID(_message.Message):
__slots__ = ("approachID",)
APPROACHID_FIELD_NUMBER: _ClassVar[int]
approachID: int
def __init__(self, approachID: _Optional[int] = ...) -> None: ...
class Approach(_message.Message):
__slots__ = ("reps", "weight")
REPS_FIELD_NUMBER: _ClassVar[int]
WEIGHT_FIELD_NUMBER: _ClassVar[int]
reps: int
weight: float
def __init__(self, reps: _Optional[int] = ..., weight: _Optional[float] = ...) -> None: ...

View file

@ -1,97 +0,0 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import warnings
from api.proto import routes_pb2 as api_dot_proto_dot_routes__pb2
GRPC_GENERATED_VERSION = '1.70.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False
try:
from grpc._utilities import first_version_is_lower
_version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION)
except ImportError:
_version_not_supported = True
if _version_not_supported:
raise RuntimeError(
f'The grpc package installed is at version {GRPC_VERSION},'
+ f' but the generated code in api/proto/routes_pb2_grpc.py depends on'
+ f' grpcio>={GRPC_GENERATED_VERSION}.'
+ f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}'
+ f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.'
)
class RouteApproachesStub(object):
"""Missing associated documentation comment in .proto file."""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.GetApproach = channel.unary_unary(
'/RouteApproaches/GetApproach',
request_serializer=api_dot_proto_dot_routes__pb2.ApproachID.SerializeToString,
response_deserializer=api_dot_proto_dot_routes__pb2.Approach.FromString,
_registered_method=True)
class RouteApproachesServicer(object):
"""Missing associated documentation comment in .proto file."""
def GetApproach(self, request, context):
"""Missing associated documentation comment in .proto file."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_RouteApproachesServicer_to_server(servicer, server):
rpc_method_handlers = {
'GetApproach': grpc.unary_unary_rpc_method_handler(
servicer.GetApproach,
request_deserializer=api_dot_proto_dot_routes__pb2.ApproachID.FromString,
response_serializer=api_dot_proto_dot_routes__pb2.Approach.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'RouteApproaches', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
server.add_registered_method_handlers('RouteApproaches', rpc_method_handlers)
# This class is part of an EXPERIMENTAL API.
class RouteApproaches(object):
"""Missing associated documentation comment in .proto file."""
@staticmethod
def GetApproach(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(
request,
target,
'/RouteApproaches/GetApproach',
api_dot_proto_dot_routes__pb2.ApproachID.SerializeToString,
api_dot_proto_dot_routes__pb2.Approach.FromString,
options,
channel_credentials,
insecure,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
_registered_method=True)

View file

@ -63,25 +63,28 @@ def parse_training_header(
def serialize_exercise(reps: str, weight: str, name: str) -> Exercise: def serialize_exercise(reps: str, weight: str, name: str) -> Exercise:
# Split reps into array of int's # Split reps into array of int's
reps: List[int] = [int(rep) for rep in reps.split("-")] reps_list: List[int] = [int(rep) for rep in reps.split("-")]
weight_splitted: bool = False weight_splitted: bool = False
weight_list: List[float] = []
if weight: if weight:
weight: List[str] = [weight for weight in weight.split("-")] weight_str_list: List[str] = [weight for weight in weight.split("-")]
# Check if weight is splitted # Check if weight is splitted
if any(split_anchor in weight[0] for split_anchor in ["x", "х"]): if any(split_anchor in weight_str_list[0] for split_anchor in ["x", "х"]):
weight_splitted = True weight_splitted = True
splitter = "x" if "x" in weight[0] else "х" splitter = "x" if "x" in weight_str_list[0] else "х"
weight: List[float] = [xweight.split(splitter)[0] for xweight in weight] weight_list = [float(xweight.split(splitter)[0]) for xweight in weight_str_list]
else:
weight_list = [float(w) for w in weight_str_list]
approaches = [] approaches = []
if not weight: if not weight:
for rep_index in range(0, len(reps)): for rep_index in range(0, len(reps_list)):
approach = Approach(weight=0, reps=reps[rep_index]) approach = Approach(weight=0.0, reps=reps_list[rep_index])
approaches.append(approach) approaches.append(approach)
else: else:
weight_pointer = 0 weight_pointer = 0
for rep_index in range(0, len(reps)): for rep_index in range(0, len(reps_list)):
approach = Approach(weight=weight[weight_pointer], reps=reps[rep_index]) approach = Approach(weight=weight_list[weight_pointer], reps=reps_list[rep_index])
if rep_index < len(weight) - 1: if rep_index < len(weight_list) - 1:
weight_pointer += 1 weight_pointer += 1
approaches.append(approach) approaches.append(approach)
exercise = Exercise( exercise = Exercise(
@ -103,6 +106,7 @@ def parse_training_exercises(exercise_line: str) -> Exercise:
return serialize_exercise( return serialize_exercise(
name=stripped[0], weight=stripped[1], reps=stripped[2] name=stripped[0], weight=stripped[1], reps=stripped[2]
) )
raise ValueError("No valid exercise data found")
def parse_training_data(): def parse_training_data():
@ -135,11 +139,13 @@ def parse_training_data():
def remap_unique_exercises(apple_trainings: List[Training]) -> List[Training]: def remap_unique_exercises(apple_trainings: List[Training]) -> List[Training]:
for apple_training in apple_trainings: for apple_training in apple_trainings:
if not apple_training: if not apple_training or not apple_training.exercises:
continue continue
for apple_exercise in apple_training.exercises: for apple_exercise in apple_training.exercises:
if not apple_exercise: if not apple_exercise:
continue continue
print(f"{apple_training.date} : {apple_exercise}") print(f"{apple_training.date} : {apple_exercise}")
apple_exercise.name = unique_apple_exercises_mapper.get(apple_exercise.name) mapped_name = unique_apple_exercises_mapper.get(apple_exercise.name)
if mapped_name is not None:
apple_exercise.name = mapped_name
return apple_trainings return apple_trainings

1414
data/test.py Normal file

File diff suppressed because it is too large Load diff

20
main.py
View file

@ -1,18 +1,4 @@
from pprint import pprint from parsers.text_data_parser import parse_old_data
from apple.notes_parser import parse_training_data as aptd
from obsidian.notes_parser import parse_training_data as optd
from obsidian.py_models import Training
o_dates = [train.date for train in optd()] text_trainings = parse_old_data()
print(len(o_dates)) print(len(text_trainings))
a_dates = [train.date for train in aptd()]
print(len(a_dates))
def print_train(training: Training):
print(f"Training date: {training.date}")
for ex in training.exercises:
print(f" {ex.name}")
for tr in aptd():
print_train(tr)

View file

@ -18,15 +18,15 @@ from obsidian.py_models import Training
# LOADING DATA # LOADING DATA
# load_dotenv() load_dotenv()
#
# DB_USERNAME = os.getenv("POSTGRES_USER") DB_USERNAME = os.getenv("POSTGRES_USER")
# DB_PASS = os.getenv("POSTGRES_PASSWORD") DB_PASS = os.getenv("POSTGRES_PASSWORD")
#
# engine = create_engine( engine = create_engine(
# f"postgresql+psycopg2://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database", f"postgresql+psycopg2://{DB_USERNAME}:{DB_PASS}@localhost:5433/fitness_database",
# echo=True, echo=True,
# ) )
# #
# # Creating async engine for Database connection # # Creating async engine for Database connection
# async_engine = create_async_engine( # async_engine = create_async_engine(
@ -57,8 +57,8 @@ from obsidian.py_models import Training
# TODO: Check how migrations are done # TODO: Check how migrations are done
# NOTE: Drop all Tables from database # NOTE: Drop all Tables from database
metadata_obj.drop_all(engine) # metadata_obj.drop_all(engine)
metadata_obj.create_all(engine) # metadata_obj.create_all(engine)
# NOTE: Table reflection - generating table object from existing tables (only tables, that are stored in metadata) # NOTE: Table reflection - generating table object from existing tables (only tables, that are stored in metadata)
# some_table = Table("some_table", metadata_obj, autoload_with=engine) # some_table = Table("some_table", metadata_obj, autoload_with=engine)
@ -70,13 +70,13 @@ metadata_obj.create_all(engine)
trainings: List[Training] = parse_training_data() trainings: List[Training] = parse_training_data()
for train in trainings: for train in trainings:
if not train: if not train or not train.exercises:
continue continue
else: else:
print(train) print(train)
new_training_pk: int = TrainingRepository(engine).create_training(train.date) new_training_pk: int = TrainingRepository(engine).create_training(train.date)
for exr in train.exercises: for exr in train.exercises:
approach_statements = [] approach_statements: List[str] = []
new_exercise_pk: int = ExerciseRepository(engine).create_exercise( new_exercise_pk: int = ExerciseRepository(engine).create_exercise(
training_pk=new_training_pk, exercise_name=exr.name training_pk=new_training_pk, exercise_name=exr.name
) )

View file

@ -31,25 +31,28 @@ def read_example_file(example_file_name: str):
def serialize_exercise(reps: str, weight: str, name: str) -> Exercise: def serialize_exercise(reps: str, weight: str, name: str) -> Exercise:
# Split reps into array of int's # Split reps into array of int's
reps: List[int] = [int(rep) for rep in reps.split("-")] reps_list: List[int] = [int(rep) for rep in reps.split("-")]
weight_splitted: bool = False weight_splitted: bool = False
weight_list: List[float] = []
if weight: if weight:
weight: List[str] = [weight for weight in weight.split("-")] weight_str_list: List[str] = [weight for weight in weight.split("-")]
# Check if weight is splitted # Check if weight is splitted
if any(split_anchor in weight[0] for split_anchor in ["x", "х"]): if any(split_anchor in weight_str_list[0] for split_anchor in ["x", "х"]):
weight_splitted = True weight_splitted = True
splitter = "x" if "x" in weight[0] else "х" splitter = "x" if "x" in weight_str_list[0] else "х"
weight: List[float] = [xweight.split(splitter)[0] for xweight in weight] weight_list = [float(xweight.split(splitter)[0]) for xweight in weight_str_list]
else:
weight_list = [float(w) for w in weight_str_list]
approaches = [] approaches = []
if not weight: if not weight:
for rep_index in range(0, len(reps)): for rep_index in range(0, len(reps_list)):
approach = Approach(weight=0, reps=reps[rep_index]) approach = Approach(weight=0.0, reps=reps_list[rep_index])
approaches.append(approach) approaches.append(approach)
else: else:
weight_pointer = 0 weight_pointer = 0
for rep_index in range(0, len(reps)): for rep_index in range(0, len(reps_list)):
approach = Approach(weight=weight[weight_pointer], reps=reps[rep_index]) approach = Approach(weight=weight_list[weight_pointer], reps=reps_list[rep_index])
if rep_index < len(weight) - 1: if rep_index < len(weight_list) - 1:
weight_pointer += 1 weight_pointer += 1
approaches.append(approach) approaches.append(approach)
exercise = Exercise( exercise = Exercise(
@ -71,6 +74,7 @@ def parse_training_exercises(exercise_line: str) -> Exercise:
return serialize_exercise( return serialize_exercise(
name=stripped[0], weight=stripped[1], reps=stripped[2] name=stripped[0], weight=stripped[1], reps=stripped[2]
) )
raise ValueError("No valid exercise data found")
def parse_training_header( def parse_training_header(
@ -118,10 +122,10 @@ def parse_training_data():
def remap_unique_exercises(obsidian_trainings: List[Training]) -> List[Training]: def remap_unique_exercises(obsidian_trainings: List[Training]) -> List[Training]:
for obsidian_training in obsidian_trainings: for obsidian_training in obsidian_trainings:
if not obsidian_training: if not obsidian_training or not obsidian_training.exercises:
continue continue
for obsidian_exercise in obsidian_training.exercises: for obsidian_exercise in obsidian_training.exercises:
obsidian_exercise.name = obsidian_unique_exercies_mapping.get( mapped_name = obsidian_unique_exercies_mapping.get(obsidian_exercise.name)
obsidian_exercise.name if mapped_name is not None:
) obsidian_exercise.name = mapped_name
return obsidian_trainings return obsidian_trainings

View file

@ -0,0 +1,15 @@
from typing import List
from apple.notes_parser import parse_training_data as aptd
from obsidian.notes_parser import parse_training_data as optd
from obsidian.py_models import Training
def parse_old_data() -> List[Training]:
"""Method for parsing all old data from apple and obsidian notes
Returns:
List of trainings
"""
o_dates = [train for train in optd()]
a_dates = [train for train in aptd()]
return o_dates + a_dates

View file

@ -38,7 +38,7 @@ class AsyncContextPlayground:
def __init__(self) -> None: def __init__(self) -> None:
self.keka = "Hi, i am async Keka" self.keka = "Hi, i am async Keka"
async def __aenter__(self) -> None: async def __aenter__(self):
print("\tEntered async context") print("\tEntered async context")
return self return self

View file

@ -14,3 +14,10 @@ dependencies = [
"grpcio>=1.70.0", "grpcio>=1.70.0",
"grpcio-tools>=1.70.0", "grpcio-tools>=1.70.0",
] ]
[dependency-groups]
dev = [
"mypy>=1.17.1",
"types-grpcio>=1.0.0.20250703",
"types-protobuf>=6.30.2.20250822",
]

78
uv.lock
View file

@ -1,5 +1,5 @@
version = 1 version = 1
revision = 2 revision = 3
requires-python = ">=3.13" requires-python = ">=3.13"
[[package]] [[package]]
@ -51,6 +51,13 @@ dependencies = [
{ name = "sqlalchemy" }, { name = "sqlalchemy" },
] ]
[package.dev-dependencies]
dev = [
{ name = "mypy" },
{ name = "types-grpcio" },
{ name = "types-protobuf" },
]
[package.metadata] [package.metadata]
requires-dist = [ requires-dist = [
{ name = "asyncpg", specifier = ">=0.30.0" }, { name = "asyncpg", specifier = ">=0.30.0" },
@ -63,6 +70,13 @@ requires-dist = [
{ name = "sqlalchemy", specifier = ">=2.0.37" }, { name = "sqlalchemy", specifier = ">=2.0.37" },
] ]
[package.metadata.requires-dev]
dev = [
{ name = "mypy", specifier = ">=1.17.1" },
{ name = "types-grpcio", specifier = ">=1.0.0.20250703" },
{ name = "types-protobuf", specifier = ">=6.30.2.20250822" },
]
[[package]] [[package]]
name = "greenlet" name = "greenlet"
version = "3.2.4" version = "3.2.4"
@ -137,6 +151,41 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" },
] ]
[[package]]
name = "mypy"
version = "1.17.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mypy-extensions" },
{ name = "pathspec" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/8e/22/ea637422dedf0bf36f3ef238eab4e455e2a0dcc3082b5cc067615347ab8e/mypy-1.17.1.tar.gz", hash = "sha256:25e01ec741ab5bb3eec8ba9cdb0f769230368a22c959c4937360efb89b7e9f01", size = 3352570, upload-time = "2025-07-31T07:54:19.204Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/5b/82/aec2fc9b9b149f372850291827537a508d6c4d3664b1750a324b91f71355/mypy-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93378d3203a5c0800c6b6d850ad2f19f7a3cdf1a3701d3416dbf128805c6a6a7", size = 11075338, upload-time = "2025-07-31T07:53:38.873Z" },
{ url = "https://files.pythonhosted.org/packages/07/ac/ee93fbde9d2242657128af8c86f5d917cd2887584cf948a8e3663d0cd737/mypy-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15d54056f7fe7a826d897789f53dd6377ec2ea8ba6f776dc83c2902b899fee81", size = 10113066, upload-time = "2025-07-31T07:54:14.707Z" },
{ url = "https://files.pythonhosted.org/packages/5a/68/946a1e0be93f17f7caa56c45844ec691ca153ee8b62f21eddda336a2d203/mypy-1.17.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:209a58fed9987eccc20f2ca94afe7257a8f46eb5df1fb69958650973230f91e6", size = 11875473, upload-time = "2025-07-31T07:53:14.504Z" },
{ url = "https://files.pythonhosted.org/packages/9f/0f/478b4dce1cb4f43cf0f0d00fba3030b21ca04a01b74d1cd272a528cf446f/mypy-1.17.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:099b9a5da47de9e2cb5165e581f158e854d9e19d2e96b6698c0d64de911dd849", size = 12744296, upload-time = "2025-07-31T07:53:03.896Z" },
{ url = "https://files.pythonhosted.org/packages/ca/70/afa5850176379d1b303f992a828de95fc14487429a7139a4e0bdd17a8279/mypy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa6ffadfbe6994d724c5a1bb6123a7d27dd68fc9c059561cd33b664a79578e14", size = 12914657, upload-time = "2025-07-31T07:54:08.576Z" },
{ url = "https://files.pythonhosted.org/packages/53/f9/4a83e1c856a3d9c8f6edaa4749a4864ee98486e9b9dbfbc93842891029c2/mypy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:9a2b7d9180aed171f033c9f2fc6c204c1245cf60b0cb61cf2e7acc24eea78e0a", size = 9593320, upload-time = "2025-07-31T07:53:01.341Z" },
{ url = "https://files.pythonhosted.org/packages/38/56/79c2fac86da57c7d8c48622a05873eaab40b905096c33597462713f5af90/mypy-1.17.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:15a83369400454c41ed3a118e0cc58bd8123921a602f385cb6d6ea5df050c733", size = 11040037, upload-time = "2025-07-31T07:54:10.942Z" },
{ url = "https://files.pythonhosted.org/packages/4d/c3/adabe6ff53638e3cad19e3547268482408323b1e68bf082c9119000cd049/mypy-1.17.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:55b918670f692fc9fba55c3298d8a3beae295c5cded0a55dccdc5bbead814acd", size = 10131550, upload-time = "2025-07-31T07:53:41.307Z" },
{ url = "https://files.pythonhosted.org/packages/b8/c5/2e234c22c3bdeb23a7817af57a58865a39753bde52c74e2c661ee0cfc640/mypy-1.17.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:62761474061feef6f720149d7ba876122007ddc64adff5ba6f374fda35a018a0", size = 11872963, upload-time = "2025-07-31T07:53:16.878Z" },
{ url = "https://files.pythonhosted.org/packages/ab/26/c13c130f35ca8caa5f2ceab68a247775648fdcd6c9a18f158825f2bc2410/mypy-1.17.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c49562d3d908fd49ed0938e5423daed8d407774a479b595b143a3d7f87cdae6a", size = 12710189, upload-time = "2025-07-31T07:54:01.962Z" },
{ url = "https://files.pythonhosted.org/packages/82/df/c7d79d09f6de8383fe800521d066d877e54d30b4fb94281c262be2df84ef/mypy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:397fba5d7616a5bc60b45c7ed204717eaddc38f826e3645402c426057ead9a91", size = 12900322, upload-time = "2025-07-31T07:53:10.551Z" },
{ url = "https://files.pythonhosted.org/packages/b8/98/3d5a48978b4f708c55ae832619addc66d677f6dc59f3ebad71bae8285ca6/mypy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:9d6b20b97d373f41617bd0708fd46aa656059af57f2ef72aa8c7d6a2b73b74ed", size = 9751879, upload-time = "2025-07-31T07:52:56.683Z" },
{ url = "https://files.pythonhosted.org/packages/1d/f3/8fcd2af0f5b806f6cf463efaffd3c9548a28f84220493ecd38d127b6b66d/mypy-1.17.1-py3-none-any.whl", hash = "sha256:a9f52c0351c21fe24c21d8c0eb1f62967b262d6729393397b6f443c3b773c3b9", size = 2283411, upload-time = "2025-07-31T07:53:24.664Z" },
]
[[package]]
name = "mypy-extensions"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
]
[[package]] [[package]]
name = "packaging" name = "packaging"
version = "25.0" version = "25.0"
@ -146,6 +195,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" },
] ]
[[package]]
name = "pathspec"
version = "0.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" },
]
[[package]] [[package]]
name = "pluggy" name = "pluggy"
version = "1.6.0" version = "1.6.0"
@ -295,6 +353,24 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" }, { url = "https://files.pythonhosted.org/packages/b8/d9/13bdde6521f322861fab67473cec4b1cc8999f3871953531cf61945fad92/sqlalchemy-2.0.43-py3-none-any.whl", hash = "sha256:1681c21dd2ccee222c2fe0bef671d1aef7c504087c9c4e800371cfcc8ac966fc", size = 1924759, upload-time = "2025-08-11T15:39:53.024Z" },
] ]
[[package]]
name = "types-grpcio"
version = "1.0.0.20250703"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/59/17/c80b7231e337993b808aef32bcc556aad60810ca51eeb7acd1204eefe518/types_grpcio-1.0.0.20250703.tar.gz", hash = "sha256:baf100184e5353cb60f045fb4fd47f37a360bedf0f19581535e4c3a3a1f7912b", size = 14552, upload-time = "2025-07-03T03:14:01.923Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/e2/9c682ba59a7f9cf477bc7954c25ce1c20f881693651f354d15df577996c0/types_grpcio-1.0.0.20250703-py3-none-any.whl", hash = "sha256:78d1bfc33b58a56697ef99e666e34be4c6887631341c75fdd28d58587aef5d9f", size = 15274, upload-time = "2025-07-03T03:14:00.907Z" },
]
[[package]]
name = "types-protobuf"
version = "6.30.2.20250822"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/61/68/0c7144be5c6dc16538e79458839fc914ea494481c7e64566de4ecc0c3682/types_protobuf-6.30.2.20250822.tar.gz", hash = "sha256:faacbbe87bd8cba4472361c0bd86f49296bd36f7761e25d8ada4f64767c1bde9", size = 62379, upload-time = "2025-08-22T03:01:56.572Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/52/64/b926a6355993f712d7828772e42b9ae942f2d306d25072329805c374e729/types_protobuf-6.30.2.20250822-py3-none-any.whl", hash = "sha256:5584c39f7e36104b5f8bdfd31815fa1d5b7b3455a79ddddc097b62320f4b1841", size = 76523, upload-time = "2025-08-22T03:01:55.157Z" },
]
[[package]] [[package]]
name = "typing-extensions" name = "typing-extensions"
version = "4.14.1" version = "4.14.1"