src/Entity/Disc.php line 30

Open in your IDE?
  1. <?php
  2. namespace MedBrief\MSR\Entity;
  3. use DH\Auditor\Provider\Doctrine\Auditing\Annotation as Audit;
  4. use Doctrine\Common\Collections\ArrayCollection;
  5. use Doctrine\Common\Collections\Collection as DoctrineCollection;
  6. use Doctrine\ORM\Mapping as ORM;
  7. use Gedmo\Mapping\Annotation as Gedmo;
  8. use MedBrief\MSR\Repository\DiscRepository;
  9. use MedBrief\MSR\Service\ProjectClosure\RemoveRecords\RemovableRecordInterface;
  10. use MedBrief\MSR\Service\VirusScan\Async\VirusScannableInterface;
  11. use MedBrief\MSR\Service\VirusScan\Async\Visitor\VirusScannerPathVisitorInterface;
  12. use MedBrief\MSR\Service\VirusScan\Async\Visitor\VirusScannerVisitorInterface;
  13. use Symfony\Component\HttpFoundation\File\File;
  14. /**
  15. * Disc
  16. *
  17. * @ORM\Table(name="Disc", indexes={@ORM\Index(name="status_index", columns={"status"})})
  18. *
  19. * @ORM\Entity(repositoryClass=DiscRepository::class)
  20. *
  21. * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
  22. *
  23. * @Audit\Auditable
  24. *
  25. * @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
  26. */
  27. class Disc implements RemovableRecordInterface, VirusScannableInterface
  28. {
  29. public const STATUS_PENDING_UPLOAD = 1;
  30. public const STATUS_PENDING_PROCESSING = 2;
  31. public const STATUS_ACTIVE = 3;
  32. public const STATUS_PENDING_ARCHIVE = 12;
  33. public const STATUS_ARCHIVED = 14;
  34. public const STATUS_ARCHIVED_FAILED = 15;
  35. public const STATUS_FAILED_VIRUS = 17;
  36. // all the processing statuses
  37. public const STATUS_PROCESSING_FAILED = 4;
  38. public const STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED = 16;
  39. public const STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT = 5;
  40. public const STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE = 6;
  41. public const STATUS_PROCESSING_CURRENT_IMPORT_DICOM = 10;
  42. public const STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE = 11;
  43. public const STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC = 7;
  44. public const STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE = 8;
  45. public const STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE = 9;
  46. public const STATUS_PROCESSING_CURRENT_ARCHIVING = 13;
  47. public const STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE = 100;
  48. public const STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE = 101;
  49. public const STATUS_MODERN_PROCESSING_IMPORT_DICOM = 110;
  50. public const STATUS_MODERN_PROCESSING_IMPORT_COMPLETE = 111;
  51. public const STATUS_MODERN_PROCESSING_SYNC_STRUCTURE = 120;
  52. public const STATUS_MODERN_PROCESSING_SYNC_COMPLETE = 121;
  53. public const STATUS_MODERN_PROCESSING_CLEANUP = 130;
  54. public const STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE = 131;
  55. public const STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN = 1;
  56. public const STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN__LABEL = 'An Unknown Error Occurred';
  57. public const STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID = 2;
  58. public const STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID__LABEL = 'Incorrect Password Provided';
  59. public const STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES = 3;
  60. public const STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES__LABEL = 'Archive Contains No DICOMs';
  61. public const STATUS_REASON_CODE_ORTHANC_UNAVAILABLE = 4;
  62. public const STATUS_REASON_CODE_ORTHANC_UNAVAILABLE__LABEL = 'Orthanc Unavailable During Processing';
  63. public const STATUS_REASON_CODE_EXCEPTION_THROWN = 5;
  64. public const STATUS_REASON_CODE_EXCEPTION_THROWN__LABEL = 'An Exception Occurred During Processing';
  65. public const STATUS_REASON_CODE_ARCHIVE_NONEXISTENT = 6;
  66. public const STATUS_REASON_CODE_ARCHIVE_NONEXISTENT__LABEL = 'Could Not Find an Archive to Process';
  67. // Set this to 100 so we can add the previous failure's code to it and sneakily store both
  68. public const STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED = 100;
  69. public const STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED__LABEL = 'Exceeded The Maximum Number of Attempts';
  70. //All delete statuses
  71. public const DELETE_STATUS_PENDING = 'pending';
  72. public const DELETE_STATUS_IN_PROGRESS = 'in_progress';
  73. public const DELETE_STATUS_FAILED = 'failed';
  74. public const DELETE_STATUS__LABEL = 'Deletion in progress';
  75. // Valid values for processingPriority
  76. public const PROCESSING_PRIORITY_HIGH = 1;
  77. public const PROCESSING_PRIORITY_LOW = 0;
  78. /**
  79. * @var int
  80. *
  81. * @ORM\Column(name="id", type="integer")
  82. *
  83. * @ORM\Id
  84. *
  85. * @ORM\GeneratedValue(strategy="IDENTITY")
  86. */
  87. protected $id;
  88. /**
  89. * @var \DateTime|null
  90. *
  91. * @ORM\Column(name="deletedAt", type="datetime", nullable=true)
  92. */
  93. protected $deletedAt;
  94. /**
  95. * @var string
  96. *
  97. * @ORM\Column(name="name", type="string", nullable=false)
  98. */
  99. protected $name;
  100. /**
  101. * @var \DateTime|null
  102. *
  103. * @ORM\Column(name="date_received", type="datetime", nullable=true)
  104. */
  105. protected $date_received;
  106. /**
  107. * @var \DateTime
  108. *
  109. * @ORM\Column(name="created", type="datetime")
  110. *
  111. * @Gedmo\Timestampable(on="create")
  112. */
  113. protected $created;
  114. /**
  115. * @var \DateTime
  116. *
  117. * @ORM\Column(name="updated", type="datetime")
  118. *
  119. * @Gedmo\Timestampable(on="update")
  120. */
  121. protected $updated;
  122. /**
  123. * @var Project
  124. *
  125. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="discs")
  126. *
  127. * @ORM\JoinColumns({
  128. *
  129. * @ORM\JoinColumn(name="project_id", referencedColumnName="id", nullable=false)
  130. * })
  131. */
  132. protected $project;
  133. /**
  134. * @var string
  135. *
  136. * @ORM\Column(name="archiveFilesize", type="string", nullable=true)
  137. */
  138. protected $archiveFilesize;
  139. /**
  140. * Indicates the processing priority of the Disc. Currently, this is limited to 0 or 1, with
  141. * 1 indicating a priority Disc. But, in future we may implement fine grain control using this field.
  142. *
  143. * @var int
  144. *
  145. * @ORM\Column(name="processingPriority", type="integer", options={"default"="0"})
  146. */
  147. protected $processingPriority;
  148. /**
  149. * @var BatchRequest
  150. *
  151. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\BatchRequest", inversedBy="discs")
  152. *
  153. * @ORM\JoinColumns({
  154. *
  155. * @ORM\JoinColumn(name="batchRequest_id", referencedColumnName="id", nullable=true)
  156. * })
  157. */
  158. protected $batchRequest;
  159. /**
  160. * @var int
  161. *
  162. * @ORM\Column(name="status", type="integer", nullable=false)
  163. */
  164. protected $status;
  165. /**
  166. * @var int|null
  167. *
  168. * @ORM\Column(name="status_reason_code", type="integer", nullable=true)
  169. */
  170. protected $status_reason_code;
  171. /**
  172. * @ORM\Column(type="integer", nullable=true)
  173. */
  174. protected ?int $preMigrationStatus;
  175. /**
  176. * @var string|null
  177. *
  178. * @ORM\Column(name="archiveName", type="string", length=255, nullable=true)
  179. */
  180. protected $archiveName;
  181. /**
  182. * @var string
  183. */
  184. protected $archiveFile;
  185. /**
  186. * @var string|null
  187. *
  188. * @ORM\Column(name="archiveOriginalName", type="string", length=255, nullable=true)
  189. */
  190. protected $archiveOriginalName;
  191. /**
  192. * @var Collection
  193. *
  194. * @ORM\OneToOne(targetEntity="MedBrief\MSR\Entity\Collection", inversedBy="disc", cascade={"all"})
  195. *
  196. * @ORM\JoinColumns({
  197. *
  198. * @ORM\JoinColumn(name="collection_id", referencedColumnName="id", unique=true)
  199. * })
  200. */
  201. protected $collection;
  202. /**
  203. * @var string|null
  204. *
  205. * @ORM\Column(name="source", type="string", nullable=true)
  206. */
  207. protected $source;
  208. /**
  209. * @var string|null
  210. *
  211. * @ORM\Column(name="password", type="string", length=255, nullable=true)
  212. */
  213. protected $password;
  214. /**
  215. * @var bool|null
  216. *
  217. * @ORM\Column(name="copy_created", type="boolean", nullable=true)
  218. */
  219. protected $copy_created;
  220. /**
  221. * @var bool|null
  222. *
  223. * @ORM\Column(name="is_password_protected", type="boolean", nullable=true)
  224. */
  225. protected $is_password_protected;
  226. /**
  227. * @var User
  228. *
  229. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\User", inversedBy="discs")
  230. *
  231. * @ORM\JoinColumns({
  232. *
  233. * @ORM\JoinColumn(name="creator_id", referencedColumnName="id", nullable=false)
  234. * })
  235. */
  236. protected $creator;
  237. /**
  238. * @var DoctrineCollection
  239. *
  240. * @todo We've taken out the remove cascade here as causes havoc with removing this entity. We'll create
  241. * a separate clean-up script for this (See https://medbrief.atlassian.net/browse/MSR-2980)
  242. *
  243. * @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\DiscImportSession", mappedBy="disc", cascade={"persist"})
  244. */
  245. protected $discImportSessions;
  246. /**
  247. * @var DoctrineCollection
  248. *
  249. * @ORM\ManyToMany(targetEntity="MedBrief\MSR\Entity\Study", mappedBy="discs")
  250. */
  251. protected $studies;
  252. /**
  253. * @var string
  254. *
  255. * @ORM\Column(type="string", length=255, nullable=true)
  256. */
  257. protected $deleteStatus;
  258. /**
  259. * @ORM\OneToOne(targetEntity=DiscMetadata::class, mappedBy="disc", cascade={"persist", "remove"})
  260. *
  261. * @ORM\JoinColumn(nullable=true)
  262. */
  263. private ?DiscMetadata $discMetadata;
  264. /**
  265. * @ORM\ManyToMany(targetEntity=Instance::class, mappedBy="discs")
  266. */
  267. private $instances;
  268. /**
  269. * Used to indicate if this disc is a duplicate of another disc
  270. *
  271. * @ORM\Column(type="integer", nullable=true)
  272. */
  273. private $originalDiscId;
  274. /**
  275. * @ORM\OneToOne(targetEntity=VirusScanItem::class, mappedBy="disc")
  276. */
  277. private ?VirusScanItem $virusScanItem = null;
  278. public function __construct()
  279. {
  280. // default status of discs is 'pending upload'
  281. $this->setStatus(self::STATUS_PENDING_UPLOAD);
  282. // Assign the default value to processingPriority
  283. $this->setProcessingPriorityToDefault();
  284. $this->discImportSessions = new ArrayCollection();
  285. $this->studies = new ArrayCollection();
  286. $this->instances = new ArrayCollection();
  287. }
  288. public function __clone()
  289. {
  290. if ($this->id) {
  291. $this->id = null;
  292. $this->project = null;
  293. $this->discMetadata = null;
  294. $this->deleteStatus = null;
  295. $this->collection = null;
  296. $this->deletedAt = null;
  297. $this->discImportSessions = new ArrayCollection();
  298. $this->studies = new ArrayCollection();
  299. $this->instances = new ArrayCollection();
  300. }
  301. }
  302. public function __toString()
  303. {
  304. return $this->getName();
  305. }
  306. /**
  307. * Get id
  308. *
  309. * @return int
  310. */
  311. public function getId(): ?int
  312. {
  313. return $this->id;
  314. }
  315. /**
  316. * Set name
  317. *
  318. * @param string $name
  319. *
  320. * @return Disc
  321. */
  322. public function setName($name)
  323. {
  324. $this->name = $name;
  325. return $this;
  326. }
  327. /**
  328. * Get name
  329. *
  330. * @return string
  331. */
  332. public function getName()
  333. {
  334. return $this->name;
  335. }
  336. /**
  337. * Set date_received
  338. *
  339. * @param \DateTime $dateReceived
  340. *
  341. * @return Disc
  342. */
  343. public function setDateReceived($dateReceived)
  344. {
  345. $this->date_received = $dateReceived;
  346. return $this;
  347. }
  348. /**
  349. * Get date_received
  350. *
  351. * @return \DateTime
  352. */
  353. public function getDateReceived()
  354. {
  355. return $this->date_received;
  356. }
  357. /**
  358. * Set created
  359. *
  360. * @param \DateTime $created
  361. *
  362. * @return Disc
  363. */
  364. public function setCreated($created)
  365. {
  366. $this->created = $created;
  367. return $this;
  368. }
  369. /**
  370. * Get created
  371. *
  372. * @return \DateTime
  373. */
  374. public function getCreated()
  375. {
  376. return $this->created;
  377. }
  378. /**
  379. * Set updated
  380. *
  381. * @param \DateTime $updated
  382. *
  383. * @return Disc
  384. */
  385. public function setUpdated($updated)
  386. {
  387. $this->updated = $updated;
  388. return $this;
  389. }
  390. /**
  391. * Get updated
  392. *
  393. * @return \DateTime
  394. */
  395. public function getUpdated()
  396. {
  397. return $this->updated;
  398. }
  399. /**
  400. * Set project
  401. *
  402. * @param Project $project
  403. *
  404. * @return Disc
  405. */
  406. public function setProject(Project $project)
  407. {
  408. $this->project = $project;
  409. return $this;
  410. }
  411. /**
  412. * Get project
  413. *
  414. * @return Project
  415. */
  416. public function getProject()
  417. {
  418. return $this->project;
  419. }
  420. /**
  421. * Set status
  422. *
  423. * @param int $status
  424. *
  425. * @return Disc
  426. */
  427. public function setStatus($status)
  428. {
  429. $this->status = $status;
  430. return $this;
  431. }
  432. /**
  433. * Get status
  434. *
  435. * @return int
  436. */
  437. public function getStatus()
  438. {
  439. return $this->status;
  440. }
  441. /**
  442. * Returns a human readable version of the status
  443. *
  444. * @return string
  445. */
  446. public function getStatusName()
  447. {
  448. $statusOptions = self::getStatusOptions();
  449. return
  450. $statusOptions[$this->getStatus()] ?? $this->getStatus();
  451. }
  452. /**
  453. * Returns a detailed human readable version of the status
  454. *
  455. * @return string
  456. */
  457. public function getStatusDetailedName()
  458. {
  459. $statusOptions = self::getStatusOptions();
  460. $detailedStatusOptions = [
  461. // Legacy Statuses
  462. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT => 'Extract and Import',
  463. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE => 'Extract and Import Complete',
  464. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC => 'Sync Collection To Orthanc',
  465. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE => 'Sync Collection To Orthanc Complete',
  466. self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE => 'Sync Patient Structure',
  467. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM => 'Import Dicom',
  468. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE => 'Import Dicom Complete',
  469. // Modern Statuses
  470. self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE => 'Extracting Disc',
  471. self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE => 'Extracted Disc',
  472. self::STATUS_MODERN_PROCESSING_IMPORT_DICOM => 'Importing DICOMs',
  473. self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE => 'Imported DICOMs',
  474. self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE => 'Replicating Patient Structure',
  475. self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE => 'Patient Structure Replicated',
  476. self::STATUS_MODERN_PROCESSING_CLEANUP => 'Cleaning Temporary Files',
  477. self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE => 'Temporary Files Removed',
  478. ];
  479. $statusOptions = array_replace_recursive($statusOptions, $detailedStatusOptions);
  480. return
  481. $statusOptions[$this->getStatus()] ?? $this->getStatus();
  482. }
  483. /**
  484. * Returns an array of permitted values for the status field and their
  485. * associated labels,
  486. *
  487. * @return array
  488. */
  489. public static function getStatusOptions()
  490. {
  491. return [
  492. self::STATUS_ACTIVE => 'Active',
  493. self::STATUS_PENDING_PROCESSING => 'Pending Processing',
  494. self::STATUS_PENDING_UPLOAD => 'Pending Upload',
  495. self::STATUS_PROCESSING_FAILED => 'Processing Failed',
  496. self::STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED => 'Processing Failed',
  497. self::STATUS_PENDING_ARCHIVE => 'Pending Archiving',
  498. self::STATUS_ARCHIVED => 'Archived',
  499. self::STATUS_ARCHIVED_FAILED => 'Archiving Failed',
  500. self::STATUS_FAILED_VIRUS => 'Processing Failed - File Failed Virus Scan',
  501. //all of the below statusses can just be shown as 'processing' for the user
  502. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT => 'Currently Processing',
  503. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE => 'Currently Processing',
  504. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC => 'Currently Processing',
  505. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE => 'Currently Processing',
  506. self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE => 'Currently Processing',
  507. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM => 'Currently Processing',
  508. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE => 'Currently Processing',
  509. // Modern Statuses
  510. self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE => 'Currently Processing',
  511. self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE => 'Currently Processing',
  512. self::STATUS_MODERN_PROCESSING_IMPORT_DICOM => 'Currently Processing',
  513. self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE => 'Currently Processing',
  514. self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE => 'Currently Processing',
  515. self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE => 'Currently Processing',
  516. self::STATUS_MODERN_PROCESSING_CLEANUP => 'Currently Processing',
  517. self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE => 'Currently Processing',
  518. self::STATUS_PROCESSING_CURRENT_ARCHIVING => 'Archiving',
  519. ];
  520. }
  521. /**
  522. * Returns true if the disc is in status.
  523. *
  524. * @return array
  525. */
  526. public static function processingStuckStatusArray()
  527. {
  528. $statuses = [
  529. // Modern Statuses
  530. self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE,
  531. self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE,
  532. self::STATUS_MODERN_PROCESSING_IMPORT_DICOM,
  533. self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE,
  534. self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE,
  535. self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE,
  536. self::STATUS_MODERN_PROCESSING_CLEANUP,
  537. self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE,
  538. ];
  539. return $statuses;
  540. }
  541. /**
  542. * Returns true if the disc is in an active status.
  543. *
  544. * @return bool
  545. */
  546. public function isActive()
  547. {
  548. return $this->getStatus() === self::STATUS_ACTIVE;
  549. }
  550. /**
  551. * Returns true if the disc is in an pending status.
  552. *
  553. * @return bool
  554. */
  555. public function isPending()
  556. {
  557. $statuses = [
  558. self::STATUS_PENDING_PROCESSING,
  559. self::STATUS_PENDING_UPLOAD,
  560. ];
  561. return in_array($this->getStatus(), $statuses);
  562. }
  563. /**
  564. * Returns true if the disc is in an processing status.
  565. *
  566. * @return bool
  567. */
  568. public function isProcessing()
  569. {
  570. $statuses = [
  571. // Legacy statuses
  572. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT,
  573. self::STATUS_PROCESSING_CURRENT_EXTRACT_IMPORT_COMPLETE,
  574. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM,
  575. self::STATUS_PROCESSING_CURRENT_IMPORT_DICOM_COMPLETE,
  576. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC,
  577. self::STATUS_PROCESSING_CURRENT_SYNC_COLLECTION_TO_ORTHANC_COMPLETE,
  578. self::STATUS_PROCESSING_CURRENT_SYNC_PATIENT_STRUCTURE,
  579. // Modern Statuses
  580. self::STATUS_MODERN_PROCESSING_EXTRACT_ARCHIVE,
  581. self::STATUS_MODERN_PROCESSING_EXTRACT_COMPLETE,
  582. self::STATUS_MODERN_PROCESSING_IMPORT_DICOM,
  583. self::STATUS_MODERN_PROCESSING_IMPORT_COMPLETE,
  584. self::STATUS_MODERN_PROCESSING_SYNC_STRUCTURE,
  585. self::STATUS_MODERN_PROCESSING_SYNC_COMPLETE,
  586. self::STATUS_MODERN_PROCESSING_CLEANUP,
  587. self::STATUS_MODERN_PROCESSING_CLEANUP_COMPLETE,
  588. ];
  589. return in_array($this->getStatus(), $statuses);
  590. }
  591. /**
  592. * Returns true if the disc is in an failed status.
  593. *
  594. * @param array $excludes - optional array of failed status names
  595. *
  596. * @return bool
  597. */
  598. public function isFailed(array $excludes = [])
  599. {
  600. $statuses = [
  601. self::STATUS_PROCESSING_FAILED,
  602. self::STATUS_PROCESSING_SYNC_COLLECTION_TO_ORTHANC_FAILED,
  603. self::STATUS_FAILED_VIRUS,
  604. ];
  605. if (empty($excludes)) {
  606. return in_array($this->getStatus(), $statuses);
  607. }
  608. return in_array($this->getStatus(), array_diff($statuses, $excludes));
  609. }
  610. /**
  611. * Set archiveName
  612. *
  613. * @param string $archiveName
  614. *
  615. * @return Disc
  616. */
  617. public function setArchiveName($archiveName)
  618. {
  619. $this->archiveName = $archiveName;
  620. return $this;
  621. }
  622. /**
  623. * Get archiveName
  624. *
  625. * @return string
  626. */
  627. public function getArchiveName()
  628. {
  629. return $this->archiveName;
  630. }
  631. /**
  632. * If manually uploading a file (i.e. not using Symfony Form) ensure an instance
  633. * of 'UploadedFile' is injected into this setter to trigger the update. If this
  634. * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
  635. * must be able to accept an instance of 'File' as the bundle will inject one here
  636. * during Doctrine hydration.
  637. *
  638. * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $archiveFile
  639. * @param string
  640. *
  641. * @return Disc
  642. */
  643. public function setArchiveFile($archiveFile)
  644. {
  645. $this->archiveFile = $archiveFile;
  646. if ($archiveFile) {
  647. // It is required that at least one field changes if you are using doctrine
  648. // otherwise the event listeners won't be called and the file is lost
  649. // NOTE: We can't actually do this because the this set method gets called when the entity
  650. // is populated from the database, therefore the update value will always be wrong
  651. //$this->updated = new \DateTime('now');
  652. }
  653. return $this;
  654. }
  655. /**
  656. * Get archiveFile
  657. *
  658. * @return string
  659. */
  660. public function getArchiveFile()
  661. {
  662. return $this->archiveFile;
  663. }
  664. /**
  665. * Set archiveOriginalName
  666. *
  667. * @param string $archiveOriginalName
  668. *
  669. * @return Disc
  670. */
  671. public function setArchiveOriginalName($archiveOriginalName)
  672. {
  673. $this->archiveOriginalName = $archiveOriginalName;
  674. return $this;
  675. }
  676. /**
  677. * Get archiveOriginalName
  678. *
  679. * @return string
  680. */
  681. public function getArchiveOriginalName()
  682. {
  683. return $this->archiveOriginalName;
  684. }
  685. /**
  686. * Set collection
  687. *
  688. * @param Collection|null $collection
  689. *
  690. * @return Disc
  691. */
  692. public function setCollection(?Collection $collection = null)
  693. {
  694. $this->collection = $collection;
  695. return $this;
  696. }
  697. /**
  698. * Get collection
  699. *
  700. * @return Collection
  701. */
  702. public function getCollection()
  703. {
  704. return $this->collection;
  705. }
  706. /**
  707. * Set source
  708. *
  709. * @param string $source
  710. *
  711. * @return Disc
  712. */
  713. public function setSource($source)
  714. {
  715. $this->source = $source;
  716. return $this;
  717. }
  718. /**
  719. * Get source
  720. *
  721. * @return string
  722. */
  723. public function getSource()
  724. {
  725. return $this->source;
  726. }
  727. /**
  728. * Set password
  729. *
  730. * @param string $password
  731. *
  732. * @return Disc
  733. */
  734. public function setPassword($password)
  735. {
  736. $this->password = $password;
  737. return $this;
  738. }
  739. /**
  740. * Get password
  741. *
  742. * @return string
  743. */
  744. public function getPassword()
  745. {
  746. return $this->password;
  747. }
  748. /**
  749. * Set copy_created
  750. *
  751. * @param bool $copyCreated
  752. *
  753. * @return Disc
  754. */
  755. public function setCopyCreated($copyCreated)
  756. {
  757. $this->copy_created = $copyCreated;
  758. return $this;
  759. }
  760. /**
  761. * Get copy_created
  762. *
  763. * @return bool
  764. */
  765. public function getCopyCreated()
  766. {
  767. return $this->copy_created;
  768. }
  769. /**
  770. * Set is_password_protected
  771. *
  772. * @param bool $isPasswordProtected
  773. *
  774. * @return Disc
  775. */
  776. public function setIsPasswordProtected($isPasswordProtected)
  777. {
  778. $this->is_password_protected = $isPasswordProtected;
  779. return $this;
  780. }
  781. /**
  782. * Get is_password_protected
  783. *
  784. * @return bool
  785. */
  786. public function getIsPasswordProtected()
  787. {
  788. return $this->is_password_protected;
  789. }
  790. /**
  791. * Set status_reason_code
  792. *
  793. * @param int|null $statusReasonCode
  794. *
  795. * @return Disc
  796. */
  797. public function setStatusReasonCode(?int $statusReasonCode)
  798. {
  799. $this->status_reason_code = $statusReasonCode;
  800. return $this;
  801. }
  802. /**
  803. * Get status_reason_code
  804. *
  805. * @return int
  806. */
  807. public function getStatusReasonCode()
  808. {
  809. return $this->status_reason_code;
  810. }
  811. /**
  812. * Set deletedAt
  813. *
  814. * @param \DateTime $deletedAt
  815. *
  816. * @return Disc
  817. */
  818. public function setDeletedAt($deletedAt)
  819. {
  820. $this->deletedAt = $deletedAt;
  821. return $this;
  822. }
  823. /**
  824. * Get deletedAt
  825. *
  826. * @return \DateTime
  827. */
  828. public function getDeletedAt()
  829. {
  830. return $this->deletedAt;
  831. }
  832. /**
  833. * Set creator
  834. *
  835. * @param User $creator
  836. *
  837. * @return Disc
  838. */
  839. public function setCreator(User $creator)
  840. {
  841. $this->creator = $creator;
  842. return $this;
  843. }
  844. /**
  845. * Get creator
  846. *
  847. * @return User
  848. */
  849. public function getCreator()
  850. {
  851. return $this->creator;
  852. }
  853. /**
  854. * Add discImportSession
  855. *
  856. * @param DiscImportSession $discImportSession
  857. *
  858. * @return Disc
  859. */
  860. public function addDiscImportSession(DiscImportSession $discImportSession)
  861. {
  862. $this->discImportSessions[] = $discImportSession;
  863. return $this;
  864. }
  865. /**
  866. * Remove discImportSession
  867. *
  868. * @param DiscImportSession $discImportSession
  869. */
  870. public function removeDiscImportSession(DiscImportSession $discImportSession)
  871. {
  872. $this->discImportSessions->removeElement($discImportSession);
  873. }
  874. /**
  875. * Get discImportSessions
  876. *
  877. * @return DoctrineCollection
  878. */
  879. public function getDiscImportSessions()
  880. {
  881. return $this->discImportSessions;
  882. }
  883. /**
  884. * Add study
  885. *
  886. * @param Study $study
  887. *
  888. * @return Disc
  889. */
  890. public function addStudy(Study $study)
  891. {
  892. $this->studies[] = $study;
  893. return $this;
  894. }
  895. /**
  896. * Remove study
  897. *
  898. * @param Study $study
  899. */
  900. public function removeStudy(Study $study)
  901. {
  902. $this->studies->removeElement($study);
  903. }
  904. /**
  905. * Get studies
  906. *
  907. * @return Study[]|DoctrineCollection
  908. */
  909. public function getStudies()
  910. {
  911. return $this->studies;
  912. }
  913. /**
  914. * Set archiveFilesize
  915. *
  916. * @param string $archiveFilesize
  917. *
  918. * @return Disc
  919. */
  920. public function setArchiveFilesize($archiveFilesize)
  921. {
  922. $this->archiveFilesize = $archiveFilesize;
  923. return $this;
  924. }
  925. /**
  926. * Get archiveFilesize
  927. *
  928. * @return string
  929. */
  930. public function getArchiveFilesize()
  931. {
  932. return $this->archiveFilesize;
  933. }
  934. /**
  935. * Set processingPriority
  936. *
  937. * @param int $processingPriority
  938. *
  939. * @return Disc
  940. */
  941. public function setProcessingPriority($processingPriority)
  942. {
  943. $this->processingPriority = $processingPriority;
  944. return $this;
  945. }
  946. /**
  947. * Get processingPriority
  948. *
  949. * @return int
  950. */
  951. public function getProcessingPriority()
  952. {
  953. return $this->processingPriority;
  954. }
  955. /**
  956. * Returns true if the Disc has been prioritised,
  957. *
  958. * @return bool
  959. */
  960. public function isPrioritised()
  961. {
  962. return $this->getProcessingPriority() === self::PROCESSING_PRIORITY_HIGH;
  963. }
  964. /**
  965. * Sets the processing priority of the Disc to the default value.
  966. *
  967. * @return void
  968. */
  969. public function setProcessingPriorityToDefault()
  970. {
  971. $this->setProcessingPriority(self::PROCESSING_PRIORITY_LOW);
  972. }
  973. /**
  974. * Set batchRequest
  975. *
  976. * @param BatchRequest $batchRequest
  977. *
  978. * @return Disc
  979. */
  980. public function setBatchRequest(?BatchRequest $batchRequest = null)
  981. {
  982. $this->batchRequest = $batchRequest;
  983. return $this;
  984. }
  985. /**
  986. * Get batchRequest
  987. *
  988. * @return BatchRequest
  989. */
  990. public function getBatchRequest()
  991. {
  992. return $this->batchRequest;
  993. }
  994. /**
  995. * Check whether Disc is available for download
  996. *
  997. * @return bool
  998. */
  999. public function isDownloadable()
  1000. {
  1001. $nonDownloadableStatuses = [
  1002. self::STATUS_PENDING_UPLOAD,
  1003. self::STATUS_FAILED_VIRUS,
  1004. ];
  1005. return !in_array($this->getStatus(), $nonDownloadableStatuses);
  1006. }
  1007. /**
  1008. * Extracts the Trust name from a Disc's name
  1009. *
  1010. * @return string
  1011. */
  1012. public function getTrustFromName()
  1013. {
  1014. // A trust is only available if the Disc has a name
  1015. if ($this->getName()) {
  1016. // We can only assume the name is conventional and properly structured. If it isn't,
  1017. // that's not our fault
  1018. return preg_match("/^(.+) - Disc \d+/i", $this->getName(), $discName) ? $discName[1] : $this->getName();
  1019. }
  1020. // A Disc has no name - Arya Stark 2020
  1021. return 'Unknown';
  1022. }
  1023. /**
  1024. * Extracts the Disc number from a Disc's name
  1025. *
  1026. * @return string
  1027. */
  1028. public function getDiscNumberFromName()
  1029. {
  1030. // Disc number is only available if the Disc has a name
  1031. if ($this->getName()) {
  1032. // We can only assume the name is conventional and properly structured. If it isn't,
  1033. // that's not our fault
  1034. return preg_match("/^.+ - Disc (\d+)/i", $this->getName(), $discNumber) ? $discNumber[1] : 'Unknown';
  1035. }
  1036. // A Disc has no name - Jaqen H'ghar 2020
  1037. return 'Unknown';
  1038. }
  1039. /**
  1040. * @return null|string
  1041. */
  1042. public function getDeleteStatus(): ?string
  1043. {
  1044. return $this->deleteStatus;
  1045. }
  1046. /**
  1047. * @param string|null $deleteStatus
  1048. *
  1049. * @return self
  1050. */
  1051. public function setDeleteStatus(?string $deleteStatus): self
  1052. {
  1053. $this->deleteStatus = $deleteStatus;
  1054. return $this;
  1055. }
  1056. /**
  1057. * @return DiscMetadata|null
  1058. */
  1059. public function getDiscMetadata(): ?DiscMetadata
  1060. {
  1061. return $this->discMetadata;
  1062. }
  1063. /**
  1064. * @param DiscMetadata $discMetadata
  1065. *
  1066. * @return $this
  1067. */
  1068. public function setDiscMetadata(DiscMetadata $discMetadata): self
  1069. {
  1070. // set the owning side of the relation if necessary
  1071. if ($discMetadata->getDisc() !== $this) {
  1072. $discMetadata->setDisc($this);
  1073. }
  1074. $this->discMetadata = $discMetadata;
  1075. return $this;
  1076. }
  1077. /**
  1078. * Determines whether the Disc has associated DiscMetadata (legacy discs will not have any and
  1079. * will need to use legacy Orthanc calls to determine some meta)
  1080. *
  1081. * @return bool
  1082. */
  1083. public function hasDiscMetadata(): bool
  1084. {
  1085. return ($this->discMetadata !== null);
  1086. }
  1087. /**
  1088. * Returns a human-readable string representation of the failure error code. Because of
  1089. * custom logic here, we don't use the FilterableClassConstants trait.
  1090. *
  1091. * @return string
  1092. */
  1093. public function getFailureReasonHumanReadable(): string
  1094. {
  1095. if (($code = $this->getStatusReasonCode()) === null) {
  1096. return 'Disc has no failure code';
  1097. }
  1098. $mapping = [
  1099. self::STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN => self::STATUS_REASON_CODE_ARCHIVE_ERROR_UNKNOWN__LABEL,
  1100. self::STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID => self::STATUS_REASON_CODE_ARCHIVE_ERROR_PASSWORD_INVALID__LABEL,
  1101. self::STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES => self::STATUS_REASON_CODE_ARCHIVE_CONTAINS_NO_DICOM_FILES__LABEL,
  1102. self::STATUS_REASON_CODE_ORTHANC_UNAVAILABLE => self::STATUS_REASON_CODE_ORTHANC_UNAVAILABLE__LABEL,
  1103. self::STATUS_REASON_CODE_EXCEPTION_THROWN => self::STATUS_REASON_CODE_EXCEPTION_THROWN__LABEL,
  1104. self::STATUS_REASON_CODE_ARCHIVE_NONEXISTENT => self::STATUS_REASON_CODE_ARCHIVE_NONEXISTENT__LABEL,
  1105. ];
  1106. if ($code > self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED) {
  1107. $previousReasonCode = $code - self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED;
  1108. return sprintf('%s: %s', self::STATUS_REASON_CODE_MAX_ATTEMPTS_EXCEEDED__LABEL, $mapping[$previousReasonCode]);
  1109. }
  1110. return $mapping[$code];
  1111. }
  1112. /**
  1113. * @return DoctrineCollection<int, Instance>
  1114. */
  1115. public function getInstances(): DoctrineCollection
  1116. {
  1117. return $this->instances;
  1118. }
  1119. /**
  1120. * @param Instance $instance
  1121. *
  1122. * @return $this
  1123. */
  1124. public function addInstance(Instance $instance): self
  1125. {
  1126. if (!$this->instances->contains($instance)) {
  1127. $this->instances[] = $instance;
  1128. $instance->addDisc($this);
  1129. }
  1130. return $this;
  1131. }
  1132. /**
  1133. * @param Instance $instance
  1134. *
  1135. * @return $this
  1136. */
  1137. public function removeInstance(Instance $instance): self
  1138. {
  1139. if ($this->instances->removeElement($instance)) {
  1140. $instance->removeDisc($this);
  1141. }
  1142. return $this;
  1143. }
  1144. /**
  1145. * @return int|null
  1146. */
  1147. public function getPreMigrationStatus(): ?int
  1148. {
  1149. return $this->preMigrationStatus;
  1150. }
  1151. /**
  1152. * @param int|null $preMigrationStatus
  1153. *
  1154. * @return self
  1155. */
  1156. public function setPreMigrationStatus(?int $preMigrationStatus): self
  1157. {
  1158. $this->preMigrationStatus = $preMigrationStatus;
  1159. return $this;
  1160. }
  1161. /**
  1162. * Returns true if the disc has a pre-migration status and that status does not match its current status.
  1163. * Active are assumed successfully migrated - as if the disc failed prior to migration, and succeeded with migration,
  1164. * we consider it a success.
  1165. *
  1166. * @return bool
  1167. */
  1168. public function hasDiscFailedMigration(): bool
  1169. {
  1170. return !$this->isActive() && $this->getPreMigrationStatus() !== null && $this->getStatus() !== $this->getPreMigrationStatus();
  1171. }
  1172. /**
  1173. * @return int|null
  1174. */
  1175. public function getOriginalDiscId(): ?int
  1176. {
  1177. return $this->originalDiscId;
  1178. }
  1179. /**
  1180. * @param int|null $originalDiscId
  1181. *
  1182. * @return self
  1183. */
  1184. public function setOriginalDiscId(?int $originalDiscId): self
  1185. {
  1186. $this->originalDiscId = $originalDiscId;
  1187. return $this;
  1188. }
  1189. /**
  1190. * Check whether Disc is archived
  1191. *
  1192. * @return bool
  1193. */
  1194. public function isArchived()
  1195. {
  1196. $archivedStatuses = [
  1197. self::STATUS_PENDING_ARCHIVE,
  1198. self::STATUS_ARCHIVED,
  1199. self::STATUS_PROCESSING_CURRENT_ARCHIVING,
  1200. self::STATUS_ARCHIVED_FAILED,
  1201. ];
  1202. return in_array($this->getStatus(), $archivedStatuses);
  1203. }
  1204. /**
  1205. * Returns a human readable version of the deleted statuses
  1206. *
  1207. * @return null|string
  1208. */
  1209. public function getDeletedStatusLabel(): ?string
  1210. {
  1211. return self::DELETE_STATUS__LABEL;
  1212. }
  1213. /**
  1214. * @return VirusScanItem|null
  1215. */
  1216. public function getVirusScanItem(): ?VirusScanItem
  1217. {
  1218. return $this->virusScanItem;
  1219. }
  1220. /**
  1221. * @param VirusScanItem|null $virusScanItem
  1222. *
  1223. * @return self
  1224. */
  1225. public function setVirusScanItem(?VirusScanItem $virusScanItem): self
  1226. {
  1227. // unset the owning side of the relation if necessary
  1228. if ($virusScanItem === null && $this->virusScanItem !== null) {
  1229. $this->virusScanItem->setDisc(null);
  1230. }
  1231. // set the owning side of the relation if necessary
  1232. if ($virusScanItem !== null && $virusScanItem->getDisc() !== $this) {
  1233. $virusScanItem->setDisc($this);
  1234. }
  1235. $this->virusScanItem = $virusScanItem;
  1236. return $this;
  1237. }
  1238. /**
  1239. * @param VirusScannerPathVisitorInterface $virusScannerPathVisitor
  1240. *
  1241. * @return VirusScannerVisitorInterface
  1242. */
  1243. public function acceptVirusScannerPathVisitor(VirusScannerPathVisitorInterface $virusScannerPathVisitor): string
  1244. {
  1245. return $virusScannerPathVisitor->visitDisc($this);
  1246. }
  1247. }