initial commit
This commit is contained in:
19
.devcontainer/devcontainer.json
Normal file
19
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/debian
|
||||||
|
{
|
||||||
|
"name": "debian-sqlsrv",
|
||||||
|
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||||
|
"image": "debian-sqlsrv:latest",
|
||||||
|
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
// "forwardPorts": [],
|
||||||
|
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
// "customizations": {},
|
||||||
|
|
||||||
|
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "root"
|
||||||
|
}
|
||||||
5
.env.dist
Normal file
5
.env.dist
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ENCRYPTION_KEY="encryption_key"
|
||||||
|
MSSQL_SERVER_NAME=server_name
|
||||||
|
MSSQL_DATABASE=user_database
|
||||||
|
MSSQL_USERNAME=user_name
|
||||||
|
MSSQL_PASSWORD="encrypted_password"
|
||||||
12
.github/dependabot.yml
vendored
Normal file
12
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for more information:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
# https://containers.dev/guide/dependabot
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "devcontainers"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/vendor/
|
||||||
|
.env
|
||||||
|
*.csv
|
||||||
|
*.xlsx
|
||||||
8
.phpcs.xml
Normal file
8
.phpcs.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="PHP_CodeSniffer">
|
||||||
|
<description>PHP_CodeSniffer configuration</description>
|
||||||
|
<rule ref="PSR12">
|
||||||
|
<exclude name="Generic.WhiteSpace.DisallowTabIndent"/>
|
||||||
|
<exclude name="PSR12.Operators.OperatorSpacing"/>
|
||||||
|
</rule>
|
||||||
|
</ruleset>
|
||||||
32
composer.json
Normal file
32
composer.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "brad/category-checker",
|
||||||
|
"type": "project",
|
||||||
|
"license": "MIT",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Bcs\\CategoryChecker\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Brad Cimbura",
|
||||||
|
"email": "hpz937@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"phpoffice/phpspreadsheet": "^2.0",
|
||||||
|
"bcs/databaseinterface": "1.0.0",
|
||||||
|
"vlucas/phpdotenv": "^5.6",
|
||||||
|
"hpz937/encryption": "1.0.0",
|
||||||
|
"php": ">=8.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"squizlabs/php_codesniffer": "^3.8",
|
||||||
|
"rector/rector": "^1.0"
|
||||||
|
},
|
||||||
|
"repositories": [{
|
||||||
|
"type": "composer",
|
||||||
|
"url": "https://git.hpz.pw/api/packages/hpz937/composer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
1813
composer.lock
generated
Normal file
1813
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
rector.php
Normal file
10
rector.php
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Rector\Config\RectorConfig;
|
||||||
|
|
||||||
|
return RectorConfig::configure()
|
||||||
|
->withPaths([__DIR__ . '/src'])
|
||||||
|
->withPhpSets()
|
||||||
|
->withPreparedSets(codeQuality: true)
|
||||||
|
->withAttributesSets(symfony: true, doctrine: true)
|
||||||
|
->withImportNames(importShortClasses: false, removeUnusedImports: true);
|
||||||
29
run.php
Normal file
29
run.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Bcs\Databaseinterface\SqlSrvClient;
|
||||||
|
use Bcs\CategoryChecker\CategoryChecker;
|
||||||
|
use Hpz937\Encryption\DataEncryptor;
|
||||||
|
|
||||||
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
|
||||||
|
// Usage
|
||||||
|
// Assuming $pdo is your PDO instance connected to the MSSQL database
|
||||||
|
// and $csvFilePath is the path to your CSV file
|
||||||
|
$enc = new DataEncryptor($_ENV['ENCRYPTION_KEY']);
|
||||||
|
$sqlSrvClient = new SqlSrvClient(
|
||||||
|
$_ENV['MSSQL_SERVER_NAME'],
|
||||||
|
$_ENV['MSSQL_DATABASE'],
|
||||||
|
$_ENV['MSSQL_USERNAME'],
|
||||||
|
$enc->decrypt($_ENV['MSSQL_PASSWORD'])
|
||||||
|
);
|
||||||
|
$csvFilePath = 'goggle-categories.csv';
|
||||||
|
$outputFilePath = 'mismatch-category-rpt.xlsx';
|
||||||
|
$outputCsvFilePath = 'remove-cat.csv';
|
||||||
|
|
||||||
|
$categoryChecker = new CategoryChecker($sqlSrvClient, $csvFilePath);
|
||||||
|
$categoryChecker->outputMismatchesAsXlsx($outputFilePath);
|
||||||
|
$categoryChecker->outputMissingCategoriesAsCsv($outputCsvFilePath);
|
||||||
190
src/CategoryChecker.php
Normal file
190
src/CategoryChecker.php
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Bcs\CategoryChecker;
|
||||||
|
|
||||||
|
use Bcs\Databaseinterface\SqlSrvClient;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
|
||||||
|
use PhpOffice\PhpSpreadsheet\Style\Alignment;
|
||||||
|
|
||||||
|
class CategoryChecker
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* CategoryChecker constructor.
|
||||||
|
*
|
||||||
|
* @param SqlSrvClient $sqlSrvClient The SQL Server client
|
||||||
|
* @param string $csvFilePath The path to the CSV file
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
private SqlSrvClient $sqlSrvClient,
|
||||||
|
private string $csvFilePath
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch master categories from MSSQL
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function fetchMasterCategories(): array
|
||||||
|
{
|
||||||
|
// Placeholder for the SQL query to select items from MSSQL
|
||||||
|
$sql = "SELECT ITEM_NO,
|
||||||
|
DESCR,
|
||||||
|
USR_MAG_CATEG_1,
|
||||||
|
USR_MAG_CATEG_2,
|
||||||
|
USR_MAG_CATEG_3,
|
||||||
|
USR_MAG_CATEG_4,
|
||||||
|
USR_MAG_CATEG_5,
|
||||||
|
USR_MAG_CATEG_6
|
||||||
|
FROM IM_ITEM i
|
||||||
|
where (descr like 'gog%' or descr like 'lens%') and ATTR_COD_3!='d9999' order by DESCR";
|
||||||
|
$rows = $this->sqlSrvClient->query($sql);
|
||||||
|
|
||||||
|
$masterData = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$sku = $row['ITEM_NO'];
|
||||||
|
$categories = array_values(
|
||||||
|
array_filter(
|
||||||
|
array_map(
|
||||||
|
fn($value) =>
|
||||||
|
// Check if the category ends with '.html' and remove it
|
||||||
|
str_ends_with($value, '.html') ? substr($value, 0, -5) : $value,
|
||||||
|
$row
|
||||||
|
),
|
||||||
|
fn($value, $key) =>
|
||||||
|
// Keep only non-empty category values and keys that start with 'category'
|
||||||
|
str_starts_with($key, 'USR_MAG_CATEG_') && !empty($value),
|
||||||
|
ARRAY_FILTER_USE_BOTH
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$masterData[$sku] = $categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $masterData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load categories from CSV file
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function loadCsvCategories(): array
|
||||||
|
{
|
||||||
|
$csvData = [];
|
||||||
|
if (($handle = fopen($this->csvFilePath, "r")) !== false) {
|
||||||
|
while (($data = fgetcsv($handle, 1000, ",")) !== false) {
|
||||||
|
//sku, name, category.path
|
||||||
|
$sku = $data[0];
|
||||||
|
$categories = explode(";", $data[2]);
|
||||||
|
$csvData[$sku] = $categories;
|
||||||
|
}
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $csvData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare categories between the two systems
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function compareCategories(): array
|
||||||
|
{
|
||||||
|
$masterData = $this->fetchMasterCategories();
|
||||||
|
$csvData = $this->loadCsvCategories();
|
||||||
|
$mismatches = [];
|
||||||
|
|
||||||
|
foreach ($masterData as $sku => $masterCategories) {
|
||||||
|
// If the SKU does not exist in the CSV data, skip it
|
||||||
|
if (!isset($csvData[$sku])) {
|
||||||
|
continue; // Skip to the next SKU since this one might not be synced yet
|
||||||
|
}
|
||||||
|
|
||||||
|
$csvCategories = $csvData[$sku];
|
||||||
|
$diffMasterToCsv = array_diff($masterCategories, $csvCategories);
|
||||||
|
$diffCsvToMaster = array_diff($csvCategories, $masterCategories);
|
||||||
|
|
||||||
|
if ($diffMasterToCsv !== [] || $diffCsvToMaster !== []) {
|
||||||
|
// There are mismatches between the categories
|
||||||
|
$mismatches[$sku] = ['master' => $diffMasterToCsv, 'csv' => $diffCsvToMaster];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mismatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the mismatches to the console
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function outputMismatches(): void
|
||||||
|
{
|
||||||
|
$mismatches = $this->compareCategories();
|
||||||
|
foreach ($mismatches as $sku => $mismatch) {
|
||||||
|
echo "SKU: {$sku}\n";
|
||||||
|
echo "Counterpoint only categories: " . implode(", ", $mismatch['master']) . "\n";
|
||||||
|
echo "Magento only categories: " . implode(", ", $mismatch['csv']) . "\n";
|
||||||
|
echo "-----------------------------------\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the mismatches as an XLSX file
|
||||||
|
*
|
||||||
|
* @param string $outputFilePath The path to the output file
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function outputMismatchesAsXlsx(string $outputFilePath): void
|
||||||
|
{
|
||||||
|
$mismatches = $this->compareCategories();
|
||||||
|
|
||||||
|
$spreadsheet = new Spreadsheet();
|
||||||
|
$sheet = $spreadsheet->getActiveSheet();
|
||||||
|
|
||||||
|
// Set the header
|
||||||
|
$sheet->setCellValue('A1', 'SKU');
|
||||||
|
$sheet->setCellValue('B1', 'Counterpoint Only Categories');
|
||||||
|
$sheet->setCellValue('C1', 'Magento Only Categories');
|
||||||
|
$sheet->getStyle('A1:C1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
||||||
|
$sheet->getStyle('A1:C1')->getFont()->setBold(true);
|
||||||
|
|
||||||
|
$row = 2; // Start from the second row to leave space for the header
|
||||||
|
foreach ($mismatches as $sku => $mismatch) {
|
||||||
|
$sheet->setCellValue('A' . $row, $sku);
|
||||||
|
$sheet->setCellValue('B' . $row, implode(", ", $mismatch['master']));
|
||||||
|
$sheet->setCellValue('C' . $row, implode(", ", $mismatch['csv']));
|
||||||
|
$row++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto size columns for content
|
||||||
|
foreach (range('A', 'C') as $columnID) {
|
||||||
|
$sheet->getColumnDimension($columnID)->setAutoSize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$writer = new Xlsx($spreadsheet);
|
||||||
|
$writer->save($outputFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the missing categories as a CSV file
|
||||||
|
*
|
||||||
|
* @param string $outputFilePath
|
||||||
|
*/
|
||||||
|
public function outputMissingCategoriesAsCsv(string $outputFilePath): void
|
||||||
|
{
|
||||||
|
$mismatches = $this->compareCategories();
|
||||||
|
|
||||||
|
if (($handle = fopen($outputFilePath, 'w')) !== false) {
|
||||||
|
foreach ($mismatches as $sku => $missingCategories) {
|
||||||
|
foreach ($missingCategories['csv'] as $category) {
|
||||||
|
fputcsv($handle, ['-CCP', $category, $sku]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user