Package org.olat.course.assessment

Source Code of org.olat.course.assessment.AssessmentNotificationsHandler

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/

package org.olat.course.assessment;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.olat.basesecurity.Constants;
import org.olat.basesecurity.Manager;
import org.olat.basesecurity.ManagerFactory;
import org.olat.core.commons.persistence.PersistenceHelper;
import org.olat.core.gui.translator.Translator;
import org.olat.core.id.Identity;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Util;
import org.olat.core.util.nodes.INode;
import org.olat.core.util.notifications.NotificationHelper;
import org.olat.core.util.notifications.NotificationsHandler;
import org.olat.core.util.notifications.NotificationsManager;
import org.olat.core.util.notifications.Publisher;
import org.olat.core.util.notifications.PublisherData;
import org.olat.core.util.notifications.Subscriber;
import org.olat.core.util.notifications.SubscriptionContext;
import org.olat.core.util.notifications.SubscriptionInfo;
import org.olat.core.util.notifications.items.SubscriptionListItem;
import org.olat.core.util.notifications.items.TitleItem;
import org.olat.course.CourseFactory;
import org.olat.course.CourseModule;
import org.olat.course.ICourse;
import org.olat.course.Structure;
import org.olat.course.groupsandrights.CourseGroupManager;
import org.olat.course.groupsandrights.CourseRights;
import org.olat.course.nodes.CourseNode;
import org.olat.course.nodes.IQTESTCourseNode;
import org.olat.course.run.environment.CourseEnvironment;
import org.olat.group.BusinessGroup;
import org.olat.ims.qti.QTIResultManager;
import org.olat.ims.qti.QTIResultSet;
import org.olat.notifications.NotificationsUpgradeHelper;

/**
* Description:<br>
* Calculates it the user has any assessment news for the notification system.
* Currently this checks for new tests
* <P>
* Initial Date: 21-giu-2005 <br>
*
* @author Roberto Bagnoli
*/
public class AssessmentNotificationsHandler implements NotificationsHandler {
  private static final OLog log = Tracing.createLoggerFor(AssessmentNotificationsHandler.class);
 
  private static final String CSS_CLASS_USER_ICON = "o_icon_user";
  private static final String CSS_CLASSS_IQTEST_ICON = "o_iqtest_icon";
  private static AssessmentNotificationsHandler INSTANCE;
  private Map<Long,SubscriptionContext> subsContexts = new HashMap<Long,SubscriptionContext>();

  /**
   * Don't use this! Always use the getInstance() method. This is only used once by Spring!
   */
  public AssessmentNotificationsHandler() {
    INSTANCE = this;
  }

  /**
   * @return the singleton instance
   */
  public static AssessmentNotificationsHandler getInstance() {
    return INSTANCE;
  }

  /**
   * Returns the <code>SubscriptionContext</code> to use for assessment
   * notification about specified <code>ICourse</code>.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * </ul>
   * If <code>ident == null</code>, the subscription context is (created and)
   * returned without authorization control
   *
   * @param ident the identity, if null, no subscription check will be made
   * @param course
   * @return the subscription context to use or <code>null</code> if the
   *         identity associated to the request is not allowed to be notified
   * @see #canSubscribeForAssessmentNotification(Identity, ICourse)
   */
  protected SubscriptionContext getAssessmentSubscriptionContext(Identity ident, ICourse course) {
    SubscriptionContext sctx = null;

    if (ident == null || canSubscribeForAssessmentNotification(ident, course)) {
      // Creates a new SubscriptionContext only if not found into cache
      Long courseId = course.getResourceableId();
      synchronized (subsContexts) { //o_clusterOK by:ld - no problem to have independent subsContexts caches for each cluster node
        sctx = subsContexts.get(courseId);
        if (sctx == null) {
          // a subscription context showing to the root node (the course's root
          // node is started when clicking such a notification)
          CourseNode cn = course.getRunStructure().getRootNode();
          CourseEnvironment ce = course.getCourseEnvironment();
          //FIXME:fg:b little problem is that the assessment tool and the course are not "the same" anymore, that is you can open the same course twice in the
          // dynamic tabs by a) klicking e.g. via repo, and b via notifications link to the assementtool
          sctx = new SubscriptionContext(CourseModule.ORES_COURSE_ASSESSMENT, ce.getCourseResourceableId(), cn.getIdent());
          subsContexts.put(courseId, sctx);
        }
      }
    }

    return sctx;
  }

  /**
   * Shortcut for
   * <code>getAssessmentSubscriptionContext((Identity) null, course)</code>
   *
   * @param course
   * @return the AssessmentSubscriptionContext
   * @see #getAssessmentSubscriptionContext(Identity, ICourse)
   */
  private SubscriptionContext getAssessmentSubscriptionContext(ICourse course) {
    return getAssessmentSubscriptionContext((Identity) null, course);
  }

