LearnPAd/learnpad

View on GitHub
lp-collaborative-workspace/lp-cw-bridge/lp-cw-bridge-implementation/src/main/java/eu/learnpad/cw/internal/CWXwikiBridge.java

Summary

Maintainability
D
2 days
Test Coverage
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package eu.learnpad.cw.internal;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.Path;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.slf4j.Logger;
import org.xwiki.component.annotation.Component;
import org.xwiki.component.phase.Initializable;
import org.xwiki.component.phase.InitializationException;
import org.xwiki.configuration.ConfigurationSource;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.model.reference.DocumentReferenceResolver;
import org.xwiki.query.Query;
import org.xwiki.query.QueryException;
import org.xwiki.query.QueryFilter;
import org.xwiki.query.QueryManager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;

import eu.learnpad.configuration.LearnpadPropertiesConfigurationSource;
import eu.learnpad.core.impl.cw.XwikiBridge;
import eu.learnpad.core.impl.cw.XwikiCoreFacadeRestResource;
import eu.learnpad.core.rest.DefaultRestResource;
import eu.learnpad.core.rest.RestResource;
import eu.learnpad.cw.UICWBridge;
import eu.learnpad.cw.internal.RecommendationWebsocketServer.WebSocketMetadata;
import eu.learnpad.cw.rest.data.ScoreRecord;
import eu.learnpad.cw.rest.data.ScoreRecordCollection;
import eu.learnpad.exception.LpRestException;
import eu.learnpad.exception.impl.LpRestExceptionXWikiImpl;
import eu.learnpad.me.rest.data.ModelSetType;
import eu.learnpad.or.rest.data.NotificationActionType;
import eu.learnpad.or.rest.data.Recommendations;
import eu.learnpad.or.rest.data.ResourceType;
import eu.learnpad.rest.model.jaxb.PFResults;
import eu.learnpad.rest.model.jaxb.PFResults.Feedbacks;
import eu.learnpad.rest.model.jaxb.PFResults.Feedbacks.Feedback;
import eu.learnpad.rest.model.jaxb.PFResults.Patches;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch.Artefact;
import eu.learnpad.rest.model.jaxb.PFResults.Patches.Patch.Artefact.Attribute;
import eu.learnpad.rest.model.jaxb.PatchType;
import eu.learnpad.sim.rest.data.ProcessInstanceData;
import eu.learnpad.sim.rest.data.UserData;
import eu.learnpad.sim.rest.data.UserDataCollection;

