Find Patient by Fingerprint

OpenMRS 1.11.7 App to be extended: Find Patient

Now that am able to register a patient by FingerPrint(through the registration App), I would like to search for the same patient using fingerprint technology. It will be great to get pointers here.

Here is my proposed-plan:

  1. Have an end-to-end understanding of the design+code for the Find Patient App.
  2. Add an icon to the find -patient widget
  3. Work with ReST+Angular on how to search for a patient record
  4. Embed the applet that does finger-print search.
  5. Redirect urls to the patient dash board app.

For now am familiarising myself with How does patient search work in Platform?

Please share comments here. @darius @wyclif

1 Like

There is a module developed/being developed https://github.com/muzima/openmrs-module-fingerprint. You also might consider discussing with @ayeung, @nyoman

1 Like

I think you’ve posted this some where else, as I mentioned on one of your posts and as Maurya mentioned, have you looked at the finger print module written by folks Ampath? It could’ve saved you time by collaborating with them and see if you can use it

1 Like

Thanks @maurya and @wyclif.

I am looking at Muzima again. I used it for benchmarking in the beginning. Surely, their search feature might be good. I use a different device from theirs though.

@ayeung and @nyoman, I would like to know the flow for the search of a patient, and the OpenMRS components involved . Could you be having any documentation?

Thanking you, Simon.

I have seen the search feature in muzuma’s applet enabled module. I would like to leverage on the same, this time round under the find-patient app. Thanks team.

Hello,

The icon has been added. Am doing the ReSTful implementation. More tips are welcome.

Here is what I have done so far. Progress Report 27-July-2016.pdf (389.7 KB)

If i was to do this searching against the your finger-print code/hash stored in the database as a person attribute, i would follow the following steps;

jQuery.ajax({
		type : "GET",
		url : "http://localhost:8081/openmrs-standalone/ws/rest/v1/patient?q=kaweesi",
		success : function(kaweesiFoundPatientResults) {
			//do something here with the returned results contained in kaweesiFoundPatientResults.results array
		}
	});
  • However i don’t think this search option q supports searching againstperson attributes, therefore i would first of all support this through the rest webservices module and use option q to search against attributes

  • I can then do my callback(load patient dashboard page) of found patient in the above jQuery do something section (success function).

This however to me seems to be a long way rather than just submitting the scanned fingerprint hash/code and do the matching at server level. In such a case rather i would simply submit/pass the scanned fingerprint to the server’s controller layer which would then invoke into the service layer where i would have a method that gets a patient based on the fingerprint’s patient attribute type whose dashboard i would load at client side

I have managed to reach the server-side. Am using Ajax and the Controller. However, one small thing is disturbing: Failed to load resource: the server responded with a status of 500 (Internal Server Error)

The client does not seem to receive a response in the success callback method.

Here is my client-side code:

<script type="text/javascript">
function searchFingerPrint(){
	var baseUrl ="http://localhost:8080/openmrs/coreapps/clinicianfacing/patient.page?patientId=";
	jq.ajax({
		url : "${ ui.actionLink('searchForPatientByFingerPrint') }",
		method : "POST",
		data: {"datakey": "fingerPrintInBase64"},
		success : function(returnedPatientUuid) {
		    alert("Data returned: "+returnedPatientUuid);
			window.location = baseUrl.concat(returnedPatientUuid);
		},
		error : function(e) {
		   alert("Ooops: "+e.message);
	    }
	});
}</script>

Server-side-code:

	public @ResponseBody String searchForPatientByFingerPrint(
        @RequestParam(value = "datakey", required = false)String fingerPrintInBase64){
	//String patientSearchPageUrl = "http://localhost:8080/openmrs/coreapps/findpatient/findPatient.page?app=patients.findPatientByFingerprint";
	String fingerPrintPersonAttributeTypeUUID = "0fe8824e-f9f8-42fa-a919-4d2dcd00a5da";
       
     String uuid ="null";
	 System.out.println("Server reached");
	//search patient attribute: leftIndexFingerPrint
	List<Patient> patients = Context.getPatientService().getAllPatients();
	
	System.out.println("Number of Patients: "+patients.size());
	
	if(fingerPrintInBase64!= null){
		for(Patient patientInstance : patients){
			List<PersonAttribute> personAttributes = patientInstance.getActiveAttributes();
			
			for(PersonAttribute personAttribute: personAttributes){
				System.out.println("Person Attribute: "+personAttribute.getValue());
				
					System.out.println("Person Attribute Type UUID: "+personAttribute.getAttributeType().getUuid());
					if(personAttribute.getAttributeType().getUuid().equalsIgnoreCase(fingerPrintPersonAttributeTypeUUID)){
    					System.out.println("Person attributeType passed...");
    					//test if the base64 generated matches our stored base64 text
    					if(personAttribute.getValue() != null ){
    						System.out.println("Patient UUID: "+patientInstance.getUuid());
    						uuid = patientInstance.getUuid();
    						break;
    					}
    				}
				
			}//end person attribute loop

			if(uuid!="null"){
				break;
			}
		}//end patient loop
	}
	
	return uuid;
}