  /**
   * Return <code>PublisherData</code> instance to use for assessment
   * notification.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * </ul>
   *
   * @param course
   * @param the business path
   * @return the publisherdata
   */
  public PublisherData getAssessmentPublisherData(ICourse course, String businessPath) {
    return new PublisherData(CourseModule.ORES_COURSE_ASSESSMENT, String.valueOf(course.getCourseEnvironment().getCourseResourceableId()), businessPath);
  }

  /**
   * Signal the <code>NotificationsManagerImpl</code> about assessment news
   * available for a course.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>courseId != null</code>
   * </ul>
   *
   * @param courseId the resourceable id of the course to signal news about
   * @param ident the identity to ignore news for
   */
  public void markPublisherNews(Identity ident, Long courseId) {
    ICourse course = loadCourseFromId(courseId);
    if (course == null) throw new AssertException("course with id " + courseId + " not found!");
    markPublisherNews(ident, course);
  }

  /**
   * Signal the <code>NotificationsManagerImpl</code> about assessment news
   * available on a course.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * </ul>
   *
   * @param course the course to signal news about
   * @param ident the identity to ignore news for
   */
  private void markPublisherNews(Identity ident, ICourse course) {
    SubscriptionContext subsContext = getAssessmentSubscriptionContext(course);
    if (subsContext != null) {
      NotificationsManager.getInstance().markPublisherNews(subsContext, ident);
    }
  }

  /**
   * Assessment notification rights check.<br>
   * Tests if an <code>Identity</code> can subscribe for assessment
   * notification for the specified <code>ICourse</code>.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * </ul>
   *
   * @param ident the identity to check rights for. Can be <code>null</code>
   * @param course the course to check rights against
   * @return if <code>ident == null</code> this method always returns false;
   *         otherwise subscriptions rights are met only by course
   *         administrators and course coaches
   */
  private boolean canSubscribeForAssessmentNotification(Identity ident, ICourse course) {
    if (ident == null) return false;

    CourseGroupManager grpMan = course.getCourseEnvironment().getCourseGroupManager();
   
    boolean isInstitutionalResourceManager = ManagerFactory.getManager().isIdentityInSecurityGroup(ident, ManagerFactory.getManager().findSecurityGroupByName(Constants.GROUP_INST_ORES_MANAGER));
    return isInstitutionalResourceManager || grpMan.isIdentityCourseAdministrator(ident) || grpMan.isIdentityCourseCoach(ident) || grpMan.hasRight(ident, CourseRights.RIGHT_ASSESSMENT);
  }

  /**
   * Utility method.<br>
   * Load an instance of <code>ICourse</code> given its numeric resourceable
   * id
   */
  private ICourse loadCourseFromId(Long courseId) {
    return CourseFactory.loadCourse(courseId);
  }

  /**
   * Utility method.<br>
   * Build (recursively) the list of all test nodes belonging to the specified
   * <code>ICourse</code>.<br>
   * The returned <code>List</code> is empty if course has no
   * IQTESTCourseNode.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * </ul>
   * <br>
   * <b>POST CONDITIONS</b>
   * <ul>
   * <li> The returned list, if not empty, contains ONLY instances of type
   * <code>IQTESTCourseNode</code>
   * </ul>
   */
  private List<IQTESTCourseNode> getCourseTestNodes(ICourse course) {
    List<IQTESTCourseNode> assessableNodes = new ArrayList<IQTESTCourseNode>(10);

    Structure courseStruct = course.getRunStructure();
    CourseNode rootNode = courseStruct.getRootNode();

    getCourseTestNodes(rootNode, assessableNodes);

    return assessableNodes;
  }

  /**
   * Recursive step used by <code>getCourseAssessableNodes(ICourse)</code>.<br>
   * <br>
   * <b>PRE CONDITIONS</b>
   * <ul>
   * <li> <code>course != null</code>
   * <li> <code>result != null</code>
   * </ul>
   *
   * @see #getCourseTestNodes(ICourse)
   */
  private void getCourseTestNodes(INode node, List<IQTESTCourseNode> result) {
    if (node != null) {
      if (node instanceof IQTESTCourseNode) result.add((IQTESTCourseNode) node);

      for (int i = 0; i < node.getChildCount(); i++) {
        getCourseTestNodes(node.getChildAt(i), result);
      }
    }
  }

