package org.genedb.web.mvc.controller.download; import org.displaytag.tags.TableTagParameters; import org.displaytag.util.ParamEncoder; import org.genedb.db.taxon.TaxonNodeList; import org.genedb.db.taxon.TaxonNodeManager; import org.genedb.querying.core.PagedQuery; import org.genedb.querying.core.Query; import org.genedb.querying.core.QueryException; import org.genedb.querying.core.QueryFactory; import org.genedb.querying.core.NumericQueryVisibility; import org.genedb.querying.history.HistoryManager; import org.genedb.querying.history.QueryHistoryItem; import org.genedb.querying.tmpquery.GeneSummary; import org.genedb.querying.tmpquery.IdsToGeneSummaryQuery; import org.genedb.querying.tmpquery.MotifQuery; import org.genedb.querying.tmpquery.OrganismLuceneQuery; import org.genedb.querying.tmpquery.QuickSearchQuery; import org.genedb.querying.tmpquery.SuggestQuery; import org.genedb.web.mvc.controller.HistoryManagerFactory; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @Controller @RequestMapping("/Query") public class QueryController extends AbstractGeneDBFormController{ private static final Logger logger = Logger.getLogger(QueryController.class); private QueryFactory<NumericQueryVisibility> queryFactory; private HistoryManagerFactory hmFactory; @Autowired private ApplicationContext applicationContext; public static final int DEFAULT_LENGTH = 30; public void setHmFactory(HistoryManagerFactory hmFactory) { this.hmFactory = hmFactory; } public void setQueryFactory(QueryFactory<NumericQueryVisibility> queryFactory) { this.queryFactory = queryFactory; } @RequestMapping(method = RequestMethod.GET) public String setUpForm() { return "redirect:/QueryList"; } @SuppressWarnings("unchecked") @RequestMapping(method = RequestMethod.GET , value="/{queryName}") public String chooseFormHandling( @PathVariable(value="queryName") String queryName, @RequestParam(value="suppress", required=false) String suppress, @RequestParam(value="redirect", required=false, defaultValue = "true") boolean redirect, HttpServletRequest request, HttpSession session, Model model) throws QueryException { logger.info(queryName); model.addAttribute("actionName" , request.getContextPath() + "/Query/" + queryName); Map<String, String[]> parameters = request.getParameterMap(); for (Map.Entry<String, String[]> entry : parameters.entrySet()) { for (String value : entry.getValue()) { logger.info(entry.getKey() + " : " + value); } } logger.debug("The number of parameters is '" + request.getParameterMap().keySet().size() + "'"); // FIXME determine if this check for parameter count is the best way to decide whether or not to perform the query if (request.getParameterMap().size() > 1) { return processForm(queryName, suppress, redirect, request, session, model); } else { return displayForm(queryName, request, session, model); } } public String displayForm( String queryName, ServletRequest request, HttpSession session, Model model) throws QueryException { logger.info("DISPLAYING FORM"); Query query = findQueryType(queryName, session); if (query==null){ logger.warn("No query, redirecting to the list"); return "redirect:/QueryList"; } //Initialise model data somehow model.addAttribute("query", query); populateModelData(model, query); String taxonNodeName = findTaxonName(query); if (taxonNodeName == null || taxonNodeName.length() == 0) { if (request.getParameter("taxonNodeName") != null) { taxonNodeName = request.getParameter("taxonNodeName"); } } logger.info("TaxonNodeName is "+taxonNodeName); model.addAttribute("taxonNodeName", taxonNodeName); return "search/"+queryName; } @SuppressWarnings("unchecked") public String processForm( String queryName, String suppress, boolean redirect, HttpServletRequest request, HttpSession session, Model model) throws QueryException { logger.info("PROCESSING FORM"); HistoryManager hm = hmFactory.getHistoryManager(session); String key = generateKey(session.getId(), (Map<String, String[]>) request.getParameterMap()); logger.info("Query key: " + key); PagedQuery query = null; QueryHistoryItem item = hm.getQueryHistoryItem(key); if (item == null) { logger.info("Could not find existing query for this session, creating ..."); query = findQueryType(queryName, session); if (query==null){ logger.error(String.format("Unable to find query of name '%s'", queryName)); return "redirect:/Query"; } } else { query = item.getQuery(); } logger.info("Using query " + query.getQueryName()); // Initialise query form Errors errors = initialiseQueryForm(query, request); if (errors.hasErrors()) { model.addAttribute(BindingResult.MODEL_KEY_PREFIX + "query", errors); logger.debug("Returning due to binding error"); for (ObjectError error : errors.getAllErrors()) { logger.error(error); } return "search/"+queryName; } /* * Bodge to make sure organism lucene queries have a taxon. * */ if (query instanceof OrganismLuceneQuery) { OrganismLuceneQuery oq = (OrganismLuceneQuery) query; if (oq.getTaxons() == null ) { TaxonNodeManager tnm = (TaxonNodeManager) applicationContext.getBean("taxonNodeManager", TaxonNodeManager.class); oq.setTaxons(new TaxonNodeList(tnm.getTaxonNodeByString("Root", false))); } } // Validate initialised form query.validate(query, errors); if (errors.hasErrors()) { logger.debug("Validator found errors"); model.addAttribute(BindingResult.MODEL_KEY_PREFIX + "query", errors); return "search/"+queryName; } logger.debug("Validator found no errors"); // Initialise model data populateModelData(model, query); logger.info(String.format("Adding %s with key %s to history manager", query, key)); item = hm.addQueryHistoryItem(key, query); model.addAttribute("query", query); Bounds bounds = getQueryBounds(request); int start = bounds.page * bounds.length; int end = start + bounds.length -1 ; //logger.info("Query page " + bounds.page + ", length " + bounds.length); logger.info(String.format("Page: %s, Length: %s, Start-End: %s-%s", bounds.page, bounds.length, start, end)); List<String> ids = query.getResults(start, end); model.addAttribute("bounds",bounds); logger.info("Size :: "); logger.info(ids.size()); // for (String id : ids) { // logger.info(id); // } List<GeneSummary> results = summaries(ids); logger.info(results.size()); // QueryHistoryItem qhi = hm.getQueryHistoryItem(key); // logger.info(String.format("Fished history item? %s", qhi)); // // List<String> uniqueNames = qhi.getIds(); // logger.info(qhi.getIds().size()); // logger.info(qhi.getIds()); // Suppress item in results // suppressResultItem(suppress, results); String taxonName = findTaxonName(query); logger.info("TaxonNodeName is " + taxonName); model.addAttribute("taxonNodeName", taxonName); int totalResultsSize = query.getTotalResultsSize(); logger.info("Total result size " + totalResultsSize); model.addAttribute("resultsSize", totalResultsSize); if (queryName.equals("quickSearch")) { QuickSearchQuery quickSearchQuery = (QuickSearchQuery) query; logger.info("Fetching quick search taxons"); model.addAttribute("taxonGroup", quickSearchQuery.getQuickSearchQueryResults().getTaxonGroup()); } if (queryName.equals("motif")) { MotifQuery motifQuery = (MotifQuery) query; logger.info("motif query, let's get motif results for " + bounds.page + " " + bounds.length); @SuppressWarnings("rawtypes") Map motifs = motifQuery.getMotifResults(bounds.page, bounds.length); model.addAttribute("motifs", motifs); } if (results.size() == 1 && redirect) { GeneSummary geneSummary = results.get(0); return "redirect:/gene/" + geneSummary.getDisplayId(); } else if (results.size() > 0) { model.addAttribute("results", results); model.addAttribute("queryName", queryName); model.addAttribute("actionName" , request.getContextPath() + "/Query/" + queryName); return "search/"+ queryName; } // if the search yielded any results, we should now have been redirected // remove unsuccessful search term, no point in hanging onto this hm.removeItem(key); logger.warn("No results found for query"); model.addAttribute("noResultFound", Boolean.TRUE); if (queryName.equals("quickSearch")) { QuickSearchQuery quickSearchQuery = (QuickSearchQuery) query; logger.info("Running suggest query as no result found"); SuggestQuery squery = (SuggestQuery) queryFactory.retrieveQuery("suggest", NumericQueryVisibility.PRIVATE); squery.setSearchText(quickSearchQuery.getSearchText()); squery.setTaxons(quickSearchQuery.getTaxons()); squery.setMax(30); List<String> sResults = squery.getResults(); model.addAttribute("suggestions", sResults); } return "search/" + queryName; } @SuppressWarnings("unused") private List<GeneSummary> motifSummaries (MotifQuery query, List<String> ids) throws QueryException { IdsToGeneSummaryQuery idsToGeneSummary = (IdsToGeneSummaryQuery) queryFactory.retrieveQuery("idsToGeneSummary", NumericQueryVisibility.PRIVATE); idsToGeneSummary.setIds(ids); List<GeneSummary> summaries = idsToGeneSummary.getResultsSummaries(); for (GeneSummary summary : summaries) { logger.info(summary.getDisplayId()); } return summaries; } private List<GeneSummary> summaries (List<String> ids) throws QueryException { IdsToGeneSummaryQuery idsToGeneSummary = (IdsToGeneSummaryQuery) queryFactory.retrieveQuery("idsToGeneSummary", NumericQueryVisibility.PRIVATE); idsToGeneSummary.setIds(ids); List<GeneSummary> summaries = idsToGeneSummary.getResultsSummaries(); for (GeneSummary summary : summaries) { logger.info(summary.getDisplayId() + " ... " + summary.getProduct()); } return summaries; } public class Bounds { private int page; private int length; public Bounds(int page, int length) { this.page = page; this.length = length; } public int getPage(){return page;} public int getLength(){return length;} } private Bounds getQueryBounds(HttpServletRequest request) { String startString = request.getParameter((new ParamEncoder("row").encodeParameterName(TableTagParameters.PARAMETER_PAGE))); // Use 1-based index for start and end int page = 0; if (startString != null) { //start = (Integer.parseInt(startString) - 1) * DEFAULT_LENGTH + 1; page = Integer.parseInt(startString) - 1 ; } return new Bounds(page,DEFAULT_LENGTH); } /*private final String displayResults ( HttpServletRequest request, Model model, HistoryItem item, Bounds bounds, List<GeneSummary> results) throws QueryException { String pName = new ParamEncoder("row").encodeParameterName(TableTagParameters.PARAMETER_PAGE); logger.debug("pName is '"+pName+"'"); String startString = request.getParameter((new ParamEncoder("row").encodeParameterName(TableTagParameters.PARAMETER_PAGE))); logger.debug("The start string is '"+startString+"'"); // Use 1-based index for start and end int start = 1; if (startString != null) { start = (Integer.parseInt(startString) - 1) * DEFAULT_LENGTH + 1; } // MyPaginatedList summaries = new MyPaginatedList<GeneSummary>(); // summaries.fullListSize = results.size(); // summaries.list = results; // summaries.objectsPerPage = DEFAULT_LENGTH; // summaries.pageNumber = bounds.getFirst(); // summaries.searchId = "xxx"; // summaries.sortDirection = SortOrderEnum.ASCENDING; // summaries.sortCriterion = "systematicId"; //logger.info(summaries.list); model.addAttribute("results", results); model.addAttribute("resultsSize", item.getQuery().getResultsSize()); int end = start + DEFAULT_LENGTH; int resultSize = item.getQuery().getResultsSize(); logger.debug("The number of results retrieved is '"+resultSize+"'"); logger.debug("The end marker, before adjustment, is '"+end+"'"); if (end > resultSize + 1) { end = resultSize + 1; } model.addAttribute("firstResultIndex", start); Query query = item.getQuery(); String queryName = queryFactory.getRealName(query); model.addAttribute("queryName", queryName); populateModelData(model, query); model.addAttribute("query", query); if (queryName.equals("quickSearch")) { QuickSearchQuery quickSearchQuery = (QuickSearchQuery) query; //model.addAttribute("query", quickSearchQuery); logger.info("taxonGroup : " + quickSearchQuery.getQuickSearchQueryResults(bounds.page, bounds.length).getTaxonGroup()); model.addAttribute("taxonGroup", quickSearchQuery.getQuickSearchQueryResults(bounds.page, bounds.length).getTaxonGroup()); if (resultSize == 0) { SuggestQuery squery = (SuggestQuery) queryFactory.retrieveQuery("suggest", NumericQueryVisibility.PRIVATE); squery.setSearchText(quickSearchQuery.getSearchText()); squery.setTaxons(quickSearchQuery.getTaxons()); squery.setMax(30); List sResults = squery.getResults(); model.addAttribute("suggestions", sResults); } } model.addAttribute("actionName" , request.getContextPath() + "/Query/" + queryName); return "search/"+ queryName; //return "list/results2"; } */ private String generateKey(String sessionId, Map<String, String[]> parameters) { int hashcode = 0; for (Entry<String, String[]> entry : parameters.entrySet()) { // ignore display tag pagination parameters if (entry.getKey().equals("d-16544-p")) { continue; } hashcode += entry.getKey().hashCode(); for (String value : entry.getValue()) { hashcode += value.hashCode(); } } String key = "query:"+ sessionId + ":"+ hashcode; return key; } private void populateModelData(Model model, Query query) { Map<String, Object> modelData = query.prepareModelData(); for (Map.Entry<String, Object> entry : modelData.entrySet()) { logger.info(entry.getKey() +" --- " + entry.getValue()); model.addAttribute(entry.getKey(), entry.getValue()); } } protected PagedQuery findQueryType(String queryName, HttpSession session){ if (!StringUtils.hasText(queryName)) { WebUtils.setFlashMessage("Unable to identify which query to use", session); logger.error("Unable to identify which query to use"); return null; } /* * Queries that the QueryController can handle must be public. PUBLIC_BUT_NO_FORMS is a special case * where links on gene pages (or elsewhere) are pointing here, rather than forms themeselves. */ PagedQuery query = (PagedQuery) queryFactory.retrieveQuery(queryName, NumericQueryVisibility.PUBLIC_BUT_NO_FORMS); if (query == null) { WebUtils.setFlashMessage("Unable to find query called '" + queryName + "'", session); logger.error("Unable to find query called '" + queryName + "'"); } return query; } /** * Remove an item in the result list * @param suppress * @param results */ @SuppressWarnings("rawtypes") protected void suppressResultItem(String suppress, List results){ if (StringUtils.hasLength(suppress)) { int index = results.indexOf(suppress); if (index != -1) { results.remove(index); } else { logger.warn("Trying to remove '" + suppress + "' from results (as a result of an n-others call but it isn't present"); } } } // class MyPaginatedList<T> implements PaginatedList { // // private int fullListSize; // private List<T> list; // private int objectsPerPage; // private int pageNumber; // private String searchId; // private String sortCriterion; // private SortOrderEnum sortDirection; // // @Override // public int getFullListSize() { // return fullListSize; // } // // @Override // public List<T> getList() { // return list; // } // // @Override // public int getObjectsPerPage() { // return objectsPerPage; // } // // @Override // public int getPageNumber() { // return pageNumber; // } // // @Override // public String getSearchId() { // return searchId; // } // // @Override // public String getSortCriterion() { // return sortCriterion; // } // // @Override // public SortOrderEnum getSortDirection() { // return sortDirection; // } // // } }