Алгоритм “хвост змеи для заполнения прямоугольной области”.




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

15 Comments

  1. kapustinag

    (0), Не совсем уловил необходимость «хвоста змеи» в данном случае.

    Имеется матрица M x N элементов, то есть всего M*N элементов. Их нужно занять этими заданиями, причем сначала (с элемента (1,1)) — первое задание, затем второе и т.д.

    Два вложенных цикла — по строкам и столбцам матрицы.

    Порядковый номер элемента, начиная от элемента (1,1), перед началом цикла присваивается 1, и увеличивается на 1 внутри вложенного цикла.

    Пока этот порядковый номер меньше или равен длине сейчас размещаемого задания — заполняем данный элемент матрицы номером задания.

    Когда очередное задание размещено, обнуляем переменную n, и переходим к следующему заданию.

    На выходе получаем матрицу, элементы которой заполнены слева направо и сверху вниз сначала числом 1, затем 2, …

    Всё.



    Или я что-то невнимательно прочитал?

    Reply
  2. milkers

    (1) kapustinag, Теоритически да, я в начала так и пробовал, но код получался очень некрасивым и сложным. Задача решается гораздо легче, если перевести ее из двумерного в одномерное пространство, что и делает мой алгоритм.

    Reply
  3. DrAku1a

    При распределении на лист A4 для проверки влезет ли новая область на страничку — используется «ПроверитьВывод()». Я таким образом разделил вручную по листам — т.е. вывел отчет вниз, далее в полученном отчете выбрал области по листу и вывел их на новом документе методом Вывести -> Присоединить -> Присоединить -> Присоединить… Получил в итоге Ваше заполнение.

    Один момент: нужно или следить за высотой строк в выводимом отчете или чтобы высота строк была одинаковой.

    Reply
  4. milkers

    (3) DrAku1a, в частном случае можно воспользоваться и Вашим способом, но для таблиц, двумерных массивов и пр. ваш способ точно не применим, плюс скорость в моем случае выше по определению, из за особенностей работы метода ПроверитьВывод(). Да и к тому же я выложил алгоритм, по принципу «не пропадать же добру». Вдруг кому-нибудь пригодиться. Да и красивое решение получилось 🙂

    Reply
  5. ser6702

    Любую N-мерную матрицу можно привести к одномерной. ИМХО

    этим еще занимался в период когда формировал и моделировал радиолокационное изображение в статистических алгоритмах и в DOS памяти было мало и делали сами свап файл в которые построчно сбрасывали данные комплексных радиосигналов с разных направлений и с разных дальностей. Когда известен размер матрицы это удобно. Принцип у автора тот же — матрица развернута в строку. Сама реализация на быстродействие не влияет — только особенности языка и способности программиста. Имеет право на существование как готовое решение известной проблемы — потому +

    Reply
  6. ildarovich

    На мой взгляд, для такой простой задачи кода слишком много. И его избыточность не служит понятности, а, наоборот, запутывает.

    И вообще никакого «алгоритма» (в смысле определенной последовательности действий) здесь не нужно. Отношение ячейки к этапу определяется ПРОСТОЙ функциональной зависимостью. Достаточно было бы в теле уже приведенной функции рассчитать «Сегмент хвоста» по номеру этапа, количеству ячеек на этап и номеру ячейки внутри этапа. Тогда заполнить ячейки можно было бы простым и понятным циклом по этапам (и вложенным циклам по ячейкам внутри этапа), а также можно было бы начинать заполнение не всегда с верхнего угла, при необходимости «транспонировать» заполнение — то есть идти не по колонкам, а по строчкам, заменив функцию отображения, перезаполнять ячейки выбранных этапов (перекрашивать при выделении) без перезаполнения всей области и так далее.

    В общем, я бы не рекомендовал такой подход к решению этой задачи.

    Reply
  7. milkers

    (6) ildarovich, То же вариант решения. По скорости одинаково. У меня тоже внешний цикл выполнится столько раз, сколько этапов разбития. А внутренний нужен только для деления этапа на столбцы. И переносить управляющую логику в функцию или оставить при вызове функции — разницы никакой.

    Reply
  8. ildarovich

    (7) методически разница очень большая. О скорости здесь речь не идет. Речь идет о способе декомпозиции задачи и вообще о том, что можно называть алгоритмом. По сути, Вы предлагаете «ползать по лабиринту» вместо того, чтобы посмотреть на него сверху, увидеть закономерность и применить ее. Весь так называемый «алгоритм» — это перенос единицы в старший разряд (переход к началу следующей колонки), которому дети учатся, когда начинают уметь считать больше двадцати. Это все равно что умножение заменять последовательным сложением. Но это мое личное мнение в плане того, что можно рекомендовать к использованию, а что нельзя. Это не образцовый подход, а как раз то, чего следует избегать, ИМХО.

    Как, например, здесь выделить цветом один из этапов. — Заново сначала проползти по лабиринту?

    Reply
  9. milkers

    (8) Должен признать, что с этой точки зрения определенный смысл в Вашем видении есть. Ну, в любом случае переделка несложная, может быть и наша дискуссия кому-нибудь поможет. Спасибо за дельный совет.

    Reply
  10. Патриот

    (0) Мне кажется слишком сложно, что легче заново написать самому, чем разобраться. Например даже такая мелочь —

    (ВысотаОбласти+СегментХвоста-1)%ВысотаОбласти
    //эквивалентно
    СегментХвоста%ВысотаОбласти-1
    Reply
  11. milkers
    Reply
  12. chmv

    Ой как сложно

    Reply
  13. milkers

    (12) «Ой как сложно» — о чем это ты?

    Reply
  14. milkers

    (8)

    Это не образцовый подход, а как раз то, чего следует избегать, ИМХО.

    Как, например, здесь выделить цветом один из этапов. — Заново сначала проползти по лабиринту?

    При условии равномерного распределения все что вы сказали истинно. Но как быть если в дальнейшем планируется распределять учитывая предполагаемую длительность этапов, определенную для них на этапе планирования, плюс возможны интервалы связанные с необходимость завершить этапы на другом оборудовании. Как тогда обойтись без «ползанья по лабиринту»?

    Reply
  15. Патриот

    (11) вынужден огорчиться, действительно лоханулся 🙂

    доводы более чем убедительны

    но может хоть так заработает?

    (ВысотаОбласти+СегментХвоста-1)%ВысотаОбласти
    //эквивалентно
    (СегментХвоста-1)%ВысотаОбласти
    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *