gov.fda.open.demo.service.FDADataProxyServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for gov.fda.open.demo.service.FDADataProxyServiceImpl.java

Source

/**
 * *********************************************************************
 * Copyright (c) 2015 InfoZen, Inc. All rights reserved. InfoZen
 * PROPRIETARY/CONFIDENTIAL. Usage is subject to license terms.
 * *********************************************************************
 */
package gov.fda.open.demo.service;

import gov.fda.open.demo.error.ApplicationException;
import gov.fda.open.demo.model.DrugFrequency;
import gov.fda.open.demo.model.enums.LogLevel;
import gov.fda.open.demo.model.enums.SummaryType;
import gov.fda.open.demo.model.request.GetDrugAdverseSummaryRequest;
import gov.fda.open.demo.model.response.GetDrugAdverseSummaryResponse;
import gov.fda.open.demo.service.loggable.Loggable;
import gov.fda.open.demo.util.DrugResultsAggregator;
import gov.fda.open.demo.util.SearchTermBuilder;
import gov.fda.open.demo.util.StringUtil;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * Implementation class for {@link FDADataProxyService}. Class acts as facade
 * implementation for <b>Open FDA Service Drugs Adverse Events</b>
 * 
 * <a href="https://open.fda.gov/api/reference/">openFDA</a>
 */
@Service
public class FDADataProxyServiceImpl implements FDADataProxyService {

    /** The Constant LOG. */
    private static final Logger LOG = LoggerFactory.getLogger(FDADataProxyServiceImpl.class);

    /** The Constant DRUG_EVENT_SEARCH_URL. */
    protected static final String DRUG_EVENT_SEARCH_URL = "/drug/event.json?api_key={appKey}&search={searchTerm}&limit={limit}&skip={skip}";

    /** The uri. */
    @Value("${fdadataset.uri:https://api.fda.gov}")
    private String uri;

    /** The app key. */
    @Value("${fdadataset.app_key:HLWyWIHnwvkPqiYm1wWdx0NcZH9ghBKzCBTeSjJG}")
    private String appKey;

    /** The limit. */
    @Value("${fdadataset.limitPerRequest:100}")
    private int limit;

    /** The template. */
    @Inject
    private RestTemplate template;

    /** The drug names. */
    private static String[] drugNames = null;

    /** Json reader */
    private ObjectMapper mapper = new ObjectMapper();

    /**
     * Returns all the drug names that start with give drug name in sorted order
     * 
     * @see gov.fda.open.demo.service.FDADataProxyService#getDrugNames(java.lang.String)
     */
    @Loggable(LogLevel.INFO)
    @Override
    public List<String> getDrugNames(String partialDrugName) {

        try {
            if (drugNames == null) {
                // TOD Add constants
                URL url = Thread.currentThread().getContextClassLoader().getResource("fdadrugnames.txt");
                List<String> lines = FileUtils.readLines(new File(url.toURI()));
                Collections.sort(lines);
                drugNames = lines.toArray(new String[lines.size()]);
            }

            String upperDrugName = partialDrugName.toUpperCase();
            int idx = StringUtil.findFirst(drugNames, upperDrugName);
            List<String> uniqueDrugNames = new ArrayList<String>();
            // If greater than -1 then found something
            if (idx > -1) {
                for (int i = idx; i <= drugNames.length; i++) {
                    if (drugNames[i].startsWith(upperDrugName) && uniqueDrugNames.size() < 10) {
                        uniqueDrugNames.add(drugNames[i]);
                    } else {
                        break;
                    }

                }
            }
            return uniqueDrugNames;
        } catch (IOException | URISyntaxException ie) {
            throw new ApplicationException(ie);
        }

    }

