From 518839603e8a4f372abd2477639d465665b04290 Mon Sep 17 00:00:00 2001
From: uhensler <urs.hensler@frentix.com>
Date: Tue, 22 Jan 2019 15:08:35 +0100
Subject: [PATCH] OO-3819: Use all keys to filter the data of the trend diagram

---
 .../model/CurriculumElementRefImpl.java       |  25 +
 .../analysis/AnalysisSearchParameter.java     | 166 ++++++
 .../analysis/manager/AnalysisFilterDAO.java   |  66 +++
 .../manager/AnalysisPresentationXStream.java  |   6 +-
 .../analysis/ui/HeatMapController.java        | 134 +++--
 .../quality/analysis/ui/HeatMapDataModel.java |   5 -
 .../quality/analysis/ui/HeatMapRow.java       |   9 +-
 .../model/QualityDataCollectionRefImpl.java   |  68 +++
 .../taxonomy/model/TaxonomyLevelRefImpl.java  |  25 +
 .../manager/AnalysisFilterDAOTest.java        | 527 ++++++++++++++++++
 .../AnalysisPresentationXStreamTest.java      | 110 +++-
 11 files changed, 1062 insertions(+), 79 deletions(-)
 create mode 100644 src/main/java/org/olat/modules/quality/model/QualityDataCollectionRefImpl.java

