Prepare Beta Version

This commit is contained in:
2025-05-29 10:05:27 -06:00
parent a7002701f5
commit ea6b04f3f4
254 changed files with 5653 additions and 6569 deletions

View File

@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
trait HandlesSeederLoggingAndReporting
{
protected array $reportData = [];
protected float $startTime;
/**
* Registra métricas clave por módulo
*/
protected function logModuleMetrics(
string $module,
string $status,
?string $file = null,
?int $records = null,
?string $details = null,
bool $truncated = false,
bool $isFake = false,
?int $fakeCount = null
): void {
$this->reportData['modules'][$module] = [
'status' => $status,
'file' => $file ? basename($file) : null,
'path' => $file ? dirname($file) : null,
'records' => $records,
'time' => round(microtime(true) - $this->startTime, 2) . 's',
'truncated' => $truncated,
'is_fake' => $isFake,
'fake_qty' => $fakeCount,
'details' => $details,
];
$this->consoleOutput($module, $status, $details);
}
/**
* Genera reportes en múltiples formatos
*/
protected function generateReports(): void
{
$this->reportData['meta'] = [
'env' => config('seeder.env', 'local'),
'date' => now()->toDateTimeString(),
'duration' => round(microtime(true) - $this->startTime, 2) . 's',
'has_errors'=> collect($this->reportData['modules'])->contains('status', 'failed')
];
$this->generateMarkdownReport();
$this->generateJsonReport();
}
/**
* Reporte Markdown Mejorado (Tabla + Resumen)
*/
protected function generateMarkdownReport(): void
{
$env = $this->reportData['meta']['env'];
$path = database_path("seeders/reports/{$env}/seed-report-{$env}-" . now()->format('Y-m-d') . ".md");
$content = [
"# 📊 Koneko ERP - Seeders Report",
"| Metadata | Value |",
"|------------------|--------------------|",
"| **Environment** | `{$env}` |",
"| **Execution** | {$this->reportData['meta']['date']} |",
"| **Duration** | {$this->reportData['meta']['duration']} |",
"| **Status** | " . ($this->reportData['meta']['has_errors'] ? '❌ With errors' : '✅ Success') . " |",
"",
"## 📦 Modules Summary",
"",
"| Module | Status | Records | Time | Truncated | Faker | File | Details |",
"|--------|--------|---------|------|-----------|-------|------|---------|",
];
foreach ($this->reportData['modules'] as $module => $data) {
$content[] = sprintf(
"| %s | %s | %s | %s | %s | %s | %s | %s |",
Str::headline($module),
$this->formatStatus($data['status']),
$data['records'] ?? ($data['is_fake'] ? "~{$data['fake_qty']}" : 'N/A'),
$data['time'],
$data['truncated'] ? '✓' : '✗',
$data['is_fake'] ? '✓' : '✗',
$data['file'] ? "`{$data['file']}`" : 'N/A',
$data['details'] ?? ''
);
}
// Sección de archivos usados
$content[] = "";
$content[] = "## 📂 Files Location";
foreach (array_unique(array_column($this->reportData['modules'], 'path')) as $filePath) {
if ($filePath) $content[] = "- `{$filePath}/`";
}
File::put($path, implode("\n", $content));
}
/**
* Helpers para formato visual
*/
private function formatStatus(string $status): string
{
return match(strtolower($status)) {
'completed' => '✅',
'failed' => '❌',
'skipped' => '⏭️',
default => ''
};
}
private function consoleOutput(string $module, string $status, ?string $details): void
{
$emoji = $this->formatStatus($status);
$this->command?->line(" {$emoji} <fg=white>{$module}</>" . ($details ? " → <fg=yellow>{$details}</>" : ""));
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
/**
* ⚡ Trait para soporte de procesamiento por lotes (chunks) en seeders.
*
* Permite:
* - Cargar archivos grandes sin agotar memoria
* - Delegar la lógica al parser correspondiente (si la soporta)
* - Integrarse con la barra de progreso
*
* Requiere que el parser implemente `parseInChunks()`
*
* @package Koneko\VuexyAdmin\Support\Traits\Seeders
*/
trait HasSeederChunkSupport
{
/**
* Procesa un archivo usando lotes si el parser lo soporta.
*
* @param object $parser Parser con método `parseInChunks()`
* @param string $path Ruta absoluta del archivo
* @return void
*/
protected function processInChunks(object $parser, string $path): void
{
$this->log("⚡ Procesando por lotes...");
$this->startProgress();
foreach ($parser->parseInChunks($path) as $chunk) {
foreach ($chunk as $row) {
$this->processSingleRow($row);
$this->advanceProgress();
}
}
$this->finishProgress();
}
/**
* Procesa una sola fila dentro de un chunk.
*
* @param array $row
* @return void
*/
protected function processSingleRow(array $row): void
{
try {
$sanitized = $this->sanitizeRow($row);
$modelClass = $this->getModelClass();
$uniqueBy = $this->getUniqueBy();
$modelClass::updateOrCreate(
is_array($uniqueBy)
? array_intersect_key($sanitized, array_flip($uniqueBy))
: [$uniqueBy => $sanitized[$uniqueBy] ?? null],
$sanitized
);
$this->processedCount++;
} catch (\Exception $e) {
$this->log("⚠️ Error en fila: " . json_encode($row) . " - " . $e->getMessage());
}
}
}

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
/**
* Trait para generación de registros faker vía Factory con fallback.
*/
trait HasSeederFactorySupport
{
public function runFake(int $total, array $config = []): void
{
// Este método se define completo en el seeder concreto.
throw new \LogicException('El método runFake debe ser implementado por el Seeder que use este trait.');
}
}

View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
/**
* Trait para emitir logs desde seeders compatibles con Artisan o CLI.
*/
trait HasSeederLogger
{
protected function log(string $message): void
{
if (property_exists($this, 'command') && $this->command instanceof \Illuminate\Console\Command) {
try {
$this->command->line($message);
return;
} catch (\Throwable) {
// fallback
}
}
echo strip_tags($message) . PHP_EOL;
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
/**
* Trait para manejar barras de progreso CLI desde seeders.
*/
trait HasSeederProgressBar
{
protected function startProgress(int $total = 0): void
{
if ($this->canUseProgress() && $total > 0) {
$this->command->getOutput()->progressStart($total);
}
}
protected function advanceProgress(): void
{
if ($this->canUseProgress()) {
$this->command->getOutput()->progressAdvance();
}
}
protected function finishProgress(): void
{
if ($this->canUseProgress()) {
$this->command->getOutput()->progressFinish();
}
}
protected function canUseProgress(): bool
{
return property_exists($this, 'command')
&& $this->command instanceof \Illuminate\Console\Command
&& method_exists($this->command, 'getOutput');
}
}

View File

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Koneko\VuexyAdmin\Application\Seeding\Concerns\Main;
trait SanitizeRowWithFillableAndCasts
{
protected function sanitizeRowWithFillableAndCasts(array $row, string $modelClass): array
{
$model = new $modelClass;
$fillable = $model->getFillable();
$casts = $model->getCasts();
$sanitized = [];
foreach ($row as $key => $value) {
if (!in_array($key, $fillable)) {
continue; // Solo campos fillable
}
// Aplicar cast manualmente si es necesario
if (isset($casts[$key])) {
$castType = $casts[$key];
$value = match ($castType) {
'boolean', 'bool' => (bool) $value,
'int', 'integer' => (int) $value,
'float', 'double' => (float) $value,
'datetime', 'date' => is_a($value, \DateTimeInterface::class) ? $value->format('Y-m-d H:i:s') : $value,
'array', 'json' => is_string($value) ? json_decode($value, true) : $value,
default => $value,
};
}
$sanitized[$key] = $value;
}
return $sanitized;
}
}

View File

@ -7,10 +7,9 @@ namespace Koneko\VuexyAdmin\Application\Seeding;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Koneko\VuexyAdmin\Application\Seeding\SeederReportBuilder;
use Koneko\VuexyAdmin\Application\Traits\Seeders\Main\HasSeederLogger;
use Koneko\VuexyAdmin\Application\UI\Avatar\AvatarImageService;
use Koneko\VuexyAdmin\Application\UI\Avatar\AvatarInitialsService;
use Koneko\VuexyAdmin\Support\Seeders\AbstractDataSeeder;
use Koneko\VuexyAdmin\Application\Seeding\Concerns\Main\HasSeederLogger;
use Koneko\VuexyAdmin\Application\UI\Avatar\{AvatarImageService, AvatarInitialsService};
use Koneko\VuexyAdmin\Support\Seeders\Base\AbstractDataSeeder;
class SeederOrchestrator
{