de.lemo.dms.processing.questions.QUserPathAnalysis.java Source code

Java tutorial

Introduction

Here is the source code for de.lemo.dms.processing.questions.QUserPathAnalysis.java

Source

/**
 * File ./src/main/java/de/lemo/dms/processing/questions/QUserPathAnalysis.java
 * Lemo-Data-Management-Server for learning analytics.
 * Copyright (C) 2013
 * Leonard Kappe, Andreas Pursian, Sebastian Schwarzrock, Boris Wenzlaff
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/

/**
 * File ./main/java/de/lemo/dms/processing/questions/QUserPathAnalysis.java
 * Date 2013-01-24
 * Project Lemo Learning Analytics
 */

package de.lemo.dms.processing.questions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import de.lemo.dms.core.config.ServerConfiguration;
import de.lemo.dms.db.IDBHandler;
import de.lemo.dms.db.mapping.abstractions.ILogMining;
import de.lemo.dms.processing.ELearningObjectType;
import de.lemo.dms.processing.MetaParam;
import de.lemo.dms.processing.Question;
import de.lemo.dms.processing.StudentHelper;
import de.lemo.dms.processing.resulttype.ResultListUserPathGraph;
import de.lemo.dms.processing.resulttype.UserPathLink;
import de.lemo.dms.processing.resulttype.UserPathNode;
import de.lemo.dms.processing.resulttype.UserPathObject;

/**
 * Generates a list of Nodes and edges, representing the user-navigation
 * 
 * @author Sebastian Schwarzrock
 */
@Path("userpathanalysis")
public class QUserPathAnalysis extends Question {

