001/*
002 *  Copyright 2011 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.survey.generators;
017
018import java.io.IOException;
019import java.time.ZoneId;
020import java.time.ZonedDateTime;
021import java.util.Arrays;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.Map;
026import java.util.Set;
027
028import org.apache.avalon.framework.parameters.ParameterException;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.cocoon.ProcessingException;
032import org.apache.cocoon.generation.ServiceableGenerator;
033import org.apache.cocoon.xml.AttributesImpl;
034import org.apache.cocoon.xml.XMLUtils;
035import org.apache.commons.lang.StringUtils;
036import org.xml.sax.SAXException;
037
038import org.ametys.core.user.UserIdentity;
039import org.ametys.plugins.repository.AmetysObjectResolver;
040import org.ametys.plugins.survey.data.SurveyAnswer;
041import org.ametys.plugins.survey.data.SurveyAnswerDao;
042import org.ametys.plugins.survey.data.SurveySession;
043import org.ametys.plugins.survey.repository.Survey;
044import org.ametys.plugins.survey.repository.SurveyQuestion;
045import org.ametys.runtime.parameter.ParameterHelper;
046
047/**
048 * Generates a specific survey user session from its ID.
049 */
050public class SurveySessionGenerator extends ServiceableGenerator
051{
052
053    /** The ametys object resolver. */
054    protected AmetysObjectResolver _resolver;
055    
056    /** The survey data DAO. */
057    protected SurveyAnswerDao _surveyDao;
058    
059    @Override
060    public void service(ServiceManager serviceManager) throws ServiceException
061    {
062        super.service(serviceManager);
063        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
064        _surveyDao = (SurveyAnswerDao) serviceManager.lookup(SurveyAnswerDao.ROLE);
065    }
066    
067    @Override
068    public void generate() throws IOException, SAXException, ProcessingException
069    {
070//        Request request = ObjectModelHelper.getRequest(objectModel);
071        
072        // Get the session ID.
073        int sessionId;
074        try
075        {
076            sessionId = parameters.getParameterAsInteger("id");
077        }
078        catch (ParameterException e)
079        {
080            throw new ProcessingException("Session ID is mandatory.", e);
081        }
082        
083        // Retrieve the corresponding session from the database.
084        SurveySession surveySession = _surveyDao.getSessionWithAnswers(sessionId);
085        
086        // Resolve the corresponding survey.
087        Survey survey = _resolver.resolveById(surveySession.getSurveyId());
088        
089        contentHandler.startDocument();
090        
091        // Generate the session and its answers.
092        saxSession(surveySession, survey, true);
093        
094        contentHandler.endDocument();
095    }
096    
097    /**
098     * Generate the data of a survey user session.
099     * @param surveySession the survey session.
100     * @param survey the survey.
101     * @param withAnswers true to generate answers along, false otherwise.
102     * @throws SAXException if an error occurs while saxing the session
103     */
104    protected void saxSession(SurveySession surveySession, Survey survey, boolean withAnswers) throws SAXException
105    {
106        ZonedDateTime submittedAt = surveySession.getSubmittedAt().toInstant().atZone(ZoneId.systemDefault());
107        
108        AttributesImpl attrs = new AttributesImpl();
109        attrs.addCDATAAttribute("id", Integer.toString(surveySession.getId()));
110        attrs.addCDATAAttribute("submittedAt", ParameterHelper.getISODateTimeFormatter().format(submittedAt));
111        UserIdentity user = surveySession.getUser();
112        if (user != null)
113        {
114            attrs.addCDATAAttribute("user", UserIdentity.userIdentityToString(user));
115        }
116        
117        XMLUtils.startElement(contentHandler, "session", attrs);
118        
119        if (withAnswers)
120        {
121            saxAnswers(surveySession, survey);
122        }
123        
124        XMLUtils.endElement(contentHandler, "session");
125    }
126
127    /**
128     * Generate the answers of a given session.
129     * @param surveySession the survey session.
130     * @param survey the survey.
131     * @throws SAXException if an error occurs while saxing the answers
132     */
133    protected void saxAnswers(SurveySession surveySession, Survey survey) throws SAXException
134    {
135        AttributesImpl attrs = new AttributesImpl();
136        
137        Map<String, SurveyAnswer> answerMap = getAnswerMap(surveySession);
138        
139        for (SurveyQuestion question : survey.getQuestions())
140        {
141            SurveyAnswer answer = answerMap.get(question.getName());
142            
143            attrs.clear();
144            attrs.addCDATAAttribute("name", question.getName());
145            attrs.addCDATAAttribute("title", question.getTitle());
146            attrs.addCDATAAttribute("type", question.getType().toString());
147            attrs.addCDATAAttribute("mandatory", Boolean.toString(question.isMandatory()));
148            XMLUtils.startElement(contentHandler, "question", attrs);
149            
150            if (answer != null)
151            {
152                Map<String, Set<String>> values = getValueMap(question, answer.getValue());
153                
154                for (String option : values.keySet())
155                {
156                    attrs.clear();
157                    attrs.addCDATAAttribute("name", option);
158                    XMLUtils.startElement(contentHandler, "option", attrs);
159                    
160                    for (String value : values.get(option))
161                    {
162                        attrs.clear();
163                        attrs.addCDATAAttribute("value", value);
164                        XMLUtils.createElement(contentHandler, "answer", attrs);
165                    }
166                    
167                    XMLUtils.endElement(contentHandler, "option");
168                }
169            }
170            
171            XMLUtils.endElement(contentHandler, "question");
172        }
173    }
174    
175    /**
176     * Get the answers of a survey session as a Map, indexed by question ID.
177     * @param surveySession the survey session.
178     * @return the answers as a Map, indexed by question ID. 
179     */
180    protected Map<String, SurveyAnswer> getAnswerMap(SurveySession surveySession)
181    {
182        Map<String, SurveyAnswer> answerMap = new HashMap<>();
183        
184        for (SurveyAnswer answer : surveySession.getAnswers())
185        {
186            answerMap.put(answer.getQuestionId(), answer);
187        }
188        
189        return answerMap;
190    }
191    
192    /**
193     * Get the user-input value as a Map from the database value, which is a single serialized string.
194     * @param question the question.
195     * @param value the value from the database.
196     * @return the value as a Map.
197     */
198    protected Map<String, Set<String>> getValueMap(SurveyQuestion question, String value)
199    {
200        Map<String, Set<String>> values = new HashMap<>();
201        
202        if (value != null)
203        {
204            switch (question.getType())
205            {
206                case SINGLE_MATRIX:
207                case MULTIPLE_MATRIX:
208                    String[] entries = StringUtils.split(value, ';');
209                    for (String entry : entries)
210                    {
211                        String[] keyValue = StringUtils.split(entry, ':');
212                        if (keyValue.length == 2 && StringUtils.isNotEmpty(keyValue[0]))
213                        {
214                            Set<String> valueSet = new HashSet<>(Arrays.asList(StringUtils.split(keyValue[1], ',')));
215                            
216                            values.put(keyValue[0], valueSet);
217                        }
218                    }
219                    break;
220                case SINGLE_CHOICE:
221                case MULTIPLE_CHOICE:
222                    Set<String> valueSet = new HashSet<>(Arrays.asList(StringUtils.split(value, ',')));
223                    values.put("values", valueSet);
224                    break;
225                case FREE_TEXT:
226                case MULTILINE_FREE_TEXT:
227                default:
228                    values.put("values", Collections.singleton(value));
229                    break;
230            }
231        }
232        
233        return values;
234    }
235
236}