Executing only one job in one run. Added Job manager tab in settings

This commit is contained in:
max-baranikov 2021-04-23 16:01:38 +03:00 committed by GitHub
parent 33f756be8c
commit 9e9c13bb66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1018 additions and 44 deletions

View File

@ -58,6 +58,7 @@ if (!defined('_PS_VERSION_')) {
class RetailcrmJobManager
{
const LAST_RUN_NAME = 'RETAILCRM_LAST_RUN';
const LAST_RUN_DETAIL_NAME = 'RETAILCRM_LAST_RUN_DETAIL';
const IN_PROGRESS_NAME = 'RETAILCRM_JOBS_IN_PROGRESS';
const CURRENT_TASK = 'RETAILCRM_JOB_CURRENT';
@ -107,9 +108,11 @@ class RetailcrmJobManager
{
$current = date_create('now');
$lastRuns = array();
$lastRunsDetails = array();
try {
$lastRuns = static::getLastRuns();
$lastRunsDetails = static::getLastRunDetails();
} catch (Exception $exception) {
static::handleError(
$exception->getFile(),
@ -140,6 +143,24 @@ class RetailcrmJobManager
}
}
uasort($jobs, function ($diff1, $diff2) {
$date1 = new \DateTime();
$date2 = new \DateTime();
if (!is_null($diff1)) {
$date1->add($diff1);
}
if (!is_null($diff2)) {
$date2->add($diff2);
}
if ($date1 == $date2) {
return 0;
}
return ($date1 > $date2) ? -1 : 1;
});
foreach ($jobs as $job => $diff) {
try {
if (isset($lastRuns[$job]) && $lastRuns[$job] instanceof DateTime) {
@ -170,11 +191,27 @@ class RetailcrmJobManager
sprintf('Executed job %s, result: %s', $job, $result ? 'true' : 'false')
);
$lastRuns[$job] = new \DateTime('now');
$lastRunsDetails[$job] = [
'success' => true,
'lastRun' => new \DateTime('now'),
'error' => null,
];
break;
}
} catch (\Exception $exception) {
if ($exception instanceof RetailcrmJobManagerException
&& $exception->getPrevious() instanceof \Exception
) {
$lastRunsDetails[$job] = [
'success' => false,
'lastRun' => new \DateTime('now'),
'error' => [
'message' => $exception->getPrevious()->getMessage(),
'trace' => $exception->getPrevious()->getTraceAsString(),
],
];
static::handleError(
$exception->getPrevious()->getFile(),
$exception->getPrevious()->getMessage(),
@ -182,6 +219,15 @@ class RetailcrmJobManager
$job
);
} else {
$lastRunsDetails[$job] = [
'success' => false,
'lastRun' => new \DateTime('now'),
'error' => [
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString(),
],
];
static::handleError(
$exception->getFile(),
$exception->getMessage(),
@ -192,14 +238,15 @@ class RetailcrmJobManager
self::clearCurrentJob($job);
}
}
if (isset($result) && $result) {
self::clearCurrentJob($job);
}
if (isset($result) && $result) {
self::clearCurrentJob($job);
}
try {
static::setLastRuns($lastRuns);
static::setLastRunDetails($lastRunsDetails);
} catch (Exception $exception) {
static::handleError(
$exception->getFile(),
@ -271,6 +318,58 @@ class RetailcrmJobManager
Configuration::updateGlobalValue(self::LAST_RUN_NAME, (string)json_encode($lastRuns));
}
/**
* Extracts jobs last runs from db
*
* @return array<string, array>
* @throws \Exception
*/
private static function getLastRunDetails()
{
$lastRuns = json_decode((string)Configuration::getGlobalValue(self::LAST_RUN_DETAIL_NAME), true);
if (json_last_error() != JSON_ERROR_NONE) {
$lastRuns = array();
} else {
foreach ($lastRuns as $job => $details) {
$lastRan = DateTime::createFromFormat(DATE_RFC3339, $details['lastRun']);
if ($lastRan instanceof DateTime) {
$lastRuns[$job]['lastRun'] = $lastRan;
} else {
$lastRuns[$job]['lastRun'] = null;
}
}
}
return (array)$lastRuns;
}
/**
* Updates jobs last runs in db
*
* @param array $lastRuns
*
* @throws \Exception
*/
private static function setLastRunDetails($lastRuns = array())
{
if (!is_array($lastRuns)) {
$lastRuns = array();
}
foreach ($lastRuns as $job => $details) {
if ($details['lastRun'] instanceof DateTime) {
$lastRuns[$job]['lastRun'] = $details['lastRun']->format(DATE_RFC3339);
} else {
$lastRuns[$job]['lastRun'] = null;
}
}
Configuration::updateGlobalValue(self::LAST_RUN_DETAIL_NAME, (string)json_encode($lastRuns));
}
/**
* Runs job
*
@ -427,6 +526,30 @@ class RetailcrmJobManager
call_user_func_array(self::$customShutdownHandler, array($error));
} else {
if (null !== $error) {
$job = self::getCurrentJob();
if(!empty($job)) {
$lastRunsDetails = self::getLastRunDetails();
$lastRunsDetails[$job] = [
'success' => false,
'lastRun' => new \DateTime('now'),
'error' => [
'message' => (isset($error['message']) ? $error['message'] : print_r($error, true)),
'trace' => print_r($error, true),
],
];
try {
self::setLastRunDetails($lastRunsDetails);
} catch (Exception $exception) {
static::handleError(
$exception->getFile(),
$exception->getMessage(),
$exception->getTraceAsString(),
$job
);
}
}
self::clearCurrentJob(null);
}
}
@ -594,7 +717,7 @@ class RetailcrmJobManager
$lastRan = static::getLastRun();
$lastRanSeconds = $lastRan->format('U');
if (($lastRanSeconds + self::getTimeLimit()) < time()) {
if ($inProcess && ($lastRanSeconds + self::getTimeLimit()) < time()) {
RetailcrmLogger::writeDebug(__METHOD__, 'Removing lock because time limit exceeded.');
static::unlock();

View File

@ -633,7 +633,7 @@ class RetailcrmTools
public static function startJobManager()
{
RetailcrmJobManager::startJobs(array(
'RetailcrmAbandonedCartsEvent' => null,
'RetailcrmAbandonedCartsEvent' => new \DateInterval('PT1M'),
'RetailcrmIcmlEvent' => new \DateInterval('PT4H'),
'RetailcrmSyncEvent' => new \DateInterval('PT7M'),
'RetailcrmInventoriesEvent' => new \DateInterval('PT15M')

View File

@ -72,6 +72,12 @@ class RetailCRM extends Module
const CONSULTANT_SCRIPT = 'RETAILCRM_CONSULTANT_SCRIPT';
const CONSULTANT_RCCT = 'RETAILCRM_CONSULTANT_RCCT';
const ENABLE_WEB_JOBS = 'RETAILCRM_ENABLE_WEB_JOBS';
const JOBS_NAMES = [
'RetailcrmAbandonedCartsEvent' => 'Abandoned Carts',
'RetailcrmIcmlEvent' => 'Icml generation',
'RetailcrmSyncEvent' => 'History synchronization',
'RetailcrmInventoriesEvent' => 'Inventories uploads'
];
/**
* @var array $templateErrors
@ -221,6 +227,11 @@ class RetailCRM extends Module
Configuration::deleteByName('RETAILCRM_LAST_SYNC') &&
Configuration::deleteByName('RETAILCRM_LAST_ORDERS_SYNC') &&
Configuration::deleteByName('RETAILCRM_LAST_CUSTOMERS_SYNC') &&
Configuration::deleteByName(RetailcrmJobManager::LAST_RUN_NAME) &&
Configuration::deleteByName(RetailcrmJobManager::LAST_RUN_DETAIL_NAME) &&
Configuration::deleteByName(RetailcrmJobManager::IN_PROGRESS_NAME) &&
Configuration::deleteByName(RetailcrmJobManager::CURRENT_TASK) &&
Configuration::deleteByName(RetailcrmCli::CURRENT_TASK_CLI) &&
$this->uninstallDB();
}
@ -1035,6 +1046,7 @@ class RetailCRM extends Module
'deliveryDefault' => json_decode(Configuration::get(static::DELIVERY_DEFAULT), true),
'paymentDefault' => json_decode(Configuration::get(static::PAYMENT_DEFAULT), true),
'statusExport' => (string)(Configuration::get(static::STATUS_EXPORT)),
'lastRunDetails' => json_decode(Configuration::get(RetailcrmJobManager::LAST_RUN_DETAIL_NAME), true),
'collectorActive' => (Configuration::get(static::COLLECTOR_ACTIVE)),
'collectorKey' => (string)(Configuration::get(static::COLLECTOR_KEY)),
'clientId' => Configuration::get(static::CLIENT_ID),
@ -1074,7 +1086,8 @@ class RetailCRM extends Module
'consultantScriptName' => static::CONSULTANT_SCRIPT,
'enableCorporateName' => static::ENABLE_CORPORATE_CLIENTS,
'enableHistoryUploadsName' => static::ENABLE_HISTORY_UPLOADS,
'enableBalancesReceivingName' => static::ENABLE_BALANCES_RECEIVING
'enableBalancesReceivingName' => static::ENABLE_BALANCES_RECEIVING,
'jobsNames' => static::JOBS_NAMES
);
}

View File

@ -52,6 +52,16 @@ $_MODULE['<{retailcrm}prestashop>settings_4d3d769b812b6faa6b76e1a8abaece2d'] = '
$_MODULE['<{retailcrm}prestashop>settings_f75d8fa5c89351544d372cf90528ccf2'] = 'Clave de la página web';
$_MODULE['<{retailcrm}prestashop>settings_c9cc8cce247e49bae79f15173ce97354'] = 'Guardar';
$_MODULE['<{retailcrm}prestashop>settings_4f18e3f1c9941a6ec5a38bc716c521b4'] = 'Código que necesita insertar en la web';
$_MODULE['<{retailcrm}prestashop>settings_9082f68bc90113d8950e4ed7fe8fa0a4'] = 'Administrador de tareas';
$_MODULE['<{retailcrm}prestashop>settings_9194de58ce560c095f02cefc1c1c61e6'] = 'Nombre de la tarea';
$_MODULE['<{retailcrm}prestashop>settings_05a3a24340b7b9cc8d4e08f0ef4f4dd9'] = 'Última ejecución';
$_MODULE['<{retailcrm}prestashop>settings_ec53a8c4f07baed5d8825072c89799be'] = 'Estado';
$_MODULE['<{retailcrm}prestashop>settings_0be8406951cdfda82f00f79328cf4efc'] = 'Comentario';
$_MODULE['<{retailcrm}prestashop>settings_fe5b6cd4d7a31615bbec8d1505089d87'] = 'StackTrace';
$_MODULE['<{retailcrm}prestashop>settings_54e85d70ea67acdcc86963b14d6223a8'] = 'Carritos abandonados';
$_MODULE['<{retailcrm}prestashop>settings_5ae96cfe7ea77a401db8e0d5dbab333c'] = 'Generación Icml';
$_MODULE['<{retailcrm}prestashop>settings_9abdd7d7fa90b7aa19e8ca8fb9f04ee4'] = 'Sincronización de historia';
$_MODULE['<{retailcrm}prestashop>settings_e1d641ca2884dbd4398e9252c0ab511e'] = 'Cargas de inventarios';
$_MODULE['<{retailcrm}prestashop>index_f545947db05aa489f59babf06c319d06'] = 'RetailCRM es un servicio para tiendas online, el cual ayuda a dejar de perder pedidos y así mejorar las ganancias de tu comercio online en todas las etapas del embudo de ventas.';
$_MODULE['<{retailcrm}prestashop>index_96f5fae5347f2f1cf560e71a30420fec'] = 'Tengo una cuenta en RetailCRM';
$_MODULE['<{retailcrm}prestashop>index_e81c4e4f2b7b93b481e13a8553c2ae1b'] = 'o';

View File

@ -52,6 +52,16 @@ $_MODULE['<{retailcrm}prestashop>settings_4d3d769b812b6faa6b76e1a8abaece2d'] = '
$_MODULE['<{retailcrm}prestashop>settings_f75d8fa5c89351544d372cf90528ccf2'] = 'Ключ сайта';
$_MODULE['<{retailcrm}prestashop>settings_c9cc8cce247e49bae79f15173ce97354'] = 'Сохранить';
$_MODULE['<{retailcrm}prestashop>settings_4f18e3f1c9941a6ec5a38bc716c521b4'] = 'Код для вставки на сайт';
$_MODULE['<{retailcrm}prestashop>settings_9082f68bc90113d8950e4ed7fe8fa0a4'] = 'Менеджер задач';
$_MODULE['<{retailcrm}prestashop>settings_9194de58ce560c095f02cefc1c1c61e6'] = 'Имя задачи';
$_MODULE['<{retailcrm}prestashop>settings_05a3a24340b7b9cc8d4e08f0ef4f4dd9'] = 'Последний запуск';
$_MODULE['<{retailcrm}prestashop>settings_ec53a8c4f07baed5d8825072c89799be'] = 'Статус';
$_MODULE['<{retailcrm}prestashop>settings_0be8406951cdfda82f00f79328cf4efc'] = 'Комментарий';
$_MODULE['<{retailcrm}prestashop>settings_fe5b6cd4d7a31615bbec8d1505089d87'] = 'StackTrace';
$_MODULE['<{retailcrm}prestashop>settings_54e85d70ea67acdcc86963b14d6223a8'] = 'Брошенные корзины';
$_MODULE['<{retailcrm}prestashop>settings_5ae96cfe7ea77a401db8e0d5dbab333c'] = 'Генерация Icml';
$_MODULE['<{retailcrm}prestashop>settings_9abdd7d7fa90b7aa19e8ca8fb9f04ee4'] = 'Синхронизация истории';
$_MODULE['<{retailcrm}prestashop>settings_e1d641ca2884dbd4398e9252c0ab511e'] = 'Выгрузка остатков';
$_MODULE['<{retailcrm}prestashop>index_f545947db05aa489f59babf06c319d06'] = 'RetailCRM — сервис для интернет магазинов, который поможет перестать терять заказы и увеличить доход на всех этапах воронки.';
$_MODULE['<{retailcrm}prestashop>index_96f5fae5347f2f1cf560e71a30420fec'] = 'У меня уже есть аккаунт RetailCRM';
$_MODULE['<{retailcrm}prestashop>index_e81c4e4f2b7b93b481e13a8553c2ae1b'] = 'или';

View File

@ -554,4 +554,118 @@ body, html {
.toggle-box {
display: none;
}
}
&-table {
width: 100%;
border: none;
border-radius: 20px;
thead {
th {
background: rgba(122, 122, 122, 0.15);
font-size: 16px;
}
}
tbody {
tr {
td {
border-bottom: 1px solid #ccc;
}
}
}
td, th {
color: #333;
font-size: 14px;
line-height: 60px;
padding: 4px 4px 4px 6px;
max-width: 300px;
vertical-align: middle;
}
&-no-wrap {
white-space: nowrap;
}
&-center {
text-align: center;
}
&-sort {
&__btn, &__switch {
cursor: pointer;
&:hover {
color: #005add;
}
}
&__btn {
float: left;
clear: both;
line-height: 15px;
font-size: 20px;
padding-left: 10px;
padding-right: 10px;
opacity: 0;
transition: opacity 0.2s ease-out;
&-wrap {
position: absolute;
}
}
&__asc {
padding-top: 15px;
padding-bottom: 0;
}
&__desc {
padding-top: 0;
padding-bottom: 15px;
}
& thead th:hover &__btn, & thead td:hover &__btn {
opacity: 1;
}
}
}
&-collapsible {
&__input {
display: none;
}
label&__title {
cursor: pointer;
font-weight: normal;
text-align: center;
padding: 0;
position: relative;
line-height: 1;
width: 100%;
&:before {
content: '\25B6';
opacity: 0;
transition: opacity 0.2s ease-out;
}
&:hover:before {
opacity: 1;
}
}
&__content {
text-align: left;
font-size: 12px;
line-height: 1.5;
background: #fff;
border: 1px solid rgba(122,122,122,0.15);
position: absolute;
top: 20px;
left: 0;
padding: 18px;
margin: 0;
border-radius: 20px;
overflow: hidden;
max-height: 0;
transition-duration: 0.2s;
transition-timing-function: ease-out;
transition-property: max-height, visibility;
visibility: hidden;
}
&__input:checked + &__title &__content {
max-height: 100vh;
visibility: visible;
}
}
&-error-msg {
&, &:after, &:before {
color: #dd2e44;
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,7 @@ $(function(){
var Main = {
init: function() {
this.selects.init();
this.tableSort.init();
this.player.init();
this.tabs.init();
this.uploadForm.init(this.settingsTabs.init());
@ -99,6 +100,73 @@ $(function(){
});
}
},
tableSort: {
init: function () {
var _this = this;
$('.retail-table-sort').each((i, table) => {
$(table).find('.retail-table-sort__switch').each((j, header) => {
const column = $(header).closest('th,td').index();
$(header).click((e) => {
e.preventDefault();
_this.sort(table, column);
})
})
$(table).find('.retail-table-sort__asc').each((j, header) => {
const column = $(header).closest('th,td').index();
$(header).click((e) => {
e.preventDefault();
_this.sort(table, column, 'asc');
})
})
$(table).find('.retail-table-sort__desc').each((j, header) => {
const column = $(header).closest('th,td').index();
$(header).click((e) => {
e.preventDefault();
_this.sort(table, column, 'desc');
})
})
$(table).find('.retail-table-sort__initial').click();
});
},
sort: function (table, column, direction = undefined) {
let rows, switching, i, x, y, shouldSwitch, dir, switchcount = 0;
switching = true;
dir = (direction ? direction : "asc");
while (switching) {
switching = false;
rows = table.rows;
for (i = 1; i < (rows.length - 1); i++) {
shouldSwitch = false;
x = rows[i].getElementsByTagName("TD")[column];
y = rows[i + 1].getElementsByTagName("TD")[column];
if (dir === "asc") {
if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
} else if (dir === "desc") {
if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) {
shouldSwitch = true;
break;
}
}
}
if (shouldSwitch) {
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
switchcount ++;
} else {
if (direction === undefined && switchcount === 0 && dir === "asc") {
dir = "desc";
switching = true;
}
}
}
}
},
player: {
init: function () {
window.player = {};
@ -150,6 +218,7 @@ $(function(){
'rcrm_tab_order_statuses': selectsUpdate,
'rcrm_tab_payment_types': selectsUpdate,
'rcrm_tab_consultant': mainSubmitHide,
'rcrm_tab_job_manager': mainSubmitHide,
'rcrm_tab_orders_upload': mainSubmitHide
});
tabs.initializeTabs();

File diff suppressed because one or more lines are too long

View File

@ -56,6 +56,7 @@
<a href="{$url_post|escape:'htmlall':'UTF-8'}&amp;configure=retailcrm" data-tab-trigger="rcrm_tab_carts_sync" class="retail-menu__btn retail-menu__btn_big retail-menu__btn_inactive"><span>{l s='Abandoned carts' mod='retailcrm'}<span/></a>
<a href="{$url_post|escape:'htmlall':'UTF-8'}&amp;configure=retailcrm" data-tab-trigger="rcrm_tab_daemon_collector" class="retail-menu__btn retail-menu__btn_big retail-menu__btn_inactive"><span>{l s='Daemon Collector' mod='retailcrm'}<span/></a>
<a href="{$url_post|escape:'htmlall':'UTF-8'}&amp;configure=retailcrm&item=consultant" data-tab-trigger="rcrm_tab_consultant" class="retail-menu__btn retail-menu__btn_big retail-menu__btn_inactive"><span>{l s='Online consultant' mod='retailcrm'}<span/></a>
<a href="{$url_post|escape:'htmlall':'UTF-8'}&amp;configure=retailcrm" data-tab-trigger="rcrm_tab_job_manager" class="retail-menu__btn retail-menu__btn_big retail-menu__btn_inactive"><span>{l s='Job Manager' mod='retailcrm'}<span/></a>
</div>
</aside>
<article class="retail-column__content">
@ -216,6 +217,65 @@
</div>
</form>
</div>
<div id="rcrm_tab_job_manager">
<div class="retail-form__title">{l s='Job Manager' mod='retailcrm'}</div>
<table class="retail-table retail-table-sort">
<thead>
<tr>
<th>
<span>{l s='Job name' mod='retailcrm'}</span></th>
<th>
<div class="retail-table-sort__btn-wrap">
<span class="retail-table-sort__asc retail-table-sort__btn">&#x25B2</span>
<span class="retail-table-sort__desc retail-table-sort__btn retail-table-sort__initial">&#x25BC</span>
</div>
<span class="retail-table-sort__switch">{l s='Last Run' mod='retailcrm'}</span></th>
<th>
<span>{l s='Status' mod='retailcrm'}</span></th>
<th>
<span>{l s='Comment' mod='retailcrm'}</span></th>
</tr>
</thead>
<tbody>
{foreach from=$lastRunDetails key=key item=item}
<tr class="retail-table__row-top">
<td>
{if isset($jobsNames[$key]) }
<span title="{$key}">{l s=$jobsNames[$key] mod='retailcrm'}</span>
{else}
{$key}
{/if}
</td>
<td class="retail-table-center retail-table-no-wrap">{if isset($item['lastRun'])}{$item['lastRun']|date_format:'Y-m-d H:i:s'}{/if}</td>
<td class="retail-table-center">
{if isset($item['success'])}
{if $item['success'] === true}
<span style="color: #2e8b57;">
&#10004;
</span>
{else}
<span style="color: #dd2e44;">
&#10060;
</span>
{/if}
{/if}
</td>
<td class="retail-collapsible">
{if isset($item['error']['message'])}
<input type="checkbox" class="retail-collapsible__input" id="error_{$key}">
<label for="error_{$key}" class="retail-collapsible__title retail-error-msg">
<span class="retail-error-msg">{$item['error']['message']}</span>
<p class="retail-collapsible__content">
<b>{l s='StackTrace' mod='retailcrm'}:</b><br>{$item['error']['trace']}
</p>
</label>
{/if}
</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
</article>
</div>