PatientInterface.class.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. <?php
  2. namespace Models {
  3. require_once 'Models/User.class.php';
  4. require_once 'Tools/Random.class.php';
  5. class PatientInterface {
  6. //
  7. protected $DataInterface;
  8. /**
  9. *
  10. */
  11. public function __construct($DataInterface) {
  12. $this->DataInterface = $DataInterface;
  13. }
  14. /**
  15. * Get patient files existing data.
  16. */
  17. public function patientFilesExistingGet($User) {
  18. $ID_user = $User->ID;
  19. $where = ' P.ID = V.fk_patient ';
  20. if($User->type=='physician') {
  21. $where .= ' AND P.fk_user = '.$User->ID;
  22. }
  23. else if($User->type=='reader') {
  24. $where .= ' AND V.fk_reader = '.$User->ID.' AND V.completed IS NULL ';
  25. }
  26. else {
  27. $where .= ' AND P.fk_user = '.$User->ID;
  28. }
  29. $statement = $this->DataInterface->DatabaseConnection->prepare(
  30. "SELECT JSON_EXTRACT(data, '$.patientListFields') AS data FROM settings"
  31. );
  32. if(!$statement->execute()) {
  33. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  34. }
  35. $settings = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  36. $patientListFields=[];
  37. foreach($settings as $S) {
  38. $patientListFields[str_replace(' ', '_', $S->name)] = $S->display;
  39. }
  40. $statement = $this->DataInterface->DatabaseConnection->prepare(
  41. "SELECT
  42. P.*,
  43. (SELECT COUNT(visit.ID) FROM visit WHERE fk_patient = P.ID) as visitCount,
  44. (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.visitDate, ',', COALESCE(visit.area, '')) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as visits,
  45. (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.number) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as reader_visits,
  46. (SELECT COUNT(media.ID) FROM media, visit WHERE JSON_VALUE(media.metrics, '$.fps') IS NULL AND media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as imageCount,
  47. (SELECT COUNT(media.ID) FROM media, visit WHERE media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as mediaCount,
  48. V.visitDate AS lastVisit,
  49. V.number AS visitNumber,
  50. V.ID AS visitID
  51. FROM patient P, visit V
  52. WHERE $where
  53. GROUP BY P.ID
  54. ORDER BY V.visitDate DESC, V.ID DESC"
  55. );
  56. if(!$statement->execute()) {
  57. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  58. }
  59. $visits = $statement->fetchAll(\PDO::FETCH_ASSOC);
  60. return [
  61. 'result' => 'OK',
  62. 'visits' => $visits,
  63. 'user' => $User,
  64. 'patientListFields' => $patientListFields
  65. ];
  66. }
  67. /**
  68. * Get patient files existing data.
  69. */
  70. public function patientFilesExistingPost($User, $data) {
  71. $ID_user = $User->ID;
  72. $where = ' P.ID = V.fk_patient ';
  73. if($User->type=='physician') {
  74. $where .= ' AND P.fk_user = '.$User->ID;
  75. }
  76. else if($User->type=='reader') {
  77. $where .= ' AND V.fk_reader = '.$User->ID.' AND V.completed IS NULL ';
  78. }
  79. else {
  80. $where .= ' AND P.fk_user = '.$User->ID;
  81. }
  82. $filter = '(P.lastname LIKE \'%'.$data['filter'].'%\' OR P.firstname LIKE \'%'.$data['filter'].'%\')';
  83. $statement = $this->DataInterface->DatabaseConnection->prepare(
  84. "SELECT JSON_EXTRACT(data, '$.patientListFields') AS data FROM settings"
  85. );
  86. if(!$statement->execute()) {
  87. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  88. }
  89. $settings = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  90. $patientListFields=[];
  91. foreach($settings as $S) {
  92. $patientListFields[str_replace(' ', '_', $S->name)] = $S->display;
  93. }
  94. $statement = $this->DataInterface->DatabaseConnection->prepare(
  95. "SELECT
  96. P.*,
  97. (SELECT COUNT(visit.ID) FROM visit WHERE fk_patient = P.ID) as visitCount,
  98. (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.visitDate, ',', COALESCE(visit.area, '')) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as visits,
  99. (SELECT GROUP_CONCAT(CONCAT(visit.ID, ',', visit.number) ORDER BY visit.visitDate DESC SEPARATOR ';') FROM visit WHERE fk_patient = P.ID) as reader_visits,
  100. (SELECT COUNT(media.ID) FROM media, visit WHERE JSON_VALUE(media.metrics, '$.fps') IS NULL AND media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as imageCount,
  101. (SELECT COUNT(media.ID) FROM media, visit WHERE media.fk_visit = visit.ID AND visit.fk_patient = P.ID) as mediaCount,
  102. V.visitDate AS lastVisit,
  103. V.number AS visitNumber,
  104. V.ID AS visitID
  105. FROM patient P, visit V
  106. WHERE $where AND $filter
  107. GROUP BY P.ID
  108. ORDER BY V.visitDate DESC, V.ID DESC"
  109. );
  110. if(!$statement->execute()) {
  111. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  112. }
  113. $visits = $statement->fetchAll(\PDO::FETCH_ASSOC);
  114. return [
  115. 'result' => 'OK',
  116. 'visits' => $visits,
  117. 'user' => $User,
  118. 'patientListFields' => $patientListFields
  119. ];
  120. }
  121. /**
  122. * Get patient files new data.
  123. */
  124. public function patientFilesNewGet($User, $lang) {
  125. $statement = $this->DataInterface->DatabaseConnection->prepare(
  126. "SELECT JSON_EXTRACT(data, '$.newPatientFields') AS data FROM settings"
  127. );
  128. if(!$statement->execute()) {
  129. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  130. }
  131. $settings = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  132. $patientListFields=[];
  133. foreach($settings as $S) {
  134. $patientListFields[str_replace(' ', '_', $S->name)] = $S->display;
  135. $patientListFields[str_replace(' ', '_', $S->name).'_required'] = $S->required;
  136. }
  137. $statement = $this->DataInterface->DatabaseConnection->prepare(
  138. "SELECT *, name_$lang AS name FROM country ORDER BY name_$lang"
  139. );
  140. if(!$statement->execute()) {
  141. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  142. }
  143. $countries = $statement->fetchAll(\PDO::FETCH_ASSOC);
  144. return [
  145. 'result' => 'OK',
  146. 'countries' => $countries,
  147. 'patientID' => \Tools\Random::getString(10),
  148. 'visitNumber' => 1,
  149. 'patientListFields' => $patientListFields
  150. ];
  151. }
  152. /**
  153. * Get patient files pacs data.
  154. */
  155. public function patientFilesPacsGet($User) {
  156. //
  157. return [
  158. 'result' => 'OK'
  159. ];
  160. }
  161. /**
  162. * Post patient data.
  163. */
  164. public function patientCreatePost($User, $data) {
  165. $statement = $this->DataInterface->DatabaseConnection->prepare(
  166. "SELECT JSON_EXTRACT(data, '$.newPatientFields') AS data FROM settings"
  167. );
  168. if(!$statement->execute()) {
  169. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  170. }
  171. $settings = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  172. $patientListFields=[];
  173. foreach($settings as $S) {
  174. $patientListFields[str_replace(' ', '_', $S->name)] = $S->required;
  175. }
  176. if($data['type']=='investigator' && empty($data['patient']['patientID'])) {
  177. return ['result' => 'ERROR', 'reason' => 'patientID'];
  178. }
  179. if($data['type']=='investigator' && empty($data['patient']['visitNumber'])) {
  180. return ['result' => 'ERROR', 'reason' => 'visitNumber'];
  181. }
  182. if($patientListFields['Visit_date'] && empty($data['patient']['visitDate'])) {
  183. return ['result' => 'ERROR', 'reason' => 'visitDate'];
  184. }
  185. if(empty($data['patient']['visitDate'])) {
  186. $data['patient']['visitDate']=date('Y-m-d');
  187. }
  188. if($patientListFields['Firstname'] && empty($data['patient']['firstname'])) {
  189. return ['result' => 'ERROR', 'reason' => 'firstname'];
  190. }
  191. if(empty($data['patient']['firstname'])) {
  192. $data['patient']['firstname']='';
  193. }
  194. if($patientListFields['Lastname'] && empty($data['patient']['lastname'])) {
  195. return ['result' => 'ERROR', 'reason' => 'lastname'];
  196. }
  197. if(empty($data['patient']['lastname'])) {
  198. $data['patient']['lastname']='';
  199. }
  200. if($patientListFields['Sex'] && empty($data['patient']['gender'])) {
  201. return ['result' => 'ERROR', 'reason' => 'gender'];
  202. }
  203. if($patientListFields['Birthdate'] && empty($data['patient']['birthDate'])) {
  204. return ['result' => 'ERROR', 'reason' => 'birthDate'];
  205. }
  206. if(empty($data['patient']['birthDate'])) {
  207. $data['patient']['birthDate']=date('Y-m-d');
  208. }
  209. if($patientListFields['Birth_country'] && empty($data['patient']['birthCountry'])) {
  210. return ['result' => 'ERROR', 'reason' => 'birthCountry'];
  211. }
  212. if($patientListFields['Residence_country'] && empty($data['patient']['residenceCountry'])) {
  213. return ['result' => 'ERROR', 'reason' => 'residenceCountry'];
  214. }
  215. if($patientListFields['Height'] && empty($data['patient']['height'])) {
  216. return ['result' => 'ERROR', 'reason' => 'height'];
  217. }
  218. if($patientListFields['Weight'] && empty($data['patient']['weight'])) {
  219. return ['result' => 'ERROR', 'reason' => 'weight'];
  220. }
  221. if(empty($data['patient']['height'])) {
  222. $data['patient']['height'] = null;
  223. }
  224. if(empty($data['patient']['weight'])) {
  225. $data['patient']['weight'] = null;
  226. }
  227. // Begin transaction
  228. $this->DataInterface->DatabaseConnection->beginTransaction();
  229. // Insert patient
  230. if($data['type']=='investigator') {
  231. $statement = $this->DataInterface->DatabaseConnection->prepare(
  232. "INSERT INTO patient(patientID, ctPatientID, firstname, lastname, gender, birthDate, height, weight, race, fk_birthCountry, fk_residenceCountry, fk_user) VALUES(:patientID, :ctPatientID, :firstname, :lastname, :gender, :birthDate, :height, :weight, :race, :fk_birthCountry, :fk_residenceCountry, :fk_user)"
  233. );
  234. $statement->bindParam(':patientID', $data['patient']['patientID']);
  235. $statement->bindParam(':ctPatientID', $data['patient']['patientID']);
  236. }
  237. else if($data['type']=='physician') {
  238. $statement = $this->DataInterface->DatabaseConnection->prepare(
  239. "INSERT INTO patient(patientID, firstname, lastname, gender, birthDate, height, weight, race, fk_birthCountry, fk_residenceCountry, fk_user) VALUES(:patientID, :firstname, :lastname, :gender, :birthDate, :height, :weight, :race, :fk_birthCountry, :fk_residenceCountry, :fk_user)"
  240. );
  241. $statement->bindParam(':patientID', $data['patient']['patientID']);
  242. }
  243. $statement->bindParam(':firstname', $data['patient']['firstname']);
  244. $statement->bindParam(':lastname', $data['patient']['lastname']);
  245. $statement->bindParam(':gender', $data['patient']['gender']);
  246. $statement->bindParam(':birthDate', $data['patient']['birthDate']);
  247. $statement->bindParam(':height', $data['patient']['height']);
  248. $statement->bindParam(':weight', $data['patient']['weight']);
  249. $statement->bindParam(':race', $data['patient']['patientRace']);
  250. $statement->bindParam(':fk_birthCountry', $data['patient']['birthCountry']);
  251. $statement->bindParam(':fk_residenceCountry', $data['patient']['residenceCountry']);
  252. $statement->bindParam(':fk_user', $User->ID);
  253. // Error check
  254. if(!$statement->execute()) {
  255. $this->DataInterface->DatabaseConnection->rollback();
  256. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo(), 'data2' => $data];
  257. }
  258. $fk_patient = $this->DataInterface->DatabaseConnection->lastInsertId();
  259. // Insert visit
  260. $statement = $this->DataInterface->DatabaseConnection->prepare(
  261. "INSERT INTO visit(number, visitDate, fk_patient) VALUES(:number, :visitDate, :fk_patient)"
  262. );
  263. $statement->bindParam(':number', $data['patient']['visitNumber']);
  264. $statement->bindParam(':visitDate', $data['patient']['visitDate']);
  265. $statement->bindParam(':fk_patient', $fk_patient);
  266. // Error check
  267. if(!$statement->execute()) {
  268. $this->DataInterface->DatabaseConnection->rollback();
  269. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  270. }
  271. $fk_visit = $this->DataInterface->DatabaseConnection->lastInsertId();
  272. // Insert context
  273. $statement = $this->DataInterface->DatabaseConnection->prepare(
  274. "INSERT INTO context(fk_visit) VALUES(:fk_visit)"
  275. );
  276. $statement->bindParam(':fk_visit', $fk_visit);
  277. // Error check
  278. if(!$statement->execute()) {
  279. $this->DataInterface->DatabaseConnection->rollback();
  280. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  281. }
  282. // Commit
  283. $this->DataInterface->DatabaseConnection->commit();
  284. return [
  285. 'result' => 'OK',
  286. 'fk_patient' => $fk_patient,
  287. 'fk_visit' => $fk_visit
  288. ];
  289. }
  290. /**
  291. * Post patient data.
  292. */
  293. public function patientCreateVisitPost($User, $data) {
  294. // Count visit
  295. $statement = $this->DataInterface->DatabaseConnection->prepare(
  296. "SELECT COUNT(ID) AS cnt FROM visit WHERE fk_patient = :fk_patient"
  297. );
  298. $statement->bindParam(':fk_patient', $data['patientID']);
  299. // Error check
  300. if(!$statement->execute()) {
  301. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  302. }
  303. $visitCount = intval($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['cnt']) + 1;
  304. // Insert visit
  305. $statement = $this->DataInterface->DatabaseConnection->prepare(
  306. "INSERT INTO visit(number, visitDate, fk_patient) VALUES(:number, NOW(), :fk_patient)"
  307. );
  308. $statement->bindParam(':number', $visitCount);
  309. $statement->bindParam(':fk_patient', $data['patientID']);
  310. // Error check
  311. if(!$statement->execute()) {
  312. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  313. }
  314. $fk_visit = $this->DataInterface->DatabaseConnection->lastInsertId();
  315. // Insert context
  316. $statement = $this->DataInterface->DatabaseConnection->prepare(
  317. "INSERT INTO context(fk_visit) VALUES(:fk_visit)"
  318. );
  319. $statement->bindParam(':fk_visit', $fk_visit);
  320. // Error check
  321. if(!$statement->execute()) {
  322. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  323. }
  324. return [
  325. 'result' => 'OK',
  326. 'fk_visit' => $fk_visit
  327. ];
  328. }
  329. /**
  330. * Post patient context data.
  331. */
  332. public function patientContextPost($User, $data) {
  333. $field = $data['what'];
  334. $values = json_encode($data['data']);
  335. $fk_visit = $data['patient']['visitID'];
  336. $statement = $this->DataInterface->DatabaseConnection->prepare(
  337. "UPDATE context SET $field = '$values' WHERE fk_visit = $fk_visit"
  338. );
  339. if(!$statement->execute()) {
  340. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  341. }
  342. return [
  343. 'result' => 'OK',
  344. 'field' => $data['what'],
  345. 'values' => $data['data']
  346. ];
  347. }
  348. /**
  349. * Get patient risks data.
  350. */
  351. public function patientRisksGet($User, $fk_visit) {
  352. $statement = $this->DataInterface->DatabaseConnection->prepare(
  353. "SELECT patient.* FROM patient, visit WHERE patient.ID = visit.fk_patient AND visit.ID = $fk_visit"
  354. );
  355. if(!$statement->execute()) {
  356. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  357. }
  358. $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  359. $statement = $this->DataInterface->DatabaseConnection->prepare(
  360. "SELECT ID, risks FROM context WHERE fk_visit = $fk_visit"
  361. );
  362. if(!$statement->execute()) {
  363. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  364. }
  365. $context = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  366. //
  367. return [
  368. 'result' => 'OK',
  369. 'patient' => $patient,
  370. 'context' => $context
  371. ];
  372. }
  373. /**
  374. * Get patient history data.
  375. */
  376. public function patientHistoryGet($User, $fk_visit, $lang) {
  377. $statement = $this->DataInterface->DatabaseConnection->prepare(
  378. "SELECT patient.* FROM patient, visit WHERE patient.ID = visit.fk_patient AND visit.ID = $fk_visit"
  379. );
  380. if(!$statement->execute()) {
  381. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  382. }
  383. $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  384. $statement = $this->DataInterface->DatabaseConnection->prepare(
  385. "SELECT ID, phistory FROM context WHERE fk_visit = $fk_visit"
  386. );
  387. if(!$statement->execute()) {
  388. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  389. }
  390. $context = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  391. $statement = $this->DataInterface->DatabaseConnection->prepare(
  392. "SELECT ID, JSON_UNQUOTE(JSON_EXTRACT(code, '$.$lang')) AS code FROM lst_coronary_type"
  393. );
  394. if(!$statement->execute()) {
  395. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  396. }
  397. $lst_coronary_type = $statement->fetchAll(\PDO::FETCH_ASSOC);
  398. $statement = $this->DataInterface->DatabaseConnection->prepare(
  399. "SELECT ID, JSON_UNQUOTE(JSON_EXTRACT(code, '$.$lang')) AS code FROM lst_stroke_type"
  400. );
  401. if(!$statement->execute()) {
  402. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  403. }
  404. $lst_stroke_type = $statement->fetchAll(\PDO::FETCH_ASSOC);
  405. //
  406. return [
  407. 'result' => 'OK',
  408. 'patient' => $patient,
  409. 'context' => $context,
  410. 'lst_coronary_type' => $lst_coronary_type,
  411. 'lst_stroke_type' => $lst_stroke_type
  412. ];
  413. }
  414. /**
  415. * Get patient family data.
  416. */
  417. public function patientFamilyGet($User, $fk_visit) {
  418. $statement = $this->DataInterface->DatabaseConnection->prepare(
  419. "SELECT patient.* FROM patient, visit WHERE patient.ID = visit.fk_patient AND visit.ID = $fk_visit"
  420. );
  421. if(!$statement->execute()) {
  422. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  423. }
  424. $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  425. $statement = $this->DataInterface->DatabaseConnection->prepare(
  426. "SELECT ID, fhistory FROM context WHERE fk_visit = $fk_visit"
  427. );
  428. if(!$statement->execute()) {
  429. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  430. }
  431. $context = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  432. //
  433. return [
  434. 'result' => 'OK',
  435. 'patient' => $patient,
  436. 'context' => $context
  437. ];
  438. }
  439. /**
  440. * Get patient examination data.
  441. */
  442. public function patientExaminationGet($User, $fk_visit) {
  443. $statement = $this->DataInterface->DatabaseConnection->prepare(
  444. "SELECT patient.* FROM patient, visit WHERE patient.ID = visit.fk_patient AND visit.ID = $fk_visit"
  445. );
  446. if(!$statement->execute()) {
  447. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  448. }
  449. $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  450. $statement = $this->DataInterface->DatabaseConnection->prepare(
  451. "SELECT ID, examination FROM context WHERE fk_visit = $fk_visit"
  452. );
  453. if(!$statement->execute()) {
  454. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  455. }
  456. $context = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  457. //
  458. return [
  459. 'result' => 'OK',
  460. 'patient' => $patient,
  461. 'context' => $context
  462. ];
  463. }
  464. /**
  465. * Get patient treatments data.
  466. */
  467. public function patientTreatmentsGet($User, $fk_visit) {
  468. $statement = $this->DataInterface->DatabaseConnection->prepare(
  469. "SELECT patient.* FROM patient, visit WHERE patient.ID = visit.fk_patient AND visit.ID = $fk_visit"
  470. );
  471. if(!$statement->execute()) {
  472. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  473. }
  474. $patient = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  475. $statement = $this->DataInterface->DatabaseConnection->prepare(
  476. "SELECT ID, treatments FROM context WHERE fk_visit = $fk_visit"
  477. );
  478. if(!$statement->execute()) {
  479. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  480. }
  481. $context = $statement->fetchAll(\PDO::FETCH_ASSOC)[0];
  482. //
  483. return [
  484. 'result' => 'OK',
  485. 'patient' => $patient,
  486. 'context' => $context
  487. ];
  488. }
  489. /**
  490. *
  491. */
  492. public function patientPacsQueryPost($User, $data) {
  493. $userID = $User->ID;
  494. $statement = $this->DataInterface->DatabaseConnection->prepare(
  495. "SELECT settings_pacs.data AS data FROM settings_pacs, user WHERE settings_pacs.fk_physician = $userID OR (settings_pacs.fk_center = user.fk_center AND user.ID = $userID)"
  496. );
  497. if(!$statement->execute()) {
  498. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  499. }
  500. $conf = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  501. $cmdLines = [];
  502. $cmdLine = 'findscu '.$conf->serverAddress.' '.$conf->queryPort.' -aet '.$conf->callingAET.' -aec '.$conf->calledAET.
  503. ' -P -k QueryRetrieveLevel="PATIENT" -k PatientID="*" -k PatientName="*'.$data['patient'].'*" -k PatientBirthDate="*" -k PatientSex="*"'.
  504. ' -v 2>&1';
  505. $cmdLines[] = $cmdLine;
  506. $output=null;
  507. $retval=null;
  508. exec($cmdLine, $output, $retval);
  509. if($retval !== 0 || count($output)<1) {
  510. return [
  511. 'result' => 'ERROR',
  512. 'cmdLine' => $cmdLine,
  513. 'output' => $output,
  514. 'retval' => $retval
  515. ];
  516. }
  517. // clean
  518. for($i=0; $i<count($output); $i++) {
  519. if($output[$i]=="I:") {
  520. array_splice($output, $i, 1);
  521. }
  522. }
  523. // parse
  524. $patients = [];
  525. $patient = [];
  526. for($i=0; $i<count($output); $i++) {
  527. if(strpos($output[$i], "I: Find Response: ")===0) {
  528. if(count($patient)!=0) {
  529. $patients[] = $patient;
  530. $patient = [];
  531. }
  532. }
  533. else if(strpos($output[$i], "I: (0010,0010)")===0) {
  534. $patient['PatientName'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  535. //$patient['PatientName'] = trim(str_replace('^',' ',$patient['PatientName']));
  536. }
  537. else if(strpos($output[$i], "I: (0010,0020)")===0) {
  538. $patient['PatientID'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  539. }
  540. else if(strpos($output[$i], "I: (0010,0030)")===0) {
  541. $patient['PatientBirthDate'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  542. }
  543. else if(strpos($output[$i], "I: (0010,0040)")===0) {
  544. $patient['PatientSex'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  545. }
  546. }
  547. if(count($patient)!=0) {
  548. $patients[] = $patient;
  549. }
  550. array_splice($patients, 0, 1);
  551. // studies
  552. $studies = [];
  553. foreach($patients as $P) {
  554. $cmdLine = 'findscu '.$conf->serverAddress.' '.$conf->queryPort.' -aet '.$conf->callingAET.' -aec '.$conf->calledAET.
  555. ' -P -k QueryRetrieveLevel="STUDY" -k PatientID="'.$P['PatientID'].'" -k StudyInstanceUID="" -k StudyID="" -k StudyDate="" -k StudyTime=""'.
  556. ' -v 2>&1';
  557. $cmdLines[] = $cmdLine;
  558. $output=null;
  559. $retval=null;
  560. exec($cmdLine, $output, $retval);
  561. if($retval !== 0 || count($output)<1) {
  562. return [
  563. 'result' => 'ERROR',
  564. 'cmdLine' => $cmdLine,
  565. 'output' => $output,
  566. 'retval' => $retval
  567. ];
  568. }
  569. $study = [
  570. 'PatientName' => $P['PatientName'],
  571. 'PatientBirthDate' => $P['PatientBirthDate'],
  572. 'PatientSex' => $P['PatientSex']
  573. ];
  574. $hasFound = false;
  575. for($i=0; $i<count($output); $i++) {
  576. if(strpos($output[$i], "I: Find Response: ")===0) {
  577. if($hasFound) {
  578. $studies[] = $study;
  579. $study = [
  580. 'PatientName' => $P['PatientName'],
  581. 'PatientBirthDate' => $P['PatientBirthDate'],
  582. 'PatientSex' => $P['PatientSex']
  583. ];
  584. }
  585. $hasFound = true;
  586. }
  587. else if(strpos($output[$i], "I: (0008,0020)")===0) {
  588. $study['StudyDate'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  589. }
  590. else if(strpos($output[$i], "I: (0008,0030)")===0) {
  591. $study['StudyTime'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  592. }
  593. else if(strpos($output[$i], "I: (0020,0010)")===0) {
  594. $study['StudyID'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  595. }
  596. else if(strpos($output[$i], "I: (0020,000d)")===0) {
  597. $study['StudyInstanceUID'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  598. }
  599. else if(strpos($output[$i], "I: (0010,0020)")===0) {
  600. $study['PatientID'] = trim(explode(']', explode('[', $output[$i])[1])[0]);
  601. }
  602. }
  603. $studies[] = $study;
  604. $studies2[] = $output;
  605. }
  606. /*return [
  607. 'result' => 'ERROR',
  608. 'studies2' => $studies2,
  609. 'studies' => $studies,
  610. 'cmdLines' => $cmdLines
  611. ];*/
  612. //sudo movescu www.dicomserver.co.uk 11112 +P 11112 -P -k QueryRetrieveLevel="PATIENT" -k PatientID="874688" -v
  613. return [
  614. 'studies' => $studies,
  615. 'studies2' => $studies2,
  616. 'result' => 'OK',
  617. 'cmdLines' => $cmdLines
  618. ];
  619. }
  620. /**
  621. *
  622. */
  623. public function patientPacsRetrievePost($User, $data) {
  624. /*return [
  625. 'result' => 'ERROR',
  626. 'data' => $data
  627. ];*/
  628. $userID = $User->ID;
  629. $statement = $this->DataInterface->DatabaseConnection->prepare(
  630. "SELECT settings_pacs.data AS data FROM settings_pacs, user WHERE settings_pacs.fk_physician = $userID OR (settings_pacs.fk_center = user.fk_center AND user.ID = $userID)"
  631. );
  632. if(!$statement->execute()) {
  633. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  634. }
  635. $conf = json_decode($statement->fetchAll(\PDO::FETCH_ASSOC)[0]['data']);
  636. // create or select patient
  637. $patientID = 0;
  638. $statement = $this->DataInterface->DatabaseConnection->prepare(
  639. "SELECT * FROM patient WHERE patientID = :patientID OR ctPatientID = :ctPatientID"
  640. );
  641. $statement->bindParam(':patientID', $data['all']['PatientID']);
  642. $statement->bindParam(':ctPatientID', $data['all']['PatientID']);
  643. if(!$statement->execute()) {
  644. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  645. }
  646. $patients = $statement->fetchAll(\PDO::FETCH_ASSOC);
  647. // insert patient
  648. if(count($patients)==0) {
  649. $statement = $this->DataInterface->DatabaseConnection->prepare(
  650. "INSERT INTO patient(ID, patientID, ctPatientID, firstname, lastname, gender, birthDate, fk_user) VALUES(0, :patientID, :ctPatientID, :firstname, :lastname, :gender, :birthDate, :fk_user)"
  651. );
  652. $lastname = $data['all']['PatientName'];
  653. $firstname = "";
  654. $t = explode('^', $lastname);
  655. if(count($t==2)) {
  656. $lastname = $t[0];
  657. $firstname = $t[1];
  658. }
  659. $birth = substr($data['all']['PatientBirthDate'],0,4).'-'.substr($data['all']['PatientBirthDate'],4,2).'-'.substr($data['all']['PatientBirthDate'],6,2);
  660. $statement->bindParam(':patientID', $data['all']['PatientID']);
  661. $statement->bindParam(':ctPatientID', $data['all']['PatientID']);
  662. $statement->bindParam(':firstname', $firstname);
  663. $statement->bindParam(':lastname', $lastname);
  664. $statement->bindParam(':gender', $data['all']['PatientSex']);
  665. $statement->bindParam(':birthDate', $birth);
  666. $statement->bindParam(':fk_user', $userID);
  667. if(!$statement->execute()) {
  668. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  669. }
  670. $patientID = $this->DataInterface->DatabaseConnection->lastInsertId();
  671. }
  672. // select patient
  673. else {
  674. $patientID = $patients[0]['ID'];
  675. }
  676. // create or select visit
  677. $visitID = 0;
  678. $statement = $this->DataInterface->DatabaseConnection->prepare(
  679. "SELECT visit.* FROM visit, patient WHERE patient.ID = visit.fk_patient AND visit.studyInstanceUID = :studyInstanceUID"
  680. );
  681. $statement->bindParam(':studyInstanceUID', $data['all']['StudyInstanceUID']);
  682. if(!$statement->execute()) {
  683. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  684. }
  685. $visits = $statement->fetchAll(\PDO::FETCH_ASSOC);
  686. // insert visit
  687. if(count($visits)==0) {
  688. $statement = $this->DataInterface->DatabaseConnection->prepare(
  689. "INSERT INTO visit(ID, number, visitDate, fk_patient, studyInstanceUID) VALUES(0, :number, :visitDate, :fk_patient, :studyInstanceUID)"
  690. );
  691. $sdate = substr($data['all']['StudyDate'],0,4).'-'.substr($data['all']['StudyDate'],4,2).'-'.substr($data['all']['StudyDate'],6,2);
  692. $statement->bindParam(':number', $data['all']['StudyID']);
  693. $statement->bindParam(':visitDate', $sdate);
  694. $statement->bindParam(':fk_patient', $patientID);
  695. $statement->bindParam(':studyInstanceUID', $data['all']['StudyInstanceUID']);
  696. if(!$statement->execute()) {
  697. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  698. }
  699. $visitID = $this->DataInterface->DatabaseConnection->lastInsertId();
  700. // Insert context
  701. $statement = $this->DataInterface->DatabaseConnection->prepare(
  702. "INSERT INTO context(fk_visit) VALUES(:fk_visit)"
  703. );
  704. $statement->bindParam(':fk_visit', $visitID);
  705. // Error check
  706. if(!$statement->execute()) {
  707. $this->DataInterface->DatabaseConnection->rollback();
  708. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  709. }
  710. }
  711. // select visit
  712. else {
  713. $visitID = $visits[0]['ID'];
  714. }
  715. $od = '../../storage/media/'.$visitID.'/';
  716. \Tools\FS::mkpath($od);
  717. //sudo www.dicomserver.co.uk 11112 +P 11112 -P -k QueryRetrieveLevel="PATIENT" -k PatientID="874688" -v
  718. $cmdLine = 'movescu '.$conf->serverAddress.' '.$conf->queryPort.' +P 11112 -aet '.$conf->callingAET.' -aec '.$conf->calledAET.' -aem '.$conf->callingAET.
  719. ' -P -k QueryRetrieveLevel="STUDY" -k StudyInstanceUID="'.$data['all']['StudyInstanceUID'].'"'.
  720. ' -od '.$od.
  721. ' -v 2>&1';
  722. $output=null;
  723. $retval=null;
  724. exec($cmdLine, $output, $retval);
  725. if($retval !== 0 || count($output)<1) {
  726. return [
  727. 'result' => 'ERROR',
  728. 'cmdLine' => $cmdLine,
  729. 'output' => $output,
  730. 'retval' => $retval
  731. ];
  732. }
  733. // get files
  734. $files = glob("$od*");
  735. $existingFiles = 0;
  736. $newFiles = 0;
  737. foreach($files as $F) {
  738. // skip previously existing files
  739. if(strpos($F, ".dicom")!==false || strpos($F, ".jpeg")!==false || strpos($F, ".mp4")!==false) {
  740. // do nothing
  741. }
  742. // new dicom file
  743. else {
  744. // existing file
  745. if(file_exists("$F.dicom")) {
  746. // do nothing
  747. $existingFiles++;
  748. unlink($F);
  749. }
  750. // real new file
  751. else {
  752. $newFiles++;
  753. rename($F, "$F.dicom");
  754. // multiframe?
  755. $cmdLine = 'dcmdump +P 0028,0008 "'.$F.'.dicom"';
  756. $output=null;
  757. $retval=null;
  758. exec($cmdLine, $output, $retval);
  759. // OK
  760. if($retval === 0 && count($output)==1) {
  761. $tab = explode(' ',trim($output[0]));
  762. $frameCount = intval(str_replace(['[', ']'], '', $tab[2]));
  763. // error
  764. if($frameCount===0) {
  765. return [
  766. 'result' => 'ERROR',
  767. 'output' => $output,
  768. ];
  769. }
  770. // make JPEG
  771. $frames = [];
  772. for($i=1; $i<=$frameCount; $i++) {
  773. $src = "$F.dicom";
  774. $pad = str_pad($i, 3, "0", STR_PAD_LEFT);
  775. $dst = str_replace('.dicom', '-'.$pad.'.jpeg', $src);
  776. $cmdLine = 'dcmj2pnm "'.$src.'" "'.$dst.'" +oj +Jq 100 +F '.$i.' -v 2>&1';
  777. $output=null;
  778. $retval=null;
  779. exec($cmdLine, $output, $retval);
  780. // error
  781. if($retval !== 0 || count($output)<1) {
  782. return [
  783. 'result' => 'ERROR',
  784. 'cmdLine' => $cmdLine,
  785. 'output' => $output,
  786. 'retval' => $retval
  787. ];
  788. }
  789. $frames[] = $dst;
  790. }
  791. // make MP4
  792. $src = "$F.dicom";
  793. $dst = str_replace('.dicom', '-%03d.jpeg', $src);
  794. $mp4 = str_replace('.dicom', '.mp4', $src);
  795. $cmdLine = 'ffmpeg -f image2 -pattern_type sequence -r 11 -i '.$dst.' -y -c:v libx264 -pix_fmt yuv420p '.$mp4;
  796. $output=null;
  797. $retval=null;
  798. exec($cmdLine, $output, $retval);
  799. // error
  800. if($retval !== 0) {
  801. return [
  802. 'result' => 'ERROR',
  803. 'cmdLine' => $cmdLine,
  804. 'output' => $output,
  805. 'retval' => $retval
  806. ];
  807. }
  808. // cleanup
  809. for($i=0; $i<count($frames); $i++) {
  810. unlink($frames[$i]);
  811. }
  812. // get metrics
  813. $cmdLine = 'ffprobe -v error -show_entries stream=width,height,r_frame_rate -of csv=p=0 '.$mp4;
  814. $output=null;
  815. $retval=null;
  816. exec($cmdLine, $output, $retval);
  817. // error
  818. if($retval !== 0 || count($output)!=1) {
  819. return [
  820. 'result' => 'ERROR',
  821. 'cmdLine' => $cmdLine,
  822. 'output' => $output,
  823. 'retval' => $retval
  824. ];
  825. }
  826. $tab = explode(',',trim($output[0]));
  827. if(count($tab)!=3) {
  828. return [
  829. 'result' => 'ERROR',
  830. 'cmdLine' => $cmdLine,
  831. 'output' => $output,
  832. 'retval' => $retval
  833. ];
  834. }
  835. $metrics['width'] = intval($tab[0]);
  836. $metrics['height'] = intval($tab[1]);
  837. $fps = $tab[2];
  838. $tab = explode('/', $fps);
  839. if(count($tab) == 2) {
  840. $fps = intval($tab[0]);
  841. }
  842. else {
  843. $fps = intval($fps);
  844. }
  845. $metrics['fps'] = $fps;
  846. }
  847. // singleframe
  848. else {
  849. $cmdLine = "dcmj2pnm $F.dicom $F.jpeg +oj +Jq 100 +F 1 -v 2>&1";
  850. $output=null;
  851. $retval=null;
  852. exec($cmdLine, $output, $retval);
  853. // error
  854. if($retval !== 0 || count($output)<1) {
  855. return [
  856. 'result' => 'ERROR',
  857. 'cmdLine' => $cmdLine,
  858. 'output' => $output,
  859. 'retval' => $retval
  860. ];
  861. }
  862. // image metrics
  863. $metrics = ['width' => 0, 'height' => 0, 'pxwidth' => 0, 'pxheight' => 0];
  864. list($metrics['width'], $metrics['height'], $type, $attr) = getimagesize("$F.jpeg");
  865. }
  866. // dicom metrics
  867. $cmdLine = 'dcmdump -s +P 0018,602c +P 0018,602e +P 0028,0008 "'.$F.'.dicom"';
  868. $output=null;
  869. $retval=null;
  870. exec($cmdLine, $output, $retval);
  871. // error
  872. if($retval !== 0) {
  873. return [
  874. 'result' => 'ERROR',
  875. 'cmdLine' => $cmdLine,
  876. 'output' => $output,
  877. 'retval' => $retval
  878. ];
  879. }
  880. if(count($output)>=2) {
  881. $tab = explode(' ',trim($output[0]));
  882. $metrics['pxwidth'] = floatval($tab[2])*10.0;
  883. $tab = explode(' ',trim($output[1]));
  884. $metrics['pxheight'] = floatval($tab[2])*10.0;
  885. }
  886. if(count($output)==3) {
  887. $tab = explode(' ',trim($output[2]));
  888. $metrics['frameCount'] = intval(str_replace(['[', ']'], '', $tab[2]));
  889. }
  890. // insert new media
  891. $statement = $this->DataInterface->DatabaseConnection->prepare(
  892. "INSERT INTO media(ID, side, filename, metrics, fk_visit) VALUES(0, 'unknown', :filename, :metrics, :fk_visit)"
  893. );
  894. $statement->bindParam(':filename', basename("$F.dicom"));
  895. $statement->bindParam(':metrics', json_encode($metrics));
  896. $statement->bindParam(':fk_visit', $visitID);
  897. if(!$statement->execute()) {
  898. return ['result' => 'ERROR', 'reason' => 'internal_error', 'message' => 'Database error', 'data' => $statement->errorInfo()];
  899. }
  900. }
  901. }
  902. }
  903. return [
  904. 'result' => 'OK',
  905. 'cmdLine' => $cmdLine,
  906. 'files' => $files,
  907. 'output' => $output,
  908. 'patientID' => $patientID,
  909. 'visitID' => $visitID,
  910. 'existingFiles' => $existingFiles,
  911. 'newFiles' => $newFiles
  912. ];
  913. }
  914. }
  915. }