Skip to content

Commit

Permalink
BAH-3024 | Sort columns in the Patient Search Queue tabular view
Browse files Browse the repository at this point in the history
* add. sort logic for clinical tabular view

* fix. tests for patient list

* fix. styles and linting issues

* update. link for Patient ID to jump to dashboard

* update. date sort functionality in clinical list view

* Arjun | BAH-3024 | [Bug] sorting for date of birth and visit time is faulty (#616)

* Arjun | BAH-3024 | fix. linting and alignment issues (#619)

* refactor. override table column ignore list and identifier column

* refactor. move constants to configs

* Parvathy | BAH-3024 | Add. Test Cases For sortVisiblePatientsBy

* BAH-3024 | Arjun | Feedbacks on config names and possible values

* BAH-3024 | Arjun | Fix. namings for config in tests

* BAH-3024 | Arjun | Test faliures due to mock updates

---------

Co-authored-by: Kavitha S <[email protected]>
Co-authored-by: parvathy00 <[email protected]>
  • Loading branch information
3 people authored Apr 24, 2024
1 parent df7510f commit 026376f
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 27 deletions.
2 changes: 0 additions & 2 deletions ui/app/common/patient-search/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ Bahmni.Common.PatientSearch = Bahmni.Common.PatientSearch || {};
Bahmni.Common.PatientSearch.Constants = {
searchExtensionTileViewType: "tile",
searchExtensionTabularViewType: "tabular",
tabularViewIgnoreHeadingsList: ["display", "uuid", "image", "$$hashKey", "activeVisitUuid", "hasBeenAdmitted", "forwardUrl", "programUuid", "enrollment"],
identifierHeading: ["ID", "Id", "id", "identifier", "DQ_COLUMN_TITLE_ACTION"],
nameHeading: ["NAME", "Name", "name"],
patientTileHeight: 100,
patientTileWidth: 100,
Expand Down
75 changes: 64 additions & 11 deletions ui/app/common/patient-search/controllers/patientsListController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ angular.module('bahmni.common.patientSearch')
'$stateParams', '$bahmniCookieStore', 'printer', 'configurationService',
function ($scope, $window, patientService, $rootScope, appService, spinner, $stateParams, $bahmniCookieStore, printer, configurationService) {
$scope.preferExtraIdInSearchResults = appService.getAppDescriptor().getConfigValue("preferExtraIdInSearchResults");
$scope.activeHeaders = [];
const DEFAULT_FETCH_DELAY = 2000;
var patientSearchConfig = appService.getAppDescriptor().getConfigValue("patientSearch");
var patientListSpinner;
var initialize = function () {
var searchTypes = appService.getAppDescriptor().getExtensions("org.bahmni.patient.search", "config").map(mapExtensionToSearchType);
$scope.ignoredTabularViewHeadingsConfig = appService.getAppDescriptor().getConfigValue("ignoredTabularViewHeadings") || [];
$scope.identifierHeadingsConfig = appService.getAppDescriptor().getConfigValue("identifierHeadings") || [];
$scope.search = new Bahmni.Common.PatientSearch.Search(_.without(searchTypes, undefined));
$scope.search.markPatientEntry();
$scope.$watch('search.searchType', function (currentSearchType) {
Expand All @@ -20,6 +23,16 @@ angular.module('bahmni.common.patientSearch')
hideSpinner(spinner, patientListSpinner, $(".tab-content"));
}
});
$scope.$watch('search.visiblePatients', function (activePatientsList) {
if (activePatientsList && activePatientsList.length > 0) {
$scope.getHeadings();
}
else {
if ($scope.activeHeaders.length != 0) {
$scope.activeHeaders = [];
}
}
});
if (patientSearchConfig && patientSearchConfig.serializeSearch) {
getPatientCountSeriallyBySearchIndex(0);
}
Expand Down Expand Up @@ -75,21 +88,61 @@ angular.module('bahmni.common.patientSearch')
$(container).children('patient-list-spinner').hide();
};

$scope.getHeadings = function (patients) {
if (patients && patients.length > 0) {
var headings = _.chain(patients[0])
$scope.getHeadings = function () {
if ($scope.search.activePatients && $scope.search.activePatients.length > 0) {
var headings = _.chain($scope.search.activePatients[0])
.keys()
.filter(function (heading) {
return _.indexOf(Bahmni.Common.PatientSearch.Constants.tabularViewIgnoreHeadingsList, heading) === -1;
return _.indexOf($scope.ignoredTabularViewHeadingsConfig, heading) === -1;
})
.value();
setActiveHeadings(headings);
}
};

return headings;
var setActiveHeadings = function (headings) {
headings.map(function (heading) {
var newHeading = { name: heading, sortInfo: heading };
if (!$scope.activeHeaders.find(function (activeHeader) {
return activeHeader.name == newHeading.name && activeHeader.sortInfo == newHeading.sortInfo;
})) {
$scope.activeHeaders.push(newHeading);
}
});
};

$scope.sortVisiblePatientsBy = function (sortColumn) {
var emptyObjects = _.filter($scope.search.searchResults, function (visiblePatient) {
return !_.property(sortColumn)(visiblePatient);
});

var nonEmptyObjects = _.difference($scope.search.searchResults, emptyObjects);
var sortedNonEmptyObjects = _.sortBy(nonEmptyObjects, function (visiblePatient) {
var value = _.get(visiblePatient, sortColumn);
if (!isNaN(Date.parse(value))) {
var parsedDate = moment(value, Bahmni.Common.Constants.clientDateDisplayFormat + " " + Bahmni.Common.Constants.timeDisplayFormat);
if (parsedDate.isValid()) {
return parsedDate.toDate().getTime();
}
}
else if (angular.isNumber(value)) {
return value;
}
else if (angular.isString(value)) {
return value.toLowerCase();
}
return value;
});
if ($scope.reverseSort) {
sortedNonEmptyObjects.reverse();
}
return [];
$scope.search.visiblePatients = sortedNonEmptyObjects.concat(emptyObjects);
$scope.sortColumn = sortColumn;
$scope.reverseSort = !$scope.reverseSort;
};

$scope.isHeadingOfLinkColumn = function (heading) {
var identifierHeading = _.includes(Bahmni.Common.PatientSearch.Constants.identifierHeading, heading);
var identifierHeading = _.includes($scope.identifierHeadingsConfig, heading);
if (identifierHeading) {
return identifierHeading;
} else if ($scope.search.searchType && $scope.search.searchType.links) {
Expand All @@ -102,10 +155,10 @@ angular.module('bahmni.common.patientSearch')
$scope.isHeadingOfName = function (heading) {
return _.includes(Bahmni.Common.PatientSearch.Constants.nameHeading, heading);
};
$scope.getPrintableHeadings = function (patients) {
var headings = $scope.getHeadings(patients);
var printableHeadings = headings.filter(function (heading) {
return _.indexOf(Bahmni.Common.PatientSearch.Constants.printIgnoreHeadingsList, heading) === -1;
$scope.getPrintableHeadings = function () {
$scope.getHeadings();
var printableHeadings = $scope.activeHeaders.filter(function (heading) {
return _.indexOf(Bahmni.Common.PatientSearch.Constants.printIgnoreHeadingsList, heading.name) === -1;
});
return printableHeadings;
};
Expand Down
20 changes: 13 additions & 7 deletions ui/app/common/patient-search/views/patientsList.html
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,28 @@
<thead>
<tr>
<!-- <th><strong>Sl.No</strong></th> -->
<th ng-repeat="heading in getHeadings(search.visiblePatients)"><strong>{{ ::heading | translate }}</strong></th>
<th ng-repeat="heading in activeHeaders" ng-click="heading.sortInfo && sortVisiblePatientsBy(heading.sortInfo)">
<strong>{{ ::heading.name | translate }}</strong>
<span class="sortIcons" ng-if="sortColumn == heading.sortInfo">
<i class="fa fa-caret-up" ng-show="reverseSort"></i>
<i class="fa fa-caret-down" ng-show="!reverseSort"></i>
</span>
</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in search.visiblePatients">
<!-- <td>
{{$index + 1}}
</td> -->
<td ng-repeat="heading in getHeadings(search.visiblePatients)">
<a ng-if="::isHeadingOfLinkColumn(heading)"
ng-click="forwardPatient(row, heading)">{{::row[heading]}}</a>
<span ng-if="::isHeadingOfName(heading)" ng-click="forwardPatient(row)">{{::row[heading]}}
<td ng-repeat="heading in activeHeaders">
<a ng-if="::isHeadingOfLinkColumn(heading.name)"
ng-click="forwardPatient(row, heading)">{{::row[heading.name]}}</a>
<span ng-if="::isHeadingOfName(heading.name)" ng-click="forwardPatient(row)">{{::row[heading.name]}}
<i class="ipd-indication fa fa-bed" ng-if="::(row.hasBeenAdmitted===true||row.hasBeenAdmitted==='true')"></i>
</span>
<span ng-if="::(!isHeadingOfLinkColumn(heading) && !isHeadingOfName(heading))">
{{::row[heading]}}
<span ng-if="::(!isHeadingOfLinkColumn(heading.name) && !isHeadingOfName(heading.name))">
{{::row[heading.name]}}
</span>
</td>
</tr>
Expand Down
4 changes: 4 additions & 0 deletions ui/app/styles/clinical/_patientSearch.scss
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
padding: 10px;
}
}

.sortIcons {
float: right;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,15 +267,26 @@ describe("PatientsListController", function () {
});

it('should print headings which are filtered from ignore headings list', function(){
var patients = [{emr_id : 'emr_Id123', treatment : 'treatment_id', uuid : '23279927', forwardUrl: 'forwardUrl',programUuid: 'programUuid',enrollment: 'enrollmentUuid', DQ_COLUMN_TITLE_ACTION: 'action url'}];
var headings = scope.getHeadings(patients);
expect(headings).toEqual(['emr_id', 'treatment', 'DQ_COLUMN_TITLE_ACTION']);
scope.search.activePatients = [{emr_id : 'emr_Id123', treatment : 'treatment_id', uuid : '23279927', forwardUrl: 'forwardUrl',programUuid: 'programUuid',enrollment: 'enrollmentUuid', DQ_COLUMN_TITLE_ACTION: 'action url'}];
scope.ignoredTabularViewHeadingsConfig = ["display", "uuid", "image", "activeVisitUuid", "forwardUrl", "hasBeenAdmitted", "programUuid", "enrollment"];
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
scope.getHeadings();
expect(scope.activeHeaders).toEqual([
{ name : 'emr_id', sortInfo : 'emr_id' },
{ name : 'treatment', sortInfo : 'treatment' },
{ name : 'DQ_COLUMN_TITLE_ACTION', sortInfo : 'DQ_COLUMN_TITLE_ACTION' }
]);
});

it('should print headings which are filtered from ignore headings list and print headings list', function(){
var patients = [{emr_id : 'emr_Id123', treatment : 'treatment_id', uuid : '23279927', forwardUrl: 'forwardUrl', DQ_COLUMN_TITLE_ACTION: 'action url'}];
var headings = scope.getPrintableHeadings(patients);
expect(headings).toEqual(['emr_id', 'treatment']);
scope.search.activePatients = [{emr_id : 'emr_Id123', treatment : 'treatment_id', uuid : '23279927', forwardUrl: 'forwardUrl', DQ_COLUMN_TITLE_ACTION: 'action url'}];
scope.ignoredTabularViewHeadingsConfig = ["display", "uuid", "image", "activeVisitUuid", "forwardUrl", "hasBeenAdmitted", "programUuid", "enrollment"];
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
var headings = scope.getPrintableHeadings();
expect(headings).toEqual([
{ name : 'emr_id', sortInfo : 'emr_id' },
{ name : 'treatment', sortInfo : 'treatment' }
]);
});

it('should print page from the config', function(){
Expand All @@ -294,13 +305,15 @@ describe("PatientsListController", function () {
it("should accept the link column from the config, when respective config present", function () {
// var search = {searchType: {linkColumn: "Status"}};
scope.search = {searchType: {linkColumn: "Status"}};
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
var heading = "Status";
var headingOfLinkColumn = scope.isHeadingOfLinkColumn(heading);
expect(headingOfLinkColumn).toBeTruthy()
});

it("should accept the default link column, when nothing specified in the config", function () {
scope.search = {searchType: {}};
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
// scope.$apply(setUp);
var heading = "identifier";
var headingOfLinkColumn = scope.isHeadingOfLinkColumn(heading);
Expand All @@ -309,14 +322,16 @@ describe("PatientsListController", function () {

it("should not have a link on the column, when no match for heading found in config and default column list", function () {
scope.search = {searchType: {}};
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
// scope.$apply(setUp);
var heading = "RandomColumn";
var headingOfLinkColumn = scope.isHeadingOfLinkColumn(heading);
expect(headingOfLinkColumn).toBeFalsy()
expect(headingOfLinkColumn).toBeFalsy();
});
});

it("should indicate if specified column in a link", function () {
scope.identifierHeadingsConfig = ["ID", "identifier", "DQ_COLUMN_TITLE_ACTION"];
scope.search.searchType = {
"id": "bahmni.clinical.patients.all",
"extensionPointId": "org.bahmni.patient.search",
Expand Down Expand Up @@ -482,4 +497,113 @@ describe("PatientsListController", function () {
expect(_window.open).toHaveBeenCalledWith("formattedUrl", "_blank");
});
});

describe("sortVisiblePatientsBy", function () {
beforeEach(function () {
scope.$apply(setUp);
});

it('should sort visible patients by string property in ascending order', function () {
scope.search = {
searchResults: [
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
],
visiblePatients: [],
reverseSort: false
};
scope.sortVisiblePatientsBy('name');
expect(scope.search.visiblePatients).toEqual([
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' },
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' }
]);
});

it("should sort visible patients by number property in ascending order", function() {
scope.search = {
searchResults: [
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
],
visiblePatients: [],
reverseSort: false
};
scope.sortVisiblePatientsBy('id');
expect(scope.search.visiblePatients).toEqual([
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
]);
});

it("should sort visible patients by date property in ascending order", function() {
scope.search = {
searchResults: [
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
],
visiblePatients: [],
reverseSort: false
};
scope.sortVisiblePatientsBy('dob');
expect(scope.search.visiblePatients).toEqual([
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' }
]);
});

it('should handle reverse sort', function () {
scope.search = {
searchResults: [
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
],
visiblePatients: [],
reverseSort: false
};
scope.sortVisiblePatientsBy('name');
expect(scope.search.visiblePatients).toEqual([
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' },
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' }
]);
scope.sortVisiblePatientsBy('name');
expect(scope.search.visiblePatients).toEqual([
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
]);
});

it("should not visible patients if sortColumn is null or undefined", function() {
scope.search = {
searchResults: [
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
],
visiblePatients: [],
reverseSort: false
};
scope.sortVisiblePatientsBy(null);
expect(scope.search.visiblePatients).toEqual([
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
]);
scope.sortVisiblePatientsBy(undefined);
expect(scope.search.visiblePatients).toEqual([
{ id: 2, name: 'Ram', dob: '26 Nov 1986' },
{ id: 1, name: 'Shyam', dob: '13 Aug 1997' },
{ id: 3, name: 'Ganesh', dob: '05 Jan 1994' }
]);
});
});

});

0 comments on commit 026376f

Please sign in to comment.