Server logs:

Server reached
Number of Patients: 3
Person Attribute: 0980008888
Person Attribute Type UUID: 14d4f066-15f5-102d-96e4-000c29c2a5d7
Person Attribute: 0789786843
Person Attribute Type UUID: 14d4f066-15f5-102d-96e4-000c29c2a5d7
Person Attribute: MyFingerPrint
Person Attribute Type UUID: 0fe8824e-f9f8-42fa-a919-4d2dcd00a5da
Person attributeType passed...
Patient UUID: 083c2982-ae98-4a29-9e89-322c97ccbb0d

I wonder why there is no success yet the client sends data to the server. I have debugged the server, and all lines in the controller are successfully reached.

Here is a screenshot of the error when debugging with the browser.

@k_joseph @wyclif @darius @dkayiwa @burke

Can you check if there’s a stack trace in the logs? Perhaps one of the patients (not the first one) has thrown an exception.

2 Likes

Whenever you get 500 (Internal Server Error), run to your tomcat logs and see if you can trace what happed wrong at the server level. post us your current logs

1 Like

ERROR - uncaughtException_jsp._jspService(450) |2016-09-03 22:34:05,944| Exception was thrown by not authenticated user org.openmrs.api.APIAuthenticationException at org.openmrs.module.appui.UiSessionContext.requireAuthentication(UiSessionContext.java:124) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoCachedMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:229) at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:52) at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116) at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:120) at SimpleTemplateScript3.run(SimpleTemplateScript3.groovy:3) at groovy.text.SimpleTemplateEngine$SimpleTemplate$1.writeTo(SimpleTemplateEngine.java:165) at groovy.text.SimpleTemplateEngine$SimpleTemplate$1.toString(SimpleTemplateEngine.java:177) at org.openmrs.ui.framework.fragment.GroovyFragmentView.render(GroovyFragmentView.java:46) at org.openmrs.ui.framework.fragment.FragmentFactory.processThisFragment(FragmentFactory.java:194) at org.openmrs.ui.framework.fragment.FragmentFactory.process(FragmentFactory.java:123) at org.openmrs.ui.framework.page.PageFactory.process(PageFactory.java:122) at org.openmrs.ui.framework.page.PageFactory.handle(PageFactory.java:85) at org.openmrs.module.uiframework.PageController.handlePath(PageController.java:115) at org.openmrs.module.uiframework.PageController.handleUrlWithDotPage(PageController.java:82) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:440) at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:428) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844) at javax.servlet.http.HttpServlet.service(HttpServlet.java:624) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829) at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:748) at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:486) at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:411) at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:338) at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:263) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1208) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:992) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:939) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:953) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:844) at javax.servlet.http.HttpServlet.service(HttpServlet.java:624) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:829) at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.module.web.filter.ForcePasswordChangeFilter.doFilter(ForcePasswordChangeFilter.java:61) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.web.filter.GZIPFilter.doFilterInternal(GZIPFilter.java:64) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:72) at org.openmrs.module.xforms.web.XformsFilter.doFilter(XformsFilter.java:69) at org.openmrs.module.web.filter.ModuleFilterChain.doFilter(ModuleFilterChain.java:70) at org.openmrs.module.web.filter.ModuleFilter.doFilter(ModuleFilter.java:54) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.web.filter.OpenmrsFilter.doFilterInternal(OpenmrsFilter.java:109) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:230) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:105) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:105) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.openmrs.web.filter.StartupFilter.doFilter(StartupFilter.java:105) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:423) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:101) … Server reached Number of Patients: 3 Person Attribute: 0980008888 Person Attribute Type UUID: 14d4f066-15f5-102d-96e4-000c29c2a5d7 Person Attribute: 0789786843 Person Attribute Type UUID: 14d4f066-15f5-102d-96e4-000c29c2a5d7 Person Attribute: MyFingerPrint Person Attribute Type UUID: 0fe8824e-f9f8-42fa-a919-4d2dcd00a5da Person attributeType passed… Patient UUID: 083c2982-ae98-4a29-9e89-322c97ccbb0d

