/**
  * @param Job $job
  * @return array|void
  */
 public function execute(Job $job)
 {
     \Keboola\StorageApi\Config\Reader::$client = $this->storageApi;
     $configBucket = \Keboola\StorageApi\Config\Reader::read("sys.c-{$this->appName}", $this->storageApi->token);
     $passed = false;
     $accountsIds = array();
     $accountsOffset = 0;
     $accountsLimit = 0;
     $jsonParams = $job->getAttribute("params");
     if (isset($jsonParams["accountsIds"])) {
         $accountsIds = explode(',', $jsonParams["accountsIds"]);
     } else {
         if (isset($jsonParams["accountsOffset"]) && is_integer($jsonParams["accountsOffset"]) && $jsonParams["accountsOffset"] > 0) {
             $accountsOffset = $jsonParams["accountsOffset"];
         }
         if (isset($jsonParams["accountsLimit"]) && is_integer($jsonParams["accountsLimit"]) && $jsonParams["accountsLimit"] > 0) {
             $accountsLimit = $jsonParams["accountsLimit"];
         }
     }
     if (isset($jsonParams["since"])) {
         $since = $jsonParams["since"];
     } else {
         $days = isset($jsonParams["days"]) ? $jsonParams["days"] : 14;
         $since = '-' . $days . ' days';
     }
     $until = isset($jsonParams["until"]) ? $jsonParams["until"] : 'today';
     if (strtotime($since) > strtotime($until)) {
         return false;
     }
     if (isset($jsonParams["config"])) {
         $jsonParams["configurationId"] = $jsonParams["config"];
     }
     $reservedTables = array('accounts');
     foreach ($configBucket["items"] as $configurationId => $configInstance) {
         if (!in_array($configurationId, $reservedTables)) {
             if (count($jsonParams) && isset($jsonParams["configurationId"]) && $jsonParams["configurationId"] != $configurationId) {
                 continue;
             }
             $passed = true;
             $connectionConfig = $configInstance;
             unset($connectionConfig["items"]);
             $connectionConfig = new \Zend_Config($connectionConfig, true);
             $runConfig = $configInstance["items"];
             $runConfig = new \Zend_Config($runConfig, true);
             try {
                 $fbImport = new Import($this->appName);
                 $fbImport->storageApi = $this->storageApi;
                 $fbImport->runId = $this->storageApi->getRunId();
                 $fbImport->log("Extraction of row {$configurationId} started");
                 if (isset($configInstance["paging"])) {
                     $fbImport->paging = $configInstance["paging"];
                 }
                 $fbImport->configurationId = $configurationId;
                 $fbImport->importConfig = $connectionConfig;
                 $fbImport->runConfig = $runConfig;
                 $fbImport->storageApiBucket = "in.c-{$this->appName}-" . $configurationId;
                 \NDebugger::timer('configuration');
                 $fbImport->log("Extraction of configuration {$configurationId} started", array('since' => $since, 'until' => $until, 'accountsOffset' => $accountsOffset, 'accountsLimit' => $accountsLimit, 'accountsIds' => $accountsIds));
                 if (!$this->storageApi->bucketExists($fbImport->storageApiBucket)) {
                     $this->storageApi->createBucket($this->appName . '-' . $configurationId, \Keboola\StorageApi\Client::STAGE_IN, "Facebook Extractor Data");
                 }
                 $tokenInfo = $this->storageApi->getLogData();
                 $tmpDir = "/tmp/" . $tokenInfo["token"] . "-" . uniqid($configurationId . "-") . "/";
                 if (!file_exists($tmpDir)) {
                     mkdir($tmpDir);
                 }
                 if (!is_dir($tmpDir)) {
                     throw new ApplicationException("Temporary directory path ({$tmpDir}) is not a directory", null, null, "TMP_DIR");
                 }
                 $fbImport->tmpDir = $tmpDir;
                 $fbImport->import($since, $until, $accountsOffset, $accountsLimit, $accountsIds);
                 $duration = \NDebugger::timer('configuration');
                 $fbImport->log("Extraction of configuration {$configurationId} ended", array(), $duration);
                 // Cleanup
                 exec("rm -rf {$tmpDir}");
                 $fbImport->log("Extraction of row {$configurationId} finished", array(), $duration);
             } catch (InvalidTokenException $e) {
                 throw new UserException("Invalid account {$e->getAccount()} or token for this account: " . $e->getMessage(), $e);
             } catch (UserException $e) {
                 throw $e;
             } catch (\Exception $e) {
                 throw new ApplicationException($e->getMessage(), $e);
             }
         }
     }
     if (!$passed) {
         throw new UserException("ConfigurationId {$jsonParams["configurationId"]} not found");
     }
     $response = array("status" => "ok");
     return $response;
 }
 /**
  * Run import
  * @param string $since
  * @param string $until
  * @param int $accountsOffset
  * @param int $accountsCount
  * @param array $accountsIds
  * @throws Exception
  */
 public function import($since = '-14 days', $until = 'today', $accountsOffset = 0, $accountsCount = 0, $accountsIds = array())
 {
     $this->_fbApi->runId = $this->runId . '-' . $this->configurationId;
     //@TODO DEBUG
     $this->_populateSapiTableCache();
     $defaultDate = new \DateTime("now", new \DateTimeZone(self::$TIME_ZONE));
     $this->defaultEndTime = $defaultDate->format(\DateTime::ATOM);
     $this->defaultEndDate = $defaultDate->format('Y-m-d\\T00:00:00P');
     $this->defaultInsightsDate = $defaultDate->sub(\DateInterval::createFromDateString("1 days"))->format('Y-m-d\\T00:00:00P');
     $this->tmpDir = sprintf('%s/%s-%s', "/tmp/ex-fb", date('Ymd-His'), uniqid());
     mkdir($this->tmpDir, 0777, true);
     if (!$this->storageApi->tableExists($this->bucket . '.' . self::ACCOUNTS_TABLE_ID)) {
         $this->log('Accounts table does not exist in configuration', array(), 0, true);
         return;
     }
     $accountsCsv = $this->storageApi->exportTable($this->bucket . '.' . self::ACCOUNTS_TABLE_ID);
     $accounts = \Keboola\StorageApi\Client::parseCsv($accountsCsv);
     if (!count($accounts)) {
         $this->log('No accounts in configuration table', array(), 0, true);
     }
     // Create files for each configuration row
     $this->_prepareCsvFiles();
     // Iterate through each configuration row
     foreach ($this->runConfig as $queryNumber => $query) {
         $this->currentConfigRowNumber = $queryNumber + 1;
         $accountsCounter = 0;
         foreach ($accounts as $account) {
             if (!isset($account["valid"]) || !$account['valid']) {
                 continue 1;
             }
             // Run for the account only if specified or offset and count are matching
             if (!(count($accountsIds) && in_array($account['id'], $accountsIds) || !count($accountsIds) && $accountsCounter >= $accountsOffset && ($accountsCount == 0 || $accountsCounter < $accountsOffset + $accountsCount))) {
                 $accountsCounter++;
                 continue 1;
             }
             $accountsCounter++;
             if (!isset($account['id']) || !isset($account['token'])) {
                 $this->log('Wrong configuration of accounts table', array(), 0, true);
                 continue 1;
             }
             \NDebugger::timer('account');
             $this->_parseQuery($account, $query, $since, $until, $this->_csvFiles[$queryNumber]["handle"]);
         }
         $this->_uploadCsvFile($queryNumber, $query->table);
         $this->log("Extraction of query {$queryNumber} finished", array('queryNumber' => $query), \NDebugger::timer('account'), false);
     }
 }