chrisbryan17's picture
Upload folder using huggingface_hub
d2897cd verified
<?php
namespace Mautic\LeadBundle\Entity;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Mautic\ApiBundle\Serializer\Driver\ApiMetadataDriver;
use Mautic\CoreBundle\Doctrine\Mapping\ClassMetadataBuilder;
use Mautic\CoreBundle\Entity\FormEntity;
use Mautic\CoreBundle\Helper\Chart\PieChart;
use Mautic\CoreBundle\Translation\Translator;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;
class Import extends FormEntity
{
/** ===== Statuses: ===== */
/**
* When the import entity is created for background processing.
*/
public const QUEUED = 1;
/**
* When the background process started the import.
*/
public const IN_PROGRESS = 2;
/**
* When the import is finished.
*/
public const IMPORTED = 3;
/**
* When the import process failed.
*/
public const FAILED = 4;
/**
* When the import has been stopped by a user.
*/
public const STOPPED = 5;
/**
* When the import happens in the browser.
*/
public const MANUAL = 6;
/**
* When the import is scheduled for later processing.
*/
public const DELAYED = 7;
/**
* ===== Priorities: =====.
*/
public const LOW = 512;
public const NORMAL = 64;
public const HIGH = 1;
/**
* @var int
*/
private $id;
/**
* Base directory of the import.
*
* @var string
*/
private $dir;
/**
* File name of the CSV file which is in the $dir.
*
* @var string
*/
private $file = 'import.csv';
/**
* Name of the original uploaded file.
*
* @var string|null
*/
private $originalFile;
/**
* Tolal line count of the CSV file.
*
* @var int
*/
private $lineCount = 0;
/**
* Count of entities which were newly created.
*
* @var int
*/
private $insertedCount = 0;
/**
* Count of entities which were updated.
*
* @var int
*/
private $updatedCount = 0;
/**
* Count of ignored items.
*
* @var int
*/
private $ignoredCount = 0;
/**
* @var int
*/
private $priority;
/**
* @var int
*/
private $status;
/**
* @var \DateTimeInterface
*/
private $dateStarted;
/**
* @var \DateTimeInterface
*/
private $dateEnded;
/**
* @var string
*/
private $object = 'lead';
/**
* @var array<mixed>|null
*/
private $properties = [];
public function __clone()
{
$this->id = null;
parent::__clone();
}
public function __construct()
{
$this->status = self::QUEUED;
$this->priority = self::LOW;
}
public static function loadMetadata(ORM\ClassMetadata $metadata): void
{
$builder = new ClassMetadataBuilder($metadata);
$builder->setTable('imports')
->setCustomRepositoryClass(ImportRepository::class)
->addIndex(['object'], 'import_object')
->addIndex(['status'], 'import_status')
->addIndex(['priority'], 'import_priority')
->addId()
->addField('dir', Types::STRING)
->addField('file', Types::STRING)
->addNullableField('originalFile', Types::STRING, 'original_file')
->addNamedField('lineCount', Types::INTEGER, 'line_count')
->addNamedField('insertedCount', Types::INTEGER, 'inserted_count')
->addNamedField('updatedCount', Types::INTEGER, 'updated_count')
->addNamedField('ignoredCount', Types::INTEGER, 'ignored_count')
->addField('priority', Types::INTEGER)
->addField('status', Types::INTEGER)
->addNullableField('dateStarted', Types::DATETIME_MUTABLE, 'date_started')
->addNullableField('dateEnded', Types::DATETIME_MUTABLE, 'date_ended')
->addField('object', Types::STRING)
->addNullableField('properties', Types::JSON);
}
public static function loadValidatorMetadata(ClassMetadata $metadata): void
{
$metadata->addPropertyConstraint('dir', new Assert\NotBlank(
['message' => 'mautic.lead.import.dir.notblank']
));
$metadata->addPropertyConstraint('file', new Assert\NotBlank(
['message' => 'mautic.lead.import.file.notblank']
));
}
/**
* Prepares the metadata for API usage.
*/
public static function loadApiMetadata(ApiMetadataDriver $metadata): void
{
$metadata->setGroupPrefix('import')
->addListProperties(
[
'id',
'dir',
'file',
'originalFile',
'lineCount',
'insertedCount',
'updatedCount',
'ignoredCount',
'priority',
'status',
'dateStarted',
'dateEnded',
'object',
'properties',
]
)
->build();
}
/**
* Checks if the import has everything needed to proceed.
*/
public function canProceed(): bool
{
if (!in_array($this->getStatus(), [self::QUEUED, self::DELAYED])) {
$this->setStatusInfo('Import could not be triggered since it is not queued nor delayed');
return false;
}
if (false === file_exists($this->getFilePath()) || false === is_readable($this->getFilePath())) {
$this->setStatus(self::FAILED);
$this->setStatusInfo($this->getFile().' not found');
return false;
}
return true;
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Decides if this import entity is triggered as the background
* job or as UI process.
*/
public function isBackgroundProcess(): bool
{
return !(self::MANUAL === $this->getStatus());
}
/**
* @param string $dir
*
* @return Import
*/
public function setDir($dir)
{
$this->isChanged('dir', $dir);
$this->dir = $dir;
return $this;
}
/**
* @return string
*/
public function getDir()
{
return $this->dir;
}
/**
* @param string $file
*
* @return Import
*/
public function setFile($file)
{
$this->isChanged('file', $file);
$this->file = $file;
return $this;
}
/**
* @return string
*/
public function getFile()
{
return $this->file;
}
/**
* Get import file path.
*/
public function getFilePath(): string
{
return $this->getDir().'/'.$this->getFile();
}
/**
* Set import file path.
*
* @param string $path
*
* @return Import
*/
public function setFilePath($path)
{
$fileName = basename($path);
$dir = substr($path, 0, -1 * (strlen($fileName) + 1));
$this->setDir($dir);
$this->setFile($fileName);
return $this;
}
/**
* Removes the file if exists.
* It won't throw any exception if the file is not readable.
* Not removing the CSV file is not considered a big trouble.
* It will be removed on the next cache:clear.
*/
public function removeFile(): void
{
$file = $this->getFilePath();
if (file_exists($file) && is_writable($file)) {
unlink($file);
}
}
/**
* @param string $originalFile
*
* @return Import
*/
public function setOriginalFile($originalFile)
{
$this->isChanged('originalFile', $originalFile);
$this->originalFile = $originalFile;
return $this;
}
/**
* @return string
*/
public function getOriginalFile()
{
return $this->originalFile;
}
/**
* getName method is used by standard templates so there it is for this entity.
*
* @return string
*/
public function getName()
{
return $this->getOriginalFile() ?: $this->getId();
}
/**
* @param int $lineCount
*
* @return Import
*/
public function setLineCount($lineCount)
{
$this->isChanged('lineCount', $lineCount);
$this->lineCount = $lineCount;
return $this;
}
/**
* @return int
*/
public function getLineCount()
{
return $this->lineCount;
}
/**
* @param int $insertedCount
*
* @return Import
*/
public function setInsertedCount($insertedCount)
{
$this->isChanged('insertedCount', $insertedCount);
$this->insertedCount = $insertedCount;
return $this;
}
/**
* @return Import
*/
public function increaseInsertedCount()
{
return $this->setInsertedCount($this->insertedCount + 1);
}
/**
* @return int
*/
public function getInsertedCount()
{
return $this->insertedCount;
}
/**
* @param int $updatedCount
*
* @return Import
*/
public function setUpdatedCount($updatedCount)
{
$this->isChanged('updatedCount', $updatedCount);
$this->updatedCount = $updatedCount;
return $this;
}
/**
* @return Import
*/
public function increaseUpdatedCount()
{
return $this->setUpdatedCount($this->updatedCount + 1);
}
/**
* @return int
*/
public function getUpdatedCount()
{
return $this->updatedCount;
}
/**
* @param int $ignoredCount
*
* @return Import
*/
public function setIgnoredCount($ignoredCount)
{
$this->isChanged('ignoredCount', $ignoredCount);
$this->ignoredCount = $ignoredCount;
return $this;
}
/**
* @return Import
*/
public function increaseIgnoredCount()
{
return $this->setIgnoredCount($this->ignoredCount + 1);
}
/**
* @return int
*/
public function getIgnoredCount()
{
return $this->ignoredCount;
}
/**
* Counts how many rows have been processed so far.
*
* @return int
*/
public function getProcessedRows()
{
return $this->getInsertedCount() + $this->getUpdatedCount() + $this->getIgnoredCount();
}
/**
* Counts current progress percentage.
*/
public function getProgressPercentage(): float|int
{
$processed = $this->getProcessedRows();
if ($processed && $total = $this->getLineCount()) {
return round(($processed / $total) * 100, 2);
}
return 0;
}
/**
* @param int $priority
*
* @return Import
*/
public function setPriority($priority)
{
$this->isChanged('priority', $priority);
$this->priority = $priority;
return $this;
}
/**
* @return int
*/
public function getPriority()
{
return $this->priority;
}
/**
* @param int $status
*
* @return Import
*/
public function setStatus($status)
{
$this->isChanged('status', $status);
$this->status = $status;
return $this;
}
/**
* @return int
*/
public function getStatus()
{
return $this->status;
}
/**
* Returns Twitter Bootstrap label class based on current status.
*/
public function getSatusLabelClass(): string
{
return match ($this->status) {
self::QUEUED => 'info',
self::IN_PROGRESS, self::MANUAL => 'primary',
self::IMPORTED => 'success',
self::FAILED => 'danger',
self::STOPPED, self::DELAYED => 'warning',
default => 'default',
};
}
/**
* @param int $dateStarted
*
* @return Import
*/
public function setDateStarted(\DateTime $dateStarted)
{
$this->isChanged('dateStarted', $dateStarted);
$this->dateStarted = $dateStarted;
return $this;
}
/**
* @return \DateTimeInterface
*/
public function getDateStarted()
{
return $this->dateStarted;
}
/**
* Modify the entity for the start of import.
*
* @return Import
*/
public function start()
{
if (empty($this->getDateStarted())) {
$this->setDateStarted(new \DateTime());
}
$this->setStatus(self::IN_PROGRESS);
return $this;
}
/**
* Modify the entity for the end of import.
*
* @return Import
*/
public function end($removeFile = true)
{
$this->setDateEnded(new \DateTime());
if (self::IN_PROGRESS === $this->getStatus()) {
$this->setStatus(self::IMPORTED);
if ($removeFile) {
$this->removeFile();
}
}
return $this;
}
/**
* @param int $dateEnded
*
* @return Import
*/
public function setDateEnded(\DateTime $dateEnded)
{
$this->isChanged('dateEnded', $dateEnded);
$this->dateEnded = $dateEnded;
return $this;
}
/**
* @return \DateTimeInterface
*/
public function getDateEnded()
{
return $this->dateEnded;
}
/**
* Counts how long the import has run so far.
*
* @return \DateInterval|null
*/
public function getRunTime()
{
$startTime = $this->getDateStarted();
$endTime = $this->getDateEnded();
if (!$endTime && self::IN_PROGRESS === $this->getStatus()) {
$endTime = $this->getDateModified();
}
if ($startTime instanceof \DateTime && $endTime instanceof \DateTime) {
return $endTime->diff($startTime);
}
return null;
}
/**
* Returns run time in seconds.
*
* @return int
*/
public function getRunTimeSeconds()
{
$startTime = $this->getDateStarted();
$endTime = $this->getDateEnded();
if (!$endTime && self::IN_PROGRESS === $this->getStatus()) {
$endTime = $this->getDateModified();
}
if ($startTime instanceof \DateTime && $endTime instanceof \DateTime) {
return $endTime->format('U') - $startTime->format('U');
}
return 0;
}
/**
* Counts speed in items per second.
*
* @return float
*/
public function getSpeed()
{
$runtime = $this->getRunTimeSeconds();
$processedRows = $this->getProcessedRows();
if ($runtime && $processedRows) {
return round($processedRows / $runtime, 2);
}
return $processedRows;
}
/**
* @param string $object
*
* @return Import
*/
public function setObject($object)
{
$this->isChanged('object', $object);
$this->object = $object;
return $this;
}
/**
* @return string
*/
public function getObject()
{
return $this->object;
}
/**
* @return Import
*/
public function setMatchedFields(array $fields)
{
$properties = $this->properties;
$properties['fields'] = $fields;
return $this->setProperties($properties);
}
public function setLastLineImported($line): void
{
$this->properties['line'] = (int) $line;
}
/**
* @return int
*/
public function getLastLineImported()
{
return $this->properties['line'] ?? 0;
}
/**
* @return array
*/
public function getMatchedFields()
{
return empty($this->properties['fields']) ? [] : $this->properties['fields'];
}
/**
* @param array $properties
*
* @return Import
*/
public function setProperties($properties)
{
$this->isChanged('properties', $properties);
$this->properties = $properties;
return $this;
}
/**
* @param array<mixed> $properties
*
* @return Import
*/
public function mergeToProperties($properties)
{
return $this->setProperties(array_merge($this->properties, $properties));
}
/**
* Get array of default values.
*
* @return array
*/
public function getDefaults()
{
return $this->properties['defaults'] ?? [];
}
/**
* Set a default value to the defaults array.
*
* @param string $key
* @param mixed $value
*
* @return Import
*/
public function setDefault($key, $value)
{
return $this->mergeToProperties([
'defaults' => array_merge($this->getDefaults(), [$key => $value]),
]);
}
/**
* @param string $key
*
* @return string|null
*/
public function getDefault($key)
{
return empty($this->properties['defaults'][$key]) ? null : $this->properties['defaults'][$key];
}
/**
* Set headers array to the properties.
*
* @return Import
*/
public function setHeaders(array $headers)
{
$properties = $this->properties;
$properties['headers'] = $headers;
return $this->setProperties($properties);
}
/**
* @return array
*/
public function getHeaders()
{
return empty($this->properties['headers']) ? [] : $this->properties['headers'];
}
/**
* Set parser config array to the properties.
*
* @return Import
*/
public function setParserConfig(array $parser)
{
$properties = $this->properties;
$properties['parser'] = $parser;
return $this->setProperties($properties);
}
/**
* @return array
*/
public function getParserConfig()
{
return empty($this->properties['parser']) ? [] : $this->properties['parser'];
}
/**
* @return array
*/
public function getProperties()
{
return $this->properties;
}
/**
* @return string
*/
public function getStatusInfo()
{
return empty($this->properties['status_info']) ? 'unknown' : $this->properties['status_info'];
}
/**
* @param string $info
*
* @return Import
*/
public function setStatusInfo($info)
{
$properties = $this->properties;
$properties['status_info'] = $info;
return $this->setProperties($properties);
}
/**
* Overwrite this method so we could change import status based on it.
*
* @param bool $isPublished
*
* @return $this
*/
public function setIsPublished($isPublished)
{
if ($isPublished && self::STOPPED === $this->getStatus()) {
$this->setStatus(self::QUEUED);
}
if (!$isPublished && (self::IN_PROGRESS === $this->getStatus() || self::QUEUED === $this->getStatus())) {
$this->setStatus(self::STOPPED);
}
return parent::setIsPublished($isPublished);
}
/**
* Get pie graph data for row status counts.
*
* @return array{labels: mixed[], datasets: mixed[]}
*/
public function getRowStatusesPieChart(Translator $translator): array
{
$chart = new PieChart();
$chart->setDataset($translator->trans('mautic.lead.import.inserted.count'), $this->getInsertedCount());
$chart->setDataset($translator->trans('mautic.lead.import.updated.count'), $this->getUpdatedCount());
$chart->setDataset($translator->trans('mautic.lead.import.ignored.count'), $this->getIgnoredCount());
return $chart->render();
}
}