src/Entity/Series.php line 26

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;
  6. use Doctrine\Common\Collections\Criteria;
  7. use Doctrine\ORM\Mapping as ORM;
  8. use Gedmo\Mapping\Annotation as Gedmo;
  9. use MedBrief\MSR\Repository\SeriesRepository;
  10. /**
  11. * Series
  12. *
  13. * @ORM\Table(name="Series", indexes={@ORM\Index(name="orthanc_series_id_index", columns={"orthanc_series_id"})})
  14. *
  15. * @ORM\Entity(repositoryClass=SeriesRepository::class)
  16. *
  17. * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
  18. *
  19. * @Audit\Auditable
  20. *
  21. * @Audit\Security(view={"ROLE_ALLOWED_TO_AUDIT"})
  22. */
  23. class Series
  24. {
  25. public const ORTHANC_SYNC_STATUS_PENDING = 1;
  26. public const ORTHANC_SYNC_STATUS_FAILED = 2;
  27. public const ORTHANC_SYNC_STATUS_SUCCESS = 3;
  28. /**
  29. * @var int
  30. *
  31. * @ORM\Column(name="id", type="integer")
  32. *
  33. * @ORM\Id
  34. *
  35. * @ORM\GeneratedValue(strategy="IDENTITY")
  36. */
  37. private $id;
  38. /**
  39. * @var \DateTime|null
  40. *
  41. * @ORM\Column(name="deletedAt", type="datetime", nullable=true)
  42. */
  43. private $deletedAt;
  44. /**
  45. * @var string|null
  46. *
  47. * @ORM\Column(name="dicom_series_name", type="string", nullable=true)
  48. */
  49. private $dicom_series_name;
  50. /**
  51. * @var string|null
  52. *
  53. * @ORM\Column(name="dicom_series_description", type="string", nullable=true)
  54. */
  55. private $dicom_series_description;
  56. /**
  57. * @var string|null
  58. *
  59. * @ORM\Column(name="dicom_series_number", type="string", nullable=true)
  60. */
  61. private $dicom_series_number;
  62. /**
  63. * @var string|null
  64. *
  65. * @ORM\Column(name="dicom_series_id", type="string", nullable=true)
  66. */
  67. private $dicom_series_id;
  68. /**
  69. * @var int|null
  70. *
  71. * @ORM\Column(name="orthanc_sync_status", type="integer", nullable=true)
  72. */
  73. private $orthanc_sync_status;
  74. /**
  75. * @var string|null
  76. *
  77. * @ORM\Column(name="orthanc_series_id", type="string", nullable=true)
  78. */
  79. private $orthanc_series_id;
  80. /**
  81. * @var string|null
  82. *
  83. * @ORM\Column(name="orthanc_parent_study_id", type="string", length=255, nullable=true)
  84. */
  85. private $orthanc_parent_study_id;
  86. /**
  87. * @ORM\Column(type="string", nullable=true)
  88. */
  89. private ?string $name;
  90. /**
  91. * @ORM\Column(type="string", nullable=true)
  92. */
  93. private ?string $description;
  94. /**
  95. * @var \DateTime
  96. *
  97. * @ORM\Column(name="created", type="datetime")
  98. *
  99. * @Gedmo\Timestampable(on="create")
  100. */
  101. private $created;
  102. /**
  103. * @var \DateTime
  104. *
  105. * @ORM\Column(name="updated", type="datetime")
  106. *
  107. * @Gedmo\Timestampable(on="update")
  108. */
  109. private $updated;
  110. /**
  111. * @var Collection
  112. *
  113. * @ORM\OneToMany(targetEntity="MedBrief\MSR\Entity\OrthancTransaction", mappedBy="series", cascade={"all"})
  114. */
  115. private $orthancTransactions;
  116. /**
  117. * @var Study
  118. *
  119. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Study", inversedBy="series")
  120. *
  121. * @ORM\JoinColumns({
  122. *
  123. * @ORM\JoinColumn(name="study_id", referencedColumnName="id", nullable=true)
  124. * })
  125. */
  126. private $study;
  127. /**
  128. * @var Project
  129. *
  130. * @ORM\ManyToOne(targetEntity="MedBrief\MSR\Entity\Project", inversedBy="series")
  131. *
  132. * @ORM\JoinColumns({
  133. *
  134. * @ORM\JoinColumn(name="project_id", referencedColumnName="id", nullable=false)
  135. * })
  136. */
  137. private $project;
  138. /**
  139. * @ORM\OneToMany(targetEntity=Instance::class, mappedBy="parentSeries", orphanRemoval=true)
  140. */
  141. private $instances;
  142. /**
  143. * Constructor
  144. */
  145. public function __construct()
  146. {
  147. $this->orthancTransactions = new ArrayCollection();
  148. $this->instances = new ArrayCollection();
  149. }
  150. public function __clone()
  151. {
  152. if ($this->id) {
  153. $this->id = null;
  154. $this->instances = new ArrayCollection();
  155. $this->orthancTransactions = new ArrayCollection();
  156. }
  157. }
  158. public function __toString()
  159. {
  160. return $this->getName();
  161. }
  162. /**
  163. * Get id
  164. *
  165. * @return int
  166. */
  167. public function getId()
  168. {
  169. return $this->id;
  170. }
  171. /**
  172. * Set dicom_series_name
  173. *
  174. * @param string $dicomSeriesName
  175. *
  176. * @return Series
  177. */
  178. public function setDicomSeriesName($dicomSeriesName)
  179. {
  180. $this->dicom_series_name = $dicomSeriesName;
  181. return $this;
  182. }
  183. /**
  184. * Get dicom_series_name
  185. *
  186. * @return string
  187. */
  188. public function getDicomSeriesName()
  189. {
  190. return $this->dicom_series_name;
  191. }
  192. /**
  193. * Set dicom_series_id
  194. *
  195. * @param string $dicomSeriesId
  196. *
  197. * @return Series
  198. */
  199. public function setDicomSeriesId($dicomSeriesId)
  200. {
  201. $this->dicom_series_id = $dicomSeriesId;
  202. return $this;
  203. }
  204. /**
  205. * Get dicom_series_id
  206. *
  207. * @return string
  208. */
  209. public function getDicomSeriesId()
  210. {
  211. return $this->dicom_series_id;
  212. }
  213. /**
  214. * Set created
  215. *
  216. * @param \DateTime $created
  217. *
  218. * @return Series
  219. */
  220. public function setCreated($created)
  221. {
  222. $this->created = $created;
  223. return $this;
  224. }
  225. /**
  226. * Get created
  227. *
  228. * @return \DateTime
  229. */
  230. public function getCreated()
  231. {
  232. return $this->created;
  233. }
  234. /**
  235. * Set updated
  236. *
  237. * @param \DateTime $updated
  238. *
  239. * @return Series
  240. */
  241. public function setUpdated($updated)
  242. {
  243. $this->updated = $updated;
  244. return $this;
  245. }
  246. /**
  247. * Get updated
  248. *
  249. * @return \DateTime
  250. */
  251. public function getUpdated()
  252. {
  253. return $this->updated;
  254. }
  255. /**
  256. * Set study
  257. *
  258. * @param Study $study
  259. *
  260. * @return Series
  261. */
  262. public function setStudy(Study $study)
  263. {
  264. $this->study = $study;
  265. return $this;
  266. }
  267. /**
  268. * Get study
  269. *
  270. * @return Study
  271. */
  272. public function getStudy()
  273. {
  274. return $this->study;
  275. }
  276. /**
  277. * Set project
  278. *
  279. * @param Project $project
  280. *
  281. * @return Series
  282. */
  283. public function setProject(Project $project)
  284. {
  285. $this->project = $project;
  286. return $this;
  287. }
  288. /**
  289. * Get project
  290. *
  291. * @return Project
  292. */
  293. public function getProject()
  294. {
  295. return $this->project;
  296. }
  297. /**
  298. * Set dicom_series_description
  299. *
  300. * @param string $dicomSeriesDescription
  301. *
  302. * @return Series
  303. */
  304. public function setDicomSeriesDescription($dicomSeriesDescription)
  305. {
  306. $this->dicom_series_description = $dicomSeriesDescription;
  307. return $this;
  308. }
  309. /**
  310. * Get dicom_series_description
  311. *
  312. * @return string
  313. */
  314. public function getDicomSeriesDescription()
  315. {
  316. return $this->dicom_series_description;
  317. }
  318. /**
  319. * Set dicom_series_number
  320. *
  321. * @param string $dicomSeriesNumber
  322. *
  323. * @return Series
  324. */
  325. public function setDicomSeriesNumber($dicomSeriesNumber)
  326. {
  327. $this->dicom_series_number = $dicomSeriesNumber;
  328. return $this;
  329. }
  330. /**
  331. * Get dicom_series_number
  332. *
  333. * @return string
  334. */
  335. public function getDicomSeriesNumber()
  336. {
  337. return $this->dicom_series_number;
  338. }
  339. /**
  340. * Set orthancSeriesId
  341. *
  342. * @param string $orthancSeriesId
  343. *
  344. * @return Series
  345. */
  346. public function setOrthancSeriesId($orthancSeriesId)
  347. {
  348. $this->orthanc_series_id = $orthancSeriesId;
  349. return $this;
  350. }
  351. /**
  352. * Get orthancSeriesId
  353. *
  354. * @return string
  355. */
  356. public function getOrthancSeriesId()
  357. {
  358. return $this->orthanc_series_id;
  359. }
  360. /**
  361. * Add orthancTransaction
  362. *
  363. * @param OrthancTransaction $orthancTransaction
  364. *
  365. * @return Series
  366. */
  367. public function addOrthancTransaction(OrthancTransaction $orthancTransaction)
  368. {
  369. $this->orthancTransactions[] = $orthancTransaction;
  370. // based on the type of the transaction, we run the appropriate function
  371. // on this Series
  372. switch ($orthancTransaction->getType()) {
  373. case OrthancTransaction::TYPE_SERIES_DETAILS:
  374. $this->processOrthancSeriesDetailsTransaction($orthancTransaction);
  375. break;
  376. default:
  377. throw new \Exception('Invalid OrthancTransaction Type Assigned to Series: ' . $orthancTransaction->getType());
  378. }
  379. return $this;
  380. }
  381. /**
  382. * Given the orthancTransaction (which is assumed to be of type SERIES DETAILS)
  383. * We update the appropriate values on this Series to indicate the various
  384. * orthanc data returned by the storage transaction
  385. *
  386. * @param OrthancTransaction $orthancTransaction
  387. */
  388. public function processOrthancSeriesDetailsTransaction(OrthancTransaction $orthancTransaction)
  389. {
  390. // first - set the submissions status to pending at the outset
  391. $this->setOrthancSyncStatus(self::ORTHANC_SYNC_STATUS_PENDING);
  392. // if the given transaction wasn't a success, the storage status of this
  393. // document is automatically also a fail
  394. if ($orthancTransaction->getTransactionStatus() != OrthancTransaction::TRANSACTION_STATUS_SUCCESS) {
  395. $this->setOrthancSyncStatus(self::ORTHANC_SYNC_STATUS_FAILED);
  396. return false;
  397. }
  398. $jsonObject = json_decode($orthancTransaction->getTransactionResponse());
  399. $this->setOrthancParentStudyId($jsonObject->ParentStudy);
  400. // set the status as successful
  401. $this->setOrthancSyncStatus(self::ORTHANC_SYNC_STATUS_SUCCESS);
  402. }
  403. /**
  404. * Remove orthancTransaction
  405. *
  406. * @param OrthancTransaction $orthancTransaction
  407. */
  408. public function removeOrthancTransaction(OrthancTransaction $orthancTransaction)
  409. {
  410. $this->orthancTransactions->removeElement($orthancTransaction);
  411. }
  412. /**
  413. * Get orthancTransactions
  414. *
  415. * @return Collection
  416. */
  417. public function getOrthancTransactions()
  418. {
  419. return $this->orthancTransactions;
  420. }
  421. /**
  422. * Set orthancSyncStatus
  423. *
  424. * @param int $orthancSyncStatus
  425. *
  426. * @return Series
  427. */
  428. public function setOrthancSyncStatus($orthancSyncStatus)
  429. {
  430. $this->orthanc_sync_status = $orthancSyncStatus;
  431. return $this;
  432. }
  433. /**
  434. * Get orthancSyncStatus
  435. *
  436. * @return int
  437. */
  438. public function getOrthancSyncStatus()
  439. {
  440. return $this->orthanc_sync_status;
  441. }
  442. /**
  443. * Set orthancParentStudyId
  444. *
  445. * @param string $orthancParentStudyId
  446. *
  447. * @return Series
  448. */
  449. public function setOrthancParentStudyId($orthancParentStudyId)
  450. {
  451. $this->orthanc_parent_study_id = $orthancParentStudyId;
  452. return $this;
  453. }
  454. /**
  455. * Get orthancParentStudyId
  456. *
  457. * @return string
  458. */
  459. public function getOrthancParentStudyId()
  460. {
  461. return $this->orthanc_parent_study_id;
  462. }
  463. /**
  464. * Returns a Associative Array which is a json_decoded array from the latest Orthanc
  465. * Transaction that performed a details request for this Series
  466. *
  467. * @return array
  468. */
  469. public function getOrthancDetails()
  470. {
  471. // get the latest successfull Series Detals Transaction
  472. $latestOrthancSeriesDetailsTransaction = $this->getLatestOrthancSeriesDetailsTransaction();
  473. // if there isn't one, then we return null
  474. if (!$latestOrthancSeriesDetailsTransaction) {
  475. return null;
  476. }
  477. // otherwise we return the json decoded response
  478. $transactionResponse = $latestOrthancSeriesDetailsTransaction->getTransactionResponse();
  479. return json_decode($transactionResponse, true);
  480. }
  481. /**
  482. * Returns the most recent Orthanc Series Details Transaction linked to this
  483. * Series. If there is one, then it will contain all the details about
  484. * this Series that are currently stored in Orthanc
  485. *
  486. * @return OrthancTransaction
  487. */
  488. public function getLatestOrthancSeriesDetailsTransaction()
  489. {
  490. // create some criteria that will find sucessful transactions of the
  491. // appropriate type, ordered latest first
  492. $criteria = Criteria::create()
  493. ->andWhere(Criteria::expr()->eq('transaction_status', OrthancTransaction::TRANSACTION_STATUS_SUCCESS))
  494. ->andWhere(Criteria::expr()->eq('type', OrthancTransaction::TYPE_SERIES_DETAILS))
  495. ->orderBy(['created' => Criteria::DESC])
  496. ;
  497. // apply the criteria
  498. $matchingTransactions = $this->getOrthancTransactions()->matching($criteria);
  499. // if there are not matches, then return null
  500. if ($matchingTransactions->isEmpty()) {
  501. return null;
  502. }
  503. // else return the first match
  504. return $matchingTransactions->first();
  505. }
  506. public function getOrthancDicomModality()
  507. {
  508. // first prize is that this Study has already been synced to Orthanc
  509. // and the details have already been retrieved for this Study
  510. $orthancDetails = $this->getOrthancDetails();
  511. // if not then we need to at least return something
  512. if (!$orthancDetails) {
  513. return 'Unknown Modality';
  514. }
  515. // otherwise we assume that our Orthanc Details are structured correctly
  516. // and we return the Study Description
  517. return $orthancDetails['MainDicomTags']['Modality'] ?: 'Unknown Modality';
  518. }
  519. public function getOrthancDicomSeriesDescription()
  520. {
  521. // first prize is that this Study has already been synced to Orthanc
  522. // and the details have already been retrieved for this Study
  523. $orthancDetails = $this->getOrthancDetails();
  524. // if not then we need to at least return something
  525. if (!$orthancDetails) {
  526. return 'No Series Description';
  527. }
  528. // otherwise we assume that our Orthanc Details are structured correctly
  529. // and we return the Study Description
  530. return @$orthancDetails['MainDicomTags']['SeriesDescription'] ?: 'No Series Description';
  531. }
  532. public function getOrthancDicomSeriesNumber()
  533. {
  534. // first prize is that this Study has already been synced to Orthanc
  535. // and the details have already been retrieved for this Study
  536. $orthancDetails = $this->getOrthancDetails();
  537. // if not then we need to at least return something
  538. if (!$orthancDetails) {
  539. return 'No Series Number';
  540. }
  541. // otherwise we assume that our Orthanc Details are structured correctly
  542. // and we return the Study Description
  543. return @$orthancDetails['MainDicomTags']['SeriesNumber'] ?: 'No Series Number';
  544. }
  545. /**
  546. * @param \DateTime $deletedAt
  547. *
  548. * @return Series
  549. */
  550. public function setDeletedAt($deletedAt)
  551. {
  552. $this->deletedAt = $deletedAt;
  553. return $this;
  554. }
  555. /**
  556. * @return \DateTime
  557. */
  558. public function getDeletedAt()
  559. {
  560. return $this->deletedAt;
  561. }
  562. /**
  563. * @return string
  564. */
  565. public function getName(): string
  566. {
  567. return ($this->name ?? $this->getDicomSeriesName()) ?? 'Unknown Series';
  568. }
  569. /**
  570. * @param string|null $name
  571. *
  572. * @return Series
  573. */
  574. public function setName(?string $name): Series
  575. {
  576. $this->name = $name;
  577. return $this;
  578. }
  579. /**
  580. * @return string|null
  581. */
  582. public function getDescription(): ?string
  583. {
  584. return $this->description ?? $this->getOrthancDicomSeriesDescription();
  585. }
  586. /**
  587. * @param string|null $description
  588. *
  589. * @return Series
  590. */
  591. public function setDescription(?string $description): Series
  592. {
  593. $this->description = $description;
  594. return $this;
  595. }
  596. /**
  597. * @return Collection<int, Instance>
  598. */
  599. public function getInstances(): Collection
  600. {
  601. return $this->instances;
  602. }
  603. /**
  604. * @param Instance $instance
  605. *
  606. * @return $this
  607. */
  608. public function addInstance(Instance $instance): self
  609. {
  610. if (!$this->instances->contains($instance)) {
  611. $this->instances[] = $instance;
  612. $instance->setParentSeries($this);
  613. }
  614. return $this;
  615. }
  616. /**
  617. * @param Instance $instance
  618. *
  619. * @return $this
  620. */
  621. public function removeInstance(Instance $instance): self
  622. {
  623. if ($this->instances->removeElement($instance)) {
  624. // set the owning side to null (unless already changed)
  625. if ($instance->getParentSeries() === $this) {
  626. $instance->setParentSeries(null);
  627. }
  628. }
  629. return $this;
  630. }
  631. }