/** * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * <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 the * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> * <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> * Initial code contributed and copyrighted by<br> * frentix GmbH, http://www.frentix.com * <p> */ package org.olat.core.commons.services.commentAndRating.manager; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.TypedQuery; import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.commentAndRating.UserRatingsDelegate; import org.olat.core.commons.services.commentAndRating.model.OLATResourceableRating; import org.olat.core.commons.services.commentAndRating.model.UserRating; import org.olat.core.commons.services.commentAndRating.model.UserRatingImpl; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.resource.OresHelper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * * Initial date: 31.01.2014<br> * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com * */ @Service("userRatingsDAO") public class UserRatingsDAO { private static final OLog log = Tracing.createLoggerFor(UserRatingsDAO.class); @Autowired private DB dbInstance; private List<UserRatingsDelegate> delegates = new ArrayList<>(); public void addDegelate(UserRatingsDelegate delegate) { delegates.add(delegate); } public UserRating createRating(Identity creator, OLATResourceable ores, String resSubPath, int ratingValue) { UserRatingImpl rating = new UserRatingImpl(); rating.setCreator(creator); rating.setResName(ores.getResourceableTypeName()); rating.setResId(ores.getResourceableId()); rating.setResSubPath(resSubPath); rating.setCreationDate(new Date()); rating.setLastModified(new Date()); rating.setRating(ratingValue); updateDelegateRatings(rating); dbInstance.getCurrentEntityManager().persist(rating); return rating; } public List<UserRating> getAllRatings(OLATResourceable ores, String resSubPath){ TypedQuery<UserRating> query; if (resSubPath == null) { // special query when sub path is null String sb = "select rating from userrating as rating where resName=:resname AND resId=:resId AND resSubPath is NULL"; query = dbInstance.getCurrentEntityManager().createQuery(sb, UserRating.class); } else { String sb = "select rating from userrating as rating where resName=:resname AND resId=:resId AND resSubPath=:resSubPath"; query = dbInstance.getCurrentEntityManager().createQuery(sb, UserRating.class) .setParameter("resSubPath", resSubPath); } return query.setParameter("resname", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .setHint("org.hibernate.cacheable", Boolean.TRUE) .getResultList(); } public float getRatingAverage(OLATResourceable ores, String resSubPath) { StringBuilder sb = new StringBuilder(); sb.append("select avg(rating) from userrating where resName=:resname and resId=:resId and resSubPath"); if (resSubPath == null) { sb.append(" is null"); } else { sb.append("=:resSubPath"); } TypedQuery<Number> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Number.class) .setParameter("resname", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()); // When no ratings are found, a null value is returned! if(resSubPath != null) { query.setParameter("resSubPath", resSubPath); } Number average = query.getSingleResult(); return average == null ? 0f : average.floatValue(); } public List<OLATResourceableRating> getMostRatedResourceables(OLATResourceable ores, int maxResults) { StringBuilder sb = new StringBuilder(); sb.append("select new ").append(OLATResourceableRating.class.getName()).append("(") .append(" rating.resName, rating.resId, rating.resSubPath, avg(rating.rating))") .append(" from userrating as rating ") .append(" where rating.resName=:resName and rating.resId=:resId") .append(" group by rating.resName, rating.resId, rating.resSubPath") .append(" order by avg(rating.rating) desc"); TypedQuery<OLATResourceableRating> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), OLATResourceableRating.class) .setParameter("resName", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()); if(maxResults > 0) { query.setMaxResults(maxResults); } List<OLATResourceableRating> mostRated = query.getResultList(); return mostRated; } public int countRatings(OLATResourceable ores, String resSubPath) { TypedQuery<Number> query; if (resSubPath == null) { // special query when sub path is null query = dbInstance.getCurrentEntityManager() .createQuery("select count(*) from userrating where resName=:resname AND resId=:resId AND resSubPath is NULL", Number.class); } else { query = dbInstance.getCurrentEntityManager() .createQuery("select count(*) from userrating where resName=:resname AND resId=:resId AND resSubPath=:resSubPath", Number.class) .setParameter("resSubPath", resSubPath); } Number count = query.setParameter("resname", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .setHint("org.hibernate.cacheable", Boolean.TRUE) .getSingleResult(); return count.intValue(); } public UserRatingImpl getRating(Identity identity, OLATResourceable ores, String resSubPath) { StringBuilder sb = new StringBuilder(); sb.append("select r from userrating as r where r.creator.key=:creatorKey and r.resName=:resname and r.resId=:resId"); TypedQuery<UserRatingImpl> query; if (resSubPath == null) { sb.append(" and r.resSubPath is NULL"); query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), UserRatingImpl.class); } else { sb.append(" and resSubPath=:resSubPath"); query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), UserRatingImpl.class) .setParameter("resSubPath", resSubPath); } List<UserRatingImpl> results = query .setParameter("resname", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .setParameter("creatorKey", identity.getKey()) .setHint("org.hibernate.cacheable", Boolean.TRUE) .getResultList(); if (results.size() == 0) return null; return results.get(0); } public Integer getRatingValue(Identity identity, OLATResourceable ores, String resSubPath) { StringBuilder sb = new StringBuilder(); sb.append("select r.rating from userrating as r where r.creator.key=:creatorKey and r.resName=:resname and r.resId=:resId"); TypedQuery<Integer> query; if (resSubPath == null) { sb.append(" and r.resSubPath is NULL"); query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Integer.class); } else { sb.append(" and resSubPath=:resSubPath"); query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Integer.class) .setParameter("resSubPath", resSubPath); } List<Integer> results = query .setParameter("resname", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .setParameter("creatorKey", identity.getKey()) .setHint("org.hibernate.cacheable", Boolean.TRUE) .getResultList(); if (results.size() == 0) return null; return results.get(0); } public UserRating updateRating(Identity identity, OLATResourceable ores, String resSubPath, int newRatingValue) { UserRatingImpl rating = getRating(identity, ores, resSubPath); if (rating == null) { return createRating(identity, ores, resSubPath, newRatingValue); } // Update DB entry rating.setRating(newRatingValue); rating.setLastModified(new Date()); updateDelegateRatings(rating); rating = dbInstance.getCurrentEntityManager().merge(rating); return rating; } public UserRating updateRating(UserRating rating, int newRatingValue) { // First reload parent from cache to prevent stale object or cache issues rating = reloadRating(rating); if (rating == null) { // Original rating has been deleted in the meantime. Don't update it return null; } // Update DB entry rating.setRating(newRatingValue); rating.setLastModified(new Date()); updateDelegateRatings(rating); rating = dbInstance.getCurrentEntityManager().merge(rating); return rating; } public UserRating reloadRating(UserRating rating) { try { return (UserRating)dbInstance.loadObject(rating); } catch (Exception e) { // Huh, most likely the given object does not exist anymore on the // db, probably deleted by someone else log.warn("Tried to reload a user rating but got an exception. Probably deleted in the meantime", e); return null; } } private void updateDelegateRatings(UserRating rating) { if(delegates == null || delegates.isEmpty()) return; OLATResourceable ores = OresHelper.createOLATResourceableInstance(rating.getResName(), rating.getResId()); for(UserRatingsDelegate delegate:delegates) { if(delegate.accept(ores, rating.getResSubPath())) { StringBuilder sb = new StringBuilder(); sb.append("select count(rating.key), sum(rating.rating) from userrating as rating") .append(" where rating.resName=:resname and rating.resId=:resId"); if(rating.getResSubPath() == null) { sb.append(" and rating.resSubPath is null"); } else { sb.append(" and rating.resSubPath=:resSubPath"); } if(rating.getKey() != null) { sb.append(" and rating.key!=:ratingKey"); } TypedQuery<Object[]> query = dbInstance.getCurrentEntityManager() .createQuery(sb.toString(), Object[].class) .setParameter("resname", rating.getResName()) .setParameter("resId", rating.getResId()); if(rating.getKey() != null) { query.setParameter("ratingKey", rating.getKey()); } if(rating.getResSubPath() != null) { query.setParameter("resSubPath", rating.getResSubPath()); } Object[] stats = query.getSingleResult(); if(stats == null || stats[0] == null || stats[1] == null) { Integer rate = rating.getRating(); delegate.update(ores, rating.getResSubPath(), rate.doubleValue(), 1l); } else { long numOfRatings = ((Number)stats[0]).longValue(); long sumOfRatings = ((Number)stats[1]).longValue(); double rate = (sumOfRatings + rating.getRating().intValue()) / (numOfRatings + 1); delegate.update(ores, rating.getResSubPath(), rate, numOfRatings + 1); } } } } public int deleteRating(UserRating rating) { // First reload parent from cache to prevent stale object or cache issues UserRating ratingRef = dbInstance.getCurrentEntityManager().getReference(UserRatingImpl.class, rating.getKey()); if (ratingRef == null) { // Original rating has been deleted in the meantime. Don't delete it again. return 0; } // Delete this rating and finish dbInstance.getCurrentEntityManager().remove(ratingRef); return 1; } public int deleteAllRatings(OLATResourceable ores, String resSubPath) { // special query when sub path is null if (resSubPath == null) { String sb = "delete from userrating where resName=:resName and resId=:resId and resSubPath is null"; return dbInstance.getCurrentEntityManager().createQuery(sb.toString()) .setParameter("resName", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .executeUpdate(); } else { String sb = "delete from userrating where resName=:resName and resId=:resId and resSubPath=:resSubPath"; return dbInstance.getCurrentEntityManager().createQuery(sb.toString()) .setParameter("resName", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .setParameter("resSubPath", resSubPath) .executeUpdate(); } } public int deleteAllRatingsIgnoringSubPath(OLATResourceable ores) { String sb = "delete from userrating where resName=:resName and resId=:resId"; return dbInstance.getCurrentEntityManager().createQuery(sb.toString()) .setParameter("resName", ores.getResourceableTypeName()) .setParameter("resId", ores.getResourceableId()) .executeUpdate(); } }