    /**
     * Queries <b>openFDA</b> using RESTFul Service.
     * 
     * @see <a href="https://open.fda.gov/drug/event/">Drug Adverse Events</a>
     * @see gov.fda.open.demo.service.FDADataProxyService#getDrugAdverseSummary(gov.fda.open.demo.model.request.GetDrugAdverseSummaryRequest)
     */
    @Loggable(LogLevel.INFO)
    @Override
    public GetDrugAdverseSummaryResponse getDrugAdverseSummary(GetDrugAdverseSummaryRequest request) {

        // Build search term from the request object
        String searchTerm = buildSearchTerm(request);

        Map<String, String> params = new HashMap<String, String>();
        params.put("searchTerm", searchTerm);
        params.put("appKey", appKey);
        params.put("limit", String.valueOf(limit));

        boolean success = true;
        int skip = 0, total = 0;
        String errorCode = null, message = null;
        DrugFrequency[] reactionFrequencies = null;

        // Get the summary
        SummaryType summaryType = request.getSummaryType();
        // Max total records to be fetched
        int totalFetch = request.getMaxFetchCnt();
        DrugResultsAggregator drugResultAggregrator = new DrugResultsAggregator(summaryType);
        try {
            do {
                params.put("skip", String.valueOf(skip));
                // Build drug events URI
                URI httpUri = buildDrugEventsRequestURI(params);
                // Execute
                String responseBody = template.getForObject(httpUri, String.class);
                // Parse the JSON node
                JsonNode jRootNode = mapper.readTree(responseBody);
                if (jRootNode.has("error") == false) {
                    total = jRootNode.get("meta").get("results").get("total").asInt(0);
                } else {
                    JsonNode jErrorNode = jRootNode.get("error");
                    errorCode = jErrorNode.get("code").asText();
                    message = jErrorNode.get("message").asText();
                    // Log the error
                    success = false;
                    break;
                }

                JsonNode resultsNode = jRootNode.get("results");
                drugResultAggregrator.aggregate(resultsNode);

                skip += limit;
            } while (total > skip && skip < totalFetch);

            // Get aggregated results
            Map<Date, Map<String, Integer>> aggregateResults = drugResultAggregrator.getResults();
            // Build the Drug frequency
            reactionFrequencies = new DrugFrequency[aggregateResults.size()];
            List<Date> dates = new ArrayList<Date>(aggregateResults.keySet());
            // Sort the date keys
            Collections.sort(dates);
            int i = 0;
            for (Date date : dates) {
                reactionFrequencies[i++] = new DrugFrequency(date, aggregateResults.get(date));
            }
        } catch (HttpClientErrorException e) {
            LOG.debug("Open FDA returned error", e);
            success = false;
            String jsonResponse = e.getResponseBodyAsString();
            LOG.error(" Exception response {} ", jsonResponse);
            try {
                JsonNode jErrorNode = mapper.readTree(jsonResponse).get("error");
                errorCode = jErrorNode.get("code").asText();
                message = jErrorNode.get("message").asText();
            } catch (IOException ex) {
                LOG.error("Client side error", ex);
                errorCode = "404";
                message = "Json parser error";
            }
        } catch (IOException | RestClientException e) {
            LOG.error("Error occurred", e);
            throw new ApplicationException(e);
        }

        GetDrugAdverseSummaryResponse response = new GetDrugAdverseSummaryResponse(success, errorCode, message,
                reactionFrequencies);
        if (reactionFrequencies != null && total > totalFetch) {
            response.setMessage(String.format("You are viewing \"%d\" of \"%d\" results", totalFetch, total));
        }

        return response;
    }

    /**
     * Helper method to build search term
     * 
     * @param request
     * @return
     */
    private String buildSearchTerm(GetDrugAdverseSummaryRequest request) {

        SummaryType summaryType = request.getSummaryType();

        SearchTermBuilder searchParamBuilder = new SearchTermBuilder();

        searchParamBuilder.exists(summaryType.getField()).and()
                .appendTerm("patient.drug.medicinalproduct", request.getDrugName()).and()
                .between("receivedate", request.getStartDate(), request.getEndDate());

        // build query String
        return searchParamBuilder.toString();
    }

    /**
     * Helper method to build DrugEvetn Restful URI
     * 
     * @param params
     * @return
     */
    private URI buildDrugEventsRequestURI(final Map<String, String> params) {
        StringBuilder drugURL = new StringBuilder(uri);
        drugURL.append(DRUG_EVENT_SEARCH_URL);

        URI httpUri = UriComponentsBuilder.fromHttpUrl(drugURL.toString()).buildAndExpand(params).toUri();

        LOG.debug(" URI : {}", httpUri);

        return httpUri;

    }
}