diff --git a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementRefImpl.java b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementRefImpl.java
index 1e645b2257e..ef757b0b627 100644
--- a/src/main/java/org/olat/modules/curriculum/model/CurriculumElementRefImpl.java
+++ b/src/main/java/org/olat/modules/curriculum/model/CurriculumElementRefImpl.java
@@ -39,6 +39,31 @@ public class CurriculumElementRefImpl implements CurriculumElementRef {
 	public Long getKey() {
 		return key;
 	}
+	
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((key == null) ? 0 : key.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		CurriculumElementRefImpl other = (CurriculumElementRefImpl) obj;
+		if (key == null) {
+			if (other.key != null)
+				return false;
+		} else if (!key.equals(other.key))
+			return false;
+		return true;
+	}
 
 	@Override
 	public String toString() {
diff --git a/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java b/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java
index b01c07f8088..1194c711835 100644
--- a/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java
+++ b/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java
@@ -30,6 +30,7 @@ import org.olat.modules.curriculum.CurriculumElementRef;
 import org.olat.modules.curriculum.CurriculumElementTypeRef;
 import org.olat.modules.curriculum.CurriculumRef;
 import org.olat.modules.quality.QualityContextRole;
+import org.olat.modules.quality.QualityDataCollectionRef;
 import org.olat.modules.taxonomy.TaxonomyLevelRef;
 import org.olat.repository.RepositoryEntryRef;
 import org.olat.repository.model.RepositoryEntryRefImpl;
@@ -45,21 +46,37 @@ public class AnalysisSearchParameter {
 	private RepositoryEntryRef formEntryRef;
 	private Date dateRangeFrom;
 	private Date dateRangeTo;
+	private Collection<? extends QualityDataCollectionRef> dataCollectionRefs;
 	private Collection<? extends IdentityRef> topicIdentityRefs;
 	private List<? extends OrganisationRef> topicOrganisationRefs;
 	private Collection<? extends CurriculumRef> topicCurriculumRefs;
 	private List<? extends CurriculumElementRef> topicCurriculumElementRefs;
 	private List<? extends RepositoryEntryRef> topicRepositoryRefs;
 	private Collection<String> contextLocations;
+	private OrganisationRef contextOrganisationRef; // of the executor
 	private List<? extends OrganisationRef> contextOrganisationRefs; // of the executor
 	private Collection<? extends CurriculumRef> contextCurriculumRefs;
+	private CurriculumElementRef contextCurriculumElementRef;
 	private List<? extends CurriculumElementRef> contextCurriculumElementRefs;
 	private Collection<? extends CurriculumElementTypeRef> contextCurriculumElementTypeRefs;
+	private OrganisationRef contextCurriculumOrganisationRef;
 	private List<? extends OrganisationRef> contextCurriculumOrganisationRefs;
+	private TaxonomyLevelRef contextTaxonomyLevelRef;
 	private List<? extends TaxonomyLevelRef> contextTaxonomyLevelRefs;
 	private Collection<Integer> seriesIndexes;
 	private Collection<QualityContextRole> contextRoles;
 	private boolean withUserInfosOnly;
+	private boolean topicIdentityNull;
+	private boolean topicOrganisationNull;
+	private boolean topicCurriculumNull;
+	private boolean topicCurriculumElementNull;
+	private boolean topicRepositoryNull;
+	private boolean contextOrganisationNull;
+	private boolean contextCurriculumNull;
+	private boolean contextCurriculumElementNull;
+	private boolean contextCurriculumOrganisationNull;
+	private boolean contextTaxonomyLevelNull;
+	private boolean contextLocationNull;
 
 	public RepositoryEntryRef getFormEntryRef() {
 		return formEntryRef;
@@ -86,6 +103,14 @@ public class AnalysisSearchParameter {
 		this.dateRangeTo = dateRangeTo;
 	}
 
+	public Collection<? extends QualityDataCollectionRef> getDataCollectionRefs() {
+		return dataCollectionRefs;
+	}
+
+	public void setDataCollectionRefs(Collection<? extends QualityDataCollectionRef> dataCollectionRefs) {
+		this.dataCollectionRefs = dataCollectionRefs;
+	}
+
 	public Collection<? extends IdentityRef> getTopicIdentityRefs() {
 		return topicIdentityRefs;
 	}
@@ -118,6 +143,22 @@ public class AnalysisSearchParameter {
 		this.topicCurriculumElementRefs = topicCurriculumElementRefs;
 	}
 
+	public OrganisationRef getContextOrganisationRef() {
+		return contextOrganisationRef;
+	}
+
+	public void setContextOrganisationRef(OrganisationRef contextOrganisationRef) {
+		this.contextOrganisationRef = contextOrganisationRef;
+	}
+
+	public OrganisationRef getContextCurriculumOrganisationRef() {
+		return contextCurriculumOrganisationRef;
+	}
+
+	public void setContextCurriculumOrganisationRef(OrganisationRef contextCurriculumOrganisationRef) {
+		this.contextCurriculumOrganisationRef = contextCurriculumOrganisationRef;
+	}
+
 	public List<? extends OrganisationRef> getContextCurriculumOrganisationRefs() {
 		return contextCurriculumOrganisationRefs;
 	}
@@ -158,6 +199,14 @@ public class AnalysisSearchParameter {
 		this.contextCurriculumRefs = contextCurriculumRefs;
 	}
 	
+	public CurriculumElementRef getContextCurriculumElementRef() {
+		return contextCurriculumElementRef;
+	}
+
+	public void setContextCurriculumElementRef(CurriculumElementRef contextCurriculumElementRef) {
+		this.contextCurriculumElementRef = contextCurriculumElementRef;
+	}
+
 	public List<? extends CurriculumElementRef> getContextCurriculumElementRefs() {
 		return contextCurriculumElementRefs;
 	}
@@ -175,6 +224,14 @@ public class AnalysisSearchParameter {
 		this.contextCurriculumElementTypeRefs = contextCurriculumElementTypeRefs;
 	}
 
+	public TaxonomyLevelRef getContextTaxonomyLevelRef() {
+		return contextTaxonomyLevelRef;
+	}
+
+	public void setContextTaxonomyLevelRef(TaxonomyLevelRef contextTaxonomyLevelRef) {
+		this.contextTaxonomyLevelRef = contextTaxonomyLevelRef;
+	}
+
 	public List<? extends TaxonomyLevelRef> getContextTaxonomyLevelRefs() {
 		return contextTaxonomyLevelRefs;
 	}
@@ -207,12 +264,103 @@ public class AnalysisSearchParameter {
 		this.withUserInfosOnly = withUserInfosOnly;
 	}
 
+	public boolean isTopicIdentityNull() {
+		return topicIdentityNull;
+	}
+
+	public void setTopicIdentityNull(boolean topicIdentityNull) {
+		this.topicIdentityNull = topicIdentityNull;
+	}
+
+	public boolean isTopicOrganisationNull() {
+		return topicOrganisationNull;
+	}
+
+	public void setTopicOrganisationNull(boolean topicOrganisationNull) {
+		this.topicOrganisationNull = topicOrganisationNull;
+	}
+
+	public boolean isTopicCurriculumNull() {
+		return topicCurriculumNull;
+	}
+
+	public void setTopicCurriculumNull(boolean topicCurriculumNull) {
+		this.topicCurriculumNull = topicCurriculumNull;
+	}
+
+	public boolean isTopicCurriculumElementNull() {
+		return topicCurriculumElementNull;
+	}
+
+	public void setTopicCurriculumElementNull(boolean topicCurriculumElementNull) {
+		this.topicCurriculumElementNull = topicCurriculumElementNull;
+	}
+
+	public boolean isTopicRepositoryNull() {
+		return topicRepositoryNull;
+	}
+
+	public void setTopicRepositoryNull(boolean topicRepositoryNull) {
+		this.topicRepositoryNull = topicRepositoryNull;
+	}
+
+	public boolean isContextOrganisationNull() {
+		return contextOrganisationNull;
+	}
+
+	public void setContextOrganisationNull(boolean contextOrganisationNull) {
+		this.contextOrganisationNull = contextOrganisationNull;
+	}
+
+	public boolean isContextCurriculumNull() {
+		return contextCurriculumNull;
+	}
+
+	public void setContextCurriculumNull(boolean contextCurriculumNull) {
+		this.contextCurriculumNull = contextCurriculumNull;
+	}
+
+	public boolean isContextCurriculumElementNull() {
+		return contextCurriculumElementNull;
+	}
+
+	public void setContextCurriculumElementNull(boolean contextCurriculumElementNull) {
+		this.contextCurriculumElementNull = contextCurriculumElementNull;
+	}
+
+	public boolean isContextCurriculumOrganisationNull() {
+		return contextCurriculumOrganisationNull;
+	}
+
+	public void setContextCurriculumOrganisationNull(boolean contextCurriculumOrganisationNull) {
+		this.contextCurriculumOrganisationNull = contextCurriculumOrganisationNull;
+	}
+
+	public boolean isContextTaxonomyLevelNull() {
+		return contextTaxonomyLevelNull;
+	}
+
+	public void setContextTaxonomyLevelNull(boolean contextTaxonomyLevelNull) {
+		this.contextTaxonomyLevelNull = contextTaxonomyLevelNull;
+	}
+
+	public boolean isContextLocationNull() {
+		return contextLocationNull;
+	}
+
+	public void setContextLocationNull(boolean contextLocationNull) {
+		this.contextLocationNull = contextLocationNull;
+	}
+
 	@Override
 	public AnalysisSearchParameter clone() {
 		AnalysisSearchParameter clone = new AnalysisSearchParameter();
 		clone.formEntryRef = this.formEntryRef;
 		clone.dateRangeFrom = this.dateRangeFrom;
 		clone.dateRangeTo = this.dateRangeTo;
+		clone.dataCollectionRefs = this.dataCollectionRefs != null
+				? new ArrayList<>(this.dataCollectionRefs)
+				: null;
 		clone.topicIdentityRefs = this.topicIdentityRefs != null
 				? new ArrayList<>(this.topicIdentityRefs)
 				: null;
@@ -231,18 +379,25 @@ public class AnalysisSearchParameter {
 		clone.contextLocations = this.contextLocations != null
 				? new ArrayList<>(this.contextLocations)
 				: null;
+		clone.contextCurriculumElementRef = this.contextCurriculumElementRef;
 		clone.contextOrganisationRefs = this.contextOrganisationRefs != null
 				? new ArrayList<>(this.contextOrganisationRefs)
 				: null;
 		clone.contextCurriculumRefs = this.contextCurriculumRefs != null
 				? new ArrayList<>(this.contextCurriculumRefs)
 				: null;
+		clone.contextCurriculumElementRef = this.contextCurriculumElementRef;
 		clone.contextCurriculumElementRefs = this.contextCurriculumElementRefs != null
 				? new ArrayList<>(this.contextCurriculumElementRefs)
 				: null;
 		clone.contextCurriculumElementTypeRefs = this.contextCurriculumElementTypeRefs != null
 				? new ArrayList<>(this.contextCurriculumElementTypeRefs)
 				: null;
+		clone.contextCurriculumOrganisationRef = this.contextCurriculumOrganisationRef;
+		clone.contextCurriculumOrganisationRefs = this.contextCurriculumOrganisationRefs != null
+				? new ArrayList<>(this.contextCurriculumOrganisationRefs)
+				: null;
+		clone.contextTaxonomyLevelRef = this.contextTaxonomyLevelRef;
 		clone.contextTaxonomyLevelRefs = this.contextTaxonomyLevelRefs != null
 				? new ArrayList<>(this.contextTaxonomyLevelRefs)
 				: null;
@@ -253,6 +408,17 @@ public class AnalysisSearchParameter {
 				? new ArrayList<>(this.contextRoles)
 				: null;
 		clone.withUserInfosOnly = this.withUserInfosOnly;
+		clone.topicIdentityNull = this.topicIdentityNull;
+		clone.topicOrganisationNull = this.topicOrganisationNull;
+		clone.topicCurriculumNull = this.topicCurriculumNull;
+		clone.topicCurriculumElementNull = this.topicCurriculumElementNull;
+		clone.topicRepositoryNull = this.topicRepositoryNull;
+		clone.contextOrganisationNull = this.contextOrganisationNull;
+		clone.contextCurriculumNull = this.contextCurriculumNull;
+		clone.contextCurriculumElementNull = this.contextCurriculumElementNull;
+		clone.contextCurriculumOrganisationNull = this.contextCurriculumOrganisationNull;
+		clone.contextTaxonomyLevelNull = this.contextTaxonomyLevelNull;
+		clone.contextLocationNull = this.contextLocationNull;
 		return clone;
 	}
 
diff --git a/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java b/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java
index c8c2e404d78..a49118e6f69 100644
--- a/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java
+++ b/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java
@@ -42,6 +42,7 @@ import org.olat.modules.curriculum.CurriculumRef;
 import org.olat.modules.quality.QualityContextRole;
 import org.olat.modules.quality.QualityDataCollection;
 import org.olat.modules.quality.QualityDataCollectionLight;
+import org.olat.modules.quality.QualityDataCollectionRef;
 import org.olat.modules.quality.QualityDataCollectionStatus;
 import org.olat.modules.quality.analysis.AnalysisSearchParameter;
 import org.olat.modules.quality.analysis.AnlaysisFigures;
@@ -527,6 +528,9 @@ public class AnalysisFilterDAO {
 		if (searchParams.getDateRangeTo() != null) {
 			sb.and().append("collection.deadline <= :dateRangeTo");
 		}
+		if (searchParams.getDataCollectionRefs() != null && !searchParams.getDataCollectionRefs().isEmpty()) {
+			sb.and().append("collection.key in :dataCollectionKeys");
+		}
 		if (searchParams.getTopicIdentityRefs() != null && !searchParams.getTopicIdentityRefs().isEmpty()) {
 			sb.and().append("collection.topicIdentity.key in :topicIdentityKeys");
 		}
@@ -545,6 +549,9 @@ public class AnalysisFilterDAO {
 		if (searchParams.getContextLocations() != null && !searchParams.getContextLocations().isEmpty()) {
 			sb.and().append("context.location in :contextLocations");
 		}
+		if (searchParams.getContextOrganisationRef() != null) {
+			sb.and().append("contextOrganisation.key = :contextOrganisationKey");
+		}
 		if (searchParams.getContextOrganisationRefs() != null && !searchParams.getContextOrganisationRefs().isEmpty()) {
 			// load the organisations and all children
 			sb.and();
@@ -563,6 +570,9 @@ public class AnalysisFilterDAO {
 		if (searchParams.getContextCurriculumRefs() != null && !searchParams.getContextCurriculumRefs().isEmpty()) {
 			sb.and().append("contextCurriculum.key in :contextCurriculumKeys");
 		}
+		if (searchParams.getContextCurriculumElementRef() != null) {
+			sb.and().append("contextCurriculumElement.key = :contextCurriculumElementKey");
+		}
 		if (searchParams.getContextCurriculumElementRefs() != null && !searchParams.getContextCurriculumElementRefs().isEmpty()) {
 			// load the curriculum elements and all children
 			sb.and();
@@ -581,6 +591,9 @@ public class AnalysisFilterDAO {
 		if (searchParams.getContextCurriculumElementTypeRefs() != null && !searchParams.getContextCurriculumElementTypeRefs().isEmpty()) {
 			sb.and().append("contextCurriculumElement.type.key in :contextCurriculumElementTypeKeys");
 		}
+		if (searchParams.getContextCurriculumOrganisationRef() != null) {
+			sb.and().append("contextCurriculumOrganisation.key = :contextCurriculumOrganisationKey");
+		}
 		if (searchParams.getContextCurriculumOrganisationRefs() != null && !searchParams.getContextCurriculumOrganisationRefs().isEmpty()) {
 			// load the organisations and all children
 			sb.and();
@@ -596,6 +609,10 @@ public class AnalysisFilterDAO {
 				}
 			}
 		}
+
+		if (searchParams.getContextTaxonomyLevelRef() != null) {
+			sb.and().append("taxonomyLevel.key = :contextTaxonomyLevelKey");
+		}
 		if (searchParams.getContextTaxonomyLevelRefs() != null && !searchParams.getContextTaxonomyLevelRefs().isEmpty()) {
 			// load the taxonomy level and all children
 			sb.and();
@@ -629,6 +646,39 @@ public class AnalysisFilterDAO {
 			sb.append(" or sessionInfo.studySubject is not null");
 			sb.append(")");
 		}
+		if (searchParams.isTopicIdentityNull()) {
+			sb.and().append("collection.topicIdentity is null");
+		}
+		if (searchParams.isTopicOrganisationNull()) {
+			sb.and().append("collection.topicOrganisation is null");
+		}
+		if (searchParams.isTopicCurriculumNull()) {
+			sb.and().append("collection.topicCurriculum is null");
+		}
+		if (searchParams.isTopicCurriculumElementNull()) {
+			sb.and().append("collection.topicCurriculumElement is null");
+		}
+		if (searchParams.isTopicRepositoryNull()) {
+			sb.and().append("collection.topicRepositoryEntry is null");
+		}
+		if (searchParams.isContextOrganisationNull()) {
+			sb.and().append("contextOrganisation is null");
+		}
+		if (searchParams.isContextCurriculumNull()) {
+			sb.and().append("contextCurriculum is null");
+		}
+		if (searchParams.isContextCurriculumElementNull()) {
+			sb.and().append("contextCurriculumElement is null");
+		}
+		if (searchParams.isContextCurriculumOrganisationNull()) {
+			sb.and().append("contextCurriculumOrganisation is null");
+		}
+		if (searchParams.isContextTaxonomyLevelNull()) {
+			sb.and().append("contextToTaxonomyLevel.taxonomyLevel is null");
+		}
+		if (searchParams.isContextLocationNull()) {
+			sb.and().append("context.location is null");
+		}
 	}
 
 	static void appendParameters(Query query, AnalysisSearchParameter searchParams) {
@@ -641,6 +691,10 @@ public class AnalysisFilterDAO {
 		if (searchParams.getDateRangeTo() != null) {
 			query.setParameter("dateRangeTo", searchParams.getDateRangeTo());
 		}
+		if (searchParams.getDataCollectionRefs() != null && !searchParams.getDataCollectionRefs().isEmpty()) {
+			List<Long> keys = searchParams.getDataCollectionRefs().stream().map(QualityDataCollectionRef::getKey).collect(toList());
+			query.setParameter("dataCollectionKeys", keys);
+		}
 		if (searchParams.getTopicIdentityRefs() != null && !searchParams.getTopicIdentityRefs().isEmpty()) {
 			List<Long> keys = searchParams.getTopicIdentityRefs().stream().map(IdentityRef::getKey).collect(toList());
 			query.setParameter("topicIdentityKeys", keys);
@@ -664,6 +718,9 @@ public class AnalysisFilterDAO {
 		if (searchParams.getContextLocations() != null && !searchParams.getContextLocations().isEmpty()) {
 			query.setParameter("contextLocations", searchParams.getContextLocations());
 		}
+		if (searchParams.getContextOrganisationRef() != null) {
+			query.setParameter("contextOrganisationKey", searchParams.getContextOrganisationRef().getKey());
+		}
 		if (searchParams.getContextOrganisationRefs() != null && !searchParams.getContextOrganisationRefs().isEmpty()) {
 			for (int i = 0; i < searchParams.getContextOrganisationRefs().size(); i++) {
 				String parameter = new StringBuilder(12).append("orgPath").append(i).toString();
@@ -676,6 +733,9 @@ public class AnalysisFilterDAO {
 			List<Long> keys = searchParams.getContextCurriculumRefs().stream().map(CurriculumRef::getKey).collect(toList());
 			query.setParameter("contextCurriculumKeys", keys);
 		}
+		if (searchParams.getContextCurriculumElementRef() != null) {
+			query.setParameter("contextCurriculumElementKey", searchParams.getContextCurriculumElementRef().getKey());
+		}
 		if (searchParams.getContextCurriculumElementRefs() != null && !searchParams.getContextCurriculumElementRefs().isEmpty()) {
 			for (int i = 0; i < searchParams.getContextCurriculumElementRefs().size(); i++) {
 				String parameter = new StringBuilder(12).append("elePath").append(i).toString();
@@ -688,6 +748,9 @@ public class AnalysisFilterDAO {
 			List<Long> keys = searchParams.getContextCurriculumElementTypeRefs().stream().map(CurriculumElementTypeRef::getKey).collect(toList());
 			query.setParameter("contextCurriculumElementTypeKeys", keys);
 		}
+		if (searchParams.getContextCurriculumOrganisationRef() != null) {
+			query.setParameter("contextCurriculumOrganisationKey", searchParams.getContextCurriculumOrganisationRef().getKey());
+		}
 		if (searchParams.getContextCurriculumOrganisationRefs() != null && !searchParams.getContextCurriculumOrganisationRefs().isEmpty()) {
 			for (int i = 0; i < searchParams.getContextCurriculumOrganisationRefs().size(); i++) {
 				String parameter = new StringBuilder(40).append("contextCurriculumOrganisationPath").append(i).toString();
@@ -696,6 +759,9 @@ public class AnalysisFilterDAO {
 				query.setParameter(parameter, value);
 			}
 		}
+		if (searchParams.getContextTaxonomyLevelRef() != null) {
+			query.setParameter("contextTaxonomyLevelKey", searchParams.getContextTaxonomyLevelRef().getKey());
+		}
 		if (searchParams.getContextTaxonomyLevelRefs() != null && !searchParams.getContextTaxonomyLevelRefs().isEmpty()) {
 			for (int i = 0; i < searchParams.getContextTaxonomyLevelRefs().size(); i++) {
 				String parameter = new StringBuilder(12).append("taxonomyLevelPath").append(i).toString();
diff --git a/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStream.java b/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStream.java
index 3cc2f368129..6878ed1de86 100644
--- a/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStream.java
+++ b/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStream.java
@@ -30,6 +30,7 @@ import org.olat.modules.curriculum.model.CurriculumRefImpl;
 import org.olat.modules.quality.analysis.AnalysisSearchParameter;
 import org.olat.modules.quality.analysis.GroupBy;
 import org.olat.modules.quality.analysis.MultiGroupBy;
+import org.olat.modules.quality.model.QualityDataCollectionRefImpl;
 import org.olat.modules.taxonomy.model.TaxonomyLevelRefImpl;
 import org.olat.repository.model.RepositoryEntryRefImpl;
 
@@ -50,13 +51,14 @@ public class AnalysisPresentationXStream {
 	static {
 		XStream.setupDefaultSecurity(xstream);
 		Class<?>[] types = new Class[] {
-				MultiGroupBy.class, GroupBy.class, AnalysisSearchParameter.class, RepositoryEntryRefImpl.class,
-				IdentityRefImpl.class, OrganisationRefImpl.class, CurriculumRefImpl.class,
+				MultiGroupBy.class, GroupBy.class, AnalysisSearchParameter.class, QualityDataCollectionRefImpl.class,
+				RepositoryEntryRefImpl.class, IdentityRefImpl.class, OrganisationRefImpl.class, CurriculumRefImpl.class,
 				CurriculumElementRefImpl.class, TaxonomyLevelRefImpl.class };
 		xstream.addPermission(new ExplicitTypePermission(types));
 		xstream.alias("multiGroupBy", MultiGroupBy.class);
 		xstream.alias("groupBy", GroupBy.class);
 		xstream.alias("AnalysisSearchParameter", AnalysisSearchParameter.class);
+		xstream.alias("QualityDataCollectionRef", QualityDataCollectionRefImpl.class);
 		xstream.alias("RepositoryEntryRef", RepositoryEntryRefImpl.class);
 		xstream.alias("IdentityRef", IdentityRefImpl.class);
 		xstream.alias("OrganisationRef", OrganisationRefImpl.class);
diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java
index 2c2fa742f8f..a2aabc749c1 100644
--- a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java
+++ b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java
@@ -33,6 +33,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import org.olat.basesecurity.model.IdentityRefImpl;
 import org.olat.basesecurity.model.OrganisationRefImpl;
@@ -46,7 +47,6 @@ import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.components.form.flexible.impl.FormEvent;
 import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
-import org.olat.core.gui.components.form.flexible.impl.elements.table.BooleanCellRenderer;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
 import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory;
@@ -75,6 +75,7 @@ import org.olat.modules.quality.analysis.GroupedStatistics;
 import org.olat.modules.quality.analysis.MultiGroupBy;
 import org.olat.modules.quality.analysis.MultiKey;
 import org.olat.modules.quality.analysis.QualityAnalysisService;
+import org.olat.modules.quality.model.QualityDataCollectionRefImpl;
 import org.olat.modules.quality.ui.DataCollectionReportController;
 import org.olat.modules.taxonomy.model.TaxonomyLevelRefImpl;
 import org.olat.repository.model.RepositoryEntryRefImpl;
@@ -277,12 +278,10 @@ public class HeatMapController extends FormBasicController implements Filterable
 		
 		columnIndex = addSliderColumns(columnsModel, columnIndex, maxCount);
 		
-		if (!GroupBy.DATA_COLLECTION.equals(getLastGroupBy(multiGroupBy))) {
-			DefaultFlexiColumnModel trendColumn = new DefaultFlexiColumnModel("heatmap.table.title.trend", columnIndex++,
-					CMD_TREND, new BooleanCellRenderer(new StaticFlexiCellRenderer("", CMD_TREND, "o_icon o_icon-lg o_icon_qual_ana_trend", null), null));
-			trendColumn.setExportable(false);
-			columnsModel.addFlexiColumnModel(trendColumn);
-		}
+		DefaultFlexiColumnModel trendColumn = new DefaultFlexiColumnModel("heatmap.table.title.trend", columnIndex++,
+				CMD_TREND, new StaticFlexiCellRenderer("", CMD_TREND, "o_icon o_icon-lg o_icon_qual_ana_trend", null));
+		trendColumn.setExportable(false);
+		columnsModel.addFlexiColumnModel(trendColumn);
 		
 		dataModel = new HeatMapDataModel(columnsModel, getLocale());
 		if (tableEl != null) flc.remove(tableEl);
@@ -364,7 +363,6 @@ public class HeatMapController extends FormBasicController implements Filterable
 	}
 	
 	private void loadHeatMap() {
-		GroupBy lastGroupBy = getLastGroupBy(multiGroupBy);
 		String groupNameNA = translate("heatmap.not.specified");
 		List<String> identifiers = sliders.stream().map(SliderWrapper::getIdentifier).collect(toList());
 		GroupedStatistics<GroupedStatistic> statistics = loadHeatMapStatistics();
@@ -381,8 +379,7 @@ public class HeatMapController extends FormBasicController implements Filterable
 				GroupedStatistic rowStatistic = statistics.getStatistic(identifier, multiKey);
 				rowStatistics.add(rowStatistic);
 			}
-			boolean hideTrend = GroupBy.DATA_COLLECTION.equals(lastGroupBy);
-			HeatMapRow row = new HeatMapRow(multiKey, groupNames, rowStatistics, !hideTrend);
+			HeatMapRow row = new HeatMapRow(multiKey, groupNames, rowStatistics);
 			rows.add(row);
 		}
 		
@@ -466,7 +463,6 @@ public class HeatMapController extends FormBasicController implements Filterable
 			return null;
 		}
 	}
-
 	
 	public GroupedStatistics<GroupedStatistic> loadHeatMapStatistics() {
 		List<String> identifiers = sliders.stream().map(SliderWrapper::getIdentifier).collect(toList());
@@ -525,59 +521,113 @@ public class HeatMapController extends FormBasicController implements Filterable
 	}
 
 	private AnalysisSearchParameter getTrendSearchParams(MultiKey multiKey) {
-		GroupByKey groupByKey = getLastGroupByAndKey(multiGroupBy, multiKey);
 		AnalysisSearchParameter trendSearchParams = searchParams.clone();
-		ammendGroupBySearchParam(trendSearchParams, groupByKey);
+		ammendGroupBySearchParam(trendSearchParams, getGroupByAndKey(multiGroupBy, multiKey, 1));
+		ammendGroupBySearchParam(trendSearchParams, getGroupByAndKey(multiGroupBy, multiKey, 2));
+		ammendGroupBySearchParam(trendSearchParams, getGroupByAndKey(multiGroupBy, multiKey, 3));
 		return trendSearchParams;
 	}
 
-	private void ammendGroupBySearchParam(AnalysisSearchParameter searchParams, GroupByKey groupByKey) {
-		if (groupByKey == null) return;
+	private void ammendGroupBySearchParam(AnalysisSearchParameter trendSearchParams, GroupByKey groupByKey) {
+		if (groupByKey == null || groupByKey.getGroupBy() == null) return;
 		
 		String key = groupByKey.getKey();
-		switch (groupByKey.getGroupBy()) {
+		GroupBy groupBy = groupByKey.getGroupBy();
+		if (key != null) {
+			ammendGroupBySearchParamKey(trendSearchParams, groupBy, key);
+		} else {
+			ammendGroupBySearchParamNull(trendSearchParams, groupBy);
+		}
+	}
+
+	private void ammendGroupBySearchParamKey(AnalysisSearchParameter trendSearchParams, GroupBy groupBy, String key) {
+		switch (groupBy) {
 		case TOPIC_IDENTITY:
-			searchParams.setTopicIdentityRefs(singletonList(new IdentityRefImpl(toLongOrZero(key))));
+			trendSearchParams.setTopicIdentityRefs(singletonList(new IdentityRefImpl(toLongOrZero(key))));
 			break;
 		case TOPIC_ORGANISATION:
-			searchParams.setTopicOrganisationRefs(singletonList(new OrganisationRefImpl(toLongOrZero(key))));
+			trendSearchParams.setTopicOrganisationRefs(singletonList(new OrganisationRefImpl(toLongOrZero(key))));
 			break;
 		case TOPIC_CURRICULUM:
-			searchParams.setTopicCurriculumRefs(singletonList(new CurriculumRefImpl(toLongOrZero(key))));
+			trendSearchParams.setTopicCurriculumRefs(singletonList(new CurriculumRefImpl(toLongOrZero(key))));
 			break;
 		case TOPIC_CURRICULUM_ELEMENT:
-			searchParams.setTopicCurriculumElementRefs(singletonList(new CurriculumElementRefImpl(toLongOrZero(key))));
+			trendSearchParams.setTopicCurriculumElementRefs(singletonList(new CurriculumElementRefImpl(toLongOrZero(key))));
 			break;
 		case TOPIC_REPOSITORY:
-			searchParams.setTopicRepositoryRefs(singletonList(new RepositoryEntryRefImpl(toLongOrZero(key))));
+			trendSearchParams.setTopicRepositoryRefs(singletonList(new RepositoryEntryRefImpl(toLongOrZero(key))));
 			break;
 		case CONTEXT_ORGANISATION:
-			searchParams.setContextOrganisationRefs(singletonList(new OrganisationRefImpl(toLongOrZero(key))));
+			trendSearchParams.setContextOrganisationRef(new OrganisationRefImpl(toLongOrZero(key)));
 			break;
 		case CONTEXT_CURRICULUM:
-			searchParams.setContextCurriculumRefs(singletonList(new CurriculumRefImpl(toLongOrZero(key))));
+			trendSearchParams.setContextCurriculumRefs(singletonList(new CurriculumRefImpl(toLongOrZero(key))));
 			break;
 		case CONTEXT_CURRICULUM_ELEMENT:
-			searchParams.setContextCurriculumElementRefs(singletonList(new CurriculumElementRefImpl(toLongOrZero(key))));
+			trendSearchParams.setContextCurriculumElementRef(new CurriculumElementRefImpl(toLongOrZero(key)));
 			break;
 		case CONTEXT_CURRICULUM_ORGANISATION:
-			searchParams.setContextCurriculumOrganisationRefs(singletonList(new OrganisationRefImpl(toLongOrZero(key))));
+			trendSearchParams.setContextCurriculumOrganisationRef(new OrganisationRefImpl(toLongOrZero(key)));
 			break;
 		case CONTEXT_TAXONOMY_LEVEL:
-			searchParams.setContextTaxonomyLevelRefs(singletonList(new TaxonomyLevelRefImpl(toLongOrZero(key))));
+			trendSearchParams.setContextTaxonomyLevelRef(new TaxonomyLevelRefImpl(toLongOrZero(key)));
 			break;
 		case CONTEXT_LOCATION:
-			searchParams.setContextLocations(singletonList(key));
+			trendSearchParams.setContextLocations(singletonList(key));
 			break;
 		case DATA_COLLECTION:
+			trendSearchParams.setDataCollectionRefs(singletonList(new QualityDataCollectionRefImpl(toLongOrZero(key))));
+			break;
+		default:
+		}
+	}
+
+	private void ammendGroupBySearchParamNull(AnalysisSearchParameter trendSearchParams, GroupBy groupBy) {
+		switch (groupBy) {
+		case TOPIC_IDENTITY:
+			trendSearchParams.setTopicIdentityNull(true);
+			break;
+		case TOPIC_ORGANISATION:
+			trendSearchParams.setTopicOrganisationNull(true);
+			break;
+		case TOPIC_CURRICULUM:
+			trendSearchParams.setTopicCurriculumNull(true);
+			break;
+		case TOPIC_CURRICULUM_ELEMENT:
+			trendSearchParams.setTopicCurriculumElementNull(true);
+			break;
+		case TOPIC_REPOSITORY:
+			trendSearchParams.setTopicRepositoryNull(true);
+			break;
+		case CONTEXT_ORGANISATION:
+			trendSearchParams.setContextOrganisationNull(true);
+			break;
+		case CONTEXT_CURRICULUM:
+			trendSearchParams.setContextCurriculumNull(true);
+			break;
+		case CONTEXT_CURRICULUM_ELEMENT:
+			trendSearchParams.setContextCurriculumElementNull(true);
+			break;
+		case CONTEXT_CURRICULUM_ORGANISATION:
+			trendSearchParams.setContextCurriculumOrganisationNull(true);
+			break;
+		case CONTEXT_TAXONOMY_LEVEL:
+			trendSearchParams.setContextTaxonomyLevelNull(true);
+			break;
+		case CONTEXT_LOCATION:
+			trendSearchParams.setContextLocationNull(true);
+			break;
+		case DATA_COLLECTION:
+			// is never null
 		default:
 		}
 	}
 
 	private String getTrendTitle(MultiKey multiKey) {
-		GroupByKey groupByKey = getLastGroupByAndKey(multiGroupBy, multiKey);
-		String name = groupByNames.getName(groupByKey);
-		return name != null? name: translate("heatmap.not.specified");
+		String groupNameNA = translate("heatmap.not.specified");
+		return getGroupNames(multiKey, groupNameNA)
+				.stream()
+				.collect(Collectors.joining(", "));
 	}
 
 	private void doShowDetails(UserRequest ureq, HeatMapRow row, int index) {
@@ -597,30 +647,6 @@ public class HeatMapController extends FormBasicController implements Filterable
 		}
 	}
 	
-	private GroupByKey getLastGroupByAndKey(MultiGroupBy mGroupBy, MultiKey mKey) {
-		GroupByKey groupByKey = null;
-		if (mGroupBy.getGroupBy3() != null) {
-			groupByKey = new GroupByKey(mGroupBy.getGroupBy3(), mKey.getKey3());
-		} else if (mGroupBy.getGroupBy2() != null) {
-			groupByKey = new GroupByKey(mGroupBy.getGroupBy2(), mKey.getKey2());
-		} else if (multiGroupBy.getGroupBy1() != null) {
-			groupByKey =new GroupByKey(mGroupBy.getGroupBy1(), mKey.getKey1());
-		}
-		return groupByKey;
-	}
-	
-	private GroupBy getLastGroupBy(MultiGroupBy mGroupBy) {
-		GroupBy groupBy = null;
-		if (mGroupBy.getGroupBy3() != null) {
-			groupBy = mGroupBy.getGroupBy3();
-		} else if (mGroupBy.getGroupBy2() != null) {
-			groupBy = mGroupBy.getGroupBy2();
-		} else if (multiGroupBy.getGroupBy1() != null) {
-			groupBy = mGroupBy.getGroupBy1();
-		}
-		return groupBy;
-	}
-	
 	private GroupByKey getGroupByAndKey(MultiGroupBy mGroupBy, MultiKey mKey, int index) {
 		GroupByKey groupByKey = null;
 		if (index == 3) {
diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapDataModel.java b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapDataModel.java
index 0e7594e4516..b871432c115 100644
--- a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapDataModel.java
+++ b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapDataModel.java
@@ -66,11 +66,6 @@ class HeatMapDataModel extends DefaultFlexiTableDataModel<HeatMapRow>
 		if (index < row.getStatisticsSize()) {
 			return row.getStatistic(index);
 		}
-		// trend icon
-		index -= row.getStatisticsSize();
-		if (index == 0) {
-			return Boolean.valueOf(row.isShowTrend());
-		}
 		return null;
 	}
 
diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapRow.java b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapRow.java
index 05fa7361d8e..574a957db49 100644
--- a/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapRow.java
+++ b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapRow.java
@@ -35,14 +35,11 @@ public class HeatMapRow {
 	private final MultiKey multiKey;
 	private final List<String> groupNames;
 	private final List<GroupedStatistic> statistics;
-	private final boolean showTrend;
 	
-	public HeatMapRow(MultiKey multiKey, List<String> groupNames, List<GroupedStatistic> statistics,
-			boolean showTrend) {
+	public HeatMapRow(MultiKey multiKey, List<String> groupNames, List<GroupedStatistic> statistics) {
 		this.multiKey = multiKey;
 		this.groupNames = groupNames;
 		this.statistics = statistics;
-		this.showTrend = showTrend;
 	}
 	
 	public MultiKey getMultiKey() {
@@ -69,8 +66,4 @@ public class HeatMapRow {
 		return statistics.get(index);
 	}
 
-	public boolean isShowTrend() {
-		return showTrend;
-	}
-
 }
diff --git a/src/main/java/org/olat/modules/quality/model/QualityDataCollectionRefImpl.java b/src/main/java/org/olat/modules/quality/model/QualityDataCollectionRefImpl.java
new file mode 100644
index 00000000000..35eec075d41
--- /dev/null
+++ b/src/main/java/org/olat/modules/quality/model/QualityDataCollectionRefImpl.java
@@ -0,0 +1,68 @@
+/**
+ * <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.modules.quality.model;
+
+import org.olat.modules.quality.QualityDataCollectionRef;
+
+/**
+ * 
+ * Initial date: 22 Jan 2019<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class QualityDataCollectionRefImpl implements QualityDataCollectionRef {
+
+	private final Long key;
+	
+	public QualityDataCollectionRefImpl(Long key) {
+		this.key = key;
+	}
+
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((key == null) ? 0 : key.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		QualityDataCollectionRefImpl other = (QualityDataCollectionRefImpl) obj;
+		if (key == null) {
+			if (other.key != null)
+				return false;
+		} else if (!key.equals(other.key))
+			return false;
+		return true;
+	}
+
+}
diff --git a/src/main/java/org/olat/modules/taxonomy/model/TaxonomyLevelRefImpl.java b/src/main/java/org/olat/modules/taxonomy/model/TaxonomyLevelRefImpl.java
index e382c21ce4f..e1f7d138341 100644
--- a/src/main/java/org/olat/modules/taxonomy/model/TaxonomyLevelRefImpl.java
+++ b/src/main/java/org/olat/modules/taxonomy/model/TaxonomyLevelRefImpl.java
@@ -39,4 +39,29 @@ public class TaxonomyLevelRefImpl implements TaxonomyLevelRef {
 	public Long getKey() {
 		return key;
 	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((key == null) ? 0 : key.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		TaxonomyLevelRefImpl other = (TaxonomyLevelRefImpl) obj;
+		if (key == null) {
+			if (other.key != null)
+				return false;
+		} else if (!key.equals(other.key))
+			return false;
+		return true;
+	}
 }
diff --git a/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java b/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java
index 8f7a5ccbdf9..9064c6df581 100644
--- a/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java
+++ b/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java
@@ -1183,6 +1183,35 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 		assertThat(count).isEqualTo(2);
 	}
 	
+	@Test
+	public void shouldFilterByDataCollections() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		// Data collection 1
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		qualityService.createContextBuilder(dc1, participations1.get(0)).build();
+		// Data collection 2
+		QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations2 = qualityService.addParticipations(dc2, Collections.singletonList(executor));
+		qualityService.createContextBuilder(dc2, participations2.get(0)).build();
+		// Other data collection
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		qualityService.createContextBuilder(dcOther, participationsOther.get(0)).build();
+		finish(asList(dc1, dc2, dcOther));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setDataCollectionRefs(asList(dc1, dc2));
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections)
+				.containsExactlyInAnyOrder(dc1, dc2)
+				.doesNotContain(dcOther);
+	}
+	
 	@Test
 	public void shouldFilterByTopicIdentity() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1421,6 +1450,49 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 				.doesNotContain(dcOther);
 	}
 	
+	@Test
+	public void shouldFilterByContextOrganisation() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Organisation organisation1 = qualityTestHelper.createOrganisation();
+		Organisation organisation2 = qualityTestHelper.createOrganisation();
+		Organisation subOrganisation = organisationService.createOrganisation("", "", null, organisation1, null);
+		Organisation otherOrganisation = qualityTestHelper.createOrganisation();
+		// Participation with two organisations
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder1 = qualityService.createContextBuilder(dc1, participations1.get(0));
+		contextBuilder1.addExecutorOrganisation(organisation1).addExecutorOrganisation(organisation2).build();
+		// Participation with the same organisation
+		QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations2 = qualityService.addParticipations(dc2, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder2 = qualityService.createContextBuilder(dc2, participations2.get(0));
+		contextBuilder2.addExecutorOrganisation(organisation2).build();
+		// Participation in a child organisation (include them)
+		QualityDataCollection dcChild = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationscild = qualityService.addParticipations(dcChild, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderChild = qualityService.createContextBuilder(dcChild, participationscild.get(0));
+		contextBuilderChild.addExecutorOrganisation(subOrganisation).build();
+		// Participation with an other organisation
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderOther = qualityService.createContextBuilder(dcOther, participationsOther.get(0));
+		contextBuilderOther.addExecutorOrganisation(otherOrganisation).build();
+		// Participation without organisation
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		qualityService.addParticipations(dcNull, Collections.singletonList(executor));
+		finish(asList(dc1, dc2, dcOther, dcChild, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextOrganisationRef(organisation1);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dc1);
+	}
+	
 	@Test
 	public void shouldFilterByContextOrganisations() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1464,6 +1536,45 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 		assertThat(count).isEqualTo(expected);
 	}
 	
+	@Test
+	public void shouldFilterByContextCurriculum() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Curriculum curriculum1 = qualityTestHelper.createCurriculum();
+		CurriculumElement element1 = qualityTestHelper.createCurriculumElement(curriculum1);
+		Curriculum curriculum2 = qualityTestHelper.createCurriculum();
+		CurriculumElement element2 = qualityTestHelper.createCurriculumElement(curriculum2);
+		Curriculum curriculumOther = qualityTestHelper.createCurriculum();
+		CurriculumElement elementOther = qualityTestHelper.createCurriculumElement(curriculumOther);
+		// Participation with curriculum
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder1 = qualityService.createContextBuilder(dc1, participations1.get(0));
+		contextBuilder1.addCurriculumElement(element1).build();
+		// Participation with another curriculum
+		QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations2 = qualityService.addParticipations(dc2, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder2 = qualityService.createContextBuilder(dc2, participations2.get(0));
+		contextBuilder2.addCurriculumElement(element2).build();
+		// Participation with other curriculum
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderOther = qualityService.createContextBuilder(dcOther, participationsOther.get(0));
+		contextBuilderOther.addCurriculumElement(elementOther).build();
+		// Participation without curriculum
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		qualityService.addParticipations(dcNull, Collections.singletonList(executor));
+		finish(asList(dc1, dc2, dcOther, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumRefs(asList(curriculum1, curriculum2));
+		Long count = sut.loadAnalyticFigures(searchParams).getDataCollectionCount();
+		
+		assertThat(count).isEqualTo(2);
+	}
+	
 	@Test
 	public void shouldFilterByContextCurriculums() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1503,6 +1614,50 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 		assertThat(count).isEqualTo(2);
 	}
 	
+	@Test
+	public void shouldFilterByContextCurriculumElement() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Curriculum curriculum = qualityTestHelper.createCurriculum();
+		CurriculumElement element1 = qualityTestHelper.createCurriculumElement();
+		CurriculumElement element2 = qualityTestHelper.createCurriculumElement();
+		CurriculumElement subElement = curriculumService.createCurriculumElement("", "", null, null, element1, null, CurriculumCalendars.disabled, curriculum);
+		CurriculumElement otherElement = qualityTestHelper.createCurriculumElement();
+		// Participation with curriculum element
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder1 = qualityService.createContextBuilder(dc1, participations1.get(0));
+		contextBuilder1.addCurriculumElement(element1).build();
+		// Participation with another curriculum element
+		QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations2 = qualityService.addParticipations(dc2, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder2 = qualityService.createContextBuilder(dc2, participations2.get(0));
+		contextBuilder2.addCurriculumElement(element2).build();
+		// Participation with a child element
+		QualityDataCollection dcChild = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsChild = qualityService.addParticipations(dcChild, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderChild = qualityService.createContextBuilder(dcChild, participationsChild.get(0));
+		contextBuilderChild.addCurriculumElement(subElement).build();
+		// Participation with other curriculum element
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderOther = qualityService.createContextBuilder(dcOther, participationsOther.get(0));
+		contextBuilderOther.addCurriculumElement(otherElement).build();
+		// Participation without curriculum
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		qualityService.addParticipations(dcNull, Collections.singletonList(executor));
+		finish(asList(dc1, dc2, dcChild, dcOther, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumElementRef(element1);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dc1);
+	}
+	
 	@Test
 	public void shouldFilterByContextCurriculumElements() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1594,6 +1749,52 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 				.doesNotContain(dcTypeNull, dcNull, dcOther);
 	}
 	
+	@Test
+	public void shouldFilterByContextCurriculumOrganisation() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Organisation organisation1 = qualityTestHelper.createOrganisation();
+		Curriculum curriculum1 = qualityTestHelper.createCurriculum(organisation1);
+		CurriculumElement element1 = qualityTestHelper.createCurriculumElement(curriculum1);
+		Organisation organisation2 = qualityTestHelper.createOrganisation();
+		Curriculum curriculum2 = qualityTestHelper.createCurriculum(organisation2);
+		CurriculumElement element2 = qualityTestHelper.createCurriculumElement(curriculum2);
+		Organisation organisationSub = organisationService.createOrganisation("", "", null, organisation1, null);
+		Curriculum curriculumSub = qualityTestHelper.createCurriculum(organisationSub);
+		CurriculumElement elementSub = qualityTestHelper.createCurriculumElement(curriculumSub);
+		Organisation organisationOther = qualityTestHelper.createOrganisation();
+		Curriculum curriculumOther = qualityTestHelper.createCurriculum(organisationOther);
+		CurriculumElement elementOther = qualityTestHelper.createCurriculumElement(curriculumOther);
+		// Participation with two organisations
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder1 = qualityService.createContextBuilder(dc1, participations1.get(0));
+		contextBuilder1.addCurriculumElement(element1).addCurriculumElement(element2).build();
+		// Participation in a child organisation (include them)
+		QualityDataCollection dcChild = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationscild = qualityService.addParticipations(dcChild, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderChild = qualityService.createContextBuilder(dcChild, participationscild.get(0));
+		contextBuilderChild.addCurriculumElement(elementSub).build();
+		// Participation with an other organisation
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderOther = qualityService.createContextBuilder(dcOther, participationsOther.get(0));
+		contextBuilderOther.addCurriculumElement(elementOther).build();
+		// Participation without organisation
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		qualityService.addParticipations(dcNull, Collections.singletonList(executor));
+		finish(asList(dc1, dcOther, dcChild, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumOrganisationRef(organisation1);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dc1);
+	}
+	
 	@Test
 	public void shouldFilterByContextCurriculumOrganisations() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1645,6 +1846,50 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 		assertThat(count).isEqualTo(expected);
 	}
 	
+	@Test
+	public void shouldFilterByContextTaxonomyLevel() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Taxonomy taxonomy = qualityTestHelper.createTaxonomy();
+		TaxonomyLevel taxonomyLevel1 = qualityTestHelper.createTaxonomyLevel(taxonomy);
+		TaxonomyLevel taxonomyLevel2 = qualityTestHelper.createTaxonomyLevel();
+		TaxonomyLevel subTaxonomyLevel = qualityTestHelper.createTaxonomyLevel(taxonomyLevel1);
+		TaxonomyLevel otherTaxonomyLevel = qualityTestHelper.createTaxonomyLevel();
+		// Participation with two taxonomyLevels
+		QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations1 = qualityService.addParticipations(dc1, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder1 = qualityService.createContextBuilder(dc1, participations1.get(0));
+		contextBuilder1.addTaxonomyLevel(taxonomyLevel1).addTaxonomyLevel(taxonomyLevel2).build();
+		// Participation with the same taxonomyLevel
+		QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations2 = qualityService.addParticipations(dc2, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilder2 = qualityService.createContextBuilder(dc2, participations2.get(0));
+		contextBuilder2.addTaxonomyLevel(taxonomyLevel2).build();
+		// Participation in a child taxonomyLevel (include them)
+		QualityDataCollection dcChild = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationscild = qualityService.addParticipations(dcChild, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderChild = qualityService.createContextBuilder(dcChild, participationscild.get(0));
+		contextBuilderChild.addTaxonomyLevel(subTaxonomyLevel).build();
+		// Participation with an other taxonomyLevel
+		QualityDataCollection dcOther = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsOther = qualityService.addParticipations(dcOther, Collections.singletonList(executor));
+		QualityContextBuilder contextBuilderOther = qualityService.createContextBuilder(dcOther, participationsOther.get(0));
+		contextBuilderOther.addTaxonomyLevel(otherTaxonomyLevel).build();
+		// Participation without organisation
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		qualityService.addParticipations(dcNull, Collections.singletonList(executor));
+		finish(asList(dc1, dc2, dcOther, dcChild, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextTaxonomyLevelRef(taxonomyLevel1);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dc1);
+	}
+	
 	@Test
 	public void shouldFilterByContextTaxonomyLevels() {
 		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
@@ -1756,6 +2001,288 @@ public class AnalysisFilterDAOTest extends OlatTestCase {
 		long expected = asList(dc1, dc3, dc4, dc6).size();
 		assertThat(count).isEqualTo(expected);
 	}
+
+	@Test
+	public void shouldFilterByTopicIdentityNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		dc.setTopicIdentity(executor);
+		dc = qualityService.updateDataCollection(dc);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setTopicIdentityNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByTopicOrganisationNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		dc.setTopicOrganisation(dcOrganisation);
+		dc = qualityService.updateDataCollection(dc);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setTopicOrganisationNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByTopicCurriculumNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		Curriculum curriculum = qualityTestHelper.createCurriculum();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		dc.setTopicCurriculum(curriculum);
+		dc = qualityService.updateDataCollection(dc);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setTopicCurriculumNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByTopicCurriculumElementNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		CurriculumElement curriculumElement = qualityTestHelper.createCurriculumElement();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		dc.setTopicCurriculumElement(curriculumElement);
+		dc = qualityService.updateDataCollection(dc);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setTopicCurriculumElementNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByTopicRepositoryEntryNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		RepositoryEntry re = qualityTestHelper.createRepositoryEntry();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		dc.setTopicRepositoryEntry(re);
+		dc = qualityService.updateDataCollection(dc);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setTopicRepositoryNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByContextOrganisationNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).addExecutorOrganisation(dcOrganisation).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextOrganisationNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByContextCurriculumNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		CurriculumElement curriculumElement = qualityTestHelper.createCurriculumElement();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).addCurriculumElement(curriculumElement).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByContextCurriculumElementNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		CurriculumElement curriculumElement = qualityTestHelper.createCurriculumElement();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).addCurriculumElement(curriculumElement).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumElementNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByContextCurriculumOraganisationNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		CurriculumElement curriculumElement = qualityTestHelper.createCurriculumElement();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).addCurriculumElement(curriculumElement).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextCurriculumOrganisationNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	@Test
+	public void shouldFilterByContextTaxonomyLevelNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		TaxonomyLevel taxonomyLevel = qualityTestHelper.createTaxonomyLevel();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).addTaxonomyLevel(taxonomyLevel).build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextTaxonomyLevelNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
+	
+	
+	@Test
+	public void shouldFilterByContextLocationNull() {
+		RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry();
+		Identity executor = JunitTestHelper.createAndPersistIdentityAsUser("");
+		Organisation dcOrganisation = qualityTestHelper.createOrganisation();
+		// Data collection ok
+		QualityDataCollection dc = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participations = qualityService.addParticipations(dc, singletonList(executor));
+		qualityService.createContextBuilder(dc, participations.get(0)).withLocation("somewhere").build();
+		// Data collection with null value
+		QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry);
+		List<EvaluationFormParticipation> participationsNull = qualityService.addParticipations(dcNull, singletonList(executor));
+		qualityService.createContextBuilder(dcNull, participationsNull.get(0)).build();
+		finish(asList(dc, dcNull));
+		dbInstance.commitAndCloseSession();
+		
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		searchParams.setContextLocationNull(true);
+		List<QualityDataCollection> dataCollections = sut.loadDataCollection(searchParams);
+		
+		assertThat(dataCollections).hasSize(1);
+		assertThat(dataCollections.get(0)).isEqualTo(dcNull);
+	}
 	
 	private void finish(Collection<QualityDataCollection> dataCollections) {
 		for (QualityDataCollection dataCollection: dataCollections) {
diff --git a/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStreamTest.java b/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStreamTest.java
index 1055fd50989..f8bde229b83 100644
--- a/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStreamTest.java
+++ b/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisPresentationXStreamTest.java
@@ -36,9 +36,12 @@ import org.olat.modules.curriculum.CurriculumElementRef;
 import org.olat.modules.curriculum.CurriculumRef;
 import org.olat.modules.curriculum.model.CurriculumElementRefImpl;
 import org.olat.modules.curriculum.model.CurriculumRefImpl;
+import org.olat.modules.quality.QualityContextRole;
+import org.olat.modules.quality.QualityDataCollectionRef;
 import org.olat.modules.quality.analysis.AnalysisSearchParameter;
 import org.olat.modules.quality.analysis.GroupBy;
 import org.olat.modules.quality.analysis.MultiGroupBy;
+import org.olat.modules.quality.model.QualityDataCollectionRefImpl;
 import org.olat.modules.taxonomy.TaxonomyLevelRef;
 import org.olat.modules.taxonomy.model.TaxonomyLevelRefImpl;
 import org.olat.repository.RepositoryEntryRef;
@@ -74,13 +77,32 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamSimpleValues() {
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		RepositoryEntryRef formEntryRef = new RepositoryEntryRefImpl(6l);
+		OrganisationRef organisationRef = new OrganisationRefImpl(9l);
+		CurriculumElementRef curriculumElementRef = new CurriculumElementRefImpl(1l);
+		OrganisationRef organisationCurriculumeRef = new OrganisationRefImpl(91l);
+		TaxonomyLevelRef taxonomyLevelRef = new TaxonomyLevelRefImpl(23l);
 		Date dateRangeFrom = new Date();
 		Date dateRangeTo = new Date();
 		boolean withUserInfosOnly = true;
 		searchParams.setFormEntryRef(formEntryRef);
 		searchParams.setDateRangeFrom(dateRangeFrom);
 		searchParams.setDateRangeTo(dateRangeTo);
+		searchParams.setContextOrganisationRef(organisationRef);
+		searchParams.setContextCurriculumElementRef(curriculumElementRef);
+		searchParams.setContextCurriculumOrganisationRef(organisationCurriculumeRef);
+		searchParams.setContextTaxonomyLevelRef(taxonomyLevelRef);
 		searchParams.setWithUserInfosOnly(withUserInfosOnly);
+		searchParams.setTopicIdentityNull(true);
+		searchParams.setTopicOrganisationNull(true);
+		searchParams.setTopicCurriculumNull(true);
+		searchParams.setTopicCurriculumElementNull(true);
+		searchParams.setTopicRepositoryNull(true);
+		searchParams.setContextOrganisationNull(true);
+		searchParams.setContextCurriculumNull(true);
+		searchParams.setContextCurriculumElementNull(true);
+		searchParams.setContextCurriculumOrganisationNull(true);
+		searchParams.setContextTaxonomyLevelNull(true);
+		searchParams.setContextLocationNull(true);
 
 		String xml = AnalysisPresentationXStream.toXml(searchParams);
 		AnalysisSearchParameter searchParamsFromXml = AnalysisPresentationXStream.fromXml(xml,
@@ -90,10 +112,41 @@ public class AnalysisPresentationXStreamTest {
 		softly.assertThat(searchParamsFromXml.getFormEntryRef().getKey()).isEqualTo(formEntryRef.getKey());
 		softly.assertThat(searchParamsFromXml.getDateRangeFrom()).isEqualTo(dateRangeFrom);
 		softly.assertThat(searchParamsFromXml.getDateRangeTo()).isEqualTo(dateRangeTo);
-		softly.assertThat(searchParams.isWithUserInfosOnly()).isEqualTo(withUserInfosOnly);
+		softly.assertThat(searchParamsFromXml.getContextOrganisationRef()).isEqualTo(organisationRef);
+		softly.assertThat(searchParamsFromXml.getContextCurriculumElementRef()).isEqualTo(curriculumElementRef);
+		softly.assertThat(searchParamsFromXml.getContextCurriculumOrganisationRef()).isEqualTo(organisationCurriculumeRef);
+		softly.assertThat(searchParamsFromXml.getContextTaxonomyLevelRef()).isEqualTo(taxonomyLevelRef);
+		softly.assertThat(searchParamsFromXml.isWithUserInfosOnly()).isEqualTo(withUserInfosOnly);
+		softly.assertThat(searchParamsFromXml.isTopicIdentityNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isTopicOrganisationNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isTopicCurriculumNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isTopicCurriculumElementNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isTopicRepositoryNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextOrganisationNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextCurriculumNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextCurriculumElementNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextCurriculumOrganisationNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextTaxonomyLevelNull()).isTrue();
+		softly.assertThat(searchParamsFromXml.isContextLocationNull()).isTrue();
 		softly.assertAll();
 	}
 
+	@Test
+	public void shouldSerializeSearchParamDataCollectionRefs() {
+		QualityDataCollectionRef ref1 = new QualityDataCollectionRefImpl(1l);
+		QualityDataCollectionRef ref2 = new QualityDataCollectionRefImpl(2l);
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		Collection<QualityDataCollectionRef> refs = asList(ref1, ref2);
+		searchParams.setDataCollectionRefs(refs);
+
+		String xml = AnalysisPresentationXStream.toXml(searchParams);
+		AnalysisSearchParameter searchParamsFromXml = AnalysisPresentationXStream.fromXml(xml,
+				AnalysisSearchParameter.class);
+
+		assertThat(searchParamsFromXml.getDataCollectionRefs()).extracting(QualityDataCollectionRef::getKey)
+				.containsExactlyInAnyOrder(ref1.getKey(), ref2.getKey());
+	}
+
 	@Test
 	public void shouldSerializeSearchParamTopicIdentityRefs() {
 		IdentityRef ref1 = new IdentityRefImpl(1l);
@@ -114,7 +167,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamTopicOrganisations() {
 		OrganisationRef ref1 = new OrganisationRefImpl(8l);
 		OrganisationRef ref2 = new OrganisationRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends OrganisationRef> topicOrganisationRefs = asList(ref1, ref2);
 		searchParams.setTopicOrganisationRefs(topicOrganisationRefs);
@@ -131,7 +183,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamTopicCurriculums() {
 		CurriculumRef ref1 = new CurriculumRefImpl(8l);
 		CurriculumRef ref2 = new CurriculumRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends CurriculumRef> topicCurriculumRefs = asList(ref1, ref2);
 		searchParams.setTopicCurriculumRefs(topicCurriculumRefs);
@@ -148,7 +199,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamTopicCurriculumElements() {
 		CurriculumElementRef ref1 = new CurriculumElementRefImpl(8l);
 		CurriculumElementRef ref2 = new CurriculumElementRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends CurriculumElementRef> topicCurriculumElementRefs = asList(ref1, ref2);
 		searchParams.setTopicCurriculumElementRefs(topicCurriculumElementRefs);
@@ -165,7 +215,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamTopicRepositoryEntrys() {
 		RepositoryEntryRef ref1 = new RepositoryEntryRefImpl(8l);
 		RepositoryEntryRef ref2 = new RepositoryEntryRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends RepositoryEntryRef> topicRepositoryEntryRefs = asList(ref1, ref2);
 		searchParams.setTopicRepositoryRefs(topicRepositoryEntryRefs);
@@ -182,7 +231,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamContextLocations() {
 		String loc1 = "l1";
 		String loc2 = "l2";
-
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		Collection<String> contextLocations = asList(loc1, loc2);
 		searchParams.setContextLocations(contextLocations);
@@ -198,7 +246,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamContextOrganisations() {
 		OrganisationRef ref1 = new OrganisationRefImpl(8l);
 		OrganisationRef ref2 = new OrganisationRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends OrganisationRef> topicOrganisationRefs = asList(ref1, ref2);
 		searchParams.setContextOrganisationRefs(topicOrganisationRefs);
@@ -215,7 +262,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamContextCurriculums() {
 		CurriculumRef ref1 = new CurriculumRefImpl(8l);
 		CurriculumRef ref2 = new CurriculumRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends CurriculumRef> topicCurriculumRefs = asList(ref1, ref2);
 		searchParams.setContextCurriculumRefs(topicCurriculumRefs);
@@ -232,7 +278,6 @@ public class AnalysisPresentationXStreamTest {
 	public void shouldSerializeSearchParamContextCurriculumElements() {
 		CurriculumElementRef ref1 = new CurriculumElementRefImpl(8l);
 		CurriculumElementRef ref2 = new CurriculumElementRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends CurriculumElementRef> topicCurriculumElementRefs = asList(ref1, ref2);
 		searchParams.setContextCurriculumElementRefs(topicCurriculumElementRefs);
@@ -245,11 +290,26 @@ public class AnalysisPresentationXStreamTest {
 				.containsExactlyInAnyOrder(ref1.getKey(), ref2.getKey());
 	}
 
+	@Test
+	public void shouldSerializeSearchParamContextCurriculumOrganisations() {
+		OrganisationRef ref1 = new OrganisationRefImpl(8l);
+		OrganisationRef ref2 = new OrganisationRefImpl(8l);
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		List<? extends OrganisationRef> topicCurriculumOrganisationRefs = asList(ref1, ref2);
+		searchParams.setContextCurriculumOrganisationRefs(topicCurriculumOrganisationRefs);
+		
+		String xml = AnalysisPresentationXStream.toXml(searchParams);
+		AnalysisSearchParameter searchParamsFromXml = AnalysisPresentationXStream.fromXml(xml,
+				AnalysisSearchParameter.class);
+
+		assertThat(searchParamsFromXml.getContextCurriculumOrganisationRefs()).extracting(OrganisationRef::getKey)
+				.containsExactlyInAnyOrder(ref1.getKey(), ref2.getKey());
+	}
+
 	@Test
 	public void shouldSerializeSearchParamContextTaxonomyLevels() {
 		TaxonomyLevelRef ref1 = new TaxonomyLevelRefImpl(8l);
 		TaxonomyLevelRef ref2 = new TaxonomyLevelRefImpl(8l);
-		
 		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
 		List<? extends TaxonomyLevelRef> topicTaxonomyLevelRefs = asList(ref1, ref2);
 		searchParams.setContextTaxonomyLevelRefs(topicTaxonomyLevelRefs);
@@ -261,4 +321,34 @@ public class AnalysisPresentationXStreamTest {
 		assertThat(searchParamsFromXml.getContextTaxonomyLevelRefs()).extracting(TaxonomyLevelRef::getKey)
 				.containsExactlyInAnyOrder(ref1.getKey(), ref2.getKey());
 	}
+
+	@Test
+	public void shouldSerializeSearchParamSeriesIndex() {
+		Integer i1 = 3;
+		Integer i2 = 6;
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		Collection<Integer> seriesIndexes = asList(i1, i2);
+		searchParams.setSeriesIndexes(seriesIndexes);
+		
+		String xml = AnalysisPresentationXStream.toXml(searchParams);
+		AnalysisSearchParameter searchParamsFromXml = AnalysisPresentationXStream.fromXml(xml,
+				AnalysisSearchParameter.class);
+
+		assertThat(searchParamsFromXml.getSeriesIndexes()).containsExactlyInAnyOrder(i1, i2);
+	}
+
+	@Test
+	public void shouldSerializeSearchParamQualityContextRoles() {
+		QualityContextRole role1 = QualityContextRole.coach;
+		QualityContextRole role2 = QualityContextRole.participant;
+		AnalysisSearchParameter searchParams = new AnalysisSearchParameter();
+		Collection<QualityContextRole> contextRoles = asList(role1, role2);
+		searchParams.setContextRoles(contextRoles);
+		
+		String xml = AnalysisPresentationXStream.toXml(searchParams);
+		AnalysisSearchParameter searchParamsFromXml = AnalysisPresentationXStream.fromXml(xml,
+				AnalysisSearchParameter.class);
+
+		assertThat(searchParamsFromXml.getContextRoles()).containsExactlyInAnyOrder(role1, role2);
+	}
 }
-- 
GitLab