    /**
     * Returns a list of Nodes and edges, representing the user-navigation
     * matching the requirements given by the parameters.
     * 
     * @see ELearningObjectType
     * @param courses
     *            List of course-identifiers
     * @param users
     *            List of user-identifiers
     * @param types
     *            List of learn object types (see ELearnObjType)
     * @param considerLogouts
     *            If user-paths should be cut when a logout appears this must be
     *            set to "true".
     * @param startTime
     *            LongInteger time stamp
     * @param endTime
     *            LongInteger time stamp
     * @return
     */
    @POST
    public ResultListUserPathGraph compute(@FormParam(MetaParam.COURSE_IDS) final List<Long> courses,
            @FormParam(MetaParam.USER_IDS) List<Long> users, @FormParam(MetaParam.TYPES) final List<String> types,
            @FormParam(MetaParam.LOGOUT_FLAG) final Boolean considerLogouts,
            @FormParam(MetaParam.START_TIME) final Long startTime,
            @FormParam(MetaParam.END_TIME) final Long endTime,
            @FormParam(MetaParam.GENDER) final List<Long> gender) {

        validateTimestamps(startTime, endTime);

        // DB-initialization
        final IDBHandler dbHandler = ServerConfiguration.getInstance().getMiningDbHandler();
        final Session session = dbHandler.getMiningSession();

        Criteria criteria;
        if (users == null || users.size() == 0) {
            users = new ArrayList<Long>(StudentHelper.getCourseStudentsAliasKeys(courses, gender).values());
        } else {
            Map<Long, Long> userMap = StudentHelper.getCourseStudentsAliasKeys(courses, gender);
            List<Long> tmp = new ArrayList<Long>();
            for (int i = 0; i < users.size(); i++) {
                tmp.add(userMap.get(users.get(i)));
            }
            users = tmp;
        }

        // Create criteria for log-file-search
        criteria = session.createCriteria(ILogMining.class, "log");

        criteria.add(Restrictions.between("log.timestamp", startTime, endTime));
        criteria.addOrder(Order.asc("log.timestamp"));

        if (!courses.isEmpty()) {
            criteria.add(Restrictions.in("log.course.id", courses));
        }

        if (!users.isEmpty()) {
            criteria.add(Restrictions.in("log.user.id", users));
        }

        @SuppressWarnings("unchecked")
        final List<ILogMining> list = criteria.list();

        Collections.sort(list);

        this.logger.debug("Total matched entries: " + list.size());

        // Map for UserPathObjects
        final LinkedHashMap<String, UserPathObject> pathObjects = Maps.newLinkedHashMap();

        // Map for user histories
        final HashMap<Long, List<ILogMining>> userHis = Maps.newHashMap();

        int skippedUsers = 0;
        // Generate user histories
        for (final ILogMining log : list) {
            if (log.getUser() == null) {
                skippedUsers++;
                continue;
            }
            boolean typeOk = true;
            if (!types.isEmpty()) {
                typeOk = false;
                for (final String type : types) {
                    /*
                     * XXX why is this checked for every log object and not a
                     * criteria restriction?
                     */
                    // Check if ILog-object has acceptable learningObjectType
                    if (ELearningObjectType.fromLogMiningType(log).toString().toUpperCase().equals(type)) {
                        typeOk = true;
                        break;
                    }
                }
            }

            if (typeOk) {
                if (userHis.get(log.getUser().getId()) == null) {
                    // If user is new create a new entry in the hash map and add
                    // log item
                    userHis.put(log.getUser().getId(), new ArrayList<ILogMining>());
                    userHis.get(log.getUser().getId()).add(log);
                } else {
                    userHis.get(log.getUser().getId()).add(log);
                }
            }
        }

        this.logger.debug("Skipped entries with missing user id: " + skippedUsers);

        int skippedLogs = 0;
        // Generate paths from user histories
        for (final List<ILogMining> l : userHis.values()) {
            String predNode = null;
            for (int i = 0; i < l.size(); i++) {
                if ((l.get(i) != null) && (l.get(i).getUser() != null)) {
                    final ILogMining current = l.get(i);

                    final Long learnObjId = current.getLearnObjId();
                    if (learnObjId == null) {
                        skippedLogs++;
                        continue;
                    }
                    final String learnObjType = ELearningObjectType.fromLogMiningType(current).toString();
                    final String type = current.getClass().toString().substring(
                            current.getClass().toString().lastIndexOf(".") + 1,
                            current.getClass().toString().lastIndexOf("Log"));
                    final String cId = learnObjId + "-" + learnObjType;
                    // Determines whether it's a new path (no predecessor for
                    // current node) or not

                    UserPathObject knownPath;
                    if (predNode != null) {
                        String cIdPos = null;
                        if ((knownPath = pathObjects.get(cId)) == null) {
                            // If the node is new create entry in hash map
                            cIdPos = String.valueOf(pathObjects.size());
                            pathObjects.put(cId, new UserPathObject(cIdPos, current.getTitle(), 1L, type,
                                    Double.valueOf(current.getDuration()), 1L, 0L, 0L, 0L));
                        } else {
                            // If the node is already known, increase weight
                            pathObjects.get(cId).increaseWeight(Double.valueOf(current.getDuration()));
                            cIdPos = knownPath.getId();
                        }

                        // Increment or create predecessor edge
                        pathObjects.get(predNode).addEdgeOrIncrement(cIdPos);
                    } else if (pathObjects.get(cId) == null) {
                        final String cIdPos = String.valueOf(pathObjects.size());
                        pathObjects.put(cId, new UserPathObject(cIdPos, current.getTitle(), 1L, type,
                                Double.valueOf(current.getDuration()), 1L, 0L, 0L, 0L));
                    } else {
                        pathObjects.get(cId).increaseWeight(Double.valueOf(current.getDuration()));
                    }

                    if (considerLogouts && (current.getDuration() == -1L)) {
                        predNode = null;
                    } else {
                        predNode = cId;
                    }
                }
            }
        }
        this.logger.debug("Skipped entries with missing learn object id: " + skippedLogs);

        final ArrayList<UserPathNode> nodes = Lists.newArrayList();
        final ArrayList<UserPathLink> links = Lists.newArrayList();

        for (final UserPathObject pathEntry : pathObjects.values()) {

            final UserPathObject path = pathEntry;
            path.setPathId(pathEntry.getPathId());
            nodes.add(new UserPathNode(path));
            final String sourcePos = path.getId();

            for (final Entry<String, Integer> linkEntry : pathEntry.getEdges().entrySet()) {
                final UserPathLink link = new UserPathLink();
                link.setSource(sourcePos);
                link.setPathId(path.getPathId());
                link.setTarget(linkEntry.getKey());
                link.setValue(String.valueOf(linkEntry.getValue()));
                if (link.getSource() != link.getTarget()) {
                    links.add(link);
                }
            }
        }
        session.close();
        return new ResultListUserPathGraph(nodes, links);
    }

}