From aa22499fc7712a949afda6a366d4034e2a708f5b Mon Sep 17 00:00:00 2001 From: Arsen Mirzaev Tatyano-Muradovich Date: Tue, 6 Aug 2024 14:31:52 +0300 Subject: [PATCH] =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B5=D1=80=D1=88=D0=B5=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 10 +- crontab | 28 ++ .../system/controllers/core.php | 0 .../system/controllers/index.php | 0 .../system/models/core.php | 0 .../system/models/session.php | 0 .../system/models/traits/status.php | 0 .../system/public/avito.php | 261 ++++++++++++++++ .../system/public/css/themes/default/main.css | 0 .../parser_from_rossko/system/public/drom.php | 154 +++++++++ .../system/public/import.php | 292 ++++++++++++++++++ .../system/public/index.php | 0 .../parser_from_rossko/system/public/mail.php | 50 +++ .../system/settings/.gitignore | 0 .../system/settings/account.php.sample | 7 + .../system/settings/arangodb.php.sample | 0 .../system/storage/.gitignore | 1 + .../system/views/templater.php | 0 .../system/views/themes/default/core.html | 0 .../system/views/themes/default/footer.html | 0 .../system/views/themes/default/head.html | 0 .../system/views/themes/default/header.html | 0 .../system/views/themes/default/index.html | 0 .../system/views/themes/default/js.html | 0 rossko.service | 14 + 25 files changed, 812 insertions(+), 5 deletions(-) create mode 100644 crontab rename {author/project => mirzaev/parser_from_rossko}/system/controllers/core.php (100%) rename {author/project => mirzaev/parser_from_rossko}/system/controllers/index.php (100%) rename {author/project => mirzaev/parser_from_rossko}/system/models/core.php (100%) rename {author/project => mirzaev/parser_from_rossko}/system/models/session.php (100%) rename {author/project => mirzaev/parser_from_rossko}/system/models/traits/status.php (100%) create mode 100644 mirzaev/parser_from_rossko/system/public/avito.php rename {author/project => mirzaev/parser_from_rossko}/system/public/css/themes/default/main.css (100%) create mode 100644 mirzaev/parser_from_rossko/system/public/drom.php create mode 100644 mirzaev/parser_from_rossko/system/public/import.php rename {author/project => mirzaev/parser_from_rossko}/system/public/index.php (100%) create mode 100644 mirzaev/parser_from_rossko/system/public/mail.php rename {author/project => mirzaev/parser_from_rossko}/system/settings/.gitignore (100%) create mode 100644 mirzaev/parser_from_rossko/system/settings/account.php.sample rename {author/project => mirzaev/parser_from_rossko}/system/settings/arangodb.php.sample (100%) create mode 100644 mirzaev/parser_from_rossko/system/storage/.gitignore rename {author/project => mirzaev/parser_from_rossko}/system/views/templater.php (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/core.html (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/footer.html (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/head.html (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/header.html (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/index.html (100%) rename {author/project => mirzaev/parser_from_rossko}/system/views/themes/default/js.html (100%) create mode 100644 rossko.service diff --git a/composer.json b/composer.json index 92b2b5f..1679e83 100755 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "mirzaev/parser_from_rossko", "description": "", "readme": "README.md", - "keywords": [], + "keywords": ["parser", "rossko"], "type": "site", "homepage": "https://git.mirzaev.sexy/mirzaev/parser_from_rossko", "license": "WTFPL", @@ -22,10 +22,10 @@ "php": "~8.3", "ext-sodium": "~8.3", "mirzaev/minimal": "^2.2.0", - "mirzaev/accounts": "~1.2.x-dev", - "mirzaev/arangodb": "^1.0.0", - "triagens/arangodb": "~3.9.x-dev", - "twig/twig": "^3.4" + "guzzlehttp/guzzle": "^7.8", + "php-imap/php-imap": "^5.0", + "avadim/fast-excel-reader": "^2.17", + "avadim/fast-excel-writer": "^5.7" }, "require-dev": { "phpunit/phpunit": "~9.5" diff --git a/crontab b/crontab new file mode 100644 index 0000000..a09ba68 --- /dev/null +++ b/crontab @@ -0,0 +1,28 @@ +# Edit this file to introduce tasks to be run by cron. +# +# Each task to run has to be defined through a single line +# indicating with different fields when the task will be run +# and what command to run for the task +# +# To define the time you can provide concrete values for +# minute (m), hour (h), day of month (dom), month (mon), +# and day of week (dow) or use '*' in these fields (for 'any'). +# +# Notice that tasks will be started based on the cron's system +# daemon's notion of time and timezones. +# +# Output of the crontab jobs (including errors) is sent through +# email to the user the crontab file belongs to (unless redirected). +# +# For example, you can run a backup of all your user accounts +# at 5 a.m every week with: +# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ +# +# For more information see the manual pages of crontab(5) and cron(8) +# +# m h dom mon dow command +0 0 * * * php /var/www/parser_from_rossko/mirzaev/parser_from_rossko/system/public/mail.php +# 30 0 * * * php /var/www/parser_from_rossko/mirzaev/parser_from_rossko/system/public/import.php +0 20 * * * php /var/www/parser_from_rossko/mirzaev/parser_from_rossko/system/public/avito.php +0 21 * * * php /var/www/parser_from_rossko/mirzaev/parser_from_rossko/system/public/drom.php + diff --git a/author/project/system/controllers/core.php b/mirzaev/parser_from_rossko/system/controllers/core.php similarity index 100% rename from author/project/system/controllers/core.php rename to mirzaev/parser_from_rossko/system/controllers/core.php diff --git a/author/project/system/controllers/index.php b/mirzaev/parser_from_rossko/system/controllers/index.php similarity index 100% rename from author/project/system/controllers/index.php rename to mirzaev/parser_from_rossko/system/controllers/index.php diff --git a/author/project/system/models/core.php b/mirzaev/parser_from_rossko/system/models/core.php similarity index 100% rename from author/project/system/models/core.php rename to mirzaev/parser_from_rossko/system/models/core.php diff --git a/author/project/system/models/session.php b/mirzaev/parser_from_rossko/system/models/session.php similarity index 100% rename from author/project/system/models/session.php rename to mirzaev/parser_from_rossko/system/models/session.php diff --git a/author/project/system/models/traits/status.php b/mirzaev/parser_from_rossko/system/models/traits/status.php similarity index 100% rename from author/project/system/models/traits/status.php rename to mirzaev/parser_from_rossko/system/models/traits/status.php diff --git a/mirzaev/parser_from_rossko/system/public/avito.php b/mirzaev/parser_from_rossko/system/public/avito.php new file mode 100644 index 0000000..b192ab8 --- /dev/null +++ b/mirzaev/parser_from_rossko/system/public/avito.php @@ -0,0 +1,261 @@ +query('SELECT * FROM product;'); + +// Инициализация директории с документом avito +define('AVITO', STORAGE . DIRECTORY_SEPARATOR . 'avito'); + +// Инициализация хранилища фотографий товаров +define('IMAGES', STORAGE . DIRECTORY_SEPARATOR . 'images'); + +// Инициализация таблицы +$spreadsheet = excel::create(['export']); + +// Инициализация рабочего листа +$worksheet = $spreadsheet->sheet(); + +// Инициализация заголовков +$worksheet->writeHeader([ + 'id', + 'ListingFee', + 'Category', + 'GoodsType', + 'ProductType', + 'SparePartType', + 'Title', + 'Description', + 'Price', + 'ImageUrls', + 'Condition', + 'Originality', + 'Address', + 'Availability', + 'Brand', + 'OEM', + 'EMail', + 'ContactPhone', + 'AdType', + 'CompanyName', +]); + +while ($product = $products->fetchArray()) { + // Перебор товаров + + // Пропустить товары с отсутствием названия + if (empty($product['description'])) continue; + + // Инициализация списка изображений товара + $images = []; + + // Поиск изображений + if (file_exists($file = IMAGES . DIRECTORY_SEPARATOR . $product['nomenclature'])) + foreach (new directory($file) as $file) + if (strtolower($file->getExtension()) === 'jpg') + $images[] = "https://vinauto.store/{$product['nomenclature']}/{$file->getFilename()}"; + + // Около 40% товаров из Rossko без изображений и drom их не принимает! Временное решение + if (empty($images)) continue; + + // Преобразование типов + $product['cost'] = (float) $product['cost']; + + // Инициализация списков + $brands = $models = $engines = []; + + + foreach (json_decode((string) $product['applicability'], true) ?? [] as $brand) { + // Перебор марок автомобилей + + // Запись в список марок + $brands[] = $brand['name']; + + foreach ($brand['carModels'] as $model) { + // Перебор моделей автомобилей + + // Запись в список моделей + $models[] = $model['name']; + + foreach ($model['engines'] as $engine) { + // Перебор двигателей автомобилей + + // Запись в список двигателей + $engines[] = $engine; + } + } + } + + // Ограничение по максимальному количеству + $brands = array_slice($brands, 0, 5); + $models = array_slice($models, 0, 10); + $engines = array_slice($engines, 0, 30); + + // Форматирование названия + $product['description'] = trim(preg_replace('/\|.*/', '', $product['description']), ' '); + + // Инициализация категорий Avito связанных с ключевыми словами + $categories = [ + 'Автосвет' => ['фара', 'свет'], + 'Аккумуляторы' => ['аккумулятор', 'батарея', 'батарейки', 'зарядное'], + 'Двигатель' => ['двигатель', 'патрубок', 'подушка', 'гидрокомпенсатор', 'датчик', 'детонации', 'заслонка', 'клапан', 'колпачок', 'кольца', 'порншневых', 'поршень', 'лямбда-зонд', 'лямбда', 'зонд', 'маховик', 'гбц', 'распылитель', 'расходомер', 'поликлиновой', 'натяжной', 'ролик', 'сальник', 'распред', 'распредвал', 'стакан', 'термостат', 'толкатель', 'фильтр', 'форсунка', 'шатун', 'шестерня', 'шкиф', 'цилиндр'], + 'Кузов' => ['кузов', 'ролик', 'рычаг', 'дверь', 'стеклоочиститель', 'трос', 'капот'], + 'Подвеска' => ['рычаг', 'стойка', 'сайлентблок', 'ремкомплект', 'опора', 'втулка', 'амортизатор', 'крестовина', 'опора', 'пневмобаллон', 'подушка', 'пружина', 'пыльник'], + 'Рулевое управление' => ['наконечник', 'тяга', 'кулак', 'подшипник', 'ступица', 'полуось', 'пыльник', 'шрус', 'шруса', 'рулевой', 'рулевая', 'рейка', 'сошка', 'ступица', 'гидроусилитель', 'шланг', 'шлейф', 'насос'], + 'Салон' => ['салон'], + 'Стёкла' => ['стекло', 'стёкла'], + 'Топливная и выхлопная системы' => ['выхлопная', 'выхлопной', 'топливный', 'топливо', 'топливная', 'виброкомпенсатор', 'пламягаситель', 'выхлоп'], + 'Тормозная система' => ['тормоз', 'тормозной', 'барабан', 'тормозные', 'abs', 'диск', 'трос', 'колодка', 'колодки', 'ремкомплект', 'шланг'], + 'Трансмиссия и привод' => ['привода', 'трансмиссии', 'диск', 'сцеп', 'сцепления', 'акпп'], + 'Электрооборудование' => ['электро', 'электрический', 'бендикс', 'бензонасос', 'втягивающее', 'генератор', 'катушка', 'диодный', 'диод', 'реле', 'рычаг', 'свеча', 'зажигания', 'стартер', 'электровентилятор', 'вентилятор'], + 'Система охлаждения' => ['охлаждение', 'бак', 'бачок', 'водяной', 'радиатор', 'термостат'], + 'Автомобиль на запчасти' => ['запчасти'] + ]; + + // Инициализация списка слов из названия + $words = explode(' ', $product['description']); + + // Инициализация реестра найденных категорий Avito по совпадению слов из названия с ключевыми словами категорий Avito + $sorted = []; + + foreach ($words as $word) { + // Перебор слов в названии + + foreach ($categories as $avito => $keywords) { + // Перебор категорий Avito + + // Инициализация буфера кратчайшего расстояния между словом из названия и ключевым словом категории Avito + $shortest = null; + + // Объявление буфера ближайшего слова из названия к ключевому слову категории Avito + $closest = null; + + foreach ($keywords as $keyword) { + // Перебор ключевых слов категории Avito + + // Инициализация расстояния по технологии Левенштейна между словом из названия и ключевым словом категории Avito + $levenshtein = levenshtein($word, $keyword); + + if ($levenshtein === 0) { + // Полное совпадение слова из названия с ключевым словом + + // Запись крайтчайшего расстояния между ключевым словом и словом из названия + $shortest = 0; + + // Запись ближайшей категории Avito к слову из названия + $closest = $avito; + + // Переход к следующей итерации (успех) + continue; + } + + if ($levenshtein <= 2 && ($levenshtein <= $shortest || $shortest === null)) { + // Расстояние по технологии Левенштейна менее или равно 2 + // И Расстояние по технологии Левенштейна между словом из названия и ключевым словом категории Avito меньше чем в предыдущей итерации + // ИЛИ расстояние между словом из названия и ключевым словом категории Avito ещё не инициализировано (первая итерация) + + // Запись крайтчайшего расстояния между ключевым словом и словом из названия + $shortest = $levenshtein; + + // Запись ближайшей категории Avito к слову из названия + $closest = $avito; + } + } + + if (!empty($closest)) { + // Вычислено вхождение в реестр подобранных категорий Avito по ключевым словам относительно слова из названия + + // Запись в реестр подобранных категорий Avito по ключевым словам относительно слова из названия + $sorted[$closest] ??= []; + $sorted[$closest][] = $word; + } + } + } + + // Инициализация буфера максимального количества вхождений в категорию Avito по ключевым словам относительно всех слов из названия + $maximum = null; + + // Объявление буфера категории с максимальным количеством вхождений в реестре подобранных категорий Avito по ключевым словам относительно всех слов из названия + $category = null; + + foreach ($sorted as $avito => $entries) { + // Перебор реестра подобранных категорий Avito по ключевым словам относительно всех слов из названия + + // Инициализация количества вхождений в категорию Avito по ключевым словам относительно всех слов из названия + $amount = count($entries); + + if ($maximum <= $amount || $maximum === null) { + // Число вхождений в категорию Avito по ключевым словам относительно всех слов из названия больше чем в предыдущей итерации + // ИЛИ число вхождений в категорию Avito по ключевым словам относительно всех слов из названия ещё не инициализировано + + // Запись максимального значения вхождений в категории Avito по ключевым словам относительно всех слов из названия + $maximum = $amount; + + // Запись категории с максимальным количеством вхождений в реестре подобранных категорий Avito по ключевым словам относительно всех слов из названия + $category = $avito; + } + } + + // Запись строки + $worksheet->writeRow([ + $product['code'], + 'Package', + 'Запчасти и аксессуары', + 'Запчасти', + 'Для автомобилей', + $category, + $product['description'], + <<save(AVITO . DIRECTORY_SEPARATOR . 'export.xlsx'); diff --git a/author/project/system/public/css/themes/default/main.css b/mirzaev/parser_from_rossko/system/public/css/themes/default/main.css similarity index 100% rename from author/project/system/public/css/themes/default/main.css rename to mirzaev/parser_from_rossko/system/public/css/themes/default/main.css diff --git a/mirzaev/parser_from_rossko/system/public/drom.php b/mirzaev/parser_from_rossko/system/public/drom.php new file mode 100644 index 0000000..c30f9b4 --- /dev/null +++ b/mirzaev/parser_from_rossko/system/public/drom.php @@ -0,0 +1,154 @@ +query('SELECT * FROM product;'); + +// Инициализация директории с документом Drom +define('DROM', STORAGE . DIRECTORY_SEPARATOR . 'drom'); + +// Инициализация хранилища фотографий товаров +define('IMAGES', STORAGE . DIRECTORY_SEPARATOR . 'images'); + +// Инициализация таблицы +$spreadsheet = excel::create(['export']); + +// Инициализация рабочего листа +$worksheet = $spreadsheet->sheet(); + +// Инициализация заголовков +$worksheet->writeHeader([ + 'Артикул', + 'Наименование товара', + 'Новый или б/у', + 'Марка', + 'Модель', + 'Кузов', + 'Номер', + 'Производитель', + 'Двигатель', + 'Год', + 'L-R', + 'F-R', + 'U-D', + 'Цвет', + 'Номера замен', + 'Применимость', + 'Примечание', + 'Количество', + 'Цена', + 'Наличие', + 'Сроки доставки', + 'Фотография' +]); + +while ($product = $products->fetchArray()) { + // Перебор товаров + + // Пропустить товары с отсутствием названия + if (empty($product['description'])) continue; + + // Инициализация списка изображений товара + $images = []; + + // Поиск изображений + if (file_exists($file = IMAGES . DIRECTORY_SEPARATOR . $product['nomenclature'])) + foreach (new directory($file) as $file) + if (strtolower($file->getExtension()) === 'jpg') + $images[] = "https://vinauto.store/{$product['nomenclature']}/{$file->getFilename()}"; + + // Около 40% товаров из Rossko без изображений и drom их не принимает! Временное решение + if (empty($images)) continue; + + // Преобразование типов + $product['cost'] = (float) $product['cost']; + + // Инициализация списков + $brands = $models = $engines = []; + + foreach (json_decode((string) $product['applicability'], true) ?? [] as $brand) { + // Перебор марок автомобилей + + // Запись в список марок + $brands[] = $brand['name']; + + foreach ($brand['carModels'] as $model) { + // Перебор моделей автомобилей + + // Запись в список моделей + $models[] = $model['name']; + + foreach ($model['engines'] as $engine) { + // Перебор двигателей автомобилей + + // Запись в список двигателей + $engines[] = $engine; + } + } + } + + // Ограничение по максимальному количеству + $brands = array_slice($brands, 0, 5); + $models = array_slice($models, 0, 10); + $engines = array_slice($engines, 0, 30); + + // Форматирование названия + $product['description'] = trim(preg_replace('/\|.*/', '', $product['description']), ' '); + + // Запись строки + $worksheet->writeRow([ + $product['code'], + $product['description'], + 'Новый', + implode(', ', $brands), + implode(', ', $models), + '', + '', + $product['brand'], + implode(', ', $engines), + '', + '', + '', + '', + '', + '', + '', + 'По запчастям уточняйте наличие по телефону или через кнопку "спросить", ТАК КАК ОН МОЖЕТ БЫТЬ ПРОДАН В ТЕЧЕНИИ ДНЯ ИЛИ НАХОДИТЬСЯ В РЕЗЕРВЕ, часть запчастей находится на складе, на перемещение может потребоваться от 2 часов', + rand(1, 6), + (int) ($product['cost_rossko'] + ($product['cost_rossko'] * 0.23)), + 'В наличии', + 1, + implode(' ', $images) + ]); +} + +// Сохранение файла +$spreadsheet->save(DROM . DIRECTORY_SEPARATOR . 'export.xlsx'); diff --git a/mirzaev/parser_from_rossko/system/public/import.php b/mirzaev/parser_from_rossko/system/public/import.php new file mode 100644 index 0000000..05b049a --- /dev/null +++ b/mirzaev/parser_from_rossko/system/public/import.php @@ -0,0 +1,292 @@ +exec('CREATE TABLE IF NOT EXISTS product (id INTEGER PRIMARY KEY AUTOINCREMENT, avito INTEGER UNIQUE, nomenclature TEXT, brand TEXT, code TEXT, description TEXT, weight INTEGER, multiplicity REAL, cost_rossko REAL, cost REAL, amount TEXT, delivery INTEGER, code_rossko TEXT UNIQUE, oem TEXT, applicability TEXT, code_vendor TEXT, warehouse TEXT, updated DATETIME DEFAULT CURRENT_TIMESTAMP);'); + +// Инициализация директории с документом Rossko +define('ROSSKO', STORAGE . DIRECTORY_SEPARATOR . 'rossko'); + +// Инициализация хранилища фотографий товаров +define('IMAGES', STORAGE . DIRECTORY_SEPARATOR . 'images'); + +// Объявление документа +$document; + +foreach (new directory(ROSSKO) as $file) + if ($file->getExtension() === 'xlsx') { + // Инициализация документа + $document = ROSSKO . DIRECTORY_SEPARATOR . $file->getFilename(); + }; + +if (empty($document)) { + // Инициализация архива + $archive = ROSSKO . DIRECTORY_SEPARATOR . 'rossko_price.zip'; + + if (($zip = new zip)->open($archive)) { + // Открыт архив + + // Разархивирование документа + $zip->extractTo(ROSSKO); + $zip->close(); + + // Удаление архива (необязательно) + unlink($archive); + } + + foreach (new directory(ROSSKO) as $file) + if ($file->getExtension() === 'xlsx') { + // Инициализация документа + $document = ROSSKO . DIRECTORY_SEPARATOR . $file->getFilename(); + }; +} + +if (!empty($document) ?? file_exists($document)) { + // Инициализирован документ + + /** + * Скачать изображение товара + */ + function image(string $nomenclature) + { + // Если существуют файлы (подразумеваются изображения), то пропустить проверку и скачивание изображений + if (file_exists($image = IMAGES . DIRECTORY_SEPARATOR . $nomenclature)) + foreach (new directory($image) as $file) return; + + // Инициализация клиента Guzzle + $client = new client(['cookies' => true]); + + // Инициализация сессии (cookie) + /* $cookies = ($client->send(new request('GET', 'https://krsk.rossko.ru/')))->getHeader('Set-Cookie'); + $_auth = ''; + foreach ($cookies as $cookie) + if (str_starts_with($cookie, 'auth')) { + preg_match('/auth=(.*);/', $cookie, $matches); + $_auth = $matches[1]; + } + if (empty($_auth)) return false; */ + + // Инициализация запроса + /* $request = new request( + 'GET', + "https://productcard.rossko.ru/api/Product/Card/$nomenclature", + [ */ + // 'Accept' => '*/*', + /* 'Accept-Encoding' => 'gzip, deflate, br, zstd', + 'Accept-Language' => 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3', + 'Access-Control-Allow-Origin' => '*', + 'Authorization-Domain' => 'https://krsk.rossko.ru', + 'Authorization-Session' => $_auth, + 'Cache-Control' => 'no-cache', + 'Connection' => 'keep-alive', + 'Content-Type' => 'application/json', + 'DNT' => '1', + 'Host' => 'productcard.rossko.ru', + 'Origin' => 'https://krsk.rossko.ru', + 'Pragma' => 'no-cache', + 'Priority' => 'u=4', + 'Referer' => 'https://krsk.rossko.ru/', + 'Sec-Fetch-Dest' => 'empty', + 'Sec-Fetch-Mode' => 'no-cors', + 'Sec-Fetch-Site' => 'same-site', + 'Sec-GPC' => '1', + 'source' => 'frontend', + 'TE' => 'trailers', + 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0', + ] + ); */ + + // ОНИ НЕ ПРОВЕРЯЮТ ПОДЛИННОСТЬ СЕССИИ, ХА + // Запрос данных карточки товара в разделе "Описание" + $request = new request( + 'GET', + "https://productcard.rossko.ru/api/Product/Card/$nomenclature", + [ + 'Authorization-Domain' => 'https://krsk.rossko.ru', + 'Authorization-Session' => 'ya_tvoi_kartinki_spizdil', + ] + ); + + // Отправка запроса и инициализация ответа + $response = $client->send($request); + + // Инициализация изображений + $images = json_decode((string) $response->getBody(), true)['mainPart']['images']; + + foreach ($images ?? [] as $image) { + // Перебор изображений + + // Инициализация пути до директории изображения + $path_directory = IMAGES . DIRECTORY_SEPARATOR . $nomenclature; + + // Инициализация пути до изображения + $path_image = $path_directory . DIRECTORY_SEPARATOR . preg_replace('/.*\//', '', $image); + + // Инициализация директории изображения + if (!file_exists($path_directory)) mkdir($path_directory, 0775, true); + + /* var_dump("Проверка: $path_image"); */ + + // Инициализация изображения (скачивание) + if (!file_exists($path_image)) { + /* var_dump("Загрузка: $path_image"); */ + $client->request('GET', $image, ['sink' => $path_image]); + } + } + } + + /** + * Скачать список автомобилей для которых продукт подходит + */ + function applicability(string $nomenclature) + { + // Инициализация клиента Guzzle + $client = new client(['cookies' => true]); + + // Запрос данных карточки товара в разделе "Применимость" + $request = new request( + 'GET', + "https://productcard.rossko.ru/api/Product/CarTypes/$nomenclature", + [ + 'Authorization-Domain' => 'https://krsk.rossko.ru', + 'Authorization-Session' => 'ya_tvoi_applicabilities_spizdil', + ] + ); + + // Отправка запроса и инициализация ответа + $response = $client->send($request); + + // Инициализация данных автомобилей и возврат (успех) + return (string) $response->getBody(); + } + + // Инициализация таблицы + $spreadsheet = excel::open($document); + + // Инициализация рабочего листа + $worksheet = $spreadsheet->sheet(); + + foreach ( + $worksheet->nextRow( + [ + 'A' => 'nomenclature', + 'B' => 'brand', + 'C' => 'code', + 'D' => 'description', + 'E' => 'weight', + 'F' => 'multiplicity', + 'G' => 'cost_rossko', + 'H' => 'cost', + 'I' => 'amount', + 'J' => 'delivery', + 'K' => 'code_rossko', + 'L' => 'oem', + 'M' => 'applicability', + 'N' => 'code_vendor', + 'O' => 'warehouse' + ], + excel::KEYS_FIRST_ROW + ) as $number => $row + ) { + // Перебор строк + + if (!empty($row['nomenclature'])) { + // Инициализирован "nomenclature" (номенклатура) + + // Инициализация запроса: "SELECT" (по номенклатуре получить applicability и updated) + $request = $db->prepare('SELECT applicability, updated FROM product WHERE nomenclature=?;'); + + // Инициализация параметров запроса + $request->bindValue(1, $row['nomenclature'] ?? null, SQLITE3_TEXT); + + // Выполнение запроса + $response = $request->execute()?->fetchArray(); + + if ( + empty($response['updated']) + || (!empty($_current = datetimeimmutable::createFromFormat(DATE_W3C, date(DATE_W3C))) + && !empty($_updated = datetimeimmutable::createFromFormat(DATE_W3C, $response['updated'])) + && $_current->diff($_updated)->h > 24) + ) { + // Записи не существует (может быть не инициализирована) ИЛИ запись обновлялась более чем 24 часа назад + + // Объявление переменной для значения "applicability" ()применимость) + $applicability; + + if (empty($response['applicability'])) { + // Не найдено значение: "applicability" (применимость) + + // Инициализация значения: "applicability" (применимость) + $applicability = applicability($row['nomenclature']); + } + + // Инициализация запроса: "REPLACE" (INSERT или UPDATE) + $request = $db->prepare('REPLACE INTO product (nomenclature, brand, code, description, weight, multiplicity, cost_rossko, cost, amount, delivery, code_rossko, oem, applicability, code_vendor, warehouse, updated) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);'); + + // Инициализация параметров запроса + $request->bindValue(1, $row['nomenclature'] ?? null, SQLITE3_TEXT); + $request->bindValue(2, $row['brand'] ?? null, SQLITE3_TEXT); + $request->bindValue(3, $row['code'], SQLITE3_TEXT); + $request->bindValue(4, $row['description'] ?? null, SQLITE3_TEXT); + $request->bindValue(5, $row['weight'] ?? null, SQLITE3_INTEGER); + $request->bindValue(6, $row['multiplicity'] ?? null, SQLITE3_FLOAT); + $request->bindValue(7, $row['cost_rossko'] ?? null, SQLITE3_FLOAT); + $request->bindValue(8, $row['cost'] ?? null, SQLITE3_FLOAT); + $request->bindValue(9, $row['amount'] ?? null, SQLITE3_TEXT); + $request->bindValue(10, $row['delivery'] ?? null, SQLITE3_INTEGER); + $request->bindValue(11, $row['code_rossko'] ?? null, SQLITE3_TEXT); + $request->bindValue(12, $row['oem'] ?? null, SQLITE3_TEXT); + $request->bindValue(13, $applicability ?? null, SQLITE3_TEXT); + $request->bindValue(14, $row['code_vendor'] ?? null, SQLITE3_TEXT); + $request->bindValue(15, $row['warehouse'] ?? null, SQLITE3_TEXT); + $request->bindValue(16, date(DATE_W3C), SQLITE3_TEXT); + + // Выполнение запроса + $request->execute(); + + if (!file_exists(IMAGES . DIRECTORY_SEPARATOR . $row['nomenclature'] . '1.jpg')) { + // Не найдено изображение товара + + // Ожидание перед загрузкой изображения + /* sleep(10); */ + + // Загрузка изображения товара + image($row['nomenclature']); + } + } + } + } + + // Удаление документа + unlink($document); +} diff --git a/author/project/system/public/index.php b/mirzaev/parser_from_rossko/system/public/index.php similarity index 100% rename from author/project/system/public/index.php rename to mirzaev/parser_from_rossko/system/public/index.php diff --git a/mirzaev/parser_from_rossko/system/public/mail.php b/mirzaev/parser_from_rossko/system/public/mail.php new file mode 100644 index 0000000..48a53bc --- /dev/null +++ b/mirzaev/parser_from_rossko/system/public/mail.php @@ -0,0 +1,50 @@ +setConnectionArgs( + CL_EXPUNGE // expunge deleted mails upon mailbox close + //| OP_SECURE // don't do non-secure authentication +); + +try { + // Get all emails (messages) + + // PHP.net imap_search criteria: http://php.net/manual/en/function.imap-search.php + $mailsIds = $mailbox->searchMailbox('UNSEEN FROM "price@rossko.ru" SINCE "' . date('j F Y', strtotime('-1 day')) . '"'); +} catch(_exception $ex) { + echo "IMAP connection failed: " . implode(",", $ex->getErrors('all')); + die(); +} + +// Get the first message (автоматически скачает файл и пометит прочитанным письмо, чтобы не обработать повторно) +if (isset($mailsIds[0])) $mailbox->getMail($mailsIds[0]); diff --git a/author/project/system/settings/.gitignore b/mirzaev/parser_from_rossko/system/settings/.gitignore similarity index 100% rename from author/project/system/settings/.gitignore rename to mirzaev/parser_from_rossko/system/settings/.gitignore diff --git a/mirzaev/parser_from_rossko/system/settings/account.php.sample b/mirzaev/parser_from_rossko/system/settings/account.php.sample new file mode 100644 index 0000000..2f2eda3 --- /dev/null +++ b/mirzaev/parser_from_rossko/system/settings/account.php.sample @@ -0,0 +1,7 @@ + '{imap.gmail.com:993/imap/ssl}ROSSKO', + 'mail' => '@gmail.com', + 'password' => '' +]; diff --git a/author/project/system/settings/arangodb.php.sample b/mirzaev/parser_from_rossko/system/settings/arangodb.php.sample similarity index 100% rename from author/project/system/settings/arangodb.php.sample rename to mirzaev/parser_from_rossko/system/settings/arangodb.php.sample diff --git a/mirzaev/parser_from_rossko/system/storage/.gitignore b/mirzaev/parser_from_rossko/system/storage/.gitignore new file mode 100644 index 0000000..c291c09 --- /dev/null +++ b/mirzaev/parser_from_rossko/system/storage/.gitignore @@ -0,0 +1 @@ +images diff --git a/author/project/system/views/templater.php b/mirzaev/parser_from_rossko/system/views/templater.php similarity index 100% rename from author/project/system/views/templater.php rename to mirzaev/parser_from_rossko/system/views/templater.php diff --git a/author/project/system/views/themes/default/core.html b/mirzaev/parser_from_rossko/system/views/themes/default/core.html similarity index 100% rename from author/project/system/views/themes/default/core.html rename to mirzaev/parser_from_rossko/system/views/themes/default/core.html diff --git a/author/project/system/views/themes/default/footer.html b/mirzaev/parser_from_rossko/system/views/themes/default/footer.html similarity index 100% rename from author/project/system/views/themes/default/footer.html rename to mirzaev/parser_from_rossko/system/views/themes/default/footer.html diff --git a/author/project/system/views/themes/default/head.html b/mirzaev/parser_from_rossko/system/views/themes/default/head.html similarity index 100% rename from author/project/system/views/themes/default/head.html rename to mirzaev/parser_from_rossko/system/views/themes/default/head.html diff --git a/author/project/system/views/themes/default/header.html b/mirzaev/parser_from_rossko/system/views/themes/default/header.html similarity index 100% rename from author/project/system/views/themes/default/header.html rename to mirzaev/parser_from_rossko/system/views/themes/default/header.html diff --git a/author/project/system/views/themes/default/index.html b/mirzaev/parser_from_rossko/system/views/themes/default/index.html similarity index 100% rename from author/project/system/views/themes/default/index.html rename to mirzaev/parser_from_rossko/system/views/themes/default/index.html diff --git a/author/project/system/views/themes/default/js.html b/mirzaev/parser_from_rossko/system/views/themes/default/js.html similarity index 100% rename from author/project/system/views/themes/default/js.html rename to mirzaev/parser_from_rossko/system/views/themes/default/js.html diff --git a/rossko.service b/rossko.service new file mode 100644 index 0000000..ab6c6b2 --- /dev/null +++ b/rossko.service @@ -0,0 +1,14 @@ +[Unit] +Description=Parser from Rossko(import) +After=network.target + +[Service] +Type=simple +WorkingDirectory=/var/www/parser_from_rossko/mirzaev/parser_from_rossko/system/public +ExecStart=/usr/bin/php import.php +Restart=always +RestartSec=3600s +RuntimeMaxSec=86400s + +[Install] +WantedBy=multi-user.target