add datetable cnfcnpj
This commit is contained in:
207
users-events/app/events/batch/chunks_into_users.py
Normal file
207
users-events/app/events/batch/chunks_into_users.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import csv
|
||||
import secrets
|
||||
from io import StringIO
|
||||
from types import SimpleNamespace
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import uuid4
|
||||
|
||||
from aws_lambda_powertools.utilities.data_classes import (
|
||||
EventBridgeEvent,
|
||||
event_source,
|
||||
)
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
from layercake.batch import BatchProcessor
|
||||
from layercake.dateutils import now
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
from layercake.extra_types import CnpjStr, CpfStr, NameStr
|
||||
from pydantic import BaseModel, EmailStr, Field
|
||||
|
||||
from boto3clients import dynamodb_client, s3_client
|
||||
from config import USER_TABLE
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mypy_boto3_s3.client import S3Client
|
||||
else:
|
||||
S3Client = object
|
||||
|
||||
|
||||
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||
transport_params = {'client': s3_client}
|
||||
processor = BatchProcessor()
|
||||
|
||||
|
||||
class Org(BaseModel):
|
||||
id: str | None = Field(default=None, exclude=True)
|
||||
name: str
|
||||
cnpj: CnpjStr
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
name: NameStr
|
||||
cpf: CpfStr
|
||||
email: EmailStr
|
||||
|
||||
|
||||
class CPFConflictError(Exception): ...
|
||||
|
||||
|
||||
class EmailConflictError(Exception): ...
|
||||
|
||||
|
||||
@event_source(data_class=EventBridgeEvent)
|
||||
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
new_image = event.detail['new_image']
|
||||
csvfile = new_image['s3_uri']
|
||||
_, _, start_byte, _, end_byte = new_image['sk'].split('#')
|
||||
header = SimpleNamespace(
|
||||
**{
|
||||
column_name: int(idx)
|
||||
for idx, column_name in (
|
||||
column.split(':') for column in new_image['columns']
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
data = _get_s3_object_range(
|
||||
csvfile,
|
||||
start_byte=start_byte,
|
||||
end_byte=end_byte,
|
||||
s3_client=s3_client,
|
||||
)
|
||||
reader = csv.reader(data)
|
||||
users = [
|
||||
{
|
||||
'name': row[header.name],
|
||||
'email': row[header.email],
|
||||
'cpf': row[header.cpf],
|
||||
}
|
||||
for row in reader
|
||||
]
|
||||
ctx = {'org': new_image['org']}
|
||||
# Key pattern `FILE#{file}`
|
||||
sk = new_image['file_sk']
|
||||
|
||||
with (
|
||||
dyn.transact_writer() as transact,
|
||||
processor(records=users, handler=_create_user, context=ctx) as batch,
|
||||
):
|
||||
result = batch.process()
|
||||
|
||||
for r in result:
|
||||
transact.put(
|
||||
item={
|
||||
'id': new_image['id'],
|
||||
'sk': f'REPORTING#{sk}#ITEM#{secrets.token_urlsafe(16)}',
|
||||
'input': r.input_record,
|
||||
'status': r.status.value.upper(),
|
||||
'error': r.cause.get('type') if r.cause else None,
|
||||
}
|
||||
)
|
||||
|
||||
transact.update(
|
||||
key=KeyPair(
|
||||
pk=new_image['id'],
|
||||
sk=sk,
|
||||
),
|
||||
update_expr='ADD progress :progress',
|
||||
expr_attr_values={
|
||||
':progress': new_image['weight'],
|
||||
},
|
||||
)
|
||||
transact.delete(
|
||||
key=KeyPair(
|
||||
pk=new_image['id'],
|
||||
sk=new_image['sk'],
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _create_user(rawuser: dict, context: dict) -> None:
|
||||
now_ = now()
|
||||
user_id = uuid4()
|
||||
org = Org(**context['org'])
|
||||
user = User(**rawuser)
|
||||
|
||||
with dyn.transact_writer() as transact:
|
||||
transact.put(
|
||||
item={
|
||||
**user.model_dump(),
|
||||
'id': user_id,
|
||||
'sk': '0',
|
||||
'email_verified': False,
|
||||
'tenant_id': {org.id},
|
||||
# Post-migration: uncomment the folloing line
|
||||
# 'org_id': {org.id},
|
||||
'created_at': now_,
|
||||
},
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
'id': user_id,
|
||||
# Post-migration: rename `emails` to `EMAIL`
|
||||
'sk': f'emails#{user.email}',
|
||||
'email_verified': False,
|
||||
'email_primary': True,
|
||||
'created_at': now_,
|
||||
}
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
# Post-migration: rename `cpf` to `CPF`
|
||||
'id': 'cpf',
|
||||
'sk': user.cpf,
|
||||
'created_at': now_,
|
||||
},
|
||||
cond_expr='attribute_not_exists(sk)',
|
||||
exc_cls=CPFConflictError,
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
# Post-migration: rename `email` to `EMAIL`
|
||||
'id': 'email',
|
||||
'sk': user.email,
|
||||
'created_at': now_,
|
||||
},
|
||||
cond_expr='attribute_not_exists(sk)',
|
||||
exc_cls=EmailConflictError,
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
'id': user_id,
|
||||
'sk': f'orgs#{org.id}',
|
||||
# Post-migration: uncomment the following line
|
||||
# pk=f'ORG#{org.id}',
|
||||
'name': org.name,
|
||||
'cnpj': org.cnpj,
|
||||
'created_at': now_,
|
||||
}
|
||||
)
|
||||
transact.put(
|
||||
item={
|
||||
'id': f'orgmembers#{org.id}',
|
||||
# Post-migration: uncomment the following line
|
||||
# pk=f'MEMBER#ORG#{org_id}',
|
||||
'sk': user_id,
|
||||
'created_at': now_,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _get_s3_object_range(
|
||||
s3_uri: str,
|
||||
*,
|
||||
start_byte: int,
|
||||
end_byte: int,
|
||||
s3_client: S3Client,
|
||||
) -> StringIO:
|
||||
bucket, key = s3_uri.replace('s3://', '').split('/', 1)
|
||||
|
||||
r = s3_client.get_object(
|
||||
Bucket=bucket,
|
||||
Key=key,
|
||||
Range=f'bytes={start_byte}-{end_byte}',
|
||||
)
|
||||
|
||||
return StringIO(r['Body'].read().decode('utf-8'))
|
||||
@@ -1,22 +1,58 @@
|
||||
from decimal import Decimal
|
||||
|
||||
from aws_lambda_powertools.utilities.data_classes import (
|
||||
EventBridgeEvent,
|
||||
event_source,
|
||||
)
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
from layercake.dateutils import now
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
from boto3clients import s3_client
|
||||
from config import CHUNK_SIZE
|
||||
from boto3clients import dynamodb_client, s3_client
|
||||
from config import CHUNK_SIZE, USER_TABLE
|
||||
from csv_utils import byte_ranges
|
||||
|
||||
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||
transport_params = {'client': s3_client}
|
||||
|
||||
|
||||
@event_source(data_class=EventBridgeEvent)
|
||||
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
now_ = now()
|
||||
new_image = event.detail['new_image']
|
||||
csvfile = new_image['s3_uri']
|
||||
pairs = byte_ranges(csvfile, CHUNK_SIZE, transport_params=transport_params)
|
||||
chunks = byte_ranges(csvfile, CHUNK_SIZE, transport_params=transport_params)
|
||||
total_chunks = len(chunks)
|
||||
weight_per_chunk = round(100 / total_chunks, 2)
|
||||
weights = [weight_per_chunk] * total_chunks
|
||||
# Fix last value to balance total
|
||||
weights[-1] = round(100 - sum(weights[:-1]), 2)
|
||||
|
||||
print(pairs)
|
||||
with dyn.transact_writer() as transact:
|
||||
transact.update(
|
||||
key=KeyPair(new_image['id'], new_image['sk']),
|
||||
update_expr='SET total_chunks = :total_chunks, \
|
||||
progress = :progress, \
|
||||
started_at = :now',
|
||||
expr_attr_values={
|
||||
':total_chunks': total_chunks,
|
||||
':progress': 0,
|
||||
':now': now_,
|
||||
},
|
||||
)
|
||||
|
||||
for (start, end), weight in zip(chunks, weights):
|
||||
transact.put(
|
||||
item={
|
||||
'id': new_image['id'],
|
||||
'sk': f'CHUNK#START#{start}#END#{end}',
|
||||
'file_sk': new_image['sk'],
|
||||
's3_uri': new_image['s3_uri'],
|
||||
'columns': new_image['columns'],
|
||||
'weight': Decimal(str(weight)),
|
||||
'org': new_image['org'],
|
||||
'created_at': now_,
|
||||
}
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
14
users-events/app/events/batch/mask_as_completed.py
Normal file
14
users-events/app/events/batch/mask_as_completed.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from aws_lambda_powertools.utilities.data_classes import (
|
||||
EventBridgeEvent,
|
||||
event_source,
|
||||
)
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
|
||||
from boto3clients import s3_client
|
||||
|
||||
transport_params = {'client': s3_client}
|
||||
|
||||
|
||||
@event_source(data_class=EventBridgeEvent)
|
||||
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
return True
|
||||
@@ -1,55 +0,0 @@
|
||||
import csv
|
||||
from io import StringIO
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from aws_lambda_powertools.utilities.data_classes import (
|
||||
EventBridgeEvent,
|
||||
event_source,
|
||||
)
|
||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
||||
|
||||
from boto3clients import s3_client
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from mypy_boto3_s3.client import S3Client
|
||||
else:
|
||||
S3Client = object
|
||||
|
||||
transport_params = {'client': s3_client}
|
||||
|
||||
|
||||
@event_source(data_class=EventBridgeEvent)
|
||||
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||
new_image = event.detail['new_image']
|
||||
csvfile = new_image['s3_uri']
|
||||
|
||||
data = _get_s3_object_range(
|
||||
csvfile,
|
||||
start_byte=new_image['start_byte'],
|
||||
end_byte=new_image['end_byte'],
|
||||
s3_client=s3_client,
|
||||
)
|
||||
reader = csv.reader(data)
|
||||
|
||||
for x in reader:
|
||||
print(x)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _get_s3_object_range(
|
||||
s3_uri: str,
|
||||
*,
|
||||
start_byte: int,
|
||||
end_byte: int,
|
||||
s3_client: S3Client,
|
||||
) -> StringIO:
|
||||
bucket, key = s3_uri.replace('s3://', '').split('/', 1)
|
||||
|
||||
response = s3_client.get_object(
|
||||
Bucket=bucket,
|
||||
Key=key,
|
||||
Range=f'bytes={start_byte}-{end_byte}',
|
||||
)
|
||||
|
||||
return StringIO(response['Body'].read().decode('utf-8'))
|
||||
@@ -1,62 +0,0 @@
|
||||
# /// script
|
||||
# dependencies = [
|
||||
# "cloudflare"
|
||||
# ]
|
||||
# ///
|
||||
|
||||
from cloudflare import Cloudflare
|
||||
|
||||
CLOUDFLARE_ACCOUNT_ID = '5436b62470020c04b434ad31c3e4cf4e'
|
||||
CLOUDFLARE_API_TOKEN = 'gFndkBJCzH4pRX7mKXokdWfw1xhm8-9FHfvLfhwa'
|
||||
|
||||
|
||||
client = Cloudflare(api_token=CLOUDFLARE_API_TOKEN)
|
||||
|
||||
assistant = """
|
||||
You are a data analysis assistant specialized in identifying Brazilian
|
||||
personal data from CSV files.
|
||||
|
||||
These CSV files may or may not include headers.
|
||||
|
||||
Your task is to analyze the content and identify only three possible
|
||||
data types: 'name', 'cpf', and 'email'.
|
||||
|
||||
Ignore all other fields.
|
||||
"""
|
||||
|
||||
csv_content = """
|
||||
,RICARDO GALLES BONET,ricardo.bonet@fanucamerica.com,424.430.528-93,NR-10 (RECICLAGEM)
|
||||
,RULIO SIEFERT SERA,rulio.sera@fanucamerica.com,063.916.859-08,NR-10 (RECICLAGEM)
|
||||
,MACIEL FERREIRA BOMFIM,maciel.bomfim@fanucamerica.com,334.547.088-85,NR-10 (RECICLAGEM)
|
||||
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-12
|
||||
,JAIME EDUARDO GALVEZ AVILES,jaime.galvez@fanucamerica.com,280.238.818-50,NR-35 (RECICLAGEM)
|
||||
,HIGOR MACHADO SILVA,higor.silva@fanucamerica.com,419.879.878-88,NR-12
|
||||
,LÁZARO SOUZA DIAS,lazaro.dias@fanucamerica.com,067.179.825-19,NR-12
|
||||
,JOÃO PEDRO AGUIAR GALASSO,joao.pedro@fanucamerica.com,570.403.588-40,NR-12
|
||||
"""
|
||||
|
||||
prompt = f"""
|
||||
Here is a CSV sample:
|
||||
|
||||
{csv_content}
|
||||
|
||||
Your task is to:
|
||||
- Detect which columns most likely contain "name", "cpf", or "email".
|
||||
- Skip any category that is not present in the data.
|
||||
- Return ONLY a valid Python list of tuples, like:
|
||||
[('name', index), ('cpf', index), ('email', index)]
|
||||
- Use the column index that most likely matches each data type,
|
||||
based on frequency and data format.
|
||||
- Don't include explanations, code, or any additional text.
|
||||
"""
|
||||
|
||||
r = client.ai.run(
|
||||
model_name='@cf/meta/llama-3-8b-instruct',
|
||||
account_id=CLOUDFLARE_ACCOUNT_ID,
|
||||
messages=[
|
||||
{'role': 'system', 'content': assistant},
|
||||
{'role': 'user', 'content': prompt},
|
||||
],
|
||||
)
|
||||
|
||||
print(r)
|
||||
@@ -33,13 +33,15 @@ Resources:
|
||||
Properties:
|
||||
RetentionInDays: 90
|
||||
|
||||
EventCsvChunksFunction:
|
||||
EventCsvIntoChunksFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: events.batch.csv_chunks.lambda_handler
|
||||
Handler: events.batch.csv_into_chunks.lambda_handler
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref EventLog
|
||||
Policies:
|
||||
- DynamoDBCrudPolicy:
|
||||
TableName: !Ref UserTable
|
||||
- S3CrudPolicy:
|
||||
BucketName: !Ref BucketName
|
||||
Events:
|
||||
@@ -50,8 +52,35 @@ Resources:
|
||||
resources: [!Ref UserTable]
|
||||
detail:
|
||||
new_image:
|
||||
id:
|
||||
- prefix: BATCH_JOB#ORG#
|
||||
sk:
|
||||
- prefix: BATCH_JOB#ORG
|
||||
- prefix: FILE#
|
||||
status: [PENDING]
|
||||
|
||||
EventChunksIntoUsersFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
Properties:
|
||||
Handler: events.batch.chunks_into_user.lambda_handler
|
||||
LoggingConfig:
|
||||
LogGroup: !Ref EventLog
|
||||
Policies:
|
||||
- DynamoDBCrudPolicy:
|
||||
TableName: !Ref UserTable
|
||||
- S3CrudPolicy:
|
||||
BucketName: !Ref BucketName
|
||||
Events:
|
||||
DynamoDBEvent:
|
||||
Type: EventBridgeRule
|
||||
Properties:
|
||||
Pattern:
|
||||
resources: [!Ref UserTable]
|
||||
detail:
|
||||
new_image:
|
||||
id:
|
||||
- prefix: BATCH_JOB#ORG#
|
||||
sk:
|
||||
- prefix: CHUNK#START#
|
||||
|
||||
EventEmailReceivingFunction:
|
||||
Type: AWS::Serverless::Function
|
||||
|
||||
47
users-events/tests/events/batch/test_chunks_into_users.py
Normal file
47
users-events/tests/events/batch/test_chunks_into_users.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import pprint
|
||||
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair
|
||||
|
||||
import events.batch.chunks_into_users as app
|
||||
|
||||
|
||||
def test_chunk_csv(
|
||||
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||
lambda_context,
|
||||
):
|
||||
pk = 'BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461'
|
||||
file_sk = 'FILE#2025-11-13T16:04:53.024743'
|
||||
event = {
|
||||
'detail': {
|
||||
'new_image': {
|
||||
'id': pk,
|
||||
'sk': 'CHUNK#START#0#END#4885',
|
||||
'weight': 100,
|
||||
'created_at': '2025-11-20T19:00:41.896001-03:00',
|
||||
'file_sk': file_sk,
|
||||
's3_uri': 's3://saladeaula.digital/samples/users.csv',
|
||||
'columns': {
|
||||
'1:name',
|
||||
'2:email',
|
||||
'3:cpf',
|
||||
},
|
||||
'org': {
|
||||
'id': '1411844c-10d6-456e-959d-e91775145461',
|
||||
'name': 'EDUSEG',
|
||||
'cnpj': '15608435000190',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert app.lambda_handler(event, lambda_context) # type: ignore
|
||||
|
||||
r = dynamodb_persistence_layer.collection.query(
|
||||
KeyPair(
|
||||
pk=pk,
|
||||
sk=f'REPORTING#{file_sk}',
|
||||
),
|
||||
limit=100,
|
||||
)
|
||||
pprint.pp(r['items'])
|
||||
assert 26 == len(r['items'])
|
||||
@@ -1,13 +1,37 @@
|
||||
from layercake.dateutils import now
|
||||
from layercake.dynamodb import DynamoDBPersistenceLayer, PartitionKey
|
||||
|
||||
import events.batch.csv_into_chunks as app
|
||||
|
||||
|
||||
def test_chunk_csv(lambda_context):
|
||||
def test_chunk_csv(
|
||||
dynamodb_persistence_layer: DynamoDBPersistenceLayer,
|
||||
lambda_context,
|
||||
):
|
||||
pk = 'BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461'
|
||||
sk = 'FILE#2025-11-13T16:04:53.024743'
|
||||
event = {
|
||||
'detail': {
|
||||
'new_image': {
|
||||
'id': pk,
|
||||
'sk': sk,
|
||||
's3_uri': 's3://saladeaula.digital/samples/large_users.csv',
|
||||
'columns': {
|
||||
'1:email',
|
||||
'2:cpf',
|
||||
'3:name',
|
||||
},
|
||||
'org': {
|
||||
'id': '1411844c-10d6-456e-959d-e91775145461',
|
||||
'name': 'EDUSEG',
|
||||
'cnpj': '15608435000190',
|
||||
},
|
||||
'created_at': now(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
app.lambda_handler(event, lambda_context) # type: ignore
|
||||
|
||||
r = dynamodb_persistence_layer.collection.query(PartitionKey(pk), limit=100)
|
||||
assert len(r['items']) == 67
|
||||
|
||||
13
users-events/tests/events/batch/test_excel_to_csv.py
Normal file
13
users-events/tests/events/batch/test_excel_to_csv.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import events.batch.excel_to_csv as app
|
||||
|
||||
|
||||
def test_excel_to_csv(lambda_context):
|
||||
event = {
|
||||
'detail': {
|
||||
'new_image': {
|
||||
's3_uri': 's3://saladeaula.digital/samples/large_users.csv',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert app.lambda_handler(event, lambda_context) # type: ignore
|
||||
@@ -1,4 +1,11 @@
|
||||
{"id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}, "sk": {"S": "0"}, "name": {"S": "EDUSEG"}}
|
||||
{"id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}, "sk": {"S": "admins#5OxmMjL-ujoR5IMGegQz"}, "name": {"S": "Sérgio R Siqueira"}, "email": {"S": "sergio@somosbeta.com.br"}}
|
||||
{"id": {"S": "cnpj"}, "sk": {"S": "15608435000190"}, "user_id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}}
|
||||
{"id": {"S": "email"}, "sk": {"S": "org+15608435000190@users.noreply.saladeaula.digital"}, "user_id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}}
|
||||
{"id": {"S": "email"}, "sk": {"S": "org+15608435000190@users.noreply.saladeaula.digital"}, "user_id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}}
|
||||
|
||||
|
||||
{"id": "BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461", "sk": "FILE#2025-11-13T16:04:53.024743", "progress": 0, "s3_uri": "s3://saladeaula.digital/samples/large_users.csv"}
|
||||
{"id": "BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461", "sk": "CHUNK#START#0#END#3847", "weight": 25, "file_sk": "FILE#2025-11-13T16:04:53.024743", "s3_uri": "s3://saladeaula.digital/samples/large_users.csv"}
|
||||
{"id": "BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461", "sk": "CHUNK#START#3848#END#7925", "weight": 25, "file_sk": "FILE#2025-11-13T16:04:53.024743", "s3_uri": "s3://saladeaula.digital/samples/large_users.csv"}
|
||||
{"id": "BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461", "sk": "CHUNK#START#7926#END#11866", "weight": 25, "file_sk": "FILE#2025-11-13T16:04:53.024743", "s3_uri": "s3://saladeaula.digital/samples/large_users.csv"}
|
||||
{"id": "BATCH_JOB#ORG#1411844c-10d6-456e-959d-e91775145461", "sk": "CHUNK#START#11867#END#15913", "weight": 25, "file_sk": "FILE#2025-11-13T16:04:53.024743", "s3_uri": "s3://saladeaula.digital/samples/large_users.csv"}
|
||||
|
||||
@@ -9,8 +9,8 @@ def test_detect_delimiter():
|
||||
def test_byte_ranges():
|
||||
csvpath = 'tests/samples/users.csv'
|
||||
ranges = byte_ranges(csvpath, 10)
|
||||
*_, pair = ranges
|
||||
start_byte, end_byte = pair
|
||||
*_, chunk = ranges
|
||||
start_byte, end_byte = chunk
|
||||
|
||||
assert ranges == [(0, 808), (809, 1655), (1656, 2303)]
|
||||
|
||||
|
||||
65
users-events/uv.lock
generated
65
users-events/uv.lock
generated
@@ -472,7 +472,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "layercake"
|
||||
version = "0.11.1"
|
||||
version = "0.11.2"
|
||||
source = { directory = "../layercake" }
|
||||
dependencies = [
|
||||
{ name = "arnparse" },
|
||||
@@ -489,6 +489,7 @@ dependencies = [
|
||||
{ name = "pycpfcnpj" },
|
||||
{ name = "pydantic", extra = ["email"] },
|
||||
{ name = "pydantic-extra-types" },
|
||||
{ name = "python-calamine" },
|
||||
{ name = "python-multipart" },
|
||||
{ name = "pytz" },
|
||||
{ name = "requests" },
|
||||
@@ -513,6 +514,7 @@ requires-dist = [
|
||||
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
||||
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
||||
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
||||
{ name = "python-calamine", specifier = ">=0.5.4" },
|
||||
{ name = "python-multipart", specifier = ">=0.0.20" },
|
||||
{ name = "pytz", specifier = ">=2025.1" },
|
||||
{ name = "requests", specifier = ">=2.32.3" },
|
||||
@@ -830,6 +832,67 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl", hash = "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde", size = 23841, upload-time = "2025-04-05T14:07:49.641Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-calamine"
|
||||
version = "0.5.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/60/82/0a6581f05916e2c09a418b5624661cb51dc0b8bd10dd0e8613b90bf785ad/python_calamine-0.5.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:46b258594314f89b9b92c6919865eabf501391d000794e70dc7a6b24e7bda9c6", size = 849926, upload-time = "2025-10-21T07:11:37.835Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/ca/1d4698b2de6e5d9efc712bd4c018125021eaf9a0f20559a35654b17f1e7f/python_calamine-0.5.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:feea9a85683e66b4e87b381812086210e90521914d6960c45f30bedb9e186ffe", size = 825321, upload-time = "2025-10-21T07:11:39.299Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/dd/09bd18c8ad6327bc03de2e3ce7c2150d0e605f8aa538615a6fc8b25b2f52/python_calamine-0.5.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64ab500d7c1eb415776d722c4cda7d60fd373642f159946b5f03ae55dd246a", size = 897213, upload-time = "2025-10-21T07:11:40.801Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/80/6cd2f358b96451dbfe40ff88e50ed875264e366cea01d1ec51aa46afc55a/python_calamine-0.5.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c15a09a24e8c2de4adc0f039c05dc37b85e8a3fd0befa8b8fcb8a61f13837965", size = 887237, upload-time = "2025-10-21T07:11:42.149Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/1f/5abdf618c402c586c7d8e02664b2a4d85619e3b67c75f63c535fd819eb42/python_calamine-0.5.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d956ab6a36afe3fabe0f3aeac86b4e6c16f8c1bc0e3fa0b57d0eb3e66e40c91", size = 1044372, upload-time = "2025-10-21T07:11:43.566Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/10/164fed6f46c469f6e3a5c17f2864c8b028109f6d5da928f6aa34e0fbd396/python_calamine-0.5.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94840783be59659e367ae4f1c59fffcc54ad7f7f6935cbfbaa6879e6633c5a52", size = 942187, upload-time = "2025-10-21T07:11:45.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/43/4f/a5f167a95ef57c3e37fe8ae0a41745061442f44e4c0c4395d70c8740e453/python_calamine-0.5.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8304fc19322f05dc0af78851ca47255a088a9c0cc3874648b42038e7f27ff2f", size = 905766, upload-time = "2025-10-21T07:11:46.972Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/5c/2804120184a0b4b1510e6274e7c29f461bd80bae1935ad26ea68f4c31a6c/python_calamine-0.5.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e0ee4a18b1341600111d756c6d5d30546729b8961e0c552b4d63fc40dcd609d7", size = 948683, upload-time = "2025-10-21T07:11:48.846Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/7a/a0ec3339be0e0c4288fac04bf754c3a9c7d3c863e167359764384031469c/python_calamine-0.5.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:b5d81afbad55fd78146bad8bc31d55793fe3fdff5e49afab00c704f5f567d330", size = 1077564, upload-time = "2025-10-21T07:11:50.333Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/9c/78dd74b3cb2614c556014c205d63966043d62fe2e0a4570ccbf5a926bf18/python_calamine-0.5.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:c71c51211ce24db640099c60bccc2c93d58639664e8fb69db48a35ed3b272f8e", size = 1150587, upload-time = "2025-10-21T07:11:52.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/82/24bca60640366251fb5eb6ffa0199ad05aa638d7d228dc4ba338e9dd9835/python_calamine-0.5.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c64dec92cb1f094298e601ad10ceb6bc15668f5ae24a7e852589f8c0fdb346d2", size = 1080031, upload-time = "2025-10-21T07:11:53.664Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/97/7696c0d36f99fc6ab9770632655dd67389953b4d94e3394c280520db5e23/python_calamine-0.5.4-cp313-cp313-win32.whl", hash = "sha256:5f64e3f2166001a98c3f4218eac96fa24f96f9f9badad4b8a86d9a77e81284ad", size = 676927, upload-time = "2025-10-21T07:11:55.131Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/de/e9a1c650ba446f46e880f1bf07744c3dbc709b8f0285cf6db091bbe7f30d/python_calamine-0.5.4-cp313-cp313-win_amd64.whl", hash = "sha256:b0858c907ac3e4000ab7f4422899559e412fe4a71dba3d7c96f9ecb1cf03a9ce", size = 721241, upload-time = "2025-10-21T07:11:56.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/58/0a6483cfc5bffd3df8a76c4041aa6396566cd0dddf180055064074fc6e77/python_calamine-0.5.4-cp313-cp313-win_arm64.whl", hash = "sha256:2df6c552546f36702ae2a78f9ffeab5ecf638f27eece2737735c3fd4080d2809", size = 687761, upload-time = "2025-10-21T07:11:57.885Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/c6/cbfb8050adb339fd604f9465aa67824f6da63ee74adb88bbad907f17397c/python_calamine-0.5.4-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:7bf110052f62dcb16c507b741b5ab637b9b2e89b25406cb1bd795b2f1207439d", size = 848476, upload-time = "2025-10-21T07:11:59.651Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/ab/888592578ee23cf7377009db7a396b73f011df5cd6e7627667cdc862a813/python_calamine-0.5.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:800763dcb01d3752450a6ee204bc22e661a20221e40490f85fff1c98ad96c2e9", size = 823829, upload-time = "2025-10-21T07:12:01.03Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/22/5dbbb506462f8ce9e7445905fa0efba73a25341d2bdd7f0da0b9c8c5cd99/python_calamine-0.5.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f40f2596f2ec8085343e67e73ad5321f18e36e6c2f7b15980201aec03666cf4c", size = 895812, upload-time = "2025-10-21T07:12:02.466Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/b9/f839641ebe781cf7e82d2b58d0c3a609686f83516a946298627f20f5fc9f/python_calamine-0.5.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:859b1e8586cf9944edfa32ba1679be2b40407d67c8c071a97429ea4a79adcd08", size = 886707, upload-time = "2025-10-21T07:12:03.874Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/cf/d74743dc72128248ce598aa9eb2e82457166c380b48493f46ca001d429cf/python_calamine-0.5.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3221b145e52d708597b74832ff517adf9153b959aa17d05d2e7fc259855c6c25", size = 1042868, upload-time = "2025-10-21T07:12:05.362Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/d6/55b061c7cf7e6c06279af4abf83aef01168f2a902446c79393cfecfc1a06/python_calamine-0.5.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0294d8e677f85a178c74a5952da668a35dd0522e7852f5a398aae01a9577fd0d", size = 941310, upload-time = "2025-10-21T07:12:06.866Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/d7/457adac7eae82584ce36860ba9073e4e9492195fee6f4b41397733a92604/python_calamine-0.5.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:713df8fd08d71030bf7677712f4764e306e379e06c05f7656fed42e7cd256602", size = 904649, upload-time = "2025-10-21T07:12:08.851Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/ad/0dbb38d992245a71630c93d928d3e1b5581c98e92d214d1ec80da0036c65/python_calamine-0.5.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:adc83cd98e58fecdedce7209bad98452b2702cc3cecb8e9066e0db198b939bb5", size = 944747, upload-time = "2025-10-21T07:12:10.288Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/69/99/dcb7f5a7149afefcdfb5c1d2d0fb9b086df5dc228d54e693875b0797c680/python_calamine-0.5.4-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:c70ed54297ca49bb449df00a5e6f317df1162e042a65dd3fbeb9c9a6d85cb354", size = 1075868, upload-time = "2025-10-21T07:12:11.817Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/19/c2145b5912fadf495d66ae96bb2735340fea1183844843fe975837c315a6/python_calamine-0.5.4-cp313-cp313t-musllinux_1_1_armv7l.whl", hash = "sha256:78baabfc04a918efcc44e61385526143fd773317fc263ee59a5aa8909854bae3", size = 1149999, upload-time = "2025-10-21T07:12:13.381Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/e5/6787068c97978212ae7b71d6d6e4785474ac0c496f01c50d04866b66d72e/python_calamine-0.5.4-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:a12aa39963eaae84a1ae70fbd49171bcd901fff87c93095bd80760cb0107220c", size = 1078902, upload-time = "2025-10-21T07:12:15.202Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/99/21c377f9173af146553569f672ef8989017f1dafa80ec912930ccbaaab0c/python_calamine-0.5.4-cp313-cp313t-win_amd64.whl", hash = "sha256:7c46c472299781bf51bcf550d81fe812363e3ca13535023bd2764145fbc52823", size = 722243, upload-time = "2025-10-21T07:12:16.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/91/a7d2eb4b5f34d34b6ed8d217dee91b1d5224d15905ca8870cf62858d2b25/python_calamine-0.5.4-cp313-cp313t-win_arm64.whl", hash = "sha256:e6b1a6f969207e3729366ee2ff1b5143a9b201e59af0d2708e51a39ef702652f", size = 684569, upload-time = "2025-10-21T07:12:18.401Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/89/0b9dc4dc7ebadd088b9558bd8e09a02ac0a11edd772b77f47c4c66dd2a22/python_calamine-0.5.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:79c493cc53ca4d728a758600291ceefdec6b705a199ce75f946c8f8858102d51", size = 850140, upload-time = "2025-10-21T07:12:19.853Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/c2/379f43ad7944b8d200045c0a9c2783b3e6aac1015ad0a490996754ebf855/python_calamine-0.5.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a6001164afb03ec12725c5c8e975b73c6b6491381b03f28e5a88226e2e7473d7", size = 824651, upload-time = "2025-10-21T07:12:21.404Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/4f/c484f6f0d99d14631de9e065bdf7932fe573f7b6f0bf79d6b3c0219595d7/python_calamine-0.5.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:656cb61bd306687486a45947f632cd5afef63beb78da2c73ac59ab66aa455f7e", size = 897554, upload-time = "2025-10-21T07:12:23.733Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/eb/1966d0fde74ca7023678eacd128a14a4c136dc287a9f1ec21ed2236f43d4/python_calamine-0.5.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aa79ff3770fc88732b35f00c4f3ac884bc2b5289e7893484a8d8d4790e67c7a", size = 887612, upload-time = "2025-10-21T07:12:25.25Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c9/2a/50a4d29139ef6f67cc29b7bb2d821253f032bdbfa451faba986fc3ce1bf8/python_calamine-0.5.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2908be3d273ff2756893840b5bfeb07a444c193f55a2f2343d55870df5d228dc", size = 1046417, upload-time = "2025-10-21T07:12:26.747Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e7/3f/4130952e2646867f6a8c3f0cda8a7834a95b720fd557115ce722d96250c9/python_calamine-0.5.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbcda9f0c195584bede0518597380e9431dcacd298c5f6b627bae1a38510ff25", size = 944118, upload-time = "2025-10-21T07:12:28.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/f8/64fc1688c833ed5e79f3d657908f616909c03a4936eed8320519c6d5ffc2/python_calamine-0.5.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a78f0c8853ce544b640e9a6994690c434be7a3e9189b4f49536669d220180a63", size = 906103, upload-time = "2025-10-21T07:12:30.201Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/13/9ef73a559f492651e3588e6ecbeaf82cb91cdb084eb05b9a70f50ab857b7/python_calamine-0.5.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba6f1181dcad2f6ec7da0ea6272bf68b59ce2135800db06374b083cac599780e", size = 947955, upload-time = "2025-10-21T07:12:32.035Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8e/8d/e303b70fe8c6fa64179633445a5bf424a23153459ddcaff861300e5c2221/python_calamine-0.5.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:eea735f76e5a06efc91fe8907bca03741e71febcadd8621c6ea48df7b4a64be3", size = 1077823, upload-time = "2025-10-21T07:12:33.568Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/ce/8e9b85b7488488a7c3c673ae727ba6eb4c73f97d81acb250048f8e223196/python_calamine-0.5.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2d138e5a960ae7a8fc91674252cf2d7387a5cef2892ebdccf3eea2756e1ced0c", size = 1150733, upload-time = "2025-10-21T07:12:35.097Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/e0/ca4ad49b693d165b87de068ad78c9aca35a8657a5695cbcb212426e29bd9/python_calamine-0.5.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8ad42673f5c0bb2d30b17b2ec3de5e8eae6dde4097650332c507b4146c63bb9c", size = 1080697, upload-time = "2025-10-21T07:12:36.679Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/62/1065dbf7c554bd80ba976d60278525750c0ff0feb56812f76b6531b67f21/python_calamine-0.5.4-cp314-cp314-win32.whl", hash = "sha256:36918496befbeeddc653e1499c090923dcf803d2633eb8bd473a9d21bdd06e79", size = 677184, upload-time = "2025-10-21T07:12:38.295Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e0/2f/f21bffb13712434168f7125f733fb728f723d79262a5acb90328a13fbf11/python_calamine-0.5.4-cp314-cp314-win_amd64.whl", hash = "sha256:bc01a7c03d302d11721a0ca00f67b71ebec125abab414f604bb03749b8c3557e", size = 722692, upload-time = "2025-10-21T07:12:39.764Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/b5/7214e8105b5165653cf49c9edec17db9d2551645be1a332bf09013908bc2/python_calamine-0.5.4-cp314-cp314-win_arm64.whl", hash = "sha256:8ab116aa7aea8bb3823f7a00c95bea08940db995556d287b6c1e51f3e83b3570", size = 686400, upload-time = "2025-10-21T07:12:41.371Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/47/91/6815256d05940608c92e4d9467db04b9eab6124d8a9bd37f5c967157ead6/python_calamine-0.5.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bc004d1da2779aea2b6782d18d977f8e1121e3a245c331db545f69fc2ae5cad0", size = 848400, upload-time = "2025-10-21T07:12:43.22Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/2c/fee8ffaac4a2385e9522c0f0febb690499a00fb99c0c953e7cd4bcdc6695/python_calamine-0.5.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5fb8c85acf5ccfe961023de01ce3a36839e310b5d9dc9aac9db01f350fbd3cec", size = 825000, upload-time = "2025-10-21T07:12:45.008Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/4d/61eeddde208958518cbf9ab76f387c379bd56019c029ea5fcc6cf3b96044/python_calamine-0.5.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dd48379eabc27c2bb73356fd5d1df48a46caf94433d4f60bdd38ad416a6f46", size = 896022, upload-time = "2025-10-21T07:12:46.503Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/90/87/9ae23a3c2a7d2891c04436d0d7ed9984cb0f7145c96f6f8b36a345c7cc95/python_calamine-0.5.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:da3c2aa81de7cb20834b5326f326ba91a58123f10845864c3911e9dd819b9271", size = 887206, upload-time = "2025-10-21T07:12:48.446Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/23/9289c350b8d7976295d01474f17a22fb9a42695dc403aa0f735a4e008791/python_calamine-0.5.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9c09cd413e69f3366bdb73fc525c02963f29ca01da5a2ef9abed5486bba0e6a", size = 1042372, upload-time = "2025-10-21T07:12:50.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/66/cd2c8ec4090d1cfd0875e7a45a7a7d55a9670b18daaad45845360d4def2c/python_calamine-0.5.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b678e11378b991e551d1260e21099cd9c5cffa4c83f816cba0aa05e9023d0f06", size = 941589, upload-time = "2025-10-21T07:12:51.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7d/d5/6a8199af0efe83945beb3df5a0556d658108cbf71b2cc449f3b5106afaef/python_calamine-0.5.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c7397781c4aedf70c5e4adcd31e2209035f4eb78fcb8ed887d252965e924530", size = 904284, upload-time = "2025-10-21T07:12:53.184Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/93/0d/a419be4b036207ca61e5bbd15225f9637348a7c5c353d009ee0af5d38e90/python_calamine-0.5.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9062677c5c1ca9f16dd0d29875a9ffa841fe6b230a7c03b3ed92146fc42572fd", size = 945532, upload-time = "2025-10-21T07:12:54.692Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a1/eb/4b39fc8d42a13578b4cc695d0e1e84bd5d87087444c27f667e1d7e756f4f/python_calamine-0.5.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:0cd2881eadb30fddb84abe4fccb1544c6ba15aec45fe833a5691f5b0c8eeaec1", size = 1075965, upload-time = "2025-10-21T07:12:56.247Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/a5/d9d286986a192afd35056cbb53ca6979c09a584ca8ae9c2ab818141a9dde/python_calamine-0.5.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:6d077520c78530ad610fc1dc94463e618df8600d071409d8aa1bc195b9759f6f", size = 1150192, upload-time = "2025-10-21T07:12:58.236Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d9/2c/37612d97cf969adf39dbad04c14e8c35aedc8e6476b8e97cb5a5c2ed2b76/python_calamine-0.5.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:1ba09027e12a495b4e3eda4a7c59bb38d058e1941382bb2cc2e3a2a7bd12d3ba", size = 1078532, upload-time = "2025-10-21T07:13:00.123Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/2b/f6913d5cfc35c7d9c76df9fbabf00cbc5ddc525abc1e1dc55d5a57a059aa/python_calamine-0.5.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a45f72a0ae0184c6ae99deefba735fdf82f858bcbf25caeb14366d45b18f23ea", size = 722451, upload-time = "2025-10-21T07:13:01.902Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/88/0c/b6bf7a7033b0f0143e1494f0f6803f63ec8755dc30f054775434fe06d310/python_calamine-0.5.4-cp314-cp314t-win_arm64.whl", hash = "sha256:1ec345f20f0ea6e525e8d5a6dbb68065d374bc1feaf5bb479a93e2ed1d4db9ae", size = 684875, upload-time = "2025-10-21T07:13:03.308Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "python-dateutil"
|
||||
version = "2.9.0.post0"
|
||||
|
||||
Reference in New Issue
Block a user