feat: add HostApp and CodingSys management APIs with CRUD operations

This commit is contained in:
mahdahar 2026-02-27 16:31:55 +07:00
parent 5e0a7f21f5
commit 24e0293824
22 changed files with 1624 additions and 107 deletions

View File

@ -206,6 +206,33 @@ $routes->group('api', function ($routes) {
$routes->patch('/', 'Organization\WorkstationController::update');
$routes->delete('/', 'Organization\WorkstationController::delete');
});
// HostApp
$routes->group('hostapp', function ($routes) {
$routes->get('/', 'Organization\HostAppController::index');
$routes->get('(:any)', 'Organization\HostAppController::show/$1');
$routes->post('/', 'Organization\HostAppController::create');
$routes->patch('/', 'Organization\HostAppController::update');
$routes->delete('/', 'Organization\HostAppController::delete');
});
// HostComPara
$routes->group('hostcompara', function ($routes) {
$routes->get('/', 'Organization\HostComParaController::index');
$routes->get('(:any)', 'Organization\HostComParaController::show/$1');
$routes->post('/', 'Organization\HostComParaController::create');
$routes->patch('/', 'Organization\HostComParaController::update');
$routes->delete('/', 'Organization\HostComParaController::delete');
});
// CodingSys
$routes->group('codingsys', function ($routes) {
$routes->get('/', 'Organization\CodingSysController::index');
$routes->get('(:num)', 'Organization\CodingSysController::show/$1');
$routes->post('/', 'Organization\CodingSysController::create');
$routes->patch('/', 'Organization\CodingSysController::update');
$routes->delete('/', 'Organization\CodingSysController::delete');
});
});
// Infrastructure
@ -278,7 +305,7 @@ $routes->group('api', function ($routes) {
$routes->delete('/', 'Test\TestMapController::delete');
// Filter routes
$routes->get('by-testsite/(:num)', 'Test\TestMapController::showByTestSite/$1');
$routes->get('by-testcode/(:any)', 'Test\TestMapController::showByTestCode/$1');
// TestMapDetail nested routes
$routes->group('detail', function ($routes) {

View File

@ -0,0 +1,97 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\CodingSysModel;
class CodingSysController extends BaseController {
use ResponseTrait;
protected $db;
protected $model;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new CodingSysModel();
}
public function index() {
$filter = [
'CodingSysAbb' => $this->request->getVar('CodingSysAbb'),
'FullText' => $this->request->getVar('FullText'),
];
$builder = $this->model;
if (!empty($filter['CodingSysAbb'])) {
$builder->like('CodingSysAbb', $filter['CodingSysAbb'], 'both');
}
if (!empty($filter['FullText'])) {
$builder->like('FullText', $filter['FullText'], 'both');
}
$rows = $builder->findAll();
if (empty($rows)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => []], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $rows], 200);
}
public function show($CodingSysID = null) {
$row = $this->model->where('CodingSysID', $CodingSysID)->first();
if (empty($row)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => null], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $row], 200);
}
public function delete() {
try {
$input = $this->request->getJSON(true);
$id = $input['CodingSysID'] ?? null;
if (!$id) {
return $this->failValidationErrors('CodingSysID is required.');
}
$this->model->delete($id);
return $this->respondDeleted(['status' => 'success', 'message' => "{$id} deleted successfully."]);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function create() {
$input = $this->request->getJSON(true);
try {
$id = $this->model->insert($input, true);
return $this->respondCreated(['status' => 'success', 'message' => 'data created successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update() {
$input = $this->request->getJSON(true);
try {
$id = $input['CodingSysID'] ?? null;
if (!$id) {
return $this->failValidationErrors('CodingSysID is required.');
}
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\HostAppModel;
class HostAppController extends BaseController {
use ResponseTrait;
protected $db;
protected $model;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new HostAppModel();
}
public function index() {
$filter = [
'HostAppID' => $this->request->getVar('HostAppID'),
'HostAppName' => $this->request->getVar('HostAppName'),
];
$builder = $this->model->select('hostapp.*, site.SiteName')
->join('site', 'site.SiteID = hostapp.SiteID', 'left');
if (!empty($filter['HostAppID'])) {
$builder->like('hostapp.HostAppID', $filter['HostAppID'], 'both');
}
if (!empty($filter['HostAppName'])) {
$builder->like('hostapp.HostAppName', $filter['HostAppName'], 'both');
}
$rows = $builder->findAll();
if (empty($rows)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => []], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $rows], 200);
}
public function show($HostAppID = null) {
$row = $this->model->select('hostapp.*, site.SiteName')
->join('site', 'site.SiteID = hostapp.SiteID', 'left')
->where('hostapp.HostAppID', $HostAppID)
->first();
if (empty($row)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => null], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $row], 200);
}
public function delete() {
try {
$input = $this->request->getJSON(true);
$id = $input['HostAppID'] ?? null;
if (!$id) {
return $this->failValidationErrors('HostAppID is required.');
}
$this->model->delete($id);
return $this->respondDeleted(['status' => 'success', 'message' => "{$id} deleted successfully."]);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function create() {
$input = $this->request->getJSON(true);
try {
$id = $this->model->insert($input, true);
return $this->respondCreated(['status' => 'success', 'message' => 'data created successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update() {
$input = $this->request->getJSON(true);
try {
$id = $input['HostAppID'] ?? null;
if (!$id) {
return $this->failValidationErrors('HostAppID is required.');
}
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -0,0 +1,101 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\HostComParaModel;
class HostComParaController extends BaseController {
use ResponseTrait;
protected $db;
protected $model;
public function __construct() {
$this->db = \Config\Database::connect();
$this->model = new HostComParaModel();
}
public function index() {
$filter = [
'HostAppID' => $this->request->getVar('HostAppID'),
'HostIP' => $this->request->getVar('HostIP'),
];
$builder = $this->model->select('hostcompara.*, hostapp.HostAppName')
->join('hostapp', 'hostapp.HostAppID = hostcompara.HostAppID', 'left');
if (!empty($filter['HostAppID'])) {
$builder->where('hostcompara.HostAppID', $filter['HostAppID']);
}
if (!empty($filter['HostIP'])) {
$builder->like('hostcompara.HostIP', $filter['HostIP'], 'both');
}
$rows = $builder->findAll();
if (empty($rows)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => []], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $rows], 200);
}
public function show($HostAppID = null) {
$row = $this->model->select('hostcompara.*, hostapp.HostAppName')
->join('hostapp', 'hostapp.HostAppID = hostcompara.HostAppID', 'left')
->where('hostcompara.HostAppID', $HostAppID)
->first();
if (empty($row)) {
return $this->respond(['status' => 'success', 'message' => 'no Data.', 'data' => null], 200);
}
return $this->respond(['status' => 'success', 'message' => 'fetch success', 'data' => $row], 200);
}
public function delete() {
try {
$input = $this->request->getJSON(true);
$id = $input['HostAppID'] ?? null;
if (!$id) {
return $this->failValidationErrors('HostAppID is required.');
}
$this->model->delete($id);
return $this->respondDeleted(['status' => 'success', 'message' => "{$id} deleted successfully."]);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function create() {
$input = $this->request->getJSON(true);
try {
$id = $this->model->insert($input, true);
return $this->respondCreated(['status' => 'success', 'message' => 'data created successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update() {
$input = $this->request->getJSON(true);
try {
$id = $input['HostAppID'] ?? null;
if (!$id) {
return $this->failValidationErrors('HostAppID is required.');
}
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -20,7 +20,7 @@ class TestMapController extends BaseController {
$this->model = new TestMapModel;
$this->modelDetail = new TestMapDetailModel;
$this->rules = [
'TestSiteID' => 'required|integer',
'TestCode' => 'required',
'HostID' => 'required|integer',
'ClientID' => 'required|integer',
];
@ -105,10 +105,10 @@ class TestMapController extends BaseController {
}
}
public function showByTestSite($testSiteID = null) {
if (!$testSiteID) { return $this->failValidationErrors('TestSiteID is required.'); }
public function showByTestCode($testCode = null) {
if (!$testCode) { return $this->failValidationErrors('TestCode is required.'); }
$rows = $this->model->getMappingsByTestSite($testSiteID);
$rows = $this->model->getMappingsByTestCode($testCode);
if (empty($rows)) { return $this->respond([ 'status' => 'success', 'message' => "no Data.", 'data' => [] ], 200); }
$rows = ValueSet::transformLabels($rows, [
@ -116,11 +116,6 @@ class TestMapController extends BaseController {
'ClientType' => 'entity_type',
]);
// Include testmapdetail records for each mapping
foreach ($rows as &$row) {
$row['details'] = $this->modelDetail->getDetailsByTestMap($row['TestMapID']);
}
return $this->respond([ 'status' => 'success', 'message'=> "Data fetched successfully", 'data' => $rows ], 200);
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AddTestSiteIDToTestmap extends Migration
{
public function up()
{
$this->forge->addColumn('testmap', [
'TestSiteID' => [
'type' => 'INT',
'unsigned' => true,
'null' => true,
'after' => 'TestMapID'
]
]);
// Add foreign key if it doesn't exist
$this->forge->addForeignKey('TestSiteID', 'testdefsite', 'TestSiteID', 'CASCADE', 'CASCADE');
}
public function down()
{
$this->forge->dropForeignKey('testmap', 'testmap_TestSiteID_foreign');
$this->forge->dropColumn('testmap', 'TestSiteID');
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreateHostAppAndCodingSys extends Migration {
public function up() {
// Table: hostapp
$this->forge->addField([
'HostAppID' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => false],
'HostAppName' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => false],
'SiteID' => ['type' => 'INT', 'unsigned' => true, 'null' => true],
'CreateDate' => ['type' => 'DATETIME', 'null' => true],
'EndDate' => ['type' => 'DATETIME', 'null' => true]
]);
$this->forge->addKey('HostAppID', true);
$this->forge->createTable('hostapp');
// Table: hostcompara
$this->forge->addField([
'HostAppID' => ['type' => 'VARCHAR', 'constraint' => 5, 'null' => false],
'HostIP' => ['type' => 'VARCHAR', 'constraint' => 15, 'null' => true],
'HostPort' => ['type' => 'VARCHAR', 'constraint' => 6, 'null' => true],
'HostPwd' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'CreateDate' => ['type' => 'DATETIME', 'null' => true],
'EndDate' => ['type' => 'DATETIME', 'null' => true]
]);
$this->forge->addKey('HostAppID', true);
$this->forge->createTable('hostcompara');
// Table: codingsys
$this->forge->addField([
'CodingSysID' => ['type' => 'INT', 'unsigned' => true, 'auto_increment' => true],
'CodingSysAbb' => ['type' => 'VARCHAR', 'constraint' => 6, 'null' => false, 'unique' => true],
'FullText' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'Description' => ['type' => 'VARCHAR', 'constraint' => 255, 'null' => true],
'CreateDate' => ['type' => 'DATETIME', 'null' => true],
'EndDate' => ['type' => 'DATETIME', 'null' => true]
]);
$this->forge->addKey('CodingSysID', true);
$this->forge->createTable('codingsys');
}
public function down() {
$this->forge->dropTable('codingsys');
$this->forge->dropTable('hostcompara');
$this->forge->dropTable('hostapp');
}
}

View File

@ -0,0 +1,222 @@
<?php
namespace App\Database\Seeds;
use CodeIgniter\Database\Seeder;
class HostAppCodingSysSeeder extends Seeder
{
public function run()
{
$now = date('Y-m-d H:i:s');
// Clear existing data first (avoid foreign key constraint issues by ordering)
$this->db->table('hostcompara')->emptyTable();
$this->db->table('hostapp')->emptyTable();
$this->db->table('codingsys')->emptyTable();
// HostApp - Host Applications
$hostAppData = [
[
'HostAppID' => 'LIS01',
'HostAppName' => 'Laboratory Information System Main',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'LIS02',
'HostAppName' => 'Laboratory Information System Backup',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'EMR01',
'HostAppName' => 'Electronic Medical Record System',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'PACS01',
'HostAppName' => 'Picture Archiving System',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'BILL01',
'HostAppName' => 'Billing System',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'INS01',
'HostAppName' => 'Insurance System Integration',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'OLD01',
'HostAppName' => 'Legacy Laboratory System',
'SiteID' => 1,
'CreateDate' => $now,
'EndDate' => date('Y-m-d H:i:s', strtotime('-1 year'))
],
];
$this->db->table('hostapp')->insertBatch($hostAppData);
// HostComPara - Host Communication Parameters
$hostComParaData = [
[
'HostAppID' => 'LIS01',
'HostIP' => '192.168.1.10',
'HostPort' => '8080',
'HostPwd' => 'lis_main_pass_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'LIS02',
'HostIP' => '192.168.1.11',
'HostPort' => '8081',
'HostPwd' => 'lis_backup_pass_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'EMR01',
'HostIP' => '192.168.1.20',
'HostPort' => '443',
'HostPwd' => 'emr_secure_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'PACS01',
'HostIP' => '192.168.1.30',
'HostPort' => '8042',
'HostPwd' => 'pacs_dicom_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'BILL01',
'HostIP' => '192.168.1.40',
'HostPort' => '8443',
'HostPwd' => 'bill_payment_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'INS01',
'HostIP' => '192.168.1.50',
'HostPort' => '443',
'HostPwd' => 'ins_claim_2024',
'CreateDate' => $now,
'EndDate' => null
],
[
'HostAppID' => 'OLD01',
'HostIP' => '192.168.1.99',
'HostPort' => '8080',
'HostPwd' => 'old_legacy_pass',
'CreateDate' => $now,
'EndDate' => date('Y-m-d H:i:s', strtotime('-1 year'))
],
];
$this->db->table('hostcompara')->insertBatch($hostComParaData);
// CodingSys - Coding Systems
$codingSysData = [
[
'CodingSysAbb' => 'ICD10',
'FullText' => 'International Classification of Diseases 10th Revision',
'Description' => 'Medical diagnosis coding system for diseases and health conditions',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'ICD10PCS',
'FullText' => 'ICD-10 Procedure Coding System',
'Description' => 'Classification system for inpatient hospital procedures',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'LOINC',
'FullText' => 'Logical Observation Identifiers Names and Codes',
'Description' => 'Standard for identifying medical laboratory observations',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'SNOMED',
'FullText' => 'SNOMED CT',
'Description' => 'Systematized Nomenclature of Medicine - Clinical Terms',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'CPT',
'FullText' => 'Current Procedural Terminology',
'Description' => 'Medical code set for medical procedures and services',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'HCPCS',
'FullText' => 'Healthcare Common Procedure Coding System',
'Description' => 'Medical code set for equipment, supplies, and services',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'RXNORM',
'FullText' => 'RxNorm',
'Description' => 'Normalized naming system for medications',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'NDC',
'FullText' => 'National Drug Code',
'Description' => 'Unique identifier for human drugs in the United States',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'UCUM',
'FullText' => 'Unified Code for Units of Measure',
'Description' => 'Standard for units of measurement in clinical and scientific contexts',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'CVX',
'FullText' => 'Vaccines Administered',
'Description' => 'Vaccine codes for immunization records',
'CreateDate' => $now,
'EndDate' => null
],
[
'CodingSysAbb' => 'ICD9',
'FullText' => 'International Classification of Diseases 9th Revision',
'Description' => 'Legacy medical diagnosis coding system',
'CreateDate' => $now,
'EndDate' => date('Y-m-d H:i:s', strtotime('-2 years'))
],
[
'CodingSysAbb' => 'ICD9CM',
'FullText' => 'ICD-9-CM',
'Description' => 'Legacy procedure coding system',
'CreateDate' => $now,
'EndDate' => date('Y-m-d H:i:s', strtotime('-2 years'))
],
];
$this->db->table('codingsys')->insertBatch($codingSysData);
}
}

View File

@ -7,6 +7,25 @@ use App\Libraries\ValueSet;
class TestSeeder extends Seeder
{
private array $entityTypes = [];
private function loadEntityTypes(): void
{
if (empty($this->entityTypes)) {
$json = file_get_contents(APPPATH . 'Libraries/Data/entity_type.json');
$data = json_decode($json, true);
foreach ($data['values'] as $item) {
$this->entityTypes[$item['key']] = $item['key'];
}
}
}
private function getEntityType(string $name): ?string
{
$this->loadEntityTypes();
return $this->entityTypes[$name] ?? null;
}
private function getKey(string $lookupName, string $key): ?string
{
$data = ValueSet::getRaw($lookupName);
@ -322,9 +341,14 @@ class TestSeeder extends Seeder
],
];
// Load entity types for mapping
$entitySite = $this->getEntityType('SITE') ?? 'SITE';
$entityWst = $this->getEntityType('WST') ?? 'WST';
$entityInst = $this->getEntityType('INST') ?? 'INST';
foreach ($testMappings as $mapping) {
// Site → Workstation mapping (one header per workstation)
$testMapSiteWsID = $getTestMapID('SITE', '1', 'WORKSTATION', (string)$mapping['siteToWs']['ws']);
$testMapSiteWsID = $getTestMapID($entitySite, '1', $entityWst, (string)$mapping['siteToWs']['ws']);
foreach ($mapping['tests'] as $testCode) {
$addDetail($testMapSiteWsID, $testCode, $testCode, $mapping['siteToWs']['con'], $testCode, $testCode);
}
@ -334,7 +358,7 @@ class TestSeeder extends Seeder
// Workstation → Instrument mapping (one header per instrument)
if ($mapping['wsToInst'] !== null) {
$testMapWsInstID = $getTestMapID('WORKSTATION', (string)$mapping['wsToInst']['ws'], 'INSTRUMENT', (string)$mapping['wsToInst']['inst']);
$testMapWsInstID = $getTestMapID($entityWst, (string)$mapping['wsToInst']['ws'], $entityInst, (string)$mapping['wsToInst']['inst']);
foreach ($mapping['tests'] as $testCode) {
$addDetail($testMapWsInstID, $testCode, $testCode, $mapping['wsToInst']['con'], $testCode, $testCode);
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
class CodingSysModel extends BaseModel {
protected $table = 'codingsys';
protected $primaryKey = 'CodingSysID';
protected $allowedFields = ['CodingSysAbb', 'FullText', 'Description', 'CreateDate', 'EndDate'];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'EndDate';
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
class HostAppModel extends BaseModel {
protected $table = 'hostapp';
protected $primaryKey = 'HostAppID';
protected $allowedFields = ['HostAppID', 'HostAppName', 'SiteID', 'CreateDate', 'EndDate'];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'EndDate';
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Models\Organization;
use App\Models\BaseModel;
class HostComParaModel extends BaseModel {
protected $table = 'hostcompara';
protected $primaryKey = 'HostAppID';
protected $allowedFields = ['HostAppID', 'HostIP', 'HostPort', 'HostPwd', 'CreateDate', 'EndDate'];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'EndDate';
}

View File

@ -5,10 +5,19 @@ namespace App\Models\Test;
use App\Models\BaseModel;
class TestMapModel extends BaseModel {
private function getEntityTypes(): array {
$json = file_get_contents(APPPATH . 'Libraries/Data/entity_type.json');
$data = json_decode($json, true);
$types = [];
foreach ($data['values'] as $item) {
$types[$item['key']] = $item['key'];
}
return $types;
}
protected $table = 'testmap';
protected $primaryKey = 'TestMapID';
protected $allowedFields = [
'TestSiteID',
'HostType',
'HostID',
'ClientType',
@ -28,56 +37,69 @@ class TestMapModel extends BaseModel {
*/
public function getUniqueGroupings() {
$db = $this->db;
$types = $this->getEntityTypes();
$siteType = $types['SITE'] ?? 'SITE';
$wsType = $types['WST'] ?? 'WST';
$instType = $types['INST'] ?? 'INST';
$sql = "
SELECT
tm.TestMapID,
tm.TestSiteID,
tm.HostType,
tm.HostID,
CASE
WHEN tm.HostType = 'SITE' THEN s.SiteName
WHEN tm.HostType = 'WORKSTATION' THEN ws.WorkstationName
WHEN tm.HostType = 'INSTRUMENT' THEN el.InstrumentName
WHEN tm.HostType = ? THEN s.SiteName
WHEN tm.HostType = ? THEN ws.WorkstationName
WHEN tm.HostType = ? THEN el.InstrumentName
ELSE tm.HostID
END as HostName,
tm.ClientType,
tm.ClientID,
CASE
WHEN tm.ClientType = 'SITE' THEN cs.SiteName
WHEN tm.ClientType = 'WORKSTATION' THEN cws.WorkstationName
WHEN tm.ClientType = 'INSTRUMENT' THEN cel.InstrumentName
WHEN tm.ClientType = ? THEN cs.SiteName
WHEN tm.ClientType = ? THEN cws.WorkstationName
WHEN tm.ClientType = ? THEN cel.InstrumentName
ELSE tm.ClientID
END as ClientName
FROM testmap tm
LEFT JOIN site s ON tm.HostType = 'SITE' AND s.SiteID = tm.HostID
LEFT JOIN workstation ws ON tm.HostType = 'WORKSTATION' AND ws.WorkstationID = tm.HostID
LEFT JOIN equipmentlist el ON tm.HostType = 'INSTRUMENT' AND el.EID = tm.HostID
LEFT JOIN site cs ON tm.ClientType = 'SITE' AND cs.SiteID = tm.ClientID
LEFT JOIN workstation cws ON tm.ClientType = 'WORKSTATION' AND cws.WorkstationID = tm.ClientID
LEFT JOIN equipmentlist cel ON tm.ClientType = 'INSTRUMENT' AND cel.EID = tm.ClientID
LEFT JOIN site s ON tm.HostType = ? AND s.SiteID = tm.HostID
LEFT JOIN workstation ws ON tm.HostType = ? AND ws.WorkstationID = tm.HostID
LEFT JOIN equipmentlist el ON tm.HostType = ? AND el.EID = tm.HostID
LEFT JOIN site cs ON tm.ClientType = ? AND cs.SiteID = tm.ClientID
LEFT JOIN workstation cws ON tm.ClientType = ? AND cws.WorkstationID = tm.ClientID
LEFT JOIN equipmentlist cel ON tm.ClientType = ? AND cel.EID = tm.ClientID
WHERE tm.EndDate IS NULL
";
return $db->query($sql)->getResultArray();
return $db->query($sql, [
$siteType, $wsType, $instType,
$siteType, $wsType, $instType,
$siteType, $wsType, $instType,
$siteType, $wsType, $instType
])->getResultArray();
}
/**
* Get test mappings by test site
*/
public function getMappingsByTestSite($testSiteID) {
return $this->where('TestSiteID', $testSiteID)
->where('EndDate IS NULL')
public function getMappingsByTestCode($testCode) {
return $this->select('testmap.*')
->join('testmapdetail', 'testmapdetail.TestMapID = testmap.TestMapID', 'inner')
->groupStart()
->where('testmapdetail.HostTestCode', $testCode)
->orWhere('testmapdetail.ClientTestCode', $testCode)
->groupEnd()
->where('testmap.EndDate IS NULL')
->where('testmapdetail.EndDate IS NULL')
->groupBy('testmap.TestMapID')
->findAll();
}
/**
* Get test mappings with details by test site
*/
public function getMappingsWithDetailsByTestSite($testSiteID) {
public function getMappingsWithDetailsByTestCode($testCode) {
return $this->select('testmap.*, testmapdetail.*')
->join('testmapdetail', 'testmapdetail.TestMapID = testmap.TestMapID', 'left')
->where('testmap.TestSiteID', $testSiteID)
->join('testmapdetail', 'testmapdetail.TestMapID = testmap.TestMapID', 'inner')
->groupStart()
->where('testmapdetail.HostTestCode', $testCode)
->orWhere('testmapdetail.ClientTestCode', $testCode)
->groupEnd()
->where('testmap.EndDate IS NULL')
->where('testmapdetail.EndDate IS NULL')
->findAll();

View File

@ -1540,6 +1540,343 @@ paths:
responses:
'200':
description: Workstation details
/api/organization/hostapp:
get:
tags:
- Organization
summary: List host applications
security:
- bearerAuth: []
parameters:
- name: HostAppID
in: query
schema:
type: string
- name: HostAppName
in: query
schema:
type: string
responses:
'200':
description: List of host applications
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/HostApp'
post:
tags:
- Organization
summary: Create host application
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/HostApp'
responses:
'201':
description: Host application created
patch:
tags:
- Organization
summary: Update host application
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
HostAppName:
type: string
SiteID:
type: integer
responses:
'200':
description: Host application updated
delete:
tags:
- Organization
summary: Delete host application (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
responses:
'200':
description: Host application deleted
/api/organization/hostapp/{id}:
get:
tags:
- Organization
summary: Get host application by ID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Host application details
content:
application/json:
schema:
$ref: '#/components/schemas/HostApp'
/api/organization/hostcompara:
get:
tags:
- Organization
summary: List host communication parameters
security:
- bearerAuth: []
parameters:
- name: HostAppID
in: query
schema:
type: string
- name: HostIP
in: query
schema:
type: string
responses:
'200':
description: List of host communication parameters
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/HostComPara'
post:
tags:
- Organization
summary: Create host communication parameters
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/HostComPara'
responses:
'201':
description: Host communication parameters created
patch:
tags:
- Organization
summary: Update host communication parameters
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
HostIP:
type: string
HostPort:
type: string
HostPwd:
type: string
responses:
'200':
description: Host communication parameters updated
delete:
tags:
- Organization
summary: Delete host communication parameters (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
responses:
'200':
description: Host communication parameters deleted
/api/organization/hostcompara/{id}:
get:
tags:
- Organization
summary: Get host communication parameters by HostAppID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Host communication parameters details
content:
application/json:
schema:
$ref: '#/components/schemas/HostComPara'
/api/organization/codingsys:
get:
tags:
- Organization
summary: List coding systems
security:
- bearerAuth: []
parameters:
- name: CodingSysAbb
in: query
schema:
type: string
- name: FullText
in: query
schema:
type: string
responses:
'200':
description: List of coding systems
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '#/components/schemas/CodingSys'
post:
tags:
- Organization
summary: Create coding system
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CodingSys'
responses:
'201':
description: Coding system created
patch:
tags:
- Organization
summary: Update coding system
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- CodingSysID
properties:
CodingSysID:
type: integer
CodingSysAbb:
type: string
FullText:
type: string
Description:
type: string
responses:
'200':
description: Coding system updated
delete:
tags:
- Organization
summary: Delete coding system (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- CodingSysID
properties:
CodingSysID:
type: integer
responses:
'200':
description: Coding system deleted
/api/organization/codingsys/{id}:
get:
tags:
- Organization
summary: Get coding system by ID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Coding system details
content:
application/json:
schema:
$ref: '#/components/schemas/CodingSys'
/api/patvisit:
get:
tags:
@ -2781,12 +3118,12 @@ paths:
get:
tags:
- Tests
summary: List all test mappings (unique groupings)
summary: List all test mappings
security:
- bearerAuth: []
responses:
'200':
description: List of unique test mapping groupings
description: List of test mappings
content:
application/json:
schema:
@ -2802,6 +3139,8 @@ paths:
items:
type: object
properties:
TestMapID:
type: integer
HostType:
type: string
HostID:
@ -2827,9 +3166,9 @@ paths:
schema:
type: object
properties:
TestSiteID:
type: integer
description: Test Site ID (required)
TestCode:
type: string
description: Test Code (required) - maps to HostTestCode or ClientTestCode
HostType:
type: string
description: Host type code
@ -2859,7 +3198,7 @@ paths:
ClientTestName:
type: string
required:
- TestSiteID
- TestCode
responses:
'201':
description: Test mapping created
@ -2892,8 +3231,9 @@ paths:
TestMapID:
type: integer
description: Test Map ID (required)
TestSiteID:
type: integer
TestCode:
type: string
description: Test Code - maps to HostTestCode or ClientTestCode
HostType:
type: string
HostID:
@ -2986,23 +3326,23 @@ paths:
$ref: '#/components/schemas/TestMap'
'404':
description: Test mapping not found
/api/test/testmap/by-testsite/{testSiteID}:
/api/test/testmap/by-testcode/{testCode}:
get:
tags:
- Tests
summary: Get test mappings by test site with details
summary: Get test mappings by test code with details
security:
- bearerAuth: []
parameters:
- name: testSiteID
- name: testCode
in: path
required: true
schema:
type: integer
description: Test Site ID
type: string
description: Test Code (matches HostTestCode or ClientTestCode)
responses:
'200':
description: List of test mappings with details for the test site
description: List of test mappings with details for the test code
content:
application/json:
schema:
@ -4710,6 +5050,64 @@ components:
type: integer
DepartmentID:
type: integer
HostApp:
type: object
properties:
HostAppID:
type: string
maxLength: 5
HostAppName:
type: string
SiteID:
type: integer
SiteName:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
HostComPara:
type: object
properties:
HostAppID:
type: string
maxLength: 5
HostAppName:
type: string
HostIP:
type: string
maxLength: 15
HostPort:
type: string
maxLength: 6
HostPwd:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
CodingSys:
type: object
properties:
CodingSysID:
type: integer
CodingSysAbb:
type: string
maxLength: 6
FullText:
type: string
Description:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
Specimen:
type: object
properties:
@ -5284,8 +5682,6 @@ components:
properties:
TestMapID:
type: integer
TestSiteID:
type: integer
HostType:
type: string
description: Host type code (e.g., SITE, WORKSTATION, INSTRUMENT)

View File

@ -113,6 +113,12 @@ components:
$ref: './components/schemas/organization.yaml#/Department'
Workstation:
$ref: './components/schemas/organization.yaml#/Workstation'
HostApp:
$ref: './components/schemas/organization.yaml#/HostApp'
HostComPara:
$ref: './components/schemas/organization.yaml#/HostComPara'
CodingSys:
$ref: './components/schemas/organization.yaml#/CodingSys'
# Specimen schemas
Specimen:

View File

@ -57,3 +57,64 @@ Workstation:
type: integer
DepartmentID:
type: integer
HostApp:
type: object
properties:
HostAppID:
type: string
maxLength: 5
HostAppName:
type: string
SiteID:
type: integer
SiteName:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
HostComPara:
type: object
properties:
HostAppID:
type: string
maxLength: 5
HostAppName:
type: string
HostIP:
type: string
maxLength: 15
HostPort:
type: string
maxLength: 6
HostPwd:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time
CodingSys:
type: object
properties:
CodingSysID:
type: integer
CodingSysAbb:
type: string
maxLength: 6
FullText:
type: string
Description:
type: string
CreateDate:
type: string
format: date-time
EndDate:
type: string
format: date-time

View File

@ -426,8 +426,6 @@ TestMap:
properties:
TestMapID:
type: integer
TestSiteID:
type: integer
HostType:
type: string
description: Host type code (e.g., SITE, WORKSTATION, INSTRUMENT)

View File

@ -365,3 +365,343 @@
responses:
'200':
description: Workstation details
# HostApp
/api/organization/hostapp:
get:
tags: [Organization]
summary: List host applications
security:
- bearerAuth: []
parameters:
- name: HostAppID
in: query
schema:
type: string
- name: HostAppName
in: query
schema:
type: string
responses:
'200':
description: List of host applications
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '../components/schemas/organization.yaml#/HostApp'
post:
tags: [Organization]
summary: Create host application
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/HostApp'
responses:
'201':
description: Host application created
patch:
tags: [Organization]
summary: Update host application
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
HostAppName:
type: string
SiteID:
type: integer
responses:
'200':
description: Host application updated
delete:
tags: [Organization]
summary: Delete host application (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
responses:
'200':
description: Host application deleted
/api/organization/hostapp/{id}:
get:
tags: [Organization]
summary: Get host application by ID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Host application details
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/HostApp'
# HostComPara
/api/organization/hostcompara:
get:
tags: [Organization]
summary: List host communication parameters
security:
- bearerAuth: []
parameters:
- name: HostAppID
in: query
schema:
type: string
- name: HostIP
in: query
schema:
type: string
responses:
'200':
description: List of host communication parameters
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '../components/schemas/organization.yaml#/HostComPara'
post:
tags: [Organization]
summary: Create host communication parameters
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/HostComPara'
responses:
'201':
description: Host communication parameters created
patch:
tags: [Organization]
summary: Update host communication parameters
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
HostIP:
type: string
HostPort:
type: string
HostPwd:
type: string
responses:
'200':
description: Host communication parameters updated
delete:
tags: [Organization]
summary: Delete host communication parameters (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- HostAppID
properties:
HostAppID:
type: string
responses:
'200':
description: Host communication parameters deleted
/api/organization/hostcompara/{id}:
get:
tags: [Organization]
summary: Get host communication parameters by HostAppID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: Host communication parameters details
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/HostComPara'
# CodingSys
/api/organization/codingsys:
get:
tags: [Organization]
summary: List coding systems
security:
- bearerAuth: []
parameters:
- name: CodingSysAbb
in: query
schema:
type: string
- name: FullText
in: query
schema:
type: string
responses:
'200':
description: List of coding systems
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
data:
type: array
items:
$ref: '../components/schemas/organization.yaml#/CodingSys'
post:
tags: [Organization]
summary: Create coding system
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/CodingSys'
responses:
'201':
description: Coding system created
patch:
tags: [Organization]
summary: Update coding system
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- CodingSysID
properties:
CodingSysID:
type: integer
CodingSysAbb:
type: string
FullText:
type: string
Description:
type: string
responses:
'200':
description: Coding system updated
delete:
tags: [Organization]
summary: Delete coding system (soft delete)
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- CodingSysID
properties:
CodingSysID:
type: integer
responses:
'200':
description: Coding system deleted
/api/organization/codingsys/{id}:
get:
tags: [Organization]
summary: Get coding system by ID
security:
- bearerAuth: []
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Coding system details
content:
application/json:
schema:
$ref: '../components/schemas/organization.yaml#/CodingSys'

View File

@ -24,8 +24,6 @@
properties:
TestMapID:
type: integer
TestSiteID:
type: integer
HostType:
type: string
HostID:
@ -51,9 +49,9 @@
schema:
type: object
properties:
TestSiteID:
type: integer
description: Test Site ID (required)
TestCode:
type: string
description: Test Code (required) - maps to HostTestCode or ClientTestCode
HostType:
type: string
description: Host type code
@ -83,7 +81,7 @@
ClientTestName:
type: string
required:
- TestSiteID
- TestCode
responses:
'201':
description: Test mapping created
@ -116,8 +114,9 @@
TestMapID:
type: integer
description: Test Map ID (required)
TestSiteID:
type: integer
TestCode:
type: string
description: Test Code - maps to HostTestCode or ClientTestCode
HostType:
type: string
HostID:
@ -211,22 +210,22 @@
'404':
description: Test mapping not found
/api/test/testmap/by-testsite/{testSiteID}:
/api/test/testmap/by-testcode/{testCode}:
get:
tags: [Tests]
summary: Get test mappings by test site with details
summary: Get test mappings by test code with details
security:
- bearerAuth: []
parameters:
- name: testSiteID
- name: testCode
in: path
required: true
schema:
type: integer
description: Test Site ID
type: string
description: Test Code (matches HostTestCode or ClientTestCode)
responses:
'200':
description: List of test mappings with details for the test site
description: List of test mappings with details for the test code
content:
application/json:
schema:

View File

@ -0,0 +1,31 @@
<?php
namespace Tests\Feature\Organization;
use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
class CodingSysControllerTest extends CIUnitTestCase
{
use FeatureTestTrait;
protected $endpoint = 'api/organization/codingsys';
public function testIndexCodingSys()
{
$result = $this->get($this->endpoint);
$result->assertStatus(200);
}
public function testCreateCodingSys()
{
$payload = [
'CodingSysAbb' => 'ICD10',
'FullText' => 'International Classification of Diseases 10',
'Description' => 'Medical diagnosis coding system'
];
$result = $this->withBodyFormat('json')->post($this->endpoint, $payload);
$result->assertStatus(201);
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Tests\Feature\Organization;
use CodeIgniter\Test\FeatureTestTrait;
use CodeIgniter\Test\CIUnitTestCase;
class HostAppControllerTest extends CIUnitTestCase
{
use FeatureTestTrait;
protected $endpoint = 'api/organization/hostapp';
public function testIndexHostApp()
{
$result = $this->get($this->endpoint);
$result->assertStatus(200);
}
public function testCreateHostApp()
{
$payload = [
'HostAppID' => 'TEST1',
'HostAppName' => 'Test Host Application',
'SiteID' => null
];
$result = $this->withBodyFormat('json')->post($this->endpoint, $payload);
$result->assertStatus(201);
}
}

View File

@ -163,17 +163,11 @@ class TestDefModelsTest extends CIUnitTestCase
public function testTestMapModelAllowedFields()
{
$allowedFields = $this->testMapModel->allowedFields;
$this->assertContains('TestSiteID', $allowedFields);
$this->assertContains('HostType', $allowedFields);
$this->assertContains('HostID', $allowedFields);
$this->assertContains('HostTestCode', $allowedFields);
$this->assertContains('HostTestName', $allowedFields);
$this->assertContains('ClientType', $allowedFields);
$this->assertContains('ClientID', $allowedFields);
$this->assertContains('ConDefID', $allowedFields);
$this->assertContains('ClientTestCode', $allowedFields);
$this->assertContains('ClientTestName', $allowedFields);
}
/**