  /**
   * @see org.olat.notifications.NotificationsHandler#createSubscriptionInfo(org.olat.notifications.Subscriber,
   *      java.util.Locale, java.util.Date)
   */
  public SubscriptionInfo createSubscriptionInfo(final Subscriber subscriber, Locale locale, Date compareDate) {
    SubscriptionInfo si = null;
    Publisher p = subscriber.getPublisher();
    if(!NotificationsUpgradeHelper.checkCourse(p)) {
      //course don't exist anymore
      NotificationsManager.getInstance().deactivate(p);
      return NotificationsManager.getInstance().getNoSubscriptionInfo();
    }
   
    try {
      Date latestNews = p.getLatestNewsDate();
      Identity identity = subscriber.getIdentity();

      // do not try to create a subscription info if state is deleted - results in
      // exceptions, course
      // can't be loaded when already deleted
      if (NotificationsManager.getInstance().isPublisherValid(p) && compareDate.before(latestNews)) {
        Long courseId = new Long(p.getData());
        final ICourse course = loadCourseFromId(courseId);
        if (course != null) {
          // course admins or users with the course right to have full access to
          // the assessment tool will have full access to user tests
          CourseGroupManager cgm = course.getCourseEnvironment().getCourseGroupManager();
          final boolean hasFullAccess = (cgm.isIdentityCourseAdministrator(identity) ? true : cgm.hasRight(identity,
              CourseRights.RIGHT_ASSESSMENT));
          final List<Identity> coachedUsers = new ArrayList<Identity>();
          if (!hasFullAccess) {
            // initialize list of users, only when user has not full access
            List<BusinessGroup> coachedGroups = cgm.getOwnedLearningGroupsFromAllContexts(identity);
            Manager securityManager = ManagerFactory.getManager();
            for (Iterator<BusinessGroup> iter = coachedGroups.iterator(); iter.hasNext();) {
              BusinessGroup group = iter.next();
              coachedUsers.addAll(securityManager.getIdentitiesOfSecurityGroup(group.getPartipiciantGroup()));
            }
          }

          List<IQTESTCourseNode> testNodes = getCourseTestNodes(course);
          Translator translator = Util.createPackageTranslator(AssessmentNotificationsHandler.class, locale);

          String title = translator.translate("notifications.header", new String[]{course.getCourseTitle()});
          si = new SubscriptionInfo(new TitleItem(title, CSS_CLASSS_IQTEST_ICON), null);

          for (Iterator<IQTESTCourseNode> i = testNodes.iterator(); i.hasNext();) {
            IQTESTCourseNode test = i.next();

            QTIResultManager qrm = QTIResultManager.getInstance();
            List testResultSets = qrm.getResultSets(course.getResourceableId(), test.getIdent(),
                test.getReferencedRepositoryEntry().getKey(), null);

            for (Iterator j = testResultSets.iterator(); j.hasNext();) {
              QTIResultSet qrs = (QTIResultSet) j.next();
              // - modify date on test set must be newer than last visit of
              // assessment tool
              // - user must either be course administrator (access to all
              // users granted)
              // or in a course right group with the assessment tool right or
              // a course
              // coach. in the second case, only tests of users are shown that
              // the coach does actually
              // coach himself. he does not have access to other users
              Date modDate = qrs.getLastModified();
              if (modDate.after(compareDate) && (hasFullAccess || PersistenceHelper.listContainsObjectByKey(coachedUsers, qrs.getIdentity()))) {
                String score = (new Float(qrs.getScore())).toString();
                String desc = translator.translate("notifications.entry", new String[] { test.getShortTitle(),
                    NotificationHelper.getFormatedName(qrs.getIdentity()), score });

                String urlToSend = null;
                if(p.getBusinessPath() != null) {
                  String businessPath = p.getBusinessPath() + "[assessmentTool:0][Identity:" + qrs.getIdentity().getKey() + "][CourseNode:" + test.getIdent() + "]";
                  urlToSend = NotificationHelper.getURLFromBusinessPathString(p, businessPath);
                }
                SubscriptionListItem subListItem = new SubscriptionListItem(desc, urlToSend, modDate, CSS_CLASS_USER_ICON);
                si.addSubscriptionListItem(subListItem);
              }
            }
          }
        }
      } else {
        si = NotificationsManager.getInstance().getNoSubscriptionInfo();
      }
      return si;
    } catch (Exception e) {
      log.error("Error while creating assessment notifications", e);
      checkPublisher(p);
      return NotificationsManager.getInstance().getNoSubscriptionInfo();
    }
  }
 
  private void checkPublisher(Publisher p) {
    try {
      if(!NotificationsUpgradeHelper.checkCourse(p)) {
        log.info("deactivating publisher with key; " + p.getKey(), null);
        NotificationsManager.getInstance().deactivate(p);
      }
    } catch (Exception e) {
      log.error("", e);
    }
  }

  @Override
  public String createTitleInfo(Subscriber subscriber, Locale locale) {
    try {
      ICourse course = CourseFactory.loadCourse(subscriber.getPublisher().getResId());
      Translator trans = Util.createPackageTranslator(AssessmentNotificationsHandler.class, locale);
      String title = trans.translate("notifications.title", new String[]{ course.getCourseTitle() });
      return title;
    } catch (Exception e) {
      log.error("Error while creating assessment notifications for subscriber: " + subscriber.getKey(), e);
      checkPublisher(subscriber.getPublisher());
      return "-";
    }
  }
}
TOP

Related Classes of org.olat.course.assessment.AssessmentNotificationsHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.