add datetable cnfcnpj
This commit is contained in:
14
api.saladeaula.digital/app/routes/orgs/users/batch_jobs.py
Normal file
14
api.saladeaula.digital/app/routes/orgs/users/batch_jobs.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from aws_lambda_powertools import Logger
|
||||||
|
from aws_lambda_powertools.event_handler.api_gateway import Router
|
||||||
|
from layercake.dynamodb import DynamoDBPersistenceLayer
|
||||||
|
|
||||||
|
from boto3clients import dynamodb_client
|
||||||
|
from config import USER_TABLE
|
||||||
|
|
||||||
|
logger = Logger(__name__)
|
||||||
|
router = Router()
|
||||||
|
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/<org_id>/users/batch-jobs')
|
||||||
|
def batch_jobs(org_id: str): ...
|
||||||
@@ -206,14 +206,17 @@ function Course({
|
|||||||
<li className="flex gap-1.5">
|
<li className="flex gap-1.5">
|
||||||
<span
|
<span
|
||||||
className={cn({
|
className={cn({
|
||||||
'line-through text-muted-foreground': custom_pricing
|
'line-through text-muted-foreground': custom_pricing,
|
||||||
|
'font-bold': !custom_pricing
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{currency.format(metadata__unit_price)}
|
{currency.format(metadata__unit_price)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
{custom_pricing && (
|
{custom_pricing && (
|
||||||
<span>{currency.format(custom_pricing)}</span>
|
<span className="font-bold">
|
||||||
|
{currency.format(custom_pricing)}
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { CellContext, ColumnDef } from '@tanstack/react-table'
|
import type { ColumnDef } from '@tanstack/react-table'
|
||||||
import { useRequest, useToggle } from 'ahooks'
|
import { useRequest, useToggle } from 'ahooks'
|
||||||
import {
|
import {
|
||||||
CircleXIcon,
|
CircleXIcon,
|
||||||
@@ -13,7 +13,10 @@ import type { ComponentProps, MouseEvent } from 'react'
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { Abbr } from '@repo/ui/components/abbr'
|
import { Abbr } from '@repo/ui/components/abbr'
|
||||||
import { DataTableColumnHeader } from '@repo/ui/components/data-table'
|
import {
|
||||||
|
DataTableColumnDatetime,
|
||||||
|
DataTableColumnHeader
|
||||||
|
} from '@repo/ui/components/data-table'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
@@ -121,7 +124,6 @@ export const columns: ColumnDef<Enrollment>[] = [
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const { name } = row.getValue('course') as { name: string }
|
const { name } = row.getValue('course') as { name: string }
|
||||||
|
|
||||||
return <Abbr>{name}</Abbr>
|
return <Abbr>{name}</Abbr>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -159,43 +161,53 @@ export const columns: ColumnDef<Enrollment>[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'created_at',
|
accessorKey: 'created_at',
|
||||||
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
|
||||||
meta: { title: 'Cadastrado em' },
|
meta: { title: 'Cadastrado em' },
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: cellDate
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'started_at',
|
accessorKey: 'started_at',
|
||||||
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
|
||||||
meta: { title: 'Iniciado em' },
|
meta: { title: 'Iniciado em' },
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: cellDate
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'completed_at',
|
accessorKey: 'completed_at',
|
||||||
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
|
||||||
meta: { title: 'Concluído em' },
|
meta: { title: 'Concluído em' },
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: cellDate
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'failed_at',
|
accessorKey: 'failed_at',
|
||||||
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
|
||||||
meta: { title: 'Reprovado em' },
|
meta: { title: 'Reprovado em' },
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: cellDate
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'canceled_at',
|
accessorKey: 'canceled_at',
|
||||||
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
|
||||||
meta: { title: 'Cancelado em' },
|
meta: { title: 'Cancelado em' },
|
||||||
enableSorting: true,
|
enableSorting: true,
|
||||||
enableHiding: true,
|
enableHiding: true,
|
||||||
cell: cellDate
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
@@ -203,20 +215,6 @@ export const columns: ColumnDef<Enrollment>[] = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
function cellDate<TData>({
|
|
||||||
row: { original },
|
|
||||||
cell: { column }
|
|
||||||
}: CellContext<TData, unknown>) {
|
|
||||||
const accessorKey = column.columnDef.accessorKey as keyof TData
|
|
||||||
const value = original?.[accessorKey]
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
return formatted.format(new Date(value as string))
|
|
||||||
}
|
|
||||||
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getEnrollment(id: string) {
|
async function getEnrollment(id: string) {
|
||||||
const r = await fetch(`/~/api/enrollments/${id}`, {
|
const r = await fetch(`/~/api/enrollments/${id}`, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataTableColumnDatetime,
|
||||||
|
DataTableColumnCurrency,
|
||||||
|
DataTableColumnHeader
|
||||||
|
} from '@repo/ui/components/data-table'
|
||||||
import { type ColumnDef } from '@tanstack/react-table'
|
import { type ColumnDef } from '@tanstack/react-table'
|
||||||
|
|
||||||
// This type is used to define the shape of our data.
|
// This type is used to define the shape of our data.
|
||||||
@@ -12,14 +17,6 @@ export type Order = {
|
|||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatted = new Intl.DateTimeFormat('pt-BR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const columns: ColumnDef<Order>[] = [
|
export const columns: ColumnDef<Order>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'payment_method',
|
accessorKey: 'payment_method',
|
||||||
@@ -32,43 +29,35 @@ export const columns: ColumnDef<Order>[] = [
|
|||||||
{
|
{
|
||||||
accessorKey: 'total',
|
accessorKey: 'total',
|
||||||
header: 'Valor total',
|
header: 'Valor total',
|
||||||
cell: ({ row }) => {
|
cell: ({ row, column }) => (
|
||||||
const amount = parseFloat(row.getValue('total'))
|
<DataTableColumnCurrency row={row} column={column} />
|
||||||
const formatted = new Intl.NumberFormat('pt-BR', {
|
)
|
||||||
style: 'currency',
|
|
||||||
currency: 'BRL'
|
|
||||||
}).format(amount)
|
|
||||||
|
|
||||||
return <div className="font-medium">{formatted}</div>
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Comprado em',
|
accessorKey: 'create_date',
|
||||||
cell: ({ row }) => {
|
enableSorting: true,
|
||||||
const createdAt = new Date(row.original.create_date)
|
meta: { title: 'Comprado em' },
|
||||||
return formatted.format(createdAt)
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
}
|
cell: ({ row, column }) => (
|
||||||
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Vencimento em',
|
accessorKey: 'due_date',
|
||||||
cell: ({ row }) => {
|
enableSorting: true,
|
||||||
try {
|
meta: { title: 'Vencimento em' },
|
||||||
const dueDate = new Date(row.original.due_date)
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
return formatted.format(dueDate)
|
cell: ({ row, column }) => (
|
||||||
} catch {
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
return 'N/A'
|
)
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Pago em',
|
accessorKey: 'payment_date',
|
||||||
cell: ({ row }) => {
|
enableSorting: true,
|
||||||
if (row.original.payment_date) {
|
meta: { title: 'Pago em' },
|
||||||
const createdAt = new Date(row.original.payment_date)
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
return formatted.format(createdAt)
|
cell: ({ row, column }) => (
|
||||||
}
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
|
)
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { Route } from './+types/route'
|
|||||||
|
|
||||||
import { Suspense } from 'react'
|
import { Suspense } from 'react'
|
||||||
import { Await } from 'react-router'
|
import { Await } from 'react-router'
|
||||||
|
import { MeiliSearchFilterBuilder } from 'meilisearch-helper'
|
||||||
|
|
||||||
import { DataTable } from '@repo/ui/components/data-table'
|
import { DataTable } from '@repo/ui/components/data-table'
|
||||||
import { Skeleton } from '@repo/ui/components/skeleton'
|
import { Skeleton } from '@repo/ui/components/skeleton'
|
||||||
@@ -16,18 +17,25 @@ export function meta({}: Route.MetaArgs) {
|
|||||||
export async function loader({ params, context, request }: Route.LoaderArgs) {
|
export async function loader({ params, context, request }: Route.LoaderArgs) {
|
||||||
const { searchParams } = new URL(request.url)
|
const { searchParams } = new URL(request.url)
|
||||||
const { orgid } = params
|
const { orgid } = params
|
||||||
|
const query = searchParams.get('q') || ''
|
||||||
|
const sort = searchParams.get('sort') || 'create_date:desc'
|
||||||
const page = Number(searchParams.get('p')) + 1
|
const page = Number(searchParams.get('p')) + 1
|
||||||
const hitsPerPage = Number(searchParams.get('perPage')) || 25
|
const hitsPerPage = Number(searchParams.get('perPage')) || 25
|
||||||
|
|
||||||
|
let builder = new MeiliSearchFilterBuilder().where('tenant_id', '=', orgid)
|
||||||
|
|
||||||
|
const orders = createSearch({
|
||||||
|
index: 'betaeducacao-prod-orders',
|
||||||
|
filter: builder.build(),
|
||||||
|
sort: [sort],
|
||||||
|
query,
|
||||||
|
page,
|
||||||
|
hitsPerPage,
|
||||||
|
env: context.cloudflare.env
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
data: createSearch({
|
data: orders
|
||||||
index: 'betaeducacao-prod-orders',
|
|
||||||
sort: ['create_date:desc'],
|
|
||||||
filter: `tenant_id = ${orgid}`,
|
|
||||||
page,
|
|
||||||
hitsPerPage,
|
|
||||||
env: context.cloudflare.env
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +56,7 @@ export default function Route({ loaderData: { data } }) {
|
|||||||
{({ hits, page, hitsPerPage, totalHits }) => {
|
{({ hits, page, hitsPerPage, totalHits }) => {
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
sort={[{ id: 'created_at', desc: true }]}
|
sort={[{ id: 'create_date', desc: true }]}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={hits as Order[]}
|
data={hits as Order[]}
|
||||||
pageIndex={page - 1}
|
pageIndex={page - 1}
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ import { NavLink, useParams } from 'react-router'
|
|||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
|
|
||||||
import { Abbr } from '@repo/ui/components/abbr'
|
import { Abbr } from '@repo/ui/components/abbr'
|
||||||
import { useDataTable } from '@repo/ui/components/data-table'
|
import {
|
||||||
|
useDataTable,
|
||||||
|
DataTableColumnDatetime,
|
||||||
|
DataTableColumnHeader,
|
||||||
|
DataTableColumnCpfCnpj
|
||||||
|
} from '@repo/ui/components/data-table'
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogAction,
|
AlertDialogAction,
|
||||||
@@ -26,6 +31,7 @@ import {
|
|||||||
} from '@repo/ui/components/ui/alert-dialog'
|
} from '@repo/ui/components/ui/alert-dialog'
|
||||||
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
|
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
|
||||||
import { Button } from '@repo/ui/components/ui/button'
|
import { Button } from '@repo/ui/components/ui/button'
|
||||||
|
import { Checkbox } from '@repo/ui/components/ui/checkbox'
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -45,15 +51,30 @@ export type User = {
|
|||||||
cnpj?: string
|
cnpj?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatted = new Intl.DateTimeFormat('pt-BR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const columns: ColumnDef<User>[] = [
|
export const columns: ColumnDef<User>[] = [
|
||||||
|
{
|
||||||
|
id: 'select',
|
||||||
|
header: ({ table }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={
|
||||||
|
table.getIsAllPageRowsSelected() ||
|
||||||
|
(table.getIsSomePageRowsSelected() && 'indeterminate')
|
||||||
|
}
|
||||||
|
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
aria-label="Selecionar tudo"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<Checkbox
|
||||||
|
checked={row.getIsSelected()}
|
||||||
|
disabled={!row.getCanSelect()}
|
||||||
|
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||||
|
className="cursor-pointer"
|
||||||
|
aria-label="Selecionar linha"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
header: 'Colaborador',
|
header: 'Colaborador',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
@@ -78,38 +99,29 @@ export const columns: ColumnDef<User>[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
accessorKey: 'cpf',
|
||||||
header: 'CPF',
|
header: 'CPF',
|
||||||
cell: ({ row }) => {
|
cell: ({ row, column }) => (
|
||||||
const { cpf } = row.original
|
<DataTableColumnCpfCnpj row={row} column={column} />
|
||||||
|
)
|
||||||
if (cpf) {
|
|
||||||
return <>{formatCPF(cpf)}</>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Último accesso',
|
accessorKey: 'createDate',
|
||||||
cell: ({ row }) => {
|
enableSorting: true,
|
||||||
// Post-migration: rename `lastLogin` to `last_login`
|
meta: { title: 'Cadastrado em' },
|
||||||
if (row.original?.lastLogin) {
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
const lastLogin = new Date(row.original.lastLogin)
|
cell: ({ row, column }) => (
|
||||||
return formatted.format(lastLogin)
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
}
|
)
|
||||||
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: 'Cadastrado em',
|
accessorKey: 'lastLogin',
|
||||||
meta: {
|
enableSorting: true,
|
||||||
className: 'w-1/12'
|
meta: { title: 'Último acesso' },
|
||||||
},
|
header: ({ column }) => <DataTableColumnHeader column={column} />,
|
||||||
cell: ({ row }) => {
|
cell: ({ row, column }) => (
|
||||||
const created_at = new Date(row.original.createDate)
|
<DataTableColumnDatetime row={row} column={column} />
|
||||||
return formatted.format(created_at)
|
)
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'actions',
|
id: 'actions',
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { Route } from './+types/route'
|
|||||||
import { PlusIcon } from 'lucide-react'
|
import { PlusIcon } from 'lucide-react'
|
||||||
import { Suspense } from 'react'
|
import { Suspense } from 'react'
|
||||||
import { Await, Link, useSearchParams } from 'react-router'
|
import { Await, Link, useSearchParams } from 'react-router'
|
||||||
|
import { MeiliSearchFilterBuilder } from 'meilisearch-helper'
|
||||||
|
|
||||||
import { DataTable } from '@repo/ui/components/data-table'
|
import { DataTable } from '@repo/ui/components/data-table'
|
||||||
import { SearchForm } from '@repo/ui/components/search-form'
|
import { SearchForm } from '@repo/ui/components/search-form'
|
||||||
@@ -27,13 +28,16 @@ export async function loader({ params, context, request }: Route.LoaderArgs) {
|
|||||||
const { searchParams } = new URL(request.url)
|
const { searchParams } = new URL(request.url)
|
||||||
const { orgid } = params
|
const { orgid } = params
|
||||||
const query = searchParams.get('q') || ''
|
const query = searchParams.get('q') || ''
|
||||||
|
const sort = searchParams.get('sort') || 'createDate:desc'
|
||||||
const page = Number(searchParams.get('p')) + 1
|
const page = Number(searchParams.get('p')) + 1
|
||||||
const hitsPerPage = Number(searchParams.get('perPage')) || 25
|
const hitsPerPage = Number(searchParams.get('perPage')) || 25
|
||||||
|
|
||||||
|
let builder = new MeiliSearchFilterBuilder().where('tenant_id', '=', orgid)
|
||||||
|
|
||||||
const users = createSearch({
|
const users = createSearch({
|
||||||
index: 'betaeducacao-prod-users_d2o3r5gmm4it7j',
|
index: 'betaeducacao-prod-users_d2o3r5gmm4it7j',
|
||||||
sort: ['createDate:desc', 'create_date:desc'],
|
filter: builder.build(),
|
||||||
filter: `tenant_id = ${orgid}`,
|
sort: [sort],
|
||||||
query,
|
query,
|
||||||
page,
|
page,
|
||||||
hitsPerPage,
|
hitsPerPage,
|
||||||
@@ -61,7 +65,7 @@ export default function Route({ loaderData: { data } }) {
|
|||||||
{({ hits, page, hitsPerPage, totalHits }) => {
|
{({ hits, page, hitsPerPage, totalHits }) => {
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
sort={[{ id: 'created_at', desc: true }]}
|
sort={[{ id: 'createDate', desc: true }]}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={hits as User[]}
|
data={hits as User[]}
|
||||||
pageIndex={page - 1}
|
pageIndex={page - 1}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import warnings
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from base64 import urlsafe_b64decode, urlsafe_b64encode
|
from base64 import urlsafe_b64decode, urlsafe_b64encode
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import date, datetime
|
||||||
from ipaddress import IPv4Address
|
from ipaddress import IPv4Address
|
||||||
from typing import TYPE_CHECKING, Any, Self, Type, TypedDict
|
from typing import TYPE_CHECKING, Any, Self, Type, TypedDict
|
||||||
from urllib.parse import quote, unquote
|
from urllib.parse import quote, unquote
|
||||||
@@ -38,26 +38,52 @@ def _serialize_to_basic_types(data: Any) -> str | dict | set | list:
|
|||||||
match data:
|
match data:
|
||||||
case datetime():
|
case datetime():
|
||||||
return data.isoformat()
|
return data.isoformat()
|
||||||
|
|
||||||
|
case date():
|
||||||
|
return data.isoformat()
|
||||||
|
|
||||||
case UUID():
|
case UUID():
|
||||||
return str(data)
|
return str(data)
|
||||||
|
|
||||||
case IPv4Address():
|
case IPv4Address():
|
||||||
return str(data)
|
return str(data)
|
||||||
|
|
||||||
case tuple() | list():
|
case tuple() | list():
|
||||||
|
if not data:
|
||||||
|
return []
|
||||||
|
|
||||||
serialized = [_serialize_to_basic_types(v) for v in data]
|
serialized = [_serialize_to_basic_types(v) for v in data]
|
||||||
|
|
||||||
if any(isinstance(v, dict) for v in serialized):
|
if any(isinstance(v, (dict, list)) for v in serialized):
|
||||||
return serialized
|
return serialized
|
||||||
|
|
||||||
return set(serialized)
|
try:
|
||||||
|
return set(serialized)
|
||||||
|
except TypeError:
|
||||||
|
return serialized
|
||||||
|
|
||||||
|
case set():
|
||||||
|
if not data:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return set(_serialize_to_basic_types(v) for v in data)
|
||||||
|
|
||||||
case dict():
|
case dict():
|
||||||
return {k: _serialize_to_basic_types(v) for k, v in data.items()}
|
return {
|
||||||
|
k: _serialize_to_basic_types(v)
|
||||||
|
for k, v in data.items()
|
||||||
|
if v is not None
|
||||||
|
}
|
||||||
|
|
||||||
case _:
|
case _:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def serialize(data: dict) -> dict:
|
def serialize(data: dict) -> dict:
|
||||||
return {
|
return {
|
||||||
k: serializer.serialize(_serialize_to_basic_types(v)) for k, v in data.items()
|
k: serializer.serialize(_serialize_to_basic_types(v))
|
||||||
|
for k, v in data.items()
|
||||||
|
if v is not None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
description = "Packages shared dependencies to optimize deployment and ensure consistency across functions."
|
description = "Packages shared dependencies to optimize deployment and ensure consistency across functions."
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
authors = [
|
authors = [
|
||||||
@@ -28,6 +28,7 @@ dependencies = [
|
|||||||
"joserfc>=1.2.2",
|
"joserfc>=1.2.2",
|
||||||
"python-multipart>=0.0.20",
|
"python-multipart>=0.0.20",
|
||||||
"authlib>=1.6.5",
|
"authlib>=1.6.5",
|
||||||
|
"python-calamine>=0.5.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from ipaddress import IPv4Address
|
from ipaddress import IPv4Address
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from layercake.dateutils import ttl
|
from layercake.dateutils import ttl
|
||||||
@@ -68,6 +69,22 @@ def test_serialize():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_serialize_uuid():
|
||||||
|
uuid = UUID('12345678-1234-5678-1234-567812345678')
|
||||||
|
assert serialize({'id': uuid}) == {
|
||||||
|
'id': {'S': '12345678-1234-5678-1234-567812345678'}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_serialize_pairs():
|
||||||
|
pairs = [(1, 2), (3, 4), (5, 6)]
|
||||||
|
expected = serialize({'pairs': pairs})
|
||||||
|
|
||||||
|
assert expected == {
|
||||||
|
'pairs': {'L': [{'NS': ['1', '2']}, {'NS': ['3', '4']}, {'NS': ['5', '6']}]}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_composekey():
|
def test_composekey():
|
||||||
key = ComposeKey(('122', 'abc'), prefix='schedules', delimiter=':')
|
key = ComposeKey(('122', 'abc'), prefix='schedules', delimiter=':')
|
||||||
assert key == 'schedules:122:abc'
|
assert key == 'schedules:122:abc'
|
||||||
|
|||||||
81
layercake/uv.lock
generated
81
layercake/uv.lock
generated
@@ -1,5 +1,5 @@
|
|||||||
version = 1
|
version = 1
|
||||||
revision = 2
|
revision = 3
|
||||||
requires-python = ">=3.12"
|
requires-python = ">=3.12"
|
||||||
resolution-markers = [
|
resolution-markers = [
|
||||||
"python_full_version >= '3.14'",
|
"python_full_version >= '3.14'",
|
||||||
@@ -675,7 +675,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.10.1"
|
version = "0.11.2"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "arnparse" },
|
{ name = "arnparse" },
|
||||||
@@ -692,6 +692,7 @@ dependencies = [
|
|||||||
{ name = "pycpfcnpj" },
|
{ name = "pycpfcnpj" },
|
||||||
{ name = "pydantic", extra = ["email"] },
|
{ name = "pydantic", extra = ["email"] },
|
||||||
{ name = "pydantic-extra-types" },
|
{ name = "pydantic-extra-types" },
|
||||||
|
{ name = "python-calamine" },
|
||||||
{ name = "python-multipart" },
|
{ name = "python-multipart" },
|
||||||
{ name = "pytz" },
|
{ name = "pytz" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
@@ -727,6 +728,7 @@ requires-dist = [
|
|||||||
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
||||||
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
||||||
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
||||||
|
{ name = "python-calamine", specifier = ">=0.5.4" },
|
||||||
{ name = "python-multipart", specifier = ">=0.0.20" },
|
{ name = "python-multipart", specifier = ">=0.0.20" },
|
||||||
{ name = "pytz", specifier = ">=2025.1" },
|
{ name = "pytz", specifier = ">=2025.1" },
|
||||||
{ name = "requests", specifier = ">=2.32.3" },
|
{ name = "requests", specifier = ">=2.32.3" },
|
||||||
@@ -1274,6 +1276,81 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" },
|
{ url = "https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl", hash = "sha256:f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5", size = 24644, upload-time = "2025-06-12T10:47:45.932Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-calamine"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/60/b1ace7a0fd636581b3bb27f1011cb7b2fe4d507b58401c4d328cfcb5c849/python_calamine-0.5.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:4d711f91283d28f19feb111ed666764de69e6d2a0201df8f84e81a238f68d193", size = 850087, upload-time = "2025-10-21T07:11:17.002Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7f/32/32ca71ce50f9b7c7d6e7ec5fcc579a97ddd8b8ce314fe143ba2a19441dc7/python_calamine-0.5.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed67afd3adedb5bcfb428cf1f2d7dfd936dea9fe979ab631194495ab092973ba", size = 825659, upload-time = "2025-10-21T07:11:18.248Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/c5/27ba71a9da2a09be9ff2f0dac522769956c8c89d6516565b21c9c78bfae6/python_calamine-0.5.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13662895dac487315ccce25ea272a1ea7e7ac05d899cde4e33d59d6c43274c54", size = 897332, upload-time = "2025-10-21T07:11:19.89Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/e7/c4be6ff8e8899ace98cacc9604a2dd1abc4901839b733addfb6ef32c22ba/python_calamine-0.5.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23e354755583cfaa824ddcbe8b099c5c7ac19bf5179320426e7a88eea2f14bc5", size = 886885, upload-time = "2025-10-21T07:11:21.912Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/38/24/80258fb041435021efa10d0b528df6842e442585e48cbf130e73fed2529b/python_calamine-0.5.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e1bc3f22107dcbdeb32d4d3c5c1e8831d3c85d4b004a8606dd779721b29843d", size = 1043907, upload-time = "2025-10-21T07:11:23.3Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/20/157340787d03ef6113a967fd8f84218e867ba4c2f7fc58cc645d8665a61a/python_calamine-0.5.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:182b314117e47dbd952adaa2b19c515555083a48d6f9146f46faaabd9dab2f81", size = 942376, upload-time = "2025-10-21T07:11:24.866Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/f5/aec030f567ee14c60b6fc9028a78767687f484071cb080f7cfa328d6496e/python_calamine-0.5.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8f882e092ab23f72ea07e2e48f5f2efb1885c1836fb949f22fd4540ae11742e", size = 906455, upload-time = "2025-10-21T07:11:26.203Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/58/4affc0d1389f837439ad45f400f3792e48030b75868ec757e88cb35d7626/python_calamine-0.5.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62a9b4b7b9bd99d03373e58884dfb60d5a1c292c8e04e11f8b7420b77a46813e", size = 948132, upload-time = "2025-10-21T07:11:27.507Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/2e/70ed04f39e682a9116730f56b7fbb54453244ccc1c3dae0662d4819f1c1d/python_calamine-0.5.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:98bb011d33c0e2d183ff30ab3d96792c3493f56f67a7aa2fcadad9a03539e79b", size = 1077436, upload-time = "2025-10-21T07:11:28.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/ce/806f8ce06b5bb9db33007f85045c304cda410970e7aa07d08f6eaee67913/python_calamine-0.5.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:6b218a95489ff2f1cc1de0bba2a16fcc82981254bbb23f31d41d29191282b9ad", size = 1150570, upload-time = "2025-10-21T07:11:30.237Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/da/61f13c8d107783128c1063cf52ca9cacdc064c58d58d3cf49c1728ce8296/python_calamine-0.5.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8296a4872dbe834205d25d26dd6cfcb33ee9da721668d81b21adc25a07c07e4", size = 1080286, upload-time = "2025-10-21T07:11:31.564Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/85/c5612a63292eb7d0648b17c5ff32ad5d6c6f3e1d78825f01af5c765f4d3f/python_calamine-0.5.4-cp312-cp312-win32.whl", hash = "sha256:cebb9c88983ae676c60c8c02aa29a9fe13563f240579e66de5c71b969ace5fd9", size = 676617, upload-time = "2025-10-21T07:11:32.833Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/18/5a037942de8a8df0c805224b2fba06df6d25c1be3c9484ba9db1ca4f3ee6/python_calamine-0.5.4-cp312-cp312-win_amd64.whl", hash = "sha256:15abd7aff98fde36d7df91ac051e86e66e5d5326a7fa98d54697afe95a613501", size = 721464, upload-time = "2025-10-21T07:11:34.383Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/8b/89ca17b44bcd8be5d0e8378d87b880ae17a837573553bd2147cceca7e759/python_calamine-0.5.4-cp312-cp312-win_arm64.whl", hash = "sha256:1cef0d0fc936974020a24acf1509ed2a285b30a4e1adf346c057112072e84251", size = 687268, upload-time = "2025-10-21T07:11:36.324Z" },
|
||||||
|
{ 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]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.9.0.post0"
|
version = "2.9.0.post0"
|
||||||
|
|||||||
40
packages/ui/src/components/data-table/column-cpfcnpj.tsx
Normal file
40
packages/ui/src/components/data-table/column-cpfcnpj.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import type { Row, Column } from '@tanstack/react-table'
|
||||||
|
import {
|
||||||
|
formatCPF,
|
||||||
|
formatCNPJ,
|
||||||
|
isValidCPF,
|
||||||
|
isValidCNPJ
|
||||||
|
} from '@brazilian-utils/brazilian-utils'
|
||||||
|
|
||||||
|
interface DataTableColumnDatetimeProps<TData, TValue>
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
row: Row<TData>
|
||||||
|
column: Column<TData, TValue>
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatted = new Intl.DateTimeFormat('pt-BR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
|
||||||
|
export function DataTableColumnCpfCnpj<TData, TValue>({
|
||||||
|
row,
|
||||||
|
column
|
||||||
|
}: DataTableColumnDatetimeProps<TData, TValue>) {
|
||||||
|
// @ts-ignore
|
||||||
|
const { accessorKey } = column.columnDef
|
||||||
|
const value = row.getValue(accessorKey) as string
|
||||||
|
|
||||||
|
if (isValidCPF(value)) {
|
||||||
|
return formatCPF(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isValidCNPJ(value)) {
|
||||||
|
return formatCNPJ(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
27
packages/ui/src/components/data-table/column-currency.tsx
Normal file
27
packages/ui/src/components/data-table/column-currency.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import type { Row, Column } from '@tanstack/react-table'
|
||||||
|
|
||||||
|
interface DataTableColumnDatetimeProps<TData, TValue>
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
row: Row<TData>
|
||||||
|
column: Column<TData, TValue>
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatted = new Intl.NumberFormat('pt-BR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'BRL'
|
||||||
|
})
|
||||||
|
|
||||||
|
export function DataTableColumnCurrency<TData, TValue>({
|
||||||
|
row,
|
||||||
|
column
|
||||||
|
}: DataTableColumnDatetimeProps<TData, TValue>) {
|
||||||
|
// @ts-ignore
|
||||||
|
const { accessorKey } = column.columnDef
|
||||||
|
const value = row.getValue(accessorKey) as number
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
return formatted.format(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import type { Row, Column } from '@tanstack/react-table'
|
||||||
|
|
||||||
|
interface DataTableColumnDatetimeProps<TData, TValue>
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
|
row: Row<TData>
|
||||||
|
column: Column<TData, TValue>
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatted = new Intl.DateTimeFormat('pt-BR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
|
||||||
|
export function DataTableColumnDatetime<TData, TValue>({
|
||||||
|
row,
|
||||||
|
column
|
||||||
|
}: DataTableColumnDatetimeProps<TData, TValue>) {
|
||||||
|
// @ts-ignore
|
||||||
|
const { accessorKey } = column.columnDef
|
||||||
|
const value = row.getValue(accessorKey)
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
return formatted.format(new Date(value as string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return <></>
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
|||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
>
|
>
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
|
|
||||||
{column.getIsSorted() === 'desc' ? (
|
{column.getIsSorted() === 'desc' ? (
|
||||||
<ChevronDownIcon />
|
<ChevronDownIcon />
|
||||||
) : column.getIsSorted() === 'asc' ? (
|
) : column.getIsSorted() === 'asc' ? (
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ export function DataTable<TData, TValue>({
|
|||||||
<TableRow
|
<TableRow
|
||||||
key={row.id}
|
key={row.id}
|
||||||
data-state={row.getIsSelected() && 'selected'}
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
className="has-[[data-state=open]]:bg-muted/50"
|
className="has-data-[state=open]:bg-muted/50"
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
export { DataTableColumnHeader } from './column-header'
|
export { DataTableColumnHeader } from './column-header'
|
||||||
|
export { DataTableColumnDatetime } from './column-datetime'
|
||||||
|
export { DataTableColumnCpfCnpj } from './column-cpfcnpj'
|
||||||
|
export { DataTableColumnCurrency } from './column-currency'
|
||||||
export { DataTable, useDataTable } from './data-table'
|
export { DataTable, useDataTable } from './data-table'
|
||||||
export { DataTableViewOptions } from './view-options'
|
export { DataTableViewOptions } from './view-options'
|
||||||
|
|||||||
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 (
|
from aws_lambda_powertools.utilities.data_classes import (
|
||||||
EventBridgeEvent,
|
EventBridgeEvent,
|
||||||
event_source,
|
event_source,
|
||||||
)
|
)
|
||||||
from aws_lambda_powertools.utilities.typing import LambdaContext
|
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 boto3clients import dynamodb_client, s3_client
|
||||||
from config import CHUNK_SIZE
|
from config import CHUNK_SIZE, USER_TABLE
|
||||||
from csv_utils import byte_ranges
|
from csv_utils import byte_ranges
|
||||||
|
|
||||||
|
dyn = DynamoDBPersistenceLayer(USER_TABLE, dynamodb_client)
|
||||||
transport_params = {'client': s3_client}
|
transport_params = {'client': s3_client}
|
||||||
|
|
||||||
|
|
||||||
@event_source(data_class=EventBridgeEvent)
|
@event_source(data_class=EventBridgeEvent)
|
||||||
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
def lambda_handler(event: EventBridgeEvent, context: LambdaContext) -> bool:
|
||||||
|
now_ = now()
|
||||||
new_image = event.detail['new_image']
|
new_image = event.detail['new_image']
|
||||||
csvfile = new_image['s3_uri']
|
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
|
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:
|
Properties:
|
||||||
RetentionInDays: 90
|
RetentionInDays: 90
|
||||||
|
|
||||||
EventCsvChunksFunction:
|
EventCsvIntoChunksFunction:
|
||||||
Type: AWS::Serverless::Function
|
Type: AWS::Serverless::Function
|
||||||
Properties:
|
Properties:
|
||||||
Handler: events.batch.csv_chunks.lambda_handler
|
Handler: events.batch.csv_into_chunks.lambda_handler
|
||||||
LoggingConfig:
|
LoggingConfig:
|
||||||
LogGroup: !Ref EventLog
|
LogGroup: !Ref EventLog
|
||||||
Policies:
|
Policies:
|
||||||
|
- DynamoDBCrudPolicy:
|
||||||
|
TableName: !Ref UserTable
|
||||||
- S3CrudPolicy:
|
- S3CrudPolicy:
|
||||||
BucketName: !Ref BucketName
|
BucketName: !Ref BucketName
|
||||||
Events:
|
Events:
|
||||||
@@ -50,8 +52,35 @@ Resources:
|
|||||||
resources: [!Ref UserTable]
|
resources: [!Ref UserTable]
|
||||||
detail:
|
detail:
|
||||||
new_image:
|
new_image:
|
||||||
|
id:
|
||||||
|
- prefix: BATCH_JOB#ORG#
|
||||||
sk:
|
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:
|
EventEmailReceivingFunction:
|
||||||
Type: AWS::Serverless::Function
|
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
|
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 = {
|
event = {
|
||||||
'detail': {
|
'detail': {
|
||||||
'new_image': {
|
'new_image': {
|
||||||
|
'id': pk,
|
||||||
|
'sk': sk,
|
||||||
's3_uri': 's3://saladeaula.digital/samples/large_users.csv',
|
'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
|
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
|
||||||
@@ -2,3 +2,10 @@
|
|||||||
{"id": {"S": "cJtK9SsnJhKPyxESe7g3DG"}, "sk": {"S": "admins#5OxmMjL-ujoR5IMGegQz"}, "name": {"S": "Sérgio R Siqueira"}, "email": {"S": "sergio@somosbeta.com.br"}}
|
{"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": "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():
|
def test_byte_ranges():
|
||||||
csvpath = 'tests/samples/users.csv'
|
csvpath = 'tests/samples/users.csv'
|
||||||
ranges = byte_ranges(csvpath, 10)
|
ranges = byte_ranges(csvpath, 10)
|
||||||
*_, pair = ranges
|
*_, chunk = ranges
|
||||||
start_byte, end_byte = pair
|
start_byte, end_byte = chunk
|
||||||
|
|
||||||
assert ranges == [(0, 808), (809, 1655), (1656, 2303)]
|
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]]
|
[[package]]
|
||||||
name = "layercake"
|
name = "layercake"
|
||||||
version = "0.11.1"
|
version = "0.11.2"
|
||||||
source = { directory = "../layercake" }
|
source = { directory = "../layercake" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "arnparse" },
|
{ name = "arnparse" },
|
||||||
@@ -489,6 +489,7 @@ dependencies = [
|
|||||||
{ name = "pycpfcnpj" },
|
{ name = "pycpfcnpj" },
|
||||||
{ name = "pydantic", extra = ["email"] },
|
{ name = "pydantic", extra = ["email"] },
|
||||||
{ name = "pydantic-extra-types" },
|
{ name = "pydantic-extra-types" },
|
||||||
|
{ name = "python-calamine" },
|
||||||
{ name = "python-multipart" },
|
{ name = "python-multipart" },
|
||||||
{ name = "pytz" },
|
{ name = "pytz" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
@@ -513,6 +514,7 @@ requires-dist = [
|
|||||||
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
{ name = "pycpfcnpj", specifier = ">=1.8" },
|
||||||
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
{ name = "pydantic", extras = ["email"], specifier = ">=2.10.6" },
|
||||||
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
{ name = "pydantic-extra-types", specifier = ">=2.10.3" },
|
||||||
|
{ name = "python-calamine", specifier = ">=0.5.4" },
|
||||||
{ name = "python-multipart", specifier = ">=0.0.20" },
|
{ name = "python-multipart", specifier = ">=0.0.20" },
|
||||||
{ name = "pytz", specifier = ">=2025.1" },
|
{ name = "pytz", specifier = ">=2025.1" },
|
||||||
{ name = "requests", specifier = ">=2.32.3" },
|
{ 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" },
|
{ 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]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.9.0.post0"
|
version = "2.9.0.post0"
|
||||||
|
|||||||
Reference in New Issue
Block a user