mirror of
https://github.com/aykhans/movier.git
synced 2025-07-17 16:04:02 +00:00
Rewritten in go and python
This commit is contained in:
4
recommender/.dockerignore
Normal file
4
recommender/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
.venv
|
||||
.ipynb_checkpoints
|
||||
__pycache__
|
||||
*.ipynb
|
4
recommender/.gitignore
vendored
Normal file
4
recommender/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.venv
|
||||
.ipynb_checkpoints
|
||||
__pycache__
|
||||
*.ipynb
|
1
recommender/.python-version
Normal file
1
recommender/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
19
recommender/Dockerfile
Normal file
19
recommender/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM python:3.12.3-slim-bookworm
|
||||
|
||||
ENV UV_COMPILE_BYTECODE=1
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
|
||||
RUN apt-get update && apt-get install -y curl
|
||||
|
||||
ENV GRPC_HEALTH_PROBE_VERSION=v0.4.35
|
||||
|
||||
RUN curl -L -o /bin/grpc_health_probe \
|
||||
https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \
|
||||
chmod +x /bin/grpc_health_probe
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN uv sync --frozen
|
38
recommender/config.py
Normal file
38
recommender/config.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import os
|
||||
|
||||
def get_postgres_dsn():
|
||||
user = os.getenv('POSTGRES_USER', None)
|
||||
if user is None:
|
||||
raise ValueError('POSTGRES_USER is not set')
|
||||
|
||||
password = os.getenv('POSTGRES_PASSWORD', None)
|
||||
if password is None:
|
||||
raise ValueError('POSTGRES_PASSWORD is not set')
|
||||
|
||||
host = os.getenv('POSTGRES_HOST', None)
|
||||
if host is None:
|
||||
raise ValueError('POSTGRES_HOST is not set')
|
||||
|
||||
port = os.getenv('POSTGRES_PORT', None)
|
||||
if port is None:
|
||||
raise ValueError('POSTGRES_PORT is not set')
|
||||
try:
|
||||
port = int(port)
|
||||
except ValueError:
|
||||
raise ValueError('POSTGRES_PORT is not an integer')
|
||||
|
||||
dbname = os.getenv('POSTGRES_DB', None)
|
||||
if dbname is None:
|
||||
raise ValueError('POSTGRES_DB is not set')
|
||||
|
||||
return f'postgres://{user}:{password}@{host}:{port}/{dbname}?sslmode=disable'
|
||||
|
||||
def get_grpc_port():
|
||||
port = os.getenv('GRPC_PORT', None)
|
||||
if port is None:
|
||||
raise ValueError('GRPC_PORT is not set')
|
||||
try:
|
||||
port = int(port)
|
||||
except ValueError:
|
||||
raise ValueError('GRPC_PORT is not an integer')
|
||||
return port
|
114
recommender/main.py
Normal file
114
recommender/main.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from sys import path
|
||||
path.append('./proto')
|
||||
|
||||
from concurrent import futures
|
||||
from time import sleep
|
||||
import threading
|
||||
from recommend import Recommender, Weight, Filter
|
||||
from config import get_postgres_dsn, get_grpc_port
|
||||
|
||||
import psycopg2
|
||||
|
||||
from proto import recommender_pb2, recommender_pb2_grpc
|
||||
import grpc
|
||||
from grpc_reflection.v1alpha import reflection
|
||||
from grpc_health.v1 import health
|
||||
from grpc_health.v1 import health_pb2
|
||||
from grpc_health.v1 import health_pb2_grpc
|
||||
|
||||
postgres_dsn = get_postgres_dsn()
|
||||
|
||||
class RecommenderServicer(recommender_pb2_grpc.RecommenderServicer):
|
||||
def GetRecommendations(self, request: recommender_pb2.Request, context):
|
||||
try:
|
||||
recommender = Recommender(
|
||||
filter_=Filter(
|
||||
min_votes=request.filter.min_votes if request.filter.HasField('min_votes_oneof') else None,
|
||||
max_votes=request.filter.max_votes if request.filter.HasField('max_votes_oneof') else None,
|
||||
min_year=request.filter.min_year if request.filter.HasField('min_year_oneof') else None,
|
||||
max_year=request.filter.max_year if request.filter.HasField('max_year_oneof') else None,
|
||||
min_rating=request.filter.min_rating if request.filter.HasField('min_rating_oneof') else None,
|
||||
max_rating=request.filter.max_rating if request.filter.HasField('max_rating_oneof') else None
|
||||
),
|
||||
weight=Weight(
|
||||
year=request.weight.year,
|
||||
rating=request.weight.rating,
|
||||
genres=request.weight.genres,
|
||||
nconsts=request.weight.nconsts
|
||||
)
|
||||
)
|
||||
except ValueError as e:
|
||||
context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
|
||||
context.set_details(str(e))
|
||||
return recommender_pb2.Response()
|
||||
except Exception as e:
|
||||
context.set_code(grpc.StatusCode.INTERNAL)
|
||||
context.set_details(str(e))
|
||||
return recommender_pb2.Response()
|
||||
|
||||
with psycopg2.connect(dsn=postgres_dsn) as conn:
|
||||
try:
|
||||
data = recommender.get_recommendations(conn, request.tconsts, request.n)
|
||||
except ValueError as e:
|
||||
context.set_code(grpc.StatusCode.NOT_FOUND)
|
||||
context.set_details(str(e))
|
||||
return recommender_pb2.Response()
|
||||
except Exception as e:
|
||||
context.set_code(grpc.StatusCode.INTERNAL)
|
||||
context.set_details(str(e))
|
||||
return recommender_pb2.Response()
|
||||
|
||||
movies = []
|
||||
for k, v in data.items():
|
||||
movies.append(
|
||||
recommender_pb2.RecommendedMovie(
|
||||
tconst=k,
|
||||
weights=v
|
||||
)
|
||||
)
|
||||
|
||||
return recommender_pb2.Response(movies=movies)
|
||||
|
||||
def _toggle_health(health_servicer: health.HealthServicer, service: str):
|
||||
next_status = health_pb2.HealthCheckResponse.SERVING
|
||||
while True:
|
||||
if next_status == health_pb2.HealthCheckResponse.SERVING:
|
||||
next_status = health_pb2.HealthCheckResponse.NOT_SERVING
|
||||
else:
|
||||
next_status = health_pb2.HealthCheckResponse.SERVING
|
||||
|
||||
health_servicer.set(service, next_status)
|
||||
sleep(5)
|
||||
|
||||
def _configure_health_server(server: grpc.Server):
|
||||
health_servicer = health.HealthServicer(
|
||||
experimental_non_blocking=True,
|
||||
experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=10),
|
||||
)
|
||||
health_pb2_grpc.add_HealthServicer_to_server(health_servicer, server)
|
||||
|
||||
toggle_health_status_thread = threading.Thread(
|
||||
target=_toggle_health,
|
||||
args=(health_servicer, "recommender.Recommender"),
|
||||
daemon=True,
|
||||
)
|
||||
toggle_health_status_thread.start()
|
||||
|
||||
def serve():
|
||||
server = grpc.server(futures.ThreadPoolExecutor(max_workers=100))
|
||||
recommender_pb2_grpc.add_RecommenderServicer_to_server(RecommenderServicer(), server)
|
||||
SERVICE_NAMES = (
|
||||
recommender_pb2.DESCRIPTOR.services_by_name["Recommender"].full_name,
|
||||
reflection.SERVICE_NAME,
|
||||
)
|
||||
reflection.enable_server_reflection(SERVICE_NAMES, server)
|
||||
server.add_insecure_port(f'[::]:{get_grpc_port()}')
|
||||
_configure_health_server(server)
|
||||
server.start()
|
||||
server.wait_for_termination()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
serve()
|
||||
except KeyboardInterrupt:
|
||||
print("Shutting down server")
|
47
recommender/proto/recommender_pb2.py
Normal file
47
recommender/proto/recommender_pb2.py
Normal file
@@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# NO CHECKED-IN PROTOBUF GENCODE
|
||||
# source: recommender.proto
|
||||
# Protobuf Python Version: 5.27.2
|
||||
"""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,
|
||||
27,
|
||||
2,
|
||||
'',
|
||||
'recommender.proto'
|
||||
)
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11recommender.proto\x12\x0brecommender\"\xf8\x01\n\x06\x46ilter\x12\x13\n\tmin_votes\x18\x01 \x01(\rH\x00\x12\x13\n\tmax_votes\x18\x02 \x01(\rH\x01\x12\x12\n\x08min_year\x18\x03 \x01(\rH\x02\x12\x12\n\x08max_year\x18\x04 \x01(\rH\x03\x12\x14\n\nmin_rating\x18\x05 \x01(\x02H\x04\x12\x14\n\nmax_rating\x18\x06 \x01(\x02H\x05\x42\x11\n\x0fmin_votes_oneofB\x11\n\x0fmax_votes_oneofB\x10\n\x0emin_year_oneofB\x10\n\x0emax_year_oneofB\x12\n\x10min_rating_oneofB\x12\n\x10max_rating_oneof\"G\n\x06Weight\x12\x0c\n\x04year\x18\x01 \x01(\r\x12\x0e\n\x06rating\x18\x02 \x01(\r\x12\x0e\n\x06genres\x18\x03 \x01(\r\x12\x0f\n\x07nconsts\x18\x04 \x01(\r\"o\n\x07Request\x12\x0f\n\x07tconsts\x18\x01 \x03(\t\x12\t\n\x01n\x18\x02 \x01(\r\x12#\n\x06\x66ilter\x18\x03 \x01(\x0b\x32\x13.recommender.Filter\x12#\n\x06weight\x18\x04 \x01(\x0b\x32\x13.recommender.Weight\"9\n\x08Response\x12-\n\x06movies\x18\x01 \x03(\x0b\x32\x1d.recommender.RecommendedMovie\"3\n\x10RecommendedMovie\x12\x0e\n\x06tconst\x18\x01 \x01(\t\x12\x0f\n\x07weights\x18\x02 \x03(\t2R\n\x0bRecommender\x12\x43\n\x12GetRecommendations\x12\x14.recommender.Request\x1a\x15.recommender.Response\"\x00\x42,Z*github.com/aykhans/movier/server/pkg/protob\x06proto3')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'recommender_pb2', _globals)
|
||||
if not _descriptor._USE_C_DESCRIPTORS:
|
||||
_globals['DESCRIPTOR']._loaded_options = None
|
||||
_globals['DESCRIPTOR']._serialized_options = b'Z*github.com/aykhans/movier/server/pkg/proto'
|
||||
_globals['_FILTER']._serialized_start=35
|
||||
_globals['_FILTER']._serialized_end=283
|
||||
_globals['_WEIGHT']._serialized_start=285
|
||||
_globals['_WEIGHT']._serialized_end=356
|
||||
_globals['_REQUEST']._serialized_start=358
|
||||
_globals['_REQUEST']._serialized_end=469
|
||||
_globals['_RESPONSE']._serialized_start=471
|
||||
_globals['_RESPONSE']._serialized_end=528
|
||||
_globals['_RECOMMENDEDMOVIE']._serialized_start=530
|
||||
_globals['_RECOMMENDEDMOVIE']._serialized_end=581
|
||||
_globals['_RECOMMENDER']._serialized_start=583
|
||||
_globals['_RECOMMENDER']._serialized_end=665
|
||||
# @@protoc_insertion_point(module_scope)
|
60
recommender/proto/recommender_pb2.pyi
Normal file
60
recommender/proto/recommender_pb2.pyi
Normal file
@@ -0,0 +1,60 @@
|
||||
from google.protobuf.internal import containers as _containers
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
||||
|
||||
DESCRIPTOR: _descriptor.FileDescriptor
|
||||
|
||||
class Filter(_message.Message):
|
||||
__slots__ = ("min_votes", "max_votes", "min_year", "max_year", "min_rating", "max_rating")
|
||||
MIN_VOTES_FIELD_NUMBER: _ClassVar[int]
|
||||
MAX_VOTES_FIELD_NUMBER: _ClassVar[int]
|
||||
MIN_YEAR_FIELD_NUMBER: _ClassVar[int]
|
||||
MAX_YEAR_FIELD_NUMBER: _ClassVar[int]
|
||||
MIN_RATING_FIELD_NUMBER: _ClassVar[int]
|
||||
MAX_RATING_FIELD_NUMBER: _ClassVar[int]
|
||||
min_votes: int
|
||||
max_votes: int
|
||||
min_year: int
|
||||
max_year: int
|
||||
min_rating: float
|
||||
max_rating: float
|
||||
def __init__(self, min_votes: _Optional[int] = ..., max_votes: _Optional[int] = ..., min_year: _Optional[int] = ..., max_year: _Optional[int] = ..., min_rating: _Optional[float] = ..., max_rating: _Optional[float] = ...) -> None: ...
|
||||
|
||||
class Weight(_message.Message):
|
||||
__slots__ = ("year", "rating", "genres", "nconsts")
|
||||
YEAR_FIELD_NUMBER: _ClassVar[int]
|
||||
RATING_FIELD_NUMBER: _ClassVar[int]
|
||||
GENRES_FIELD_NUMBER: _ClassVar[int]
|
||||
NCONSTS_FIELD_NUMBER: _ClassVar[int]
|
||||
year: int
|
||||
rating: int
|
||||
genres: int
|
||||
nconsts: int
|
||||
def __init__(self, year: _Optional[int] = ..., rating: _Optional[int] = ..., genres: _Optional[int] = ..., nconsts: _Optional[int] = ...) -> None: ...
|
||||
|
||||
class Request(_message.Message):
|
||||
__slots__ = ("tconsts", "n", "filter", "weight")
|
||||
TCONSTS_FIELD_NUMBER: _ClassVar[int]
|
||||
N_FIELD_NUMBER: _ClassVar[int]
|
||||
FILTER_FIELD_NUMBER: _ClassVar[int]
|
||||
WEIGHT_FIELD_NUMBER: _ClassVar[int]
|
||||
tconsts: _containers.RepeatedScalarFieldContainer[str]
|
||||
n: int
|
||||
filter: Filter
|
||||
weight: Weight
|
||||
def __init__(self, tconsts: _Optional[_Iterable[str]] = ..., n: _Optional[int] = ..., filter: _Optional[_Union[Filter, _Mapping]] = ..., weight: _Optional[_Union[Weight, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class Response(_message.Message):
|
||||
__slots__ = ("movies",)
|
||||
MOVIES_FIELD_NUMBER: _ClassVar[int]
|
||||
movies: _containers.RepeatedCompositeFieldContainer[RecommendedMovie]
|
||||
def __init__(self, movies: _Optional[_Iterable[_Union[RecommendedMovie, _Mapping]]] = ...) -> None: ...
|
||||
|
||||
class RecommendedMovie(_message.Message):
|
||||
__slots__ = ("tconst", "weights")
|
||||
TCONST_FIELD_NUMBER: _ClassVar[int]
|
||||
WEIGHTS_FIELD_NUMBER: _ClassVar[int]
|
||||
tconst: str
|
||||
weights: _containers.RepeatedScalarFieldContainer[str]
|
||||
def __init__(self, tconst: _Optional[str] = ..., weights: _Optional[_Iterable[str]] = ...) -> None: ...
|
97
recommender/proto/recommender_pb2_grpc.py
Normal file
97
recommender/proto/recommender_pb2_grpc.py
Normal file
@@ -0,0 +1,97 @@
|
||||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
"""Client and server classes corresponding to protobuf-defined services."""
|
||||
import grpc
|
||||
import warnings
|
||||
|
||||
import recommender_pb2 as recommender__pb2
|
||||
|
||||
GRPC_GENERATED_VERSION = '1.67.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 recommender_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 RecommenderStub(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.GetRecommendations = channel.unary_unary(
|
||||
'/recommender.Recommender/GetRecommendations',
|
||||
request_serializer=recommender__pb2.Request.SerializeToString,
|
||||
response_deserializer=recommender__pb2.Response.FromString,
|
||||
_registered_method=True)
|
||||
|
||||
|
||||
class RecommenderServicer(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
def GetRecommendations(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_RecommenderServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'GetRecommendations': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.GetRecommendations,
|
||||
request_deserializer=recommender__pb2.Request.FromString,
|
||||
response_serializer=recommender__pb2.Response.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'recommender.Recommender', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
||||
server.add_registered_method_handlers('recommender.Recommender', rpc_method_handlers)
|
||||
|
||||
|
||||
# This class is part of an EXPERIMENTAL API.
|
||||
class Recommender(object):
|
||||
"""Missing associated documentation comment in .proto file."""
|
||||
|
||||
@staticmethod
|
||||
def GetRecommendations(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,
|
||||
'/recommender.Recommender/GetRecommendations',
|
||||
recommender__pb2.Request.SerializeToString,
|
||||
recommender__pb2.Response.FromString,
|
||||
options,
|
||||
channel_credentials,
|
||||
insecure,
|
||||
call_credentials,
|
||||
compression,
|
||||
wait_for_ready,
|
||||
timeout,
|
||||
metadata,
|
||||
_registered_method=True)
|
15
recommender/pyproject.toml
Normal file
15
recommender/pyproject.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[project]
|
||||
name = "movier"
|
||||
version = "0.1.0"
|
||||
description = "Movier"
|
||||
readme = "README.md"
|
||||
requires-python = "==3.12.3"
|
||||
dependencies = [
|
||||
"grpcio>=1.67.0",
|
||||
"grpcio-reflection==1.67.0",
|
||||
"grpcio-tools==1.67.0",
|
||||
"polars==1.12.0",
|
||||
"psycopg2-binary==2.9.10",
|
||||
"scikit-learn==1.5.2",
|
||||
"grpcio-health-checking==1.67.1",
|
||||
]
|
441
recommender/recommend.py
Normal file
441
recommender/recommend.py
Normal file
@@ -0,0 +1,441 @@
|
||||
from typing import Any
|
||||
import numpy as np
|
||||
from sklearn.feature_extraction.text import CountVectorizer
|
||||
from sklearn.metrics.pairwise import cosine_similarity
|
||||
import polars as pl
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Filter:
|
||||
min_votes: int = None
|
||||
max_votes: int = None
|
||||
min_year: int = None
|
||||
max_year: int = None
|
||||
min_rating: float = None
|
||||
max_rating: float = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.min_votes is not None and self.min_votes < 0:
|
||||
raise ValueError("min_votes should be greater than or equal to 0")
|
||||
if self.max_votes is not None and self.max_votes < 0:
|
||||
raise ValueError("max_votes should be greater than or equal to 0")
|
||||
if self.min_votes is not None and self.max_votes is not None and self.min_votes > self.max_votes:
|
||||
raise ValueError("min_votes should be less than or equal to max_votes")
|
||||
|
||||
if self.min_year is not None and self.min_year < 0:
|
||||
raise ValueError("min_year should be greater than or equal to 0")
|
||||
if self.max_year is not None and self.max_year < 0:
|
||||
raise ValueError("max_year should be greater than or equal to 0")
|
||||
if self.min_year is not None and self.max_year is not None and self.min_year > self.max_year:
|
||||
raise ValueError("min_year should be less than or equal to max_year")
|
||||
|
||||
if self.min_rating is not None and self.min_rating < 0:
|
||||
raise ValueError("min_rating should be greater than or equal to 0")
|
||||
if self.max_rating is not None and self.max_rating < 0:
|
||||
raise ValueError("max_rating should be greater than or equal to 0")
|
||||
if self.min_rating is not None and self.max_rating is not None and self.min_rating > self.max_rating:
|
||||
raise ValueError("min_rating should be less than or equal to max_rating")
|
||||
|
||||
@dataclass
|
||||
class Weight:
|
||||
year: int = 100
|
||||
rating: int = 100
|
||||
genres: int = 100
|
||||
nconsts: int = 100
|
||||
|
||||
def __post_init__(self):
|
||||
total_sum = 0
|
||||
total_count = 0
|
||||
for k, v in self.__dict__.items():
|
||||
if v < 0:
|
||||
raise ValueError(f'Weight for {k} must be greater than or equal to 0, got {v}')
|
||||
if v > 0:
|
||||
total_sum += v
|
||||
total_count += 1
|
||||
|
||||
if total_sum < 100:
|
||||
raise ValueError(f'Total sum of weights must be at least 100, got {total_sum}')
|
||||
if total_count*100 != total_sum:
|
||||
raise ValueError(f'Total sum of weights must be {total_count*100}, got {total_sum}')
|
||||
|
||||
class Recommender:
|
||||
def __init__(
|
||||
self,
|
||||
filter_: Filter = Filter(),
|
||||
weight: Weight = Weight()
|
||||
) -> None:
|
||||
self.filter = filter_
|
||||
self.weight = weight
|
||||
self.sql_where_clause = ''
|
||||
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f"genres != ''")
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f"nconsts != ''")
|
||||
|
||||
if filter_.min_votes:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'votes >= {filter_.min_votes}')
|
||||
if filter_.max_votes:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'votes <= {filter_.max_votes}')
|
||||
if filter_.min_year:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'year >= {filter_.min_year}')
|
||||
if filter_.max_year:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'year <= {filter_.max_year}')
|
||||
if filter_.min_rating:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'rating >= {filter_.min_rating}')
|
||||
if filter_.max_rating:
|
||||
self.sql_where_clause = self.add_sql_where_clause(self.sql_where_clause, f'rating <= {filter_.max_rating}')
|
||||
|
||||
def add_sql_where_clause(self, old: str, new: str) -> None:
|
||||
return f'WHERE {new}' if old == '' else f'{old} AND {new}'
|
||||
|
||||
def get_ordered_year_from_sql(self, conn, reference_year: int) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
reference_year: int - year to sort by closest
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame:
|
||||
First sorted by closest year, then by number of votes (descending).
|
||||
| year_index (uint32) | tconst (str) |
|
||||
| --- | --- |
|
||||
| 0 | tt0000001 |
|
||||
| 1 | tt0000002 |
|
||||
| 2 | tt0000003 |
|
||||
| ... | ... |
|
||||
"""
|
||||
return pl.read_database(
|
||||
f"""
|
||||
SELECT tconst
|
||||
FROM imdb
|
||||
{self.sql_where_clause}
|
||||
ORDER BY ABS(year - {reference_year}), votes DESC
|
||||
""",
|
||||
conn, schema_overrides={'tconst': str}
|
||||
).with_row_index('year_index')
|
||||
|
||||
def get_ordered_rating_from_sql(self, conn, reference_rating: int) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
reference_rating: int - rating to sort by closest
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame:
|
||||
First sorted by closest rating, then by number of votes (descending).
|
||||
| rating_index (uint32) | tconst (str) |
|
||||
| --- | --- |
|
||||
| 0 | tt0000001 |
|
||||
| 1 | tt0000002 |
|
||||
| 2 | tt0000003 |
|
||||
| ... | ... |
|
||||
"""
|
||||
return pl.read_database(
|
||||
f"""
|
||||
SELECT tconst
|
||||
FROM imdb
|
||||
{self.sql_where_clause}
|
||||
ORDER BY ABS(rating - {reference_rating}), votes DESC
|
||||
""",
|
||||
conn, schema_overrides={'tconst': str}
|
||||
).with_row_index('rating_index')
|
||||
|
||||
def get_ordered_genres_from_df(self, df: pl.DataFrame, reference_genres: str) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
df: DataFrame
|
||||
| tconst (str) | genres (str) | votes (uint32) |
|
||||
| --- | --- | --- |
|
||||
| tt0000001 | Drama, Romance | 123 |
|
||||
| tt0000002 | Comedy, Drama | 456 |
|
||||
| tt0000003 | Action, Drama | 789 |
|
||||
| ... | ... | ... |
|
||||
reference_genres: str - genres to calculate cosine similarities
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame:
|
||||
First sorted by cosine similarities genres (descending) and then by number of votes (descending).
|
||||
| genres_index (uint32) | tconst (str) |
|
||||
| --- | --- |
|
||||
| 0 | tt0000001 |
|
||||
| 1 | tt0000002 |
|
||||
| 2 | tt0000003 |
|
||||
| ... | ... |
|
||||
"""
|
||||
df = df.with_row_index('genres_index')
|
||||
|
||||
genres_cv = CountVectorizer(dtype=np.uint8, token_pattern=r"(?u)[\w'-]+")
|
||||
genres_count_matrix = genres_cv.fit_transform(df['genres'])
|
||||
|
||||
genres_sims = cosine_similarity(genres_cv.transform([reference_genres]), genres_count_matrix)[0]
|
||||
|
||||
return pl.DataFrame(
|
||||
{
|
||||
'tconst': df['tconst'],
|
||||
'cosine_similarity': genres_sims,
|
||||
'votes': df['votes']
|
||||
}, schema={'tconst': str, 'cosine_similarity': pl.Float32, 'votes': pl.UInt32}
|
||||
).\
|
||||
sort(['cosine_similarity', 'votes'], descending=True).\
|
||||
drop(['cosine_similarity', 'votes']).\
|
||||
with_row_index('genres_index')
|
||||
|
||||
def get_ordered_nconsts_from_df(self, df: pl.DataFrame, reference_nconsts: str) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
df: DataFrame
|
||||
| tconst (str) | nconsts (str) | votes (uint32) |
|
||||
| --- | --- | --- |
|
||||
| tt0000001 | nm0000001, nm0000002 | 123 |
|
||||
| tt0000002 | nm0000001, nm0000003 | 456 |
|
||||
| tt0000003 | nm0000004, nm0000002 | 789 |
|
||||
| ... | ... | ... |
|
||||
reference_nconsts: str - nconsts to calculate cosine similarities
|
||||
|
||||
Returns
|
||||
-------
|
||||
df: DataFrame
|
||||
First sorted by cosine similarities of nconsts (descending) and then by number of votes (descending).
|
||||
| nconsts_index (uint32) | tconst (str) |
|
||||
| --- | --- |
|
||||
| 0 | tt0000001 |
|
||||
| 1 | tt0000002 |
|
||||
| 2 | tt0000003 |
|
||||
| ... | ... |
|
||||
"""
|
||||
df = df.with_row_index('nconsts_index')
|
||||
|
||||
nconsts_cv = CountVectorizer(dtype=np.uint8, token_pattern=r"(?u)[\w'-]+")
|
||||
nconsts_count_matrix = nconsts_cv.fit_transform(df['nconsts'])
|
||||
|
||||
nconsts_sims = cosine_similarity(nconsts_cv.transform([reference_nconsts]), nconsts_count_matrix)[0]
|
||||
|
||||
return pl.DataFrame(
|
||||
{
|
||||
'tconst': df['tconst'],
|
||||
'cosine_similarity': nconsts_sims,
|
||||
'votes': df['votes']
|
||||
}, schema={'tconst': str, 'cosine_similarity': pl.Float32, 'votes': pl.UInt32}
|
||||
).\
|
||||
sort(['cosine_similarity', 'votes'], descending=True).\
|
||||
drop(['cosine_similarity', 'votes']).\
|
||||
with_row_index('nconsts_index')
|
||||
|
||||
def get_main_df(self, conn) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame:
|
||||
| tconst (str) | genres (str) | nconsts (str) | votes (uint32) |
|
||||
| --- | --- | --- | --- |
|
||||
| tt0000001 | Drama, Romance | nm0000001, nm0000002 | 123 |
|
||||
| tt0000002 | Comedy, Drama | nm0000001, nm0000003 | 456 |
|
||||
| tt0000003 | Action, Drama | nm0000004, nm0000002 | 789 |
|
||||
| ... | ... | ... | ... |
|
||||
"""
|
||||
return pl.read_database(
|
||||
f"""
|
||||
SELECT tconst, genres, nconsts, votes
|
||||
FROM imdb
|
||||
{self.sql_where_clause}
|
||||
""", conn, schema_overrides={'tconst': str, 'genres': str, 'nconsts': str, 'votes': pl.UInt32}
|
||||
)
|
||||
|
||||
def get_row_by_tconst(self, conn, tconst: str) -> dict[str, Any]:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
tconst: str - tconst to get row from database
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict: row from database
|
||||
{
|
||||
'tconst': str,
|
||||
'year': int,
|
||||
'genres': str,
|
||||
'nconsts': str,
|
||||
'rating': float,
|
||||
'votes': int
|
||||
}
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError: if tconst is not found in database
|
||||
"""
|
||||
with conn.cursor() as cursor:
|
||||
cursor.execute(
|
||||
f"""
|
||||
SELECT tconst, year, genres, nconsts, rating, votes
|
||||
FROM imdb
|
||||
WHERE tconst = '{tconst}'
|
||||
"""
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
if row is None:
|
||||
raise ValueError(f"tconst '{tconst}' not found")
|
||||
return {cursor.description[i][0]: value for i, value in enumerate(row)}
|
||||
|
||||
def set_average(self, column_name: str, features: list[str], merged_df: pl.DataFrame) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
column_name: str - name of the column to store the average
|
||||
features: list[str] - list of features to calculate the average
|
||||
merged_df: DataFrame - merged DataFrame of all features
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame: Same DataFrame with the argument column_name added to it with the average of all features
|
||||
"""
|
||||
average = merged_df[f'{features[0]}_index'] * self.weight.__getattribute__(features[0])
|
||||
for feature in features[1:]:
|
||||
average += merged_df[f'{feature}_index'] * self.weight.__getattribute__(feature)
|
||||
|
||||
return merged_df.with_columns(**{column_name: (average / (len(features) * 100))})
|
||||
|
||||
def get_single_recommendation(self, conn, tconst: str, features: list[str]) -> pl.DataFrame:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
tconst: str - tconst to get recommendations
|
||||
features: list[str] - list of features to calculate the average
|
||||
|
||||
Returns
|
||||
-------
|
||||
DataFrame: DataFrame with the average of all features
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError: if no recommendations found
|
||||
"""
|
||||
reference_row = self.get_row_by_tconst(conn, tconst)
|
||||
trained: dict[str, pl.DataFrame] = {}
|
||||
|
||||
if 'year' in features:
|
||||
df = self.get_ordered_year_from_sql(conn, reference_year=reference_row['year'])
|
||||
if len(df) > 0:
|
||||
trained['year'] = df
|
||||
if 'rating' in features:
|
||||
df = self.get_ordered_rating_from_sql(conn, reference_rating=reference_row['rating'])
|
||||
if len(df) > 0:
|
||||
trained['rating'] = df
|
||||
if 'genres' in features or 'nconsts' in features:
|
||||
main_df = self.get_main_df(conn)
|
||||
if len(main_df) > 0:
|
||||
if 'genres' in features:
|
||||
trained['genres'] = self.get_ordered_genres_from_df(
|
||||
pl.DataFrame(
|
||||
{
|
||||
'tconst': main_df['tconst'],
|
||||
'genres': main_df['genres'],
|
||||
'votes': main_df['votes']
|
||||
}
|
||||
), reference_genres=reference_row['genres']
|
||||
)
|
||||
if 'nconsts' in features:
|
||||
trained['nconsts'] = self.get_ordered_nconsts_from_df(
|
||||
pl.DataFrame(
|
||||
{
|
||||
'tconst': main_df['tconst'],
|
||||
'nconsts': main_df['nconsts'],
|
||||
'votes': main_df['votes']
|
||||
}
|
||||
), reference_nconsts=reference_row['nconsts']
|
||||
)
|
||||
|
||||
if len(trained) == 0:
|
||||
raise ValueError("No recommendations found, try changing the filter or weight")
|
||||
if len(features) > 1:
|
||||
merged = pl.concat(trained.values(), how='align')
|
||||
return self.set_average(
|
||||
"average", features=features, merged_df=merged
|
||||
)
|
||||
else:
|
||||
trained_df = trained[features[0]]
|
||||
return trained_df.with_columns(
|
||||
average=trained_df[f'{features[0]}_index']
|
||||
)
|
||||
|
||||
def get_recommendations(self, conn, tconsts: list[str], n: int = 5) -> dict[str, list[str]]:
|
||||
"""
|
||||
Args
|
||||
----
|
||||
conn: psycopg2 connection object
|
||||
tconsts: list[str] - list of tconsts to get recommendations
|
||||
n: int - number of recommendations to get
|
||||
|
||||
Returns
|
||||
-------
|
||||
list[dict[str, list[str]]]: list of dictionaries with tconst (ascending)
|
||||
as key and list of weights of columns as value (ascending)
|
||||
"""
|
||||
self.sql_where_clause = self.add_sql_where_clause(
|
||||
self.sql_where_clause,
|
||||
f"tconst NOT IN ({', '.join(f"'{tconst}'" for tconst in tconsts)})"
|
||||
)
|
||||
|
||||
features: list[str] = []
|
||||
if self.weight.year > 0:
|
||||
features.append('year')
|
||||
if self.weight.rating > 0:
|
||||
features.append('rating')
|
||||
if self.weight.genres > 0:
|
||||
features.append('genres')
|
||||
if self.weight.nconsts > 0:
|
||||
features.append('nconsts')
|
||||
|
||||
if len(tconsts) == 1:
|
||||
merged_df = self.get_single_recommendation(conn, tconsts[0], features).sort('average')[:n]
|
||||
|
||||
responses: dict[str, list[str]] = dict()
|
||||
for row in merged_df.rows(named=True):
|
||||
row.pop('average')
|
||||
t: str = row.pop('tconst')
|
||||
for f in features:
|
||||
row[f] = row[f"{f}_index"] / self.weight.__getattribute__(f)
|
||||
row.pop(f"{f}_index")
|
||||
weights: list[str] = [column for column, _ in sorted(row.items(), key=lambda item: item[1])]
|
||||
responses[t] = weights
|
||||
|
||||
return responses
|
||||
else:
|
||||
trained_dfs: dict[str, pl.DataFrame] = {}
|
||||
for tconst in tconsts:
|
||||
df = self.get_single_recommendation(conn, tconst, features)
|
||||
trained_dfs[tconst] = pl.DataFrame({
|
||||
'tconst': df['tconst'],
|
||||
f"{tconst}_average": df['average']
|
||||
})
|
||||
|
||||
merged_df: pl.DataFrame = pl.concat(trained_dfs.values(), how='align')
|
||||
|
||||
all_average = merged_df[f"{tconsts[0]}_average"]
|
||||
for tconst in tconsts[1:]:
|
||||
all_average += merged_df[f"{tconst}_average"]
|
||||
merged_df = merged_df.with_columns(all_average=all_average / len(tconsts)).sort('all_average')[:n]
|
||||
|
||||
responses: dict[str, list[str]] = dict()
|
||||
for row in merged_df.rows(named=True):
|
||||
row.pop('all_average')
|
||||
curretn_tconst: str = row.pop('tconst')
|
||||
for tconst in tconsts:
|
||||
row[tconst] = row[f"{tconst}_average"]
|
||||
row.pop(f"{tconst}_average")
|
||||
weights: list[str] = [column for column, _ in sorted(row.items(), key=lambda item: item[1])]
|
||||
responses[curretn_tconst] = weights
|
||||
|
||||
return responses
|
283
recommender/uv.lock
generated
Normal file
283
recommender/uv.lock
generated
Normal file
@@ -0,0 +1,283 @@
|
||||
version = 1
|
||||
requires-python = "==3.12.3"
|
||||
|
||||
[[package]]
|
||||
name = "grpcio"
|
||||
version = "1.67.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/20/53/d9282a66a5db45981499190b77790570617a604a38f3d103d0400974aeb5/grpcio-1.67.1.tar.gz", hash = "sha256:3dc2ed4cabea4dc14d5e708c2b426205956077cc5de419b4d4079315017e9732", size = 12580022 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/25/6f95bd18d5f506364379eabc0d5874873cc7dbdaf0757df8d1e82bc07a88/grpcio-1.67.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:267d1745894200e4c604958da5f856da6293f063327cb049a51fe67348e4f953", size = 5089809 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/3f/d79e32e5d0354be33a12db2267c66d3cfeff700dd5ccdd09fd44a3ff4fb6/grpcio-1.67.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:85f69fdc1d28ce7cff8de3f9c67db2b0ca9ba4449644488c1e0303c146135ddb", size = 10981985 },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/f2/36fbc14b3542e3a1c20fb98bd60c4732c55a44e374a4eb68f91f28f14aab/grpcio-1.67.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f26b0b547eb8d00e195274cdfc63ce64c8fc2d3e2d00b12bf468ece41a0423a0", size = 5588770 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/af/bbc1305df60c4e65de8c12820a942b5e37f9cf684ef5e49a63fbb1476a73/grpcio-1.67.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4422581cdc628f77302270ff839a44f4c24fdc57887dc2a45b7e53d8fc2376af", size = 6214476 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/cf/1d4c3e93efa93223e06a5c83ac27e32935f998bc368e276ef858b8883154/grpcio-1.67.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7616d2ded471231c701489190379e0c311ee0a6c756f3c03e6a62b95a7146e", size = 5850129 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/ca/26195b66cb253ac4d5ef59846e354d335c9581dba891624011da0e95d67b/grpcio-1.67.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8a00efecde9d6fcc3ab00c13f816313c040a28450e5e25739c24f432fc6d3c75", size = 6568489 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/94/16550ad6b3f13b96f0856ee5dfc2554efac28539ee84a51d7b14526da985/grpcio-1.67.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:699e964923b70f3101393710793289e42845791ea07565654ada0969522d0a38", size = 6149369 },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/0d/4c3b2587e8ad7f121b597329e6c2620374fccbc2e4e1aa3c73ccc670fde4/grpcio-1.67.1-cp312-cp312-win32.whl", hash = "sha256:4e7b904484a634a0fff132958dabdb10d63e0927398273917da3ee103e8d1f78", size = 3599176 },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/36/0c03e2d80db69e2472cf81c6123aa7d14741de7cf790117291a703ae6ae1/grpcio-1.67.1-cp312-cp312-win_amd64.whl", hash = "sha256:5721e66a594a6c4204458004852719b38f3d5522082be9061d6510b455c90afc", size = 4346574 },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/d2/2f032b7a153c7723ea3dea08bffa4bcaca9e0e5bdf643ce565b76da87461/grpcio-1.67.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa0162e56fd10a5547fac8774c4899fc3e18c1aa4a4759d0ce2cd00d3696ea6b", size = 5091487 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/ae/ea2ff6bd2475a082eb97db1104a903cf5fc57c88c87c10b3c3f41a184fc0/grpcio-1.67.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:beee96c8c0b1a75d556fe57b92b58b4347c77a65781ee2ac749d550f2a365dc1", size = 10943530 },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/62/646be83d1a78edf8d69b56647327c9afc223e3140a744c59b25fbb279c3b/grpcio-1.67.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a93deda571a1bf94ec1f6fcda2872dad3ae538700d94dc283c672a3b508ba3af", size = 5589079 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d0/25/71513d0a1b2072ce80d7f5909a93596b7ed10348b2ea4fdcbad23f6017bf/grpcio-1.67.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6f255980afef598a9e64a24efce87b625e3e3c80a45162d111a461a9f92955", size = 6213542 },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/9a/d21236297111052dcb5dc85cd77dc7bf25ba67a0f55ae028b2af19a704bc/grpcio-1.67.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e838cad2176ebd5d4a8bb03955138d6589ce9e2ce5d51c3ada34396dbd2dba8", size = 5850211 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/fe/70b1da9037f5055be14f359026c238821b9bcf6ca38a8d760f59a589aacd/grpcio-1.67.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a6703916c43b1d468d0756c8077b12017a9fcb6a1ef13faf49e67d20d7ebda62", size = 6572129 },
|
||||
{ url = "https://files.pythonhosted.org/packages/74/0d/7df509a2cd2a54814598caf2fb759f3e0b93764431ff410f2175a6efb9e4/grpcio-1.67.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:917e8d8994eed1d86b907ba2a61b9f0aef27a2155bca6cbb322430fc7135b7bb", size = 6149819 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/08/bc3b0155600898fd10f16b79054e1cca6cb644fa3c250c0fe59385df5e6f/grpcio-1.67.1-cp313-cp313-win32.whl", hash = "sha256:e279330bef1744040db8fc432becc8a727b84f456ab62b744d3fdb83f327e121", size = 3596561 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/96/44759eca966720d0f3e1b105c43f8ad4590c97bf8eb3cd489656e9590baa/grpcio-1.67.1-cp313-cp313-win_amd64.whl", hash = "sha256:fa0c739ad8b1996bd24823950e3cb5152ae91fca1c09cc791190bf1627ffefba", size = 4346042 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-health-checking"
|
||||
version = "1.67.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/64/dd/e3b339fa44dc75b501a1a22cb88f1af5b1f8c964488f19c4de4cfbbf05ba/grpcio_health_checking-1.67.1.tar.gz", hash = "sha256:ca90fa76a6afbb4fda71d734cb9767819bba14928b91e308cffbb0c311eb941e", size = 16775 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/8d/7a9878dca6616b48093d71c52d0bc79cb2dd1a2698ff6f5ce7406306de12/grpcio_health_checking-1.67.1-py3-none-any.whl", hash = "sha256:93753da5062152660aef2286c9b261e07dd87124a65e4dc9fbd47d1ce966b39d", size = 18924 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-reflection"
|
||||
version = "1.67.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/fe/69/08a7c3c3524e3af650d22bd8e0015e35bc284eae919fbb38bc2702809d07/grpcio_reflection-1.67.0.tar.gz", hash = "sha256:c47143738b1897b6ce4af5e0e338c85c9aee5fdcbb3355d368a8dcae46d8933c", size = 18818 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/cf/343839a5800c38a0231b78708e84db1fa4797751cb44892093ef8b215377/grpcio_reflection-1.67.0-py3-none-any.whl", hash = "sha256:2a2f6d865adecd8d5f81b1a6858252d61b1897997f0656c73807045c5e79c421", size = 22691 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grpcio-tools"
|
||||
version = "1.67.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "protobuf" },
|
||||
{ name = "setuptools" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e7/f8/62e15867651b72f6f95313e21d81f5f1c210b69a4cc664aecf52ec4c8a53/grpcio_tools-1.67.0.tar.gz", hash = "sha256:181b3d4e61b83142c182ec366f3079b0023509743986e54c9465ca38cac255f8", size = 5159163 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/b6/57e67c0244db8d7c0c312041293b806bfb1c9d66c26159e6faf39cc10356/grpcio_tools-1.67.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:dca7f053cbdb26a587d4410ddb893877c585fb60a31f22fdd128e4f7c4dab27c", size = 2307646 },
|
||||
{ url = "https://files.pythonhosted.org/packages/52/43/837f08b85b04ac225aebe1d7da1a7a79fc313f231306c865b5112cef7dc4/grpcio_tools-1.67.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:de8c4f68ffa690769d84329c17c7fdd5fbe4c61b8f8a0de03f1ad8ef8bb06963", size = 5525447 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/5f/adb8b87f5c403ba53529b6645184beddfa63abf2c524a6dabaa430e6bab3/grpcio_tools-1.67.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:6e4ecb24c27a78f09fead45d4ed873805d6026124ccb6793b6fb93a490b78ddf", size = 2281767 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/cd/3d6a7971e28b96cb618abb281325517443744ecfe48aa03f27a17cd5d4e1/grpcio_tools-1.67.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:004d6ef1b5f724480f05c0bdc904bf8c78c43d633c537d99abe51b52ce0cadeb", size = 2617363 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/a9/b8f1eae3db0f1b6f9548bd2032f48cb6f1ec9bc6781436d52dff4b352fab/grpcio_tools-1.67.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dd257072c86eb9b36791b3674a513a215ba76bbdd38fc228f0e8c6dc5ce3524", size = 2415322 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/fc/0045bf2e5c97a5ffe0ff2c9a4e4a62894402e8d7094162c2084a809c9d1c/grpcio_tools-1.67.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a8cca551317ed26e17d13b6ee27b2bd62f5fe9b3842b4e88389deb984f995848", size = 3225044 },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/73/eaf40958dd648dd98a0fbd30df2b51c5beb7ee24127c1f0bb99ea44fd435/grpcio_tools-1.67.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a7ac3b4f837c693142f6688b629d1f6408f6ab250d927159b572555f5339fe25", size = 2870418 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b4/77/e307e91816123444ff657bbae2269cb912f31a9390118ed371bde9d0c1f3/grpcio_tools-1.67.0-cp312-cp312-win32.whl", hash = "sha256:95feec33388e2a8f72c360a68efe6f0bfed9c771e94d21b7f2359d0010f60219", size = 940540 },
|
||||
{ url = "https://files.pythonhosted.org/packages/be/2a/0c1a64e88fbc17235b68d3178be6cf4a69aea5bd1deed683c0bbd2f5e9f9/grpcio_tools-1.67.0-cp312-cp312-win_amd64.whl", hash = "sha256:50a31d035193ebe7154181eac84734e25bdcdb36adba849d3b2adf1c3b0c382b", size = 1090427 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1e/44/cfe3aa14158d8becffd7823d5147039378d448097fb91ec723ad8b6d60cb/grpcio_tools-1.67.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:9ecb7c2e5da052a3feaeaa83d8f2a946a8feec8a50751b0e6175da982b49ebb1", size = 2307454 },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/9c/99b345764b355b11f1ea7d160276e9eb9d32a1c77e4bfaa2db3de025f7d2/grpcio_tools-1.67.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3c52164f2b9d41c6d75464bb45f45737dcb421e92e98d85d94fda100c67a24d8", size = 5518036 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/8d/f1b4378335f39f37f9b440b26e7ecaa19660eef6b438ac7d8c5ad7e96d73/grpcio_tools-1.67.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:471f58b919767290260d427dd9b760796e6208ee5fcda2f76bb8bd585ff842ec", size = 2281084 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/ec/c31e5ec4d01f17e38fce03dfc3e47880bc25e1dd681fffe00fab04e21e33/grpcio_tools-1.67.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c6bcdf38f672721c093c92b1fb1f9a02a365acc5bd42e1c69fe6e904b26081", size = 2616930 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/2b/82f3451ad9471ba946ca0e2ff43dc3269030d5e963d86d1cfe199e07dc38/grpcio_tools-1.67.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:833b1eb9c03d28a798294523f75294055eff78fa897adf797876337b901afeb9", size = 2414635 },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/59/2811cb32947f21075c0b53a87aa652653154e6db3f03766e29b1f80a4bb6/grpcio_tools-1.67.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1db92ad6ade1946fc5705eb04956fcfdb3a0a4682de8dc3fce31cb97b6e4fcb8", size = 3224330 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/25/dde7cef6e639dae24e93f4385e689f554a1d2a531d5703786a6b7b8366f3/grpcio_tools-1.67.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:38128310ded818e1044c0cd0979d76f7c0d3c3946a526a8aa39cd258624c3bf3", size = 2869633 },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/dc/313bbdc01e4bd062d1e86cf667d81338670b9f44afa81a7b4e5ebf566ff4/grpcio_tools-1.67.0-cp313-cp313-win32.whl", hash = "sha256:db57930dc20ab678311727883bdb9f122daf06c14f3fd3067c9ccedb7eb056c3", size = 939997 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/07/5227eb621973b6afe7e6b3d4c637ed14069b7f5f7f45cc804c59df791304/grpcio_tools-1.67.0-cp313-cp313-win_amd64.whl", hash = "sha256:7de44d8d3bb920a4973a559f2950d03382fa4aed4880306416ffa73d24838477", size = 1089819 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "joblib"
|
||||
version = "1.4.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/64/33/60135848598c076ce4b231e1b1895170f45fbcaeaa2c9d5e38b04db70c35/joblib-1.4.2.tar.gz", hash = "sha256:2382c5816b2636fbd20a09e0f4e9dad4736765fdfb7dca582943b9c1366b3f0e", size = 2116621 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl", hash = "sha256:06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6", size = 301817 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "movier"
|
||||
version = "0.1.0"
|
||||
source = { virtual = "." }
|
||||
dependencies = [
|
||||
{ name = "grpcio" },
|
||||
{ name = "grpcio-health-checking" },
|
||||
{ name = "grpcio-reflection" },
|
||||
{ name = "grpcio-tools" },
|
||||
{ name = "polars" },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "scikit-learn" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "grpcio", specifier = ">=1.67.0" },
|
||||
{ name = "grpcio-health-checking", specifier = "==1.67.1" },
|
||||
{ name = "grpcio-reflection", specifier = "==1.67.0" },
|
||||
{ name = "grpcio-tools", specifier = "==1.67.0" },
|
||||
{ name = "polars", specifier = "==1.12.0" },
|
||||
{ name = "psycopg2-binary", specifier = "==2.9.10" },
|
||||
{ name = "scikit-learn", specifier = "==1.5.2" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.1.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/ca/1166b75c21abd1da445b97bf1fa2f14f423c6cfb4fc7c4ef31dccf9f6a94/numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761", size = 20166090 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/f0/385eb9970309643cbca4fc6eebc8bb16e560de129c91258dfaa18498da8b/numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e", size = 20849658 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/4a/765b4607f0fecbb239638d610d04ec0a0ded9b4951c56dc68cef79026abf/numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958", size = 13492258 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/a7/2332679479c70b68dccbf4a8eb9c9b5ee383164b161bee9284ac141fbd33/numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8", size = 5090249 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/67/4aa00316b3b981a822c7a239d3a8135be2a6945d1fd11d0efb25d361711a/numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564", size = 6621704 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/da/1a429ae58b3b6c364eeec93bf044c532f2ff7b48a52e41050896cf15d5b1/numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512", size = 13606089 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/3e/3757f304c704f2f0294a6b8340fcf2be244038be07da4cccf390fa678a9f/numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b", size = 16043185 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/97/75329c28fea3113d00c8d2daf9bc5828d58d78ed661d8e05e234f86f0f6d/numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc", size = 16410751 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/7a/442965e98b34e0ae9da319f075b387bcb9a1e0658276cc63adb8c9686f7b/numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0", size = 14082705 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/b6/26108cf2cfa5c7e03fb969b595c93131eab4a399762b51ce9ebec2332e80/numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9", size = 6239077 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/84/fa11dad3404b7634aaab50733581ce11e5350383311ea7a7010f464c0170/numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a", size = 12566858 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/0b/620591441457e25f3404c8057eb924d04f161244cb8a3680d529419aa86e/numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f", size = 20836263 },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/e1/210b2d8b31ce9119145433e6ea78046e30771de3fe353f313b2778142f34/numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598", size = 13507771 },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/44/aa9ee3caee02fa5a45f2c3b95cafe59c44e4b278fbbf895a93e88b308555/numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57", size = 5075805 },
|
||||
{ url = "https://files.pythonhosted.org/packages/78/d6/61de6e7e31915ba4d87bbe1ae859e83e6582ea14c6add07c8f7eefd8488f/numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe", size = 6608380 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/46/48bdf9b7241e317e6cf94276fe11ba673c06d1fdf115d8b4ebf616affd1a/numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43", size = 13602451 },
|
||||
{ url = "https://files.pythonhosted.org/packages/70/50/73f9a5aa0810cdccda9c1d20be3cbe4a4d6ea6bfd6931464a44c95eef731/numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56", size = 16039822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/cd/098bc1d5a5bc5307cfc65ee9369d0ca658ed88fbd7307b0d49fab6ca5fa5/numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a", size = 16411822 },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/a2/7d4467a2a6d984549053b37945620209e702cf96a8bc658bc04bba13c9e2/numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef", size = 14079598 },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/6a/d64514dcecb2ee70bfdfad10c42b76cab657e7ee31944ff7a600f141d9e9/numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f", size = 6236021 },
|
||||
{ url = "https://files.pythonhosted.org/packages/bb/f9/12297ed8d8301a401e7d8eb6b418d32547f1d700ed3c038d325a605421a4/numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed", size = 12560405 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/45/7f9244cd792e163b334e3a7f02dff1239d2890b6f37ebf9e82cbe17debc0/numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f", size = 20859062 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/b4/a084218e7e92b506d634105b13e27a3a6645312b93e1c699cc9025adb0e1/numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4", size = 13515839 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/45/58ed3f88028dcf80e6ea580311dc3edefdd94248f5770deb980500ef85dd/numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e", size = 5116031 },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/a8/eb689432eb977d83229094b58b0f53249d2209742f7de529c49d61a124a0/numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0", size = 6629977 },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/a3/5355ad51ac73c23334c7caaed01adadfda49544f646fcbfbb4331deb267b/numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408", size = 13575951 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/70/ea9646d203104e647988cb7d7279f135257a6b7e3354ea6c56f8bafdb095/numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6", size = 16022655 },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/ce/7fc0612903e91ff9d0b3f2eda4e18ef9904814afcae5b0f08edb7f637883/numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f", size = 16399902 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/62/1d3204313357591c913c32132a28f09a26357e33ea3c4e2fe81269e0dca1/numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17", size = 14067180 },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/d7/78a40ed1d80e23a774cb8a34ae8a9493ba1b4271dde96e56ccdbab1620ef/numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48", size = 6291907 },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/09/a5ab407bd7f5f5599e6a9261f964ace03a73e7c6928de906981c31c38082/numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4", size = 12644098 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polars"
|
||||
version = "1.12.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5f/df/55127a3099e990b45ce3a29ab6789a083451e76e7109fb754aad5525360b/polars-1.12.0.tar.gz", hash = "sha256:fb5c92de1a8f7d0a3f923fe48ea89eb518bdf55315ae917012350fa072bd64f4", size = 4090738 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/ae/77c7ec395d9361ae2086693af1947c9a2b21346ba3faf092bb154b735227/polars-1.12.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8f3c4e4e423c373dda07b4c8a7ff12aa02094b524767d0ca306b1eba67f2d99e", size = 32923786 },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/1c/60736d5588309eb528c52538e116593cb275310bab82ba28702cd87a76d1/polars-1.12.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:aa6f9862f0cec6353243920d9b8d858c21ec8f25f91af203dea6ff91980e140d", size = 28887255 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/3e/31257118e7e087fa27c230b8fadf8ff15d521140bf58558dc889ee0c9c5e/polars-1.12.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb03647b5160737d2119532ee8ffe825de1d19d87f81bbbb005131786f7d59b", size = 34126501 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/e6/d03053e6064d262f2ec41172a5092b08fc20d10c059dda6c9460371cfd7e/polars-1.12.0-cp39-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:ea96aba5eb3dab8f0e6abf05ab3fc2136b329261860ef8661d20f5456a2d78e0", size = 30479546 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/28/3d44ddf56a5c95272b202ce8aa0e9b818a1310e83525c4c29176b538ae7c/polars-1.12.0-cp39-abi3-win_amd64.whl", hash = "sha256:a228a4b320a36d03a9ec9dfe7241b6d80a2f119b2dceb1da953166655e4cf43c", size = 33790337 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "5.28.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/74/6e/e69eb906fddcb38f8530a12f4b410699972ab7ced4e21524ece9d546ac27/protobuf-5.28.3.tar.gz", hash = "sha256:64badbc49180a5e401f373f9ce7ab1d18b63f7dd4a9cdc43c92b9f0b481cef7b", size = 422479 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/c5/05163fad52d7c43e124a545f1372d18266db36036377ad29de4271134a6a/protobuf-5.28.3-cp310-abi3-win32.whl", hash = "sha256:0c4eec6f987338617072592b97943fdbe30d019c56126493111cf24344c1cc24", size = 419624 },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/4c/4563ebe001ff30dca9d7ed12e471fa098d9759712980cde1fd03a3a44fb7/protobuf-5.28.3-cp310-abi3-win_amd64.whl", hash = "sha256:91fba8f445723fcf400fdbe9ca796b19d3b1242cd873907979b9ed71e4afe868", size = 431464 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/f2/baf397f3dd1d3e4af7e3f5a0382b868d25ac068eefe1ebde05132333436c/protobuf-5.28.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a3f6857551e53ce35e60b403b8a27b0295f7d6eb63d10484f12bc6879c715687", size = 414743 },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/50/cd61a358ba1601f40e7d38bcfba22e053f40ef2c50d55b55926aecc8fec7/protobuf-5.28.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:3fa2de6b8b29d12c61911505d893afe7320ce7ccba4df913e2971461fa36d584", size = 316511 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/ae/3257b09328c0b4e59535e497b0c7537d4954038bdd53a2f0d2f49d15a7c4/protobuf-5.28.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:712319fbdddb46f21abb66cd33cb9e491a5763b2febd8f228251add221981135", size = 316624 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ad/c3/2377c159e28ea89a91cf1ca223f827ae8deccb2c9c401e5ca233cd73002f/protobuf-5.28.3-py3-none-any.whl", hash = "sha256:cee1757663fa32a1ee673434fcf3bf24dd54763c79690201208bafec62f19eed", size = 169511 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.9.10"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/cb/0e/bdc8274dc0585090b4e3432267d7be4dfbfd8971c0fa59167c711105a6bf/psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", size = 385764 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/49/7d/465cc9795cf76f6d329efdafca74693714556ea3891813701ac1fee87545/psycopg2_binary-2.9.10-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", size = 3044771 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/31/6d225b7b641a1a2148e3ed65e1aa74fc86ba3fee850545e27be9e1de893d/psycopg2_binary-2.9.10-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", size = 3275336 },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/b7/a68c2b4bff1cbb1728e3ec864b2d92327c77ad52edcd27922535a8366f68/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", size = 2851637 },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/b1/cfedc0e0e6f9ad61f8657fd173b2f831ce261c02a08c0b09c652b127d813/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", size = 3082097 },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/ed/0a8e4153c9b769f59c02fb5e7914f20f0b2483a19dae7bf2db54b743d0d0/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", size = 3264776 },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/db/d09da68c6a0cdab41566b74e0a6068a425f077169bed0946559b7348ebe9/psycopg2_binary-2.9.10-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", size = 3020968 },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/28/4d6f8c255f0dfffb410db2b3f9ac5218d959a66c715c34cac31081e19b95/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", size = 2872334 },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/f7/20d7bf796593c4fea95e12119d6cc384ff1f6141a24fbb7df5a668d29d29/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", size = 2822722 },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/e4/0c407ae919ef626dbdb32835a03b6737013c3cc7240169843965cada2bdf/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", size = 2920132 },
|
||||
{ url = "https://files.pythonhosted.org/packages/2d/70/aa69c9f69cf09a01da224909ff6ce8b68faeef476f00f7ec377e8f03be70/psycopg2_binary-2.9.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", size = 2959312 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/bd/213e59854fafe87ba47814bf413ace0dcee33a89c8c8c814faca6bc7cf3c/psycopg2_binary-2.9.10-cp312-cp312-win32.whl", hash = "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", size = 1025191 },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/29/06261ea000e2dc1e22907dbbc483a1093665509ea586b29b8986a0e56733/psycopg2_binary-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", size = 1164031 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/30/d41d3ba765609c0763505d565c4d12d8f3c79793f0d0f044ff5a28bf395b/psycopg2_binary-2.9.10-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", size = 3044699 },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/44/257ddadec7ef04536ba71af6bc6a75ec05c5343004a7ec93006bee66c0bc/psycopg2_binary-2.9.10-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", size = 3275245 },
|
||||
{ url = "https://files.pythonhosted.org/packages/1b/11/48ea1cd11de67f9efd7262085588790a95d9dfcd9b8a687d46caf7305c1a/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", size = 2851631 },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/e0/62ce5ee650e6c86719d621a761fe4bc846ab9eff8c1f12b1ed5741bf1c9b/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", size = 3082140 },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/ce/63f946c098611f7be234c0dd7cb1ad68b0b5744d34f68062bb3c5aa510c8/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", size = 3264762 },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/25/c603cd81402e69edf7daa59b1602bd41eb9859e2824b8c0855d748366ac9/psycopg2_binary-2.9.10-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", size = 3020967 },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/d6/8708d8c6fca531057fa170cdde8df870e8b6a9b136e82b361c65e42b841e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", size = 2872326 },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/ac/5b1ea50fc08a9df82de7e1771537557f07c2632231bbab652c7e22597908/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", size = 2822712 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/fc/504d4503b2abc4570fac3ca56eb8fed5e437bf9c9ef13f36b6621db8ef00/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", size = 2920155 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/d1/323581e9273ad2c0dbd1902f3fb50c441da86e894b6e25a73c3fda32c57e/psycopg2_binary-2.9.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", size = 2959356 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scikit-learn"
|
||||
version = "1.5.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "joblib" },
|
||||
{ name = "numpy" },
|
||||
{ name = "scipy" },
|
||||
{ name = "threadpoolctl" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/37/59/44985a2bdc95c74e34fef3d10cb5d93ce13b0e2a7baefffe1b53853b502d/scikit_learn-1.5.2.tar.gz", hash = "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", size = 7001680 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/db/b485c1ac54ff3bd9e7e6b39d3cc6609c4c76a65f52ab0a7b22b6c3ab0e9d/scikit_learn-1.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a", size = 12110344 },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/1a/7deb52fa23aebb855431ad659b3c6a2e1709ece582cb3a63d66905e735fe/scikit_learn-1.5.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1", size = 11033502 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/32/4a7a205b14c11225609b75b28402c196e4396ac754dab6a81971b811781c/scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd", size = 12085794 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/29/044048c5e911373827c0e1d3051321b9183b2a4f8d4e2f11c08fcff83f13/scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6", size = 12945797 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/ce/c0b912f2f31aeb1b756a6ba56bcd84dd1f8a148470526a48515a3f4d48cd/scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1", size = 10985467 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/50/8891028437858cc510e13578fe7046574a60c2aaaa92b02d64aac5b1b412/scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5", size = 12025584 },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/79/17feef8a1c14149436083bec0e61d7befb4812e272d5b20f9d79ea3e9ab1/scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908", size = 10959795 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/c8/f08313f9e2e656bd0905930ae8bf99a573ea21c34666a813b749c338202f/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3", size = 12077302 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/48/fbfb4dc72bed0fe31fe045fb30e924909ad03f717c36694351612973b1a9/scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12", size = 13002811 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/e7/0c869f9e60d225a77af90d2aefa7a4a4c0e745b149325d1450f0f0ce5399/scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f", size = 10951354 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scipy"
|
||||
version = "1.14.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "numpy" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/62/11/4d44a1f274e002784e4dbdb81e0ea96d2de2d1045b2132d5af62cc31fd28/scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417", size = 58620554 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/04/2bdacc8ac6387b15db6faa40295f8bd25eccf33f1f13e68a72dc3c60a99e/scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d", size = 39128781 },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/53/35b4d41f5fd42f5781dbd0dd6c05d35ba8aa75c84ecddc7d44756cd8da2e/scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07", size = 29939542 },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/67/6ef192e0e4d77b20cc33a01e743b00bc9e68fb83b88e06e636d2619a8767/scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5", size = 23148375 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/32/3a6dedd51d68eb7b8e7dc7947d5d841bcb699f1bf4463639554986f4d782/scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc", size = 25578573 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/5a/efa92a58dc3a2898705f1dc9dbaf390ca7d4fba26d6ab8cfffb0c72f656f/scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310", size = 35319299 },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/ee/8a26858ca517e9c64f84b4c7734b89bda8e63bec85c3d2f432d225bb1886/scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066", size = 40849331 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/cd/06f72bc9187840f1c99e1a8750aad4216fc7dfdd7df46e6280add14b4822/scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1", size = 42544049 },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/7d/43ab67228ef98c6b5dd42ab386eae2d7877036970a0d7e3dd3eb47a0d530/scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f", size = 44521212 },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/ef/ac98346db016ff18a6ad7626a35808f37074d25796fd0234c2bb0ed1e054/scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79", size = 39091068 },
|
||||
{ url = "https://files.pythonhosted.org/packages/b9/cc/70948fe9f393b911b4251e96b55bbdeaa8cca41f37c26fd1df0232933b9e/scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e", size = 29875417 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/2e/35f549b7d231c1c9f9639f9ef49b815d816bf54dd050da5da1c11517a218/scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73", size = 23084508 },
|
||||
{ url = "https://files.pythonhosted.org/packages/3f/d6/b028e3f3e59fae61fb8c0f450db732c43dd1d836223a589a8be9f6377203/scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e", size = 25503364 },
|
||||
{ url = "https://files.pythonhosted.org/packages/a7/2f/6c142b352ac15967744d62b165537a965e95d557085db4beab2a11f7943b/scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d", size = 35292639 },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/46/2449e6e51e0d7c3575f289f6acb7f828938eaab8874dbccfeb0cd2b71a27/scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e", size = 40798288 },
|
||||
{ url = "https://files.pythonhosted.org/packages/32/cd/9d86f7ed7f4497c9fd3e39f8918dd93d9f647ba80d7e34e4946c0c2d1a7c/scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06", size = 42524647 },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/1b/6ee032251bf4cdb0cc50059374e86a9f076308c1512b61c4e003e241efb7/scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84", size = 44469524 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ed/22/a438e0caa4576f8c383fa4d35f1cc01655a46c75be358960d815bfbb12bd/setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686", size = 1351577 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/90/12/282ee9bce8b58130cb762fbc9beabd531549952cac11fc56add11dcb7ea0/setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd", size = 1251070 },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "threadpoolctl"
|
||||
version = "3.5.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/bd/55/b5148dcbf72f5cde221f8bfe3b6a540da7aa1842f6b491ad979a6c8b84af/threadpoolctl-3.5.0.tar.gz", hash = "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", size = 41936 }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl", hash = "sha256:56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467", size = 18414 },
|
||||
]
|
Reference in New Issue
Block a user