@Component
@Singleton
@Named("eu.learnpad.cw.internal.CWXwikiBridge")
@Path("/learnpad/cw/bridge")
public class CWXwikiBridge extends XwikiBridge implements Initializable,
        UICWBridge {

    private final String LEARNPAD_SPACE = "LPCode";

    private final String FEEDBACK_CLASS_PAGE = "FeedbackClass";

    private final String FEEDBACK_CLASS = String.format("%s.%s",
            LEARNPAD_SPACE, FEEDBACK_CLASS_PAGE);

    private final String BASEELEMENT_CLASS_PAGE = "BaseElementClass";

    private final String BASEELEMENT_CLASS = String.format("%s.%s",
            LEARNPAD_SPACE, BASEELEMENT_CLASS_PAGE);

    private final String XWIKI_SPACE = "XWiki";

    private final String USER_CLASS_PAGE = "XWikiUsers";

    private final String USER_CLASS = String.format("%s.%s", XWIKI_SPACE,
            USER_CLASS_PAGE);

    private static final Map<String, Map<String, ScoreRecord>> scoresBySessionByUser = new ConcurrentHashMap<String, Map<String, ScoreRecord>>();

    @Inject
    @Named("default")
    RestResource restResource;

    @Inject
    @Named("learnpadproperties")
    ConfigurationSource configurationSource;

    private Map<String, Long> bannedSimIDMap;

    private final long BANNING_PERIOD_IN_MILLI_SEC = 60000;

    @Inject
    private Logger logger;

    @Inject
    @Named("secure")
    private QueryManager queryManager;

    @Inject
    @Named("unique")
    private QueryFilter uniqueDocumentFilter;

    @Inject
    private Provider<XWikiContext> xcontextProvider;

    @Inject
    @Named("current")
    private DocumentReferenceResolver<String> stringDocumentReferenceResolver;

    @Override
    public void initialize() throws InitializationException {
        this.corefacade = new XwikiCoreFacadeRestResource();
        this.bannedSimIDMap = Collections
                .synchronizedMap(new HashMap<String, Long>());
    }

    @Override
    public byte[] getComments(String modelSetId, String artifactId)
            throws LpRestException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public byte[] getResource(String modelSetId, String resourceId,
            String linkedTo, String action) throws LpRestException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void modelSetImported(String modelSetId, ModelSetType type)
            throws LpRestException {
        // Get the model file from Core Platform
        InputStream modelStream = this.corefacade.getModel(modelSetId, type);
        if (modelStream == null) {
            throw new LpRestExceptionXWikiImpl("Fail to get the new modelset");
        }

        InputStream packageStream = this.corefacade
                .transform(type, modelStream);
        if (packageStream == null) {
            throw new LpRestExceptionXWikiImpl(
                    "Fail to transform the new modelset");
        }
        this.loadPackage(packageStream);
    }

    private void loadPackage(InputStream packageStream) {
        // Now send the package's path to the importer for XWiki
        HttpClient httpClient = restResource.getClient();

        String uri = DefaultRestResource.REST_URI + "/wikis/xwiki/xff";
        PostMethod postMethod = new PostMethod(uri);
        postMethod.addRequestHeader("Accept", "application/octet-stream");

        RequestEntity packageEntity = new InputStreamRequestEntity(
                packageStream);
        postMethod.setRequestEntity(packageEntity);

        try {
            httpClient.executeMethod(postMethod);
        } catch (IOException e) {
            String message = "Unable to upload the XFF package";
            logger.error(message, e);
        }
    }

    @Override
    public void contentVerified(String modelSetId, String artifactId,
            String resourceId, String result) throws LpRestException {
        // TODO Auto-generated method stub

    }

    @Override
    public void modelVerified(String modelSetId, String result)
            throws LpRestException {
        // TODO Auto-generated method stub

    }

    private List<Object> getFeedbacksDocuments(String modelSetId) {
        String queryXWQL = String
                .format("from doc.object(%s) as feedback where feedback.modelsetid = '%s'",
                        FEEDBACK_CLASS, modelSetId);
        Query query = null;
        try {
            query = queryManager.createQuery(queryXWQL, Query.XWQL);
        } catch (QueryException e) {
            String message = String
                    .format("Error in building the query to gather Feedbacks in '%s' model set.",
                            modelSetId);
            logger.error(message, e);
            return null;
        }
        try {
            return query.addFilter(uniqueDocumentFilter).execute();
        } catch (QueryException e) {
            String message = String
                    .format("Error in executing the query to gather Feedbacks in '%s' model set.",
                            modelSetId);
            logger.error(message, e);
            return null;
        }
    }

    private Feedbacks getFeedbackList(String modelSetId) {
        XWikiContext xcontext = xcontextProvider.get();
        XWiki xwiki = xcontext.getWiki();
        DocumentReference classReference = stringDocumentReferenceResolver
                .resolve(FEEDBACK_CLASS);
        List<Object> documentNames = getFeedbacksDocuments(modelSetId);
        List<Feedback> feedbacksList = new ArrayList<Feedback>();
        for (Object documentName : documentNames) {
            DocumentReference documentReference = stringDocumentReferenceResolver
                    .resolve((String) documentName);

            XWikiDocument document;
            try {
                document = xwiki.getDocument(documentReference, xcontext);
            } catch (Exception e) {
                String message = String
                        .format("Error while trying to get document '%s' to gather feedbacks on '%' model.",
                                documentReference.toString(), modelSetId);
                logger.error(message, e);
                return null;
            }
            BaseObject feedbackObject = document.getXObject(classReference);
            Integer id = Integer.parseInt(feedbackObject.getStringValue("id"));
            Integer modelId = feedbackObject.getIntValue("modelid");
            Integer artefactId = feedbackObject.getIntValue("artifactid");
            String content = feedbackObject.getStringValue("description");
            Feedback feedback = new Feedback();
            feedback.setId(id);
            feedback.setModelid(modelId);
            feedback.setArtefactid(artefactId);
            feedback.setValue(content);
            feedbacksList.add(feedback);
        }
        Feedbacks feedbacks = new Feedbacks();
        feedbacks.setFeedback(feedbacksList);
        return feedbacks;
    }

    private Patches getPatchList(String modelSetId) {
        // TODO
        Attribute attribute1 = new Attribute();
        attribute1.setId("123");
        attribute1.setName("name");
        attribute1.setValue("adsfdsafY");
        Attribute attribute2 = new Attribute();
        attribute2.setId("234");
        attribute2.setName("xs");
        attribute2.setValue("adsfdsafY");
        Attribute attribute3 = new Attribute();
        attribute3.setId("345");
        attribute3.setName("sd");
        attribute3.setValue("adsfdsafY");
        List<Attribute> attributesList = new ArrayList<Attribute>();
        attributesList.add(attribute1);
        attributesList.add(attribute2);
        attributesList.add(attribute3);
        Artefact artefact1 = new Artefact();
        artefact1.setId("");
        artefact1.setName("sdfds");
        artefact1.setClassName("Task");
        artefact1.setAttribute(attributesList);
        Artefact artefact2 = new Artefact();
        artefact2.setId("123");
        artefact2.setName("sdfasdf");
        artefact2.setClassName("Task");
        artefact2.setAttribute(attributesList);
        Artefact artefact3 = new Artefact();
        artefact3.setId("123");
        artefact3.setName("sdfasdf");
        artefact3.setClassName("End Event");
        Patch patch1 = new Patch();
        patch1.setType(PatchType.ADD);
        patch1.setId(Integer.parseInt("123"));
        patch1.setModelid(Integer.parseInt("123456"));
        patch1.setArtefact(artefact1);
        Patch patch2 = new Patch();
        patch2.setType(PatchType.EDIT);
        patch2.setId(Integer.parseInt("123"));
        patch2.setModelid(Integer.parseInt("123456"));
        patch2.setArtefact(artefact2);
        Patch patch3 = new Patch();
        patch3.setType(PatchType.DELETE);
        patch3.setId(Integer.parseInt("123"));
        patch3.setModelid(Integer.parseInt("123456"));
        patch3.setArtefact(artefact3);
        List<Patch> patchesList = new ArrayList<Patch>();
        patchesList.add(patch1);
        patchesList.add(patch2);
        patchesList.add(patch3);
        Patches patches = new Patches();
        patches.setPatch(patchesList);
        return patches;
    }

    @Override
    public PFResults getFeedbacks(String modelSetId) throws LpRestException {
        PFResults pf = new PFResults();
        pf.setFeedbacks(getFeedbackList(modelSetId));
        // pf.setPatches(getPatchList(modelSetId));
        pf.setPatches(new Patches());
        return pf;
    }

    @Override
    public Recommendations getRecommendations(String modelSetId,
            String artifactId, String userId) throws LpRestException {
        return this.corefacade.getRecommendations(modelSetId, artifactId,
                userId);
    }

    @Override
    public String getDashboardKpiDefaultViewer(String modelSetId, String userId)
            throws LpRestException {
        return this.corefacade.getDashboardKpiDefaultViewer(modelSetId, userId);
    }

    public String startDashboardKpiCalculation(String modelSetId)
            throws LpRestException {
        return this.corefacade.calculateKPI(modelSetId);
    }

    
    private UserDataCollection getUserProfiles(Collection<String> potentialUsers) {
        XWikiContext xcontext = xcontextProvider.get();
        XWiki xwiki = xcontext.getWiki();
        DocumentReference userClassReference = stringDocumentReferenceResolver
                .resolve(USER_CLASS);
        Collection<UserData> potentialUsersCollection = new ArrayList<UserData>();

        for (String userId : potentialUsers) {
            UserData user = new UserData();
            user.id = this.removePrefixes(userId);

            DocumentReference userReference = stringDocumentReferenceResolver
                    .resolve(userId);
            try {
                XWikiDocument userDocument = xwiki.getDocument(userReference,
                        xcontext);
                if (userDocument != null) {
                    BaseObject userObject = userDocument
                            .getXObject(userClassReference);
                    if (userObject != null) {
                        user.firstName = userObject
                                .getStringValue("first_name");
                        user.lastName = userObject.getStringValue("last_name");
                        user.profileURL = userDocument.getExternalURL("view",
                                xcontext);
                        Pattern pattern = Pattern.compile("(http://[^/]*/).*");
                        Matcher matcher = pattern.matcher(user.profileURL);
                        String prefix = "";
                        if (matcher.find()) {
                            prefix = matcher.group(1);
                        }
                        user.pictureURL = prefix
                                + userDocument.getAttachmentURL(
                                        userDocument.getStringValue("avatar"),
                                        xcontext);
                    }
                }
            } catch (XWikiException e) {
                String message = String
                        .format("Error while trying to get profile information for the user '%s'.",
                                userReference.toString());
                logger.error(message, e);
            }
            potentialUsersCollection.add(user);
        }
        return new UserDataCollection(potentialUsersCollection);
    }

    @Override
    public String startSimulation(String modelId, String currentUser,
            Collection<String> potentialUsers) throws LpRestException {
        UserDataCollection potentialUsersCollection = getUserProfiles(potentialUsers);
        return this.corefacade.startSimulation(modelId, currentUser,
                potentialUsersCollection);
    }

    @Override
    public String joinSimulation(String simulationId, String userId)
            throws LpRestException {
        return this.corefacade.joinSimulation(simulationId, userId);
    }

    @Override
    public Collection<String> listSimulations() throws LpRestException {
        return this.corefacade.listSimulations();
    }

    @Override
    public ProcessInstanceData getSimulationInfo(String simulationId)
            throws LpRestException {
        return this.corefacade.getSimulationInfo(simulationId);
    }

    @Override
    public String getRestPrefix(String component) throws LpRestException {
        return ((LearnpadPropertiesConfigurationSource) configurationSource)
                .getRestPrefix(component);
    }

    @Override
    public void notifyRecommendations(String modelsetid, String simulationid,
            String userid, Recommendations recommendations)
            throws LpRestException {
        String xwikiUserId = String.format("XWiki.%s", userid);
        if (this.isBanningPeriodExpired(simulationid)) {
            for (WebSocketMetadata wsmd : RecommendationWebsocketServer.socketBox
                    .byUserid(xwikiUserId)) {
                ObjectMapper mapper = new ObjectMapper();
                String msg = "";
                try {
                    msg = mapper.writeValueAsString(recommendations);
                } catch (JsonProcessingException e) {
                    msg = "";
                }
                wsmd.timeOfLastInteraction = System.currentTimeMillis();
                wsmd.socket.send(msg);
            }
        }
    }

    @Override
    public void deleteRecommendations(String modelSetId, String simulationid,
            String userId) throws LpRestException {
        String msg = "resetting Recs for : modelSetId : " + modelSetId + "\n"
                + "simulationid : " + simulationid + "\n" + "userId : "
                + userId;

        logger.info(msg);

        this.bannedSimIDMap.put(simulationid, System.currentTimeMillis());
    }

    private boolean isBanningPeriodExpired(String simId) {
        boolean status = true;
        if (this.bannedSimIDMap.containsKey(simId)) {
            status = (System.currentTimeMillis() > (this.bannedSimIDMap
                    .get(simId) + this.BANNING_PERIOD_IN_MILLI_SEC));
            if (status) {
                this.bannedSimIDMap.remove(simId);
            }
        }
        return status;
    }

    private String removePrefixes(String userId) {
        String username = userId.replaceFirst("XWiki\\.", "");
        return username;
    }

    @Override
    public void notifyScoreUpdate(ScoreRecord record) throws LpRestException {
        String userid = record.getUserArtifactId();
        String sessionid = record.getSessionId();
        if (!scoresBySessionByUser.containsKey(userid)) {
            scoresBySessionByUser.put(record.getUserArtifactId(),
                    new ConcurrentHashMap<String, ScoreRecord>());
        }
        scoresBySessionByUser.get(userid).put(sessionid, record);

    }

    @Override
    public ScoreRecordCollection getScores(String userid, String modelid)
            throws LpRestException {
        Collection<ScoreRecord> res = new ArrayList<>();
        for (String recordedUser : scoresBySessionByUser.keySet()) {
            if (userid == null || recordedUser.equals(userid)) {
                for (String session : scoresBySessionByUser.get(recordedUser)
                        .keySet()) {

                    ScoreRecord r = scoresBySessionByUser.get(recordedUser)
                            .get(session);
                    String processid = r.getProcessArtifactId();
                    if (modelid == null || processid.equals(modelid)) {
                        res.add(r);
                    }
                }
            }
        }
        return new ScoreRecordCollection(res);
    }

    @Override
    public void pageNotification(String modelSetId, String modelId,
            String artifactId, String resourceId, String action, String userId)
            throws LpRestException {
        ResourceType resourceType = ResourceType.PAGE;
        this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
                resourceType, action, userId);
    }

    @Override
    public void commentNotification(String modelSetId, String modelId,
            String artifactId, String resourceId, String action, String userId)
            throws LpRestException
    {
        ResourceType resourceType = ResourceType.COMMENT;
        this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
                resourceType, action, userId);
    }

    @Override
    public void attachmentNotification(String modelSetId, String modelId,
            String artifactId, String resourceId, String action, String userId)
            throws LpRestException
    {
        ResourceType resourceType = ResourceType.ATTACHMENT;
        this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
                resourceType, action, userId);
    }

    @Override
    public void feedbackNotification(String modelSetId, String modelId,
            String artifactId, String resourceId, String action, String userId)
            throws LpRestException {
        ResourceType resourceType = ResourceType.FEEDBACK;
        this.resourceNotification(modelSetId, modelId, artifactId, resourceId,
                resourceType, action, userId);
    }

    private void resourceNotification(String modelSetId, String modelId,
            String artifactId, String resourceId, ResourceType resourceType,
            String action, String userId) throws LpRestException {
        NotificationActionType actionType = null;
        try {
            actionType = NotificationActionType.valueOf(action.toUpperCase());
        } catch (IllegalArgumentException | NullPointerException e) {
            LpRestExceptionXWikiImpl e1 = new LpRestExceptionXWikiImpl(
                    e.getMessage(), e.getCause());
            throw e1;
        }
        this.corefacade.resourceNotification(modelSetId, modelId, artifactId,
                resourceId, resourceType, actionType, userId);
    }

}