/**********************************************************************************
* $URL: $
* $Id: $
***********************************************************************************
*
* Copyright (c) 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.poll.tool.producers;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.poll.logic.ExternalLogic;
import org.sakaiproject.poll.logic.PollListManager;
import org.sakaiproject.poll.logic.PollVoteManager;
import org.sakaiproject.poll.model.Option;
import org.sakaiproject.poll.model.Poll;
import org.sakaiproject.poll.model.Vote;
import org.sakaiproject.poll.tool.params.PollViewParameters;
import uk.org.ponder.localeutil.LocaleGetter;
import uk.org.ponder.messageutil.MessageLocator;
import uk.org.ponder.messageutil.TargettedMessage;
import uk.org.ponder.messageutil.TargettedMessageList;
import uk.org.ponder.rsf.components.UIBranchContainer;
import uk.org.ponder.rsf.components.UICommand;
import uk.org.ponder.rsf.components.UIContainer;
import uk.org.ponder.rsf.components.UIForm;
import uk.org.ponder.rsf.components.UIInternalLink;
import uk.org.ponder.rsf.components.UILink;
import uk.org.ponder.rsf.components.UIMessage;
import uk.org.ponder.rsf.components.UIOutput;
import uk.org.ponder.rsf.components.UISelect;
import uk.org.ponder.rsf.components.UIVerbatim;
import uk.org.ponder.rsf.flow.jsfnav.NavigationCase;
import uk.org.ponder.rsf.flow.jsfnav.NavigationCaseReporter;
import uk.org.ponder.rsf.view.ComponentChecker;
import uk.org.ponder.rsf.view.ViewComponentProducer;
import uk.org.ponder.rsf.viewstate.SimpleViewParameters;
import uk.org.ponder.rsf.viewstate.ViewParameters;
import uk.org.ponder.rsf.viewstate.ViewParamsReporter;
import uk.org.ponder.rsf.components.decorators.DecoratorList;
import uk.org.ponder.rsf.components.decorators.UITooltipDecorator;
public class ResultsProducer implements ViewComponentProducer,NavigationCaseReporter,ViewParamsReporter {
public static final String VIEW_ID = "voteResults";
private PollListManager pollListManager;
private PollVoteManager pollVoteManager;
private MessageLocator messageLocator;
private LocaleGetter localegetter;
private static final Log LOG = LogFactory.getLog(ResultsProducer.class);
public String getViewID() {
// TODO Auto-generated method stub
return VIEW_ID;
}
public void setMessageLocator(MessageLocator messageLocator) {
this.messageLocator = messageLocator;
}
public void setPollListManager(PollListManager pollListManager) {
this.pollListManager = pollListManager;
}
public void setLocaleGetter(LocaleGetter localegetter) {
this.localegetter = localegetter;
}
public void setPollVoteManager(PollVoteManager pvm){
this.pollVoteManager = pvm;
}
private ExternalLogic externalLogic;
public void setExternalLogic(ExternalLogic externalLogic) {
this.externalLogic = externalLogic;
}
private TargettedMessageList tml;
public void setTargettedMessageList(TargettedMessageList tml) {
this.tml = tml;
}
public void fillComponents(UIContainer tofill, ViewParameters viewparams,
ComponentChecker checker) {
PollViewParameters ecvp = (PollViewParameters) viewparams;
String strId = ecvp.id;
LOG.debug("got id of " + strId);
Poll poll = pollListManager.getPollById(Long.valueOf(strId));
if (!pollListManager.isAllowedViewResults(poll, externalLogic.getCurrentUserId())) {
tml.addMessage(new TargettedMessage("poll.noviewresult", new Object[]{}, TargettedMessage.SEVERITY_ERROR));
return;
}
//get the number of votes
int voters = pollVoteManager.getDisctinctVotersForPoll(poll);
//Object[] args = new Object[] { Integer.valueOf(voters).toString()};
if (poll.getMaxOptions()>1)
UIOutput.make(tofill,"poll-size",messageLocator.getMessage("results_poll_size",Integer.valueOf(voters).toString()));
LOG.debug(voters + " have voted on this poll");
UIOutput.make(tofill,"question",poll.getText());
LOG.debug("got poll " + poll.getText());
List<Option> pollOptions = poll.getPollOptions();
LOG.debug("got a list of " + pollOptions.size() + " options");
//Append an option for no votes
if (poll.getMinOptions()==0) {
Option noVote = new Option(Long.valueOf(0));
noVote.setOptionText(messageLocator.getMessage("result_novote"));
noVote.setPollId(poll.getPollId());
pollOptions.add(noVote);
}
List<Vote> votes = pollVoteManager.getAllVotesForPoll(poll);
int totalVotes= votes.size();
LOG.debug("got " + totalVotes + " votes");
List<CollatedVote> collation = new ArrayList<CollatedVote>();
for (int i=0; i <pollOptions.size(); i++ ) {
CollatedVote collatedVote = new CollatedVote();
Option option = (Option) pollOptions.get(i);
LOG.debug("collating option " + option.getOptionId());
collatedVote.setoptionId(option.getOptionId());
collatedVote.setOptionText(option.getOptionText());
collatedVote.setDeleted(option.getDeleted());
for (int q=0; q <votes.size(); q++ ) {
Vote vote = (Vote)votes.get(q);
if (vote.getPollOption().equals(option.getOptionId())){
LOG.debug("got a vote for option " + option.getOptionId());
collatedVote.incrementVotes();
}
}
collation.add(collatedVote);
}
UILink title = UILink.make(tofill,"answers-title",messageLocator.getMessage("results_answers_title"), "#");
title.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_answers_title_tooltip")));
UILink count = UILink.make(tofill,"answers-count",messageLocator.getMessage("results_answers_numbering"), "#");
count.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_answers_numbering_tooltip")));
UILink avotes = UILink.make(tofill,"answers-votes",messageLocator.getMessage("results_answers_votes"), "#");
avotes.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_answers_votes_tooltip")));
UILink apercent = UILink.make(tofill,"answers-percent","%", "#");
apercent.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_answers_percent_tooltip")));
UIBranchContainer adefault = UIBranchContainer.make(tofill,"answers-default:");
adefault.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_answers_default_tooltip")));
//output the votes
Map<Long,String> chartTextData = new LinkedHashMap<Long,String>();
Map<Long,String> chartValueData = new LinkedHashMap<Long,String>();
NumberFormat nf = NumberFormat.getPercentInstance(localegetter.get());
for (int i=0; i <collation.size(); i++ ) {
CollatedVote cv = (CollatedVote)collation.get(i);
UIBranchContainer resultRow = UIBranchContainer.make(tofill,"answer-row:",cv.getoptionId().toString());
String optionText = cv.getOptionText();
if (cv.getDeleted()) {
optionText += messageLocator.getMessage("deleted_option_tag_html");
}
UIVerbatim.make(resultRow,"answer-option",optionText);
UIOutput.make(resultRow,"answer-count", Integer.valueOf(i+1).toString());
UIOutput.make(resultRow,"answer-numVotes",Long.valueOf(cv.getVotes()).toString());
LOG.debug("about to do the calc: (" + cv.getVotes()+"/"+ totalVotes +")*100");
double percent = (double)0;
if (totalVotes>0 && poll.getMaxOptions() == 1)
percent = ((double)cv.getVotes()/(double)totalVotes); //*(double)100;
else if (totalVotes>0 && poll.getMaxOptions() > 1)
percent = ((double)cv.getVotes()/(double)voters); //*(double)100;
else
percent = (double) 0;
//setup chartdata, use percentages for the values
//also, remove the from the beginning of the label, POLL-139
//we use the same number formatter which adds a % to the end of the data, remove that as well.
chartTextData.put(cv.getoptionId(), StringUtils.removeStart(optionText, " "));
chartValueData.put(cv.getoptionId(), StringUtils.removeEnd(nf.format(percent), "%"));
LOG.debug("result is "+ percent);
UIOutput.make(resultRow,"answer-percVotes", nf.format(percent));
}
UIOutput.make(tofill,"votes-total",Integer.valueOf(totalVotes).toString());
if (totalVotes > 0 && poll.getMaxOptions() == 1)
UIOutput.make(tofill,"total-percent","100%");
/** CHART **/
if(externalLogic.isResultsChartEnabled() && totalVotes > 0) {
//chart selector label
UIOutput.make(tofill,"chart-type-label",messageLocator.getMessage("results_chart_type"));
//chart selector - no binding, JQuery handles it.
String[] chartTypes = new String[]{"bar","pie"};
UISelect min = UISelect.make(tofill,"chart-type",chartTypes,"null","bar");
//setup bar chart
//data separator is |
StringBuilder sbBar = new StringBuilder();
sbBar.append("https://chart.googleapis.com/chart?");
sbBar.append("cht=bvg&");
sbBar.append("chxt=y&");
sbBar.append("chs=500x400&");
sbBar.append("chd=t:" + StringUtils.join(chartValueData.values(),'|') + "&");
sbBar.append("chdl=" + StringUtils.join(chartTextData.values(),'|') + "&");
sbBar.append("chco=FF0000,00FF00,0000FF,FFFF00,00FFFF,FF00FF,C0C0C0,800080,000080,808000,800000,FF00FF,008080,800000,008000");
UILink barChart = UILink.make(tofill,"poll-chart-bar",sbBar.toString());
LOG.debug("bar chart URL:" + sbBar.toString());
//setup pie chart
//data separator is ,
StringBuilder sbPie = new StringBuilder();
sbPie.append("https://chart.googleapis.com/chart?");
sbPie.append("cht=p&");
sbPie.append("chs=500x400&");
sbPie.append("chd=t:" + StringUtils.join(chartValueData.values(),',') + "&");
sbPie.append("chl=" + StringUtils.join(chartTextData.values(),'|') + "&");
sbPie.append("chco=FF0000,00FF00,0000FF,FFFF00,00FFFF,FF00FF,C0C0C0,800080,000080,808000,800000,FF00FF,008080,800000,008000");
UILink pieChart = UILink.make(tofill,"poll-chart-pie",sbPie.toString());
LOG.debug("pie chart URL:" + sbPie.toString());
//refresh link
UIInternalLink resultsLink = UIInternalLink.make(tofill, "results-refresh", messageLocator.getMessage("action_refresh_results"), new PollViewParameters(ResultsProducer.VIEW_ID, poll.getPollId().toString()));
resultsLink.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("action_refresh_results")+ ":" + poll.getText()));
}
//the cancel button
UIForm form = UIForm.make(tofill,"actform");
UICommand cancel = UICommand.make(form,"cancel",messageLocator.getMessage("results_cancel"),"#{pollToolBean.cancel}");
cancel.decorators = new DecoratorList(new UITooltipDecorator(messageLocator.getMessage("results_cancel_tooltip")));
externalLogic.postEvent("poll.viewResult", "poll/site/" + externalLogic.getCurrentLocationId() +"/poll/" + poll.getPollId(), false);
}
public List<NavigationCase> reportNavigationCases() {
List<NavigationCase> togo = new ArrayList<NavigationCase>(); // Always navigate back to this view.
togo.add(new NavigationCase(null, new SimpleViewParameters(VIEW_ID)));
togo.add(new NavigationCase("cancel", new SimpleViewParameters(PollToolProducer.VIEW_ID)));
return togo;
}
public ViewParameters getViewParameters() {
return new PollViewParameters();
}
private static class CollatedVote {
private Long optionId ;
private String optionText;
private int votes;
private Boolean deleted;
public CollatedVote() {
this.votes=0;
}
public void setoptionId(Long val){
this.optionId = val;
}
public Long getoptionId(){
return this.optionId;
}
public void setOptionText(String t){
this.optionText = t;
}
public String getOptionText(){
return this.optionText;
}
public void setVotes(int i){
this.votes = i;
}
public int getVotes(){
return this.votes;
}
public void incrementVotes(){
this.votes++;
}
public void setDeleted(Boolean deleted) {
this.deleted = deleted;
}
public Boolean getDeleted() {
return deleted;
}
}
}