diff --git a/.gitignore b/.gitignore index afa41fe..2fc03f6 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ migrations/products_xlsx/* # Workbook inboxes: filled customer/agency workbooks live in the Nextcloud migrations/submarkets_xlsx_in/* migrations/subsolutions_xlsx_in/* +migrations/solutiongroups_xlsx_in/* diff --git a/packages/vitec/Classes/Command/ImportSolutionGroupWorkbooksCommand.php b/packages/vitec/Classes/Command/ImportSolutionGroupWorkbooksCommand.php new file mode 100644 index 0000000..20ff54a --- /dev/null +++ b/packages/vitec/Classes/Command/ImportSolutionGroupWorkbooksCommand.php @@ -0,0 +1,89 @@ +addOption('dir', null, InputOption::VALUE_REQUIRED, 'Directory with filled workbooks', 'migrations/solutiongroups_xlsx_in'); + $this->addOption('only', null, InputOption::VALUE_REQUIRED, 'Only files whose name contains this substring'); + $this->addOption('apply', null, InputOption::VALUE_NONE, 'Write changes (default: dry run)'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + Bootstrap::initializeBackendAuthentication(); + $apply = (bool)$input->getOption('apply'); + $only = mb_strtolower((string)($input->getOption('only') ?? '')); + $dir = rtrim((string)$input->getOption('dir'), '/'); + + if (!is_dir($dir)) { + $output->writeln('Directory not found: ' . $dir . ''); + return Command::FAILURE; + } + $files = []; + $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir)); + foreach ($iterator as $fileInfo) { + $name = $fileInfo->getFilename(); + if ($fileInfo->isFile() && str_ends_with($name, '.xlsx') && !str_starts_with($name, '~$')) { + if ($only === '' || str_contains(mb_strtolower($name), $only)) { + $files[] = $fileInfo->getPathname(); + } + } + } + sort($files); + if ($files === []) { + $output->writeln('No workbooks found in ' . $dir); + return Command::SUCCESS; + } + $output->writeln(count($files) . ' workbook(s) · ' . ($apply ? 'APPLY' : 'DRY-RUN')); + + $errors = 0; + foreach ($files as $file) { + try { + $result = $this->importer->importFile($file, $apply); + $output->writeln(sprintf("\n%s (group: %s)", $result['file'], $result['group'])); + foreach ($result['report'] as $line) { + $output->writeln(' ' . $line . ''); + } + $output->writeln(sprintf(' page #%d · %d element(s) touched · %s', + $result['pageUid'], $result['elements'], $apply ? 'written.' : 'dry run')); + } catch (\Throwable $e) { + $errors++; + $output->writeln('' . basename($file) . ': ' . $e->getMessage() . ''); + } + } + if (!$apply) { + $output->writeln("\nDRY RUN - nothing written. Add --apply to write."); + } + return $errors > 0 ? Command::FAILURE : Command::SUCCESS; + } +} diff --git a/packages/vitec/Classes/Import/SolutionGroupPageImporter.php b/packages/vitec/Classes/Import/SolutionGroupPageImporter.php new file mode 100644 index 0000000..2638d70 --- /dev/null +++ b/packages/vitec/Classes/Import/SolutionGroupPageImporter.php @@ -0,0 +1,558 @@ + #capabilities, bg slot 01) + * vitec_introparagraph (hero introduction) + * colPos 0: vitec_marketintro (Intro H2/body, image slot 02) + * vitec_marketintro (Supporting 50/50 H2/copy) + * vitec_introparagraph (Common Challenges H2) + * vitec_card x4 (challenges, text_only) + * vitec_introparagraph (Capabilities H2 + section intro) + * vitec_card x4 (capabilities, horizontal alternating, + * bullets as