diff --git a/.gitignore b/.gitignore index 8811b48..79e185e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ Homestead.json /.idea /_ide_helper.php /storage/ +composer.phar \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 9e16811..92cdb5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: php +services: + - mysql + php: - 5.6 - 7.0 diff --git a/AutoStart.bat b/AutoStart.bat new file mode 100644 index 0000000..833cfbd --- /dev/null +++ b/AutoStart.bat @@ -0,0 +1,14 @@ +@echo off +rem this is for windows users only +rem change the drive path to what suits your server +re + + +cd C:\path to folder\ + +start php artisan serve --host xx.xx.xx.xx --port 8000 + +rem auto display of the web browser +rem this will automatically open the application using your default set web browser + +start http://xx.xx.xx.xx:8000 diff --git a/app/Http/Controllers/APIController.php b/app/Http/Controllers/APIController.php index 65acdb9..44c071b 100644 --- a/app/Http/Controllers/APIController.php +++ b/app/Http/Controllers/APIController.php @@ -155,6 +155,7 @@ public function getAllRemainingPrescriptions() { } $clinic = Clinic::getCurrentClinic(); + // TODO Fix for large number of records $prescriptions = Prescription::whereIn('patient_id', $clinic->patients()->lists('id')) ->where('issued', false)->orderBy('id') ->with('prescriptionDrugs.dosage', 'prescriptionDrugs.frequency', 'prescriptionPharmacyDrugs', diff --git a/app/Http/Controllers/PatientController.php b/app/Http/Controllers/PatientController.php index 6a48be1..844cd94 100644 --- a/app/Http/Controllers/PatientController.php +++ b/app/Http/Controllers/PatientController.php @@ -13,17 +13,97 @@ use Illuminate\Support\Facades\Validator; class PatientController extends Controller { + /** * Get the patients list * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function getPatientList() { $clinic = Clinic::getCurrentClinic(); - $patients = $clinic->patients; - return view('patients.patients', ['patients' => $patients]); + return view('patients.patients', ['patients' => []]); } + /** + * Get the patients list for data tables, server side processing + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + */ + public function listPatients(Request $request) + { + $clinic = Clinic::getCurrentClinic(); + + $draw = $request->query('draw'); + $start = $request->query('start'); + $length = $request->query('length'); + $search = $request->query('search'); + $order = $request->query('order'); + $query = $search['value']; + $orderByColIdx = $order[0]["column"]; + $orderByDirection = $order[0]["dir"]; + + $orderByCol = 'first_name'; + if ($orderByColIdx == 2) { + $orderByCol = 'phone'; + } else if ($orderByColIdx == 3) { + $orderByCol = 'address'; + } else if ($orderByColIdx == 4) { + $orderByCol = 'dob'; + $orderByDirection = $orderByDirection == 'asc' ? 'desc' : 'asc'; + } + + Log::debug("Draw -> $draw, Start-> $start, Length-> $length, OrderByColumn-> $orderByColIdx, Query-> $query"); + + $totalRecords = $clinic->patients()->count(); + $filteredRecords = $totalRecords; + + $patients = $clinic->patients(); + + if (!empty($query)) { + $patients = $patients->where(DB::raw('concat(first_name, " ", last_name)'), 'like', "%$query%"); + $filteredRecords = $patients->count(); + } + + $patients = $patients->orderBy($orderByCol, $orderByDirection) + ->skip($start)->take($length)->get(); + + $data = []; + foreach ($patients as $patient) { + $buttons = ''; + if (\Gate::allows('delete', $patient)) { + $buttons = " + "; + } + + $row = [ + $patient->id, + $patient->first_name . ' ' . $patient->last_name, + $patient->phone, + $patient->address, + \Utils::getAge($patient->dob), + $buttons + ]; + + $data[] = $row; + } + + $result = [ + 'draw' => $draw, + 'recordsTotal' => $totalRecords, + 'recordsFiltered' => $filteredRecords, + 'data' => $data + ]; + + return response()->json($result); + } + /** * Adds a patient to the system * diff --git a/app/Http/Controllers/UtilityController.php b/app/Http/Controllers/UtilityController.php index 1ad03c7..837cc34 100644 --- a/app/Http/Controllers/UtilityController.php +++ b/app/Http/Controllers/UtilityController.php @@ -49,17 +49,24 @@ public function getDashboard() { } $clinic = Clinic::getCurrentClinic(); - $prescriptions = Prescription::whereIn('patient_id', $clinic->patients()->lists('id')); + $idList = $clinic->patients()->pluck('id')->toArray(); + + $prescriptions = collect(); + $batch_size = 10000; + foreach (array_chunk($idList, $batch_size) as $idListBatch) { + $prescriptions = Prescription::whereIn('patient_id', $idListBatch)->get()->toBase()->merge($prescriptions); + } $prescriptionCount = $prescriptions->where('issued', 1)->count(); $payments = Payment::whereIn('prescription_id', - $prescriptions->where('issued', 1)->lists('id'))->sum('amount'); - + $prescriptions->where('issued', 1)->pluck('id'))->sum('amount'); $stats = $this->calcClinicStats($clinic); return view('dashboard', [ - 'clinic' => $clinic, 'prescriptionCount' => $prescriptionCount, - 'payments' => $payments, 'stats' => $stats + 'clinic' => $clinic, + 'prescriptionCount' => $prescriptionCount, + 'payments' => $payments, + 'stats' => $stats ]); } @@ -79,7 +86,8 @@ private function calcClinicStats($clinic) { ]; $date = date('Y-m-d H:i:s', strtotime("-6 months")); - $patientIds = $clinic->patients()->lists('id')->toArray(); +// $patientIds = $clinic->patients()->lists('id')->toArray(); + $patientIds = []; if (count($patientIds) > 0) { $patientIds = implode(",", $patientIds); $query = "SELECT MONTH(created_at) AS m,COUNT(*) AS c FROM `prescriptions` WHERE `patient_id` diff --git a/app/Http/routes.php b/app/Http/routes.php index ed38c95..49e8b87 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -101,10 +101,10 @@ */ Route::group(['prefix' => 'patients'], function () { Route::get('/', ['as' => 'patients', 'uses' => 'PatientController@getPatientList']); - /* * PATIENTS */ + Route::get('/list', ['as' => 'listPatients', 'uses' => 'PatientController@listPatients']); Route::post('addPatient', ['as' => 'addPatient', 'uses' => 'PatientController@addPatient']); Route::get('patient/{id}', ['as' => 'patient', 'uses' => 'PatientController@getPatient']); Route::any('deletePatient/{id}', ['as' => 'deletePatient', 'uses' => 'PatientController@deletePatient']); diff --git a/database/seeds/PatientTableSeeder.php b/database/seeds/PatientTableSeeder.php index 5ae40eb..b50d671 100644 --- a/database/seeds/PatientTableSeeder.php +++ b/database/seeds/PatientTableSeeder.php @@ -21,5 +21,17 @@ public function run() }); } } + + // Uncomment for large number of patient records. +// $clinic = \App\Clinic::find(1); +// +// foreach ($clinic->users as $user) { +// //add patients to the clinic +// factory(App\Patient::class, 100000)->make()->each(function (\App\Patient $patient) use ($clinic, $user) { +// $patient->creator()->associate($user); +// $patient->clinic()->associate($clinic); +// $patient->save(); +// }); +// } } } diff --git a/docs/chr247.postman_collection.json b/docs/chr247.postman_collection.json new file mode 100644 index 0000000..3bce093 --- /dev/null +++ b/docs/chr247.postman_collection.json @@ -0,0 +1,177 @@ +{ + "info": { + "_postman_id": "183abd2f-a810-4516-8714-9dc61610368e", + "name": "chr247", + "description": "chr247 APIs and pages", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "id": "7c782fb2-28cd-430d-85f6-080c38d79121", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + " pm.response.to.be.withBody;", + "});", + "", + "pm.test(\"Check cookies\", function() {", + " pm.expect(pm.cookies.has(\"XSRF-TOKEN\")).to.be.true;", + " pm.expect(pm.cookies.has(\"chr247_session\")).to.be.true;", + "});", + "", + "pm.environment.set(\"xsrf_token\", pm.cookies.get(\"XSRF-TOKEN\"));", + "pm.environment.set(\"session\", pm.cookies.get(\"chr247_session\"));", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{chr247_url}}/login", + "host": [ + "{{chr247_url}}" + ], + "path": [ + "login" + ] + } + }, + "response": [] + }, + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "id": "fee7c243-2c36-4ea3-adba-b832fd446f70", + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.have.status(200);", + " pm.response.to.be.withBody;", + "});", + "", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "password", + "value": "{{password}}", + "type": "text" + }, + { + "key": "username", + "value": "{{username}}", + "type": "text" + } + ] + }, + "url": { + "raw": "{{chr247_url}}/login", + "host": [ + "{{chr247_url}}" + ], + "path": [ + "login" + ] + } + }, + "response": [] + }, + { + "name": "List Patients", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{chr247_url}}/patients/list?order[0][column]=1&order[0][dir]=asc&start=0&length=10&search[value]=a&search[regex]=false&draw=1", + "host": [ + "{{chr247_url}}" + ], + "path": [ + "patients", + "list" + ], + "query": [ + { + "key": "order[0][column]", + "value": "1" + }, + { + "key": "order[0][dir]", + "value": "asc" + }, + { + "key": "start", + "value": "0" + }, + { + "key": "length", + "value": "10" + }, + { + "key": "search[value]", + "value": "a" + }, + { + "key": "search[regex]", + "value": "false" + }, + { + "key": "draw", + "value": "1" + } + ] + }, + "description": "API used by data tables to paginate and load patients" + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "96ee9cc0-96a5-4fcd-b677-b47f58005cf0", + "type": "text/javascript", + "exec": [ + "if(pm.environment.has(\"xsrf_token\")) {", + " pm.request.headers.add({", + " key: \"X-XSRF-TOKEN\",", + " value: \"{{xsrf_token}}\"", + " });", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "id": "3a1eba76-6c9a-4497-8931-ab05d5115ff3", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "protocolProfileBehavior": {} +} \ No newline at end of file diff --git a/docs/chr247_jmeter_test.jmx b/docs/chr247_jmeter_test.jmx new file mode 100644 index 0000000..d4e9471 --- /dev/null +++ b/docs/chr247_jmeter_test.jmx @@ -0,0 +1,415 @@ + + + + + Load test to benchmard chr247 performance + false + true + false + + + + protocol + http + = + + + host + imesha-playground.southeastasia.cloudapp.azure.com + = + + + port + 80 + = + + + threads + 100 + = + + + loops + 10 + = + + + username + imesha + = + + + password + 1234 + = + + + + + + + + startnextloop + + false + ${loops} + + ${threads} + 30 + false + + + + + + + false + + + + + + + ${host} + ${port} + ${protocol} + + login + GET + true + false + true + false + + + + + + + + + + false + ${username} + = + true + username + + + false + ${password} + = + true + password + + + + ${host} + ${port} + ${protocol} + + login + POST + false + false + true + false + + + + + + + + + X-XSRF-TOKEN + ${__urldecode(${COOKIE_XSRF-TOKEN})} + + + + + + + + + + false + 1 + = + true + order[0][column] + + + false + asc + = + true + order[0][dir] + + + false + 0 + = + true + start + + + false + 10 + = + true + length + + + false + a + = + true + search[value] + + + false + false + = + true + search[regex] + + + false + 1 + = + true + draw + + + + ${host} + ${port} + ${protocol} + + patients/list + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + $.recordsTotal + + false + false + false + true + + + + + + + + false + 1 + = + true + order[0][column] + + + false + asc + = + true + order[0][dir] + + + false + 0 + = + true + start + + + false + 10 + = + true + length + + + false + a + = + true + search[value] + + + false + false + = + true + search[regex] + + + false + 1 + = + true + draw + + + + ${host} + ${port} + ${protocol} + + drugs + GET + true + false + true + false + + + + + + + + 200 + + + Assertion.response_code + false + 8 + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + diff --git a/docs/chr247_local.postman_environment.json b/docs/chr247_local.postman_environment.json new file mode 100644 index 0000000..135f384 --- /dev/null +++ b/docs/chr247_local.postman_environment.json @@ -0,0 +1,34 @@ +{ + "id": "dbb2b788-d4f3-4ee7-bcee-53f43cdd5740", + "name": "chr247_local", + "values": [ + { + "key": "session", + "value": "", + "enabled": true + }, + { + "key": "chr247_url", + "value": "http://localhost:8000", + "enabled": true + }, + { + "key": "xsrf_token", + "value": "", + "enabled": true + }, + { + "key": "username", + "value": "imesha", + "enabled": true + }, + { + "key": "password", + "value": "1234", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2019-11-30T16:36:50.427Z", + "_postman_exported_using": "Postman/7.10.0" +} \ No newline at end of file diff --git a/readme.md b/readme.md index 8aad06c..4688075 100644 --- a/readme.md +++ b/readme.md @@ -72,22 +72,27 @@ manager used internally. - Within the project root directory, - Make sure the permissions for `storage` and `bootstrap/cache` are set to `776` (i.e writable by the web server) - (Windows users make sure the current user has full permissions to read and write on the folders `storage` and `bootstrap/cache`) - - Copy the `.env.example` file as `.env` (windows Users- do this using the command prompt). Its advisable to have a copy of this file before you do this. - - Run `composer install` within the project root. + - Copy the `.env.example` file as `.env` (windows Users- do this using the command prompt **`copy .env.example .env`** ). Its advisable to have a copy of this file before you do this. + - Run `composer install` within the project root.` - Run `php artisan key:generate` to generate application key. - This command will set a newly generated application key to `.env` file. - Set the database related information within `.env` file. If you are using a DB otherthan MySQL, you may have to add `DB_CONNECTION=` to `.env` file as well. - ``` - DB_HOST= + DB_HOST= or DB_HOST=: if using non default settings for mysql DB_DATABASE= DB_USERNAME= DB_PASSWORD= ``` - Run database migrations and seeds with `php artisan migrate:refresh --seed` - - Run `php artisan serve` -- Visit [http://localhost:8000] to view the webapp. You can use the login + - Run `php artisan serve` this will only provide the page for the development computer + - Visit [http://localhost:8000] to view the webapp. + or + - Run `php artisan serve --host xxx.xxx.xxx.xxx --port 8000` this will provide page for all computers on the same network + - Visit [http://xxx.xxx.xxx.xxx:8000] to view the webapp. + +You can use the login - username: `imesha`, password: `1234` to login. ## Contributions diff --git a/resources/views/patients/patients.blade.php b/resources/views/patients/patients.blade.php index 12da2cd..603671b 100644 --- a/resources/views/patients/patients.blade.php +++ b/resources/views/patients/patients.blade.php @@ -42,6 +42,14 @@ @endif