<?php
namespace MedBrief\MSR\Entity;
use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection as DoctrineCollection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use MedBrief\MSR\Repository\DiscRepository;
use MedBrief\MSR\Service\ProjectClosure\RemoveRecords\RemovableRecordInterface;
use MedBrief\MSR\Service\VirusScan\Async\VirusScannableInterface;
use MedBrief\MSR\Service\VirusScan\Async\Visitor\VirusScannerPathVisitorInterface;
use MedBrief\MSR\Service\VirusScan\Async\Visitor\VirusScannerVisitorInterface;
use Symfony\Component\HttpFoundation\File\File;
/**
* Disc
*
* @ORM\Table(name="Disc", indexes={@ORM\Index(name="status_index", columns={"status"})})
*
* @ORM\Entity(repositoryClass=DiscRepository::class)
*
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
*
* @Audit\Auditable
*
* @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
*/
class Disc implements RemovableRecordInterface, VirusScannableInterface
{
public const STATUS_PENDING_UPLOAD = 1;
public const STATUS_PENDING_PROCESSING = 2;
public const STATUS_ACTIVE = 3;
public const STATUS_PENDING_ARCHIVE = 12;
public const STATUS_ARCHIVED = 14;
public const STATUS_ARCHIVED_FAILED = 15;
public const STATUS_FAILED_VIRUS = 17;
// all the processing statuses
public const STATUS_PROCESSING_FAILED = 4;
public const STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED = 16;
public const STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT = 5;
public const STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE = 6;
public const STATUS_PROCESSING_CURRENT_IMPORT_DICOM = 10;
public const STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE = 11;
public const STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC = 7;
public const STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE = 8;
public const STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE = 9;
public const STATUS_PROCESSING_CURRENT_ARCHIVING = 13;
public const STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE = 100;
public const STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE = 101;
public const STATUS_MODERN_PROCESSING_IMPORT_DICOM = 110;
public const STATUS_MODERN_PROCESSING_IMPORT_COMPLETE = 111;
public const STATUS_MODERN_PROCESSING_SYNC_STRUCTURE = 120;
public const STATUS_MODERN_PROCESSING_SYNC_COMPLETE = 121;
public const STATUS_MODERN_PROCESSING_CLEANUP = 130;
public const STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE = 131;
public const STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN = 1;
public const STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN__LABEL = 'An Unknown Error Occurred';
public const STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID = 2;
public const STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID__LABEL = 'Incorrect Password Provided';
public const STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES = 3;
public const STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES__LABEL = 'Archive Contains No DICOMs';
public const STATUS_REASON_CODE_ORTHANC_UNAVAILABLE = 4;
public const STATUS_REASON_CODE_ORTHANC_UNAVAILABLE__LABEL = 'Orthanc Unavailable During Processing';
public const STATUS_REASON_CODE_EXCEPTION_THROWN = 5;
public const STATUS_REASON_CODE_EXCEPTION_THROWN__LABEL = 'An Exception Occurred During Processing';
public const STATUS_REASON_CODE_ARCHIVE_NONEXISTENT = 6;
public const STATUS_REASON_CODE_ARCHIVE_NONEXISTENT__LABEL = 'Could Not Find an Archive to Process';
// Set this to 100 so we can add the previous failure's code to it and sneakily store both
public const STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED = 100;
public const STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED__LABEL = 'Exceeded The Maximum Number of Attempts';
//All delete statuses
public const DELETE_STATUS_PENDING = 'pending';
public const DELETE_STATUS_IN_PROGRESS = 'in_progress';
public const DELETE_STATUS_FAILED = 'failed';
public const DELETE_STATUS__LABEL = 'Deletion in progress';
// Valid values for processingPriority
public const PROCESSING_PRIORITY_HIGH = 1;
public const PROCESSING_PRIORITY_LOW = 0;
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
*
* @ORM\Id
*
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @var \DateTime|null
*
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
*/
protected $deletedAt;
/**
* @var string
*
* @ORM\Column(name="name", type="string", nullable=false)
*/
protected $name;
/**
* @var \DateTime|null
*
* @ORM\Column(name="date_received", type="datetime", nullable=true)
*/
protected $date_received;
/**
* @var \DateTime
*
* @ORM\Column(name="created", type="datetime")
*
* @Gedmo\Timestampable(on="create")
*/
protected $created;
/**
* @var \DateTime
*
* @ORM\Column(name="updated", type="datetime")
*
* @Gedmo\Timestampable(on="update")
*/
protected $updated;
/**
* @var Project
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="discs")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="project_id", referencedColumnName="id", nullable=false)
* })
*/
protected $project;
/**
* @var string
*
* @ORM\Column(name="archiveFilesize", type="string", nullable=true)
*/
protected $archiveFilesize;
/**
* Indicates the processing priority of the Disc. Currently, this is limited to 0 or 1, with
* 1 indicating a priority Disc. But, in future we may implement fine grain control using this field.
*
* @var int
*
* @ORM\Column(name="processingPriority", type="integer", options={"default"="0"})
*/
protected $processingPriority;
/**
* @var BatchRequest
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\BatchRequest", inversedBy="discs")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="batchRequest_id", referencedColumnName="id", nullable=true)
* })
*/
protected $batchRequest;
/**
* @var int
*
* @ORM\Column(name="status", type="integer", nullable=false)
*/
protected $status;
/**
* @var int|null
*
* @ORM\Column(name="status_reason_code", type="integer", nullable=true)
*/
protected $status_reason_code;
/**
* @ORM\Column(type="integer", nullable=true)
*/
protected ?int $preMigrationStatus;
/**
* @var string|null
*
* @ORM\Column(name="archiveName", type="string", length=255, nullable=true)
*/
protected $archiveName;
/**
* @var string
*/
protected $archiveFile;
/**
* @var string|null
*
* @ORM\Column(name="archiveOriginalName", type="string", length=255, nullable=true)
*/
protected $archiveOriginalName;
/**
* @var Collection
*
* @ORM\OneToOne(targetEntity="MedBrief\MSR\Entity\Collection", inversedBy="disc", cascade={"all"})
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="collection_id", referencedColumnName="id", unique=true)
* })
*/
protected $collection;
/**
* @var string|null
*
* @ORM\Column(name="source", type="string", nullable=true)
*/
protected $source;
/**
* @var string|null
*
* @ORM\Column(name="password", type="string", length=255, nullable=true)
*/
protected $password;
/**
* @var bool|null
*
* @ORM\Column(name="copy_created", type="boolean", nullable=true)
*/
protected $copy_created;
/**
* @var bool|null
*
* @ORM\Column(name="is_password_protected", type="boolean", nullable=true)
*/
protected $is_password_protected;
/**
* @var User
*
* @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\User", inversedBy="discs")
*
* @ORM\JoinColumns({
*
* @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=false)
* })
*/
protected $creator;
/**
* @var DoctrineCollection
*
* @todo We've taken out the remove cascade here as causes havoc with removing this entity. We'll create
* a separate clean-up script for this (See https://medbrief.atlassian.net/browse/MSR-2980)
*
* @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\DiscImportSession", mappedBy="disc", cascade={"persist"})
*/
protected $discImportSessions;
/**
* @var DoctrineCollection
*
* @ORM\ManyToMany(targetEntity="MedBrief\MSR\Entity\Study", mappedBy="discs")
*/
protected $studies;
/**
* @var string
*
* @ORM\Column(type="string", length=255, nullable=true)
*/
protected $deleteStatus;
/**
* @ORM\OneToOne(targetEntity=DiscMetadata::class, mappedBy="disc", cascade={"persist", "remove"})
*
* @ORM\JoinColumn(nullable=true)
*/
private ?DiscMetadata $discMetadata;
/**
* @ORM\ManyToMany(targetEntity=Instance::class, mappedBy="discs")
*/
private $instances;
/**
* Used to indicate if this disc is a duplicate of another disc
*
* @ORM\Column(type="integer", nullable=true)
*/
private $originalDiscId;
/**
* @ORM\OneToOne(targetEntity=VirusScanItem::class, mappedBy="disc")
*/
private ?VirusScanItem $virusScanItem = null;
public function __construct()
{
// default status of discs is 'pending upload'
$this->setStatus(self::STATUS_PENDING_UPLOAD);
// Assign the default value to processingPriority
$this->setProcessingPriorityToDefault();
$this->discImportSessions = new ArrayCollection();
$this->studies = new ArrayCollection();
$this->instances = new ArrayCollection();
}
public function __clone()
{
if ($this->id) {
$this->id = null;
$this->project = null;
$this->discMetadata = null;
$this->deleteStatus = null;
$this->collection = null;
$this->deletedAt = null;
$this->discImportSessions = new ArrayCollection();
$this->studies = new ArrayCollection();
$this->instances = new ArrayCollection();
}
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* @return int
*/
public function getId(): ?int
{
return $this->id;
}
/**
* Set name
*
* @param string $name
*
* @return Disc
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set date_received
*
* @param \DateTime $dateReceived
*
* @return Disc
*/
public function setDateReceived($dateReceived)
{
$this->date_received = $dateReceived;
return $this;
}
/**
* Get date_received
*
* @return \DateTime
*/
public function getDateReceived()
{
return $this->date_received;
}
/**
* Set created
*
* @param \DateTime $created
*
* @return Disc
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated
*
* @param \DateTime $updated
*
* @return Disc
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set project
*
* @param Project $project
*
* @return Disc
*/
public function setProject(Project $project)
{
$this->project = $project;
return $this;
}
/**
* Get project
*
* @return Project
*/
public function getProject()
{
return $this->project;
}
/**
* Set status
*
* @param int $status
*
* @return Disc
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @return int
*/
public function getStatus()
{
return $this->status;
}
/**
* Returns a human readable version of the status
*
* @return string
*/
public function getStatusName()
{
$statusOptions = self::getStatusOptions();
return
$statusOptions[$this->getStatus()] ?? $this->getStatus();
}
/**
* Returns a detailed human readable version of the status
*
* @return string
*/
public function getStatusDetailedName()
{
$statusOptions = self::getStatusOptions();
$detailedStatusOptions = [
// Legacy Statuses
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT => 'Extract and Import',
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE => 'Extract and Import Complete',
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC => 'Sync Collection To Orthanc',
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE => 'Sync Collection To Orthanc Complete',
self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE => 'Sync Patient Structure',
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM => 'Import Dicom',
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE => 'Import Dicom Complete',
// Modern Statuses
self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE => 'Extracting Disc',
self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE => 'Extracted Disc',
self::STATUS_MODERN_PROCESSING_IMPORT_DICOM => 'Importing DICOMs',
self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE => 'Imported DICOMs',
self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE => 'Replicating Patient Structure',
self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE => 'Patient Structure Replicated',
self::STATUS_MODERN_PROCESSING_CLEANUP => 'Cleaning Temporary Files',
self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE => 'Temporary Files Removed',
];
$statusOptions = array_replace_recursive($statusOptions, $detailedStatusOptions);
return
$statusOptions[$this->getStatus()] ?? $this->getStatus();
}
/**
* Returns an array of permitted values for the status field and their
* associated labels,
*
* @return array
*/
public static function getStatusOptions()
{
return [
self::STATUS_ACTIVE => 'Active',
self::STATUS_PENDING_PROCESSING => 'Pending Processing',
self::STATUS_PENDING_UPLOAD => 'Pending Upload',
self::STATUS_PROCESSING_FAILED => 'Processing Failed',
self::STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED => 'Processing Failed',
self::STATUS_PENDING_ARCHIVE => 'Pending Archiving',
self::STATUS_ARCHIVED => 'Archived',
self::STATUS_ARCHIVED_FAILED => 'Archiving Failed',
self::STATUS_FAILED_VIRUS => 'Processing Failed - File Failed Virus Scan',
//all of the below statusses can just be shown as 'processing' for the user
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE => 'Currently Processing',
// Modern Statuses
self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_IMPORT_DICOM => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_CLEANUP => 'Currently Processing',
self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE => 'Currently Processing',
self::STATUS_PROCESSING_CURRENT_ARCHIVING => 'Archiving',
];
}
/**
* Returns true if the disc is in status.
*
* @return array
*/
public static function processingStuckStatusArray()
{
$statuses = [
// Modern Statuses
self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE,
self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE,
self::STATUS_MODERN_PROCESSING_IMPORT_DICOM,
self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE,
self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE,
self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE,
self::STATUS_MODERN_PROCESSING_CLEANUP,
self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE,
];
return $statuses;
}
/**
* Returns true if the disc is in an active status.
*
* @return bool
*/
public function isActive()
{
return $this->getStatus() === self::STATUS_ACTIVE;
}
/**
* Returns true if the disc is in an pending status.
*
* @return bool
*/
public function isPending()
{
$statuses = [
self::STATUS_PENDING_PROCESSING,
self::STATUS_PENDING_UPLOAD,
];
return in_array($this->getStatus(), $statuses);
}
/**
* Returns true if the disc is in an processing status.
*
* @return bool
*/
public function isProcessing()
{
$statuses = [
// Legacy statuses
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT,
self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE,
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM,
self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE,
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC,
self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE,
self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE,
// Modern Statuses
self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE,
self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE,
self::STATUS_MODERN_PROCESSING_IMPORT_DICOM,
self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE,
self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE,
self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE,
self::STATUS_MODERN_PROCESSING_CLEANUP,
self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE,
];
return in_array($this->getStatus(), $statuses);
}
/**
* Returns true if the disc is in an failed status.
*
* @param array $excludes - optional array of failed status names
*
* @return bool
*/
public function isFailed(array $excludes = [])
{
$statuses = [
self::STATUS_PROCESSING_FAILED,
self::STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED,
self::STATUS_FAILED_VIRUS,
];
if (empty($excludes)) {
return in_array($this->getStatus(), $statuses);
}
return in_array($this->getStatus(), array_diff($statuses, $excludes));
}
/**
* Set archiveName
*
* @param string $archiveName
*
* @return Disc
*/
public function setArchiveName($archiveName)
{
$this->archiveName = $archiveName;
return $this;
}
/**
* Get archiveName
*
* @return string
*/
public function getArchiveName()
{
return $this->archiveName;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $archiveFile
* @param string
*
* @return Disc
*/
public function setArchiveFile($archiveFile)
{
$this->archiveFile = $archiveFile;
if ($archiveFile) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
// NOTE: We can't actually do this because the this set method gets called when the entity
// is populated from the database, therefore the update value will always be wrong
//$this->updated = new \DateTime('now');
}
return $this;
}
/**
* Get archiveFile
*
* @return string
*/
public function getArchiveFile()
{
return $this->archiveFile;
}
/**
* Set archiveOriginalName
*
* @param string $archiveOriginalName
*
* @return Disc
*/
public function setArchiveOriginalName($archiveOriginalName)
{
$this->archiveOriginalName = $archiveOriginalName;
return $this;
}
/**
* Get archiveOriginalName
*
* @return string
*/
public function getArchiveOriginalName()
{
return $this->archiveOriginalName;
}
/**
* Set collection
*
* @param Collection|null $collection
*
* @return Disc
*/
public function setCollection(?Collection $collection = null)
{
$this->collection = $collection;
return $this;
}
/**
* Get collection
*
* @return Collection
*/
public function getCollection()
{
return $this->collection;
}
/**
* Set source
*
* @param string $source
*
* @return Disc
*/
public function setSource($source)
{
$this->source = $source;
return $this;
}
/**
* Get source
*
* @return string
*/
public function getSource()
{
return $this->source;
}
/**
* Set password
*
* @param string $password
*
* @return Disc
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set copy_created
*
* @param bool $copyCreated
*
* @return Disc
*/
public function setCopyCreated($copyCreated)
{
$this->copy_created = $copyCreated;
return $this;
}
/**
* Get copy_created
*
* @return bool
*/
public function getCopyCreated()
{
return $this->copy_created;
}
/**
* Set is_password_protected
*
* @param bool $isPasswordProtected
*
* @return Disc
*/
public function setIsPasswordProtected($isPasswordProtected)
{
$this->is_password_protected = $isPasswordProtected;
return $this;
}
/**
* Get is_password_protected
*
* @return bool
*/
public function getIsPasswordProtected()
{
return $this->is_password_protected;
}
/**
* Set status_reason_code
*
* @param int|null $statusReasonCode
*
* @return Disc
*/
public function setStatusReasonCode(?int $statusReasonCode)
{
$this->status_reason_code = $statusReasonCode;
return $this;
}
/**
* Get status_reason_code
*
* @return int
*/
public function getStatusReasonCode()
{
return $this->status_reason_code;
}
/**
* Set deletedAt
*
* @param \DateTime $deletedAt
*
* @return Disc
*/
public function setDeletedAt($deletedAt)
{
$this->deletedAt = $deletedAt;
return $this;
}
/**
* Get deletedAt
*
* @return \DateTime
*/
public function getDeletedAt()
{
return $this->deletedAt;
}
/**
* Set creator
*
* @param User $creator
*
* @return Disc
*/
public function setCreator(User $creator)
{
$this->creator = $creator;
return $this;
}
/**
* Get creator
*
* @return User
*/
public function getCreator()
{
return $this->creator;
}
/**
* Add discImportSession
*
* @param DiscImportSession $discImportSession
*
* @return Disc
*/
public function addDiscImportSession(DiscImportSession $discImportSession)
{
$this->discImportSessions[] = $discImportSession;
return $this;
}
/**
* Remove discImportSession
*
* @param DiscImportSession $discImportSession
*/
public function removeDiscImportSession(DiscImportSession $discImportSession)
{
$this->discImportSessions->removeElement($discImportSession);
}
/**
* Get discImportSessions
*
* @return DoctrineCollection
*/
public function getDiscImportSessions()
{
return $this->discImportSessions;
}
/**
* Add study
*
* @param Study $study
*
* @return Disc
*/
public function addStudy(Study $study)
{
$this->studies[] = $study;
return $this;
}
/**
* Remove study
*
* @param Study $study
*/
public function removeStudy(Study $study)
{
$this->studies->removeElement($study);
}
/**
* Get studies
*
* @return Study[]|DoctrineCollection
*/
public function getStudies()
{
return $this->studies;
}
/**
* Set archiveFilesize
*
* @param string $archiveFilesize
*
* @return Disc
*/
public function setArchiveFilesize($archiveFilesize)
{
$this->archiveFilesize = $archiveFilesize;
return $this;
}
/**
* Get archiveFilesize
*
* @return string
*/
public function getArchiveFilesize()
{
return $this->archiveFilesize;
}
/**
* Set processingPriority
*
* @param int $processingPriority
*
* @return Disc
*/
public function setProcessingPriority($processingPriority)
{
$this->processingPriority = $processingPriority;
return $this;
}
/**
* Get processingPriority
*
* @return int
*/
public function getProcessingPriority()
{
return $this->processingPriority;
}
/**
* Returns true if the Disc has been prioritised,
*
* @return bool
*/
public function isPrioritised()
{
return $this->getProcessingPriority() === self::PROCESSING_PRIORITY_HIGH;
}
/**
* Sets the processing priority of the Disc to the default value.
*
* @return void
*/
public function setProcessingPriorityToDefault()
{
$this->setProcessingPriority(self::PROCESSING_PRIORITY_LOW);
}
/**
* Set batchRequest
*
* @param BatchRequest $batchRequest
*
* @return Disc
*/
public function setBatchRequest(?BatchRequest $batchRequest = null)
{
$this->batchRequest = $batchRequest;
return $this;
}
/**
* Get batchRequest
*
* @return BatchRequest
*/
public function getBatchRequest()
{
return $this->batchRequest;
}
/**
* Check whether Disc is available for download
*
* @return bool
*/
public function isDownloadable()
{
$nonDownloadableStatuses = [
self::STATUS_PENDING_UPLOAD,
self::STATUS_FAILED_VIRUS,
];
return !in_array($this->getStatus(), $nonDownloadableStatuses);
}
/**
* Extracts the Trust name from a Disc's name
*
* @return string
*/
public function getTrustFromName()
{
// A trust is only available if the Disc has a name
if ($this->getName()) {
// We can only assume the name is conventional and properly structured. If it isn't,
// that's not our fault
return preg_match("/^(.+) - Disc \d+/i", $this->getName(), $discName) ? $discName[1] : $this->getName();
}
// A Disc has no name - Arya Stark 2020
return 'Unknown';
}
/**
* Extracts the Disc number from a Disc's name
*
* @return string
*/
public function getDiscNumberFromName()
{
// Disc number is only available if the Disc has a name
if ($this->getName()) {
// We can only assume the name is conventional and properly structured. If it isn't,
// that's not our fault
return preg_match("/^.+ - Disc (\d+)/i", $this->getName(), $discNumber) ? $discNumber[1] : 'Unknown';
}
// A Disc has no name - Jaqen H'ghar 2020
return 'Unknown';
}
/**
* @return null|string
*/
public function getDeleteStatus(): ?string
{
return $this->deleteStatus;
}
/**
* @param string|null $deleteStatus
*
* @return self
*/
public function setDeleteStatus(?string $deleteStatus): self
{
$this->deleteStatus = $deleteStatus;
return $this;
}
/**
* @return DiscMetadata|null
*/
public function getDiscMetadata(): ?DiscMetadata
{
return $this->discMetadata;
}
/**
* @param DiscMetadata $discMetadata
*
* @return $this
*/
public function setDiscMetadata(DiscMetadata $discMetadata): self
{
// set the owning side of the relation if necessary
if ($discMetadata->getDisc() !== $this) {
$discMetadata->setDisc($this);
}
$this->discMetadata = $discMetadata;
return $this;
}
/**
* Determines whether the Disc has associated DiscMetadata (legacy discs will not have any and
* will need to use legacy Orthanc calls to determine some meta)
*
* @return bool
*/
public function hasDiscMetadata(): bool
{
return ($this->discMetadata !== null);
}
/**
* Returns a human-readable string representation of the failure error code. Because of
* custom logic here, we don't use the FilterableClassConstants trait.
*
* @return string
*/
public function getFailureReasonHumanReadable(): string
{
if (($code = $this->getStatusReasonCode()) === null) {
return 'Disc has no failure code';
}
$mapping = [
self::STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN => self::STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN__LABEL,
self::STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID => self::STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID__LABEL,
self::STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES => self::STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES__LABEL,
self::STATUS_REASON_CODE_ORTHANC_UNAVAILABLE => self::STATUS_REASON_CODE_ORTHANC_UNAVAILABLE__LABEL,
self::STATUS_REASON_CODE_EXCEPTION_THROWN => self::STATUS_REASON_CODE_EXCEPTION_THROWN__LABEL,
self::STATUS_REASON_CODE_ARCHIVE_NONEXISTENT => self::STATUS_REASON_CODE_ARCHIVE_NONEXISTENT__LABEL,
];
if ($code > self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED) {
$previousReasonCode = $code - self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED;
return sprintf('%s: %s', self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED__LABEL, $mapping[$previousReasonCode]);
}
return $mapping[$code];
}
/**
* @return DoctrineCollection<int, Instance>
*/
public function getInstances(): DoctrineCollection
{
return $this->instances;
}
/**
* @param Instance $instance
*
* @return $this
*/
public function addInstance(Instance $instance): self
{
if (!$this->instances->contains($instance)) {
$this->instances[] = $instance;
$instance->addDisc($this);
}
return $this;
}
/**
* @param Instance $instance
*
* @return $this
*/
public function removeInstance(Instance $instance): self
{
if ($this->instances->removeElement($instance)) {
$instance->removeDisc($this);
}
return $this;
}
/**
* @return int|null
*/
public function getPreMigrationStatus(): ?int
{
return $this->preMigrationStatus;
}
/**
* @param int|null $preMigrationStatus
*
* @return self
*/
public function setPreMigrationStatus(?int $preMigrationStatus): self
{
$this->preMigrationStatus = $preMigrationStatus;
return $this;
}
/**
* Returns true if the disc has a pre-migration status and that status does not match its current status.
* Active are assumed successfully migrated - as if the disc failed prior to migration, and succeeded with migration,
* we consider it a success.
*
* @return bool
*/
public function hasDiscFailedMigration(): bool
{
return !$this->isActive() && $this->getPreMigrationStatus() !== null && $this->getStatus() !== $this->getPreMigrationStatus();
}
/**
* @return int|null
*/
public function getOriginalDiscId(): ?int
{
return $this->originalDiscId;
}
/**
* @param int|null $originalDiscId
*
* @return self
*/
public function setOriginalDiscId(?int $originalDiscId): self
{
$this->originalDiscId = $originalDiscId;
return $this;
}
/**
* Check whether Disc is archived
*
* @return bool
*/
public function isArchived()
{
$archivedStatuses = [
self::STATUS_PENDING_ARCHIVE,
self::STATUS_ARCHIVED,
self::STATUS_PROCESSING_CURRENT_ARCHIVING,
self::STATUS_ARCHIVED_FAILED,
];
return in_array($this->getStatus(), $archivedStatuses);
}
/**
* Returns a human readable version of the deleted statuses
*
* @return null|string
*/
public function getDeletedStatusLabel(): ?string
{
return self::DELETE_STATUS__LABEL;
}
/**
* @return VirusScanItem|null
*/
public function getVirusScanItem(): ?VirusScanItem
{
return $this->virusScanItem;
}
/**
* @param VirusScanItem|null $virusScanItem
*
* @return self
*/
public function setVirusScanItem(?VirusScanItem $virusScanItem): self
{
// unset the owning side of the relation if necessary
if ($virusScanItem === null && $this->virusScanItem !== null) {
$this->virusScanItem->setDisc(null);
}
// set the owning side of the relation if necessary
if ($virusScanItem !== null && $virusScanItem->getDisc() !== $this) {
$virusScanItem->setDisc($this);
}
$this->virusScanItem = $virusScanItem;
return $this;
}
/**
* @param VirusScannerPathVisitorInterface $virusScannerPathVisitor
*
* @return VirusScannerVisitorInterface
*/
public function acceptVirusScannerPathVisitor(VirusScannerPathVisitorInterface $virusScannerPathVisitor): string
{
return $virusScannerPathVisitor->visitDisc($this);
}
}