The answer is on this line: at org.openmrs.module.appui.UiSessionContext.requireAuthentication(UiSessionContext.java:124)

1 Like

Thanks People. How can I add sessionUI authentication? Looking at https://wiki.openmrs.org/display/docs/How+to+Use+AJAX+in+a+Fragment . Am not using Fragment Params though. Am digging into more literature. Will be nice you share some here.

@k_joseph @dkayiwa @lluismf

I can authenticate like so;

try {
                        Context.openSession();
                        Context.authenticate("admin", "test");
                        List<Patients> patients = Context.getPatientService().getAllPatients();
                        ......
        }
                finally {
                        Context.closeSession();
        }

I thought there is a smarter way the UI framework handles this. Probably using the current user. How else is it done with other modules? Am afraid, I don’t to brake the rules.

@dkayiwa @k_joseph @lluismf @wyclif

From where are you making the call? From an openmrs browser page? If so, can you ensure that you are logged in and your login session has not expired?

Am making the call from PatientSearchWidget.gsp. It is a fragmentView. It is here where I put the javascript tags as below.

<script type="text/javascript">
function searchFingerPrint(){
	var baseUrl ="http://localhost:8080/openmrs/coreapps/clinicianfacing/patient.page?patientId=";
	jq.ajax({
		url : "${ ui.actionLink('searchForPatientByFingerPrint') }",
		method : "POST",
		data: {"datakey": "fingerPrintInBase64"},
		success : function(returnedPatientUuid) {
		    alert("Data returned: "+returnedPatientUuid);
			window.location = baseUrl.concat(returnedPatientUuid);
		},
		error : function(e) {
		   alert("Ooops: "+e.message);
	    }
	});
}</script>

Facts: Am able to send data through AJAX to the controller. The localhost.2016-09-06.log says

SEVERE: Servlet.service() for servlet [openmrs] in context with path [/openmrs] threw exception [Request processing failed; nested exception is java.lang.RuntimeException: Don't know how to handle fragment action result type: class java.lang.String] with root cause
    java.lang.RuntimeException: Don't know how to handle fragment action result type: class java.lang.String
    	at org.openmrs.module.uiframework.FragmentActionController.handlePath(FragmentActionController.java:252)
    	at org.openmrs.module.uiframework.FragmentActionController.handleUrlWithDotAction(FragmentActionController.java:103)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

Do you have a Github link for your module such that we can look at it in its entirety?

Yes. Code: https://github.com/smuwanga/openmrs-module-coreapps/tree/fingerprint

The server does not know what to do with the returned string.

The error seems to go when I change the return-type to FragmentActionResult, however the ui-session authentication error returns. Am going to try returning an object of Type SimpleObject.

NB: Having managed to understand the whole round trip of data exchange to and fro the server, am now comfortable with placing this in a custom module.

Hoorey!!! I have managed to return meaningful data(aka the patient’s uuid). How can I redirect the user to the patient dashboard?

I have tried this below, but no page does not change. jq = jQuery;

    jq(function() {
    jq('#testAjaxButton').click(function() {
		        jq.getJSON('${ ui.actionLink("searchForPatientByFingerPrint") }',
		            {
		              'datakey': 'searchForPatientByFingerPrint'
		              
		             
		            })
		        .success(function(data) {
		           var patient = JSON.parse(data);
		           var baseurl = "http://localhost:8080/openmrs/coreapps/clinicianfacing/patient.page?patientId="+patient.uuid);
		            window.location(baseurl);
		            
				   
		            
		        })
		        .error(function(xhr, status, err) {
		            alert('AJAX error ' + err);
		        })
		    });
		});
	
</script>

window.location() does not seem to work from my module. What am I doing wrong?

@dkayiwa @darius, @wyclif @burke @k_joseph

Depending on the version of the UI framework version your running, fragment controllers should return an instance of one of the sub classes of FragmentActionResult OR should specify the returnFormat request param, the value can be json if you’re expecting back json, any other value is assumed to be html.

Out of curiosity, why did you have to fork the coreapps module? I think you should have used another approach.