From 79274216ed3a786d15d9252a1e0f6539d5fb8f60 Mon Sep 17 00:00:00 2001 From: uhensler <urs.hensler@frentix.com> Date: Tue, 4 Sep 2018 14:35:09 +0200 Subject: [PATCH] OO-3304: First filters in the quality analysis tool --- .../modules/curriculum/CurriculumService.java | 4 +- .../manager/CurriculumElementDAO.java | 18 + .../manager/CurriculumServiceImpl.java | 5 + .../forms/model/jpa/RubricStatisticImpl.java | 5 +- .../analysis/AnalysisSearchParameter.java | 106 +++++ .../quality/analysis/EvaluationFormView.java | 6 +- .../analysis/QualityAnalysisService.java | 12 + .../analysis/manager/AnalysisFilterDAO.java | 199 ++++++++ .../manager/QualityAnalysisServiceImpl.java | 47 ++ .../model/EvaluationFormViewImpl.java | 10 + .../analysis/ui/AnalysisController.java | 102 +++++ .../analysis/ui/AnalysisListController.java | 34 +- .../analysis/ui/AnalysisReportController.java | 72 +++ .../quality/analysis/ui/AnalysisRow.java | 19 +- .../ui/AnalysisSegmentsController.java | 138 ++++++ .../quality/analysis/ui/FilterController.java | 269 +++++++++++ .../analysis/ui/HeatMapController.java | 64 +++ .../analysis/ui/_content/analysis.html | 4 + .../ui/_i18n/LocalStrings_de.properties | 14 +- .../ui/_i18n/LocalStrings_en.properties | 14 +- .../modules/quality/ui/QualityUIFactory.java | 26 +- .../manager/CurriculumElementDAOTest.java | 24 + .../manager/AnalysisFilterDAOTest.java | 427 ++++++++++++++++++ .../quality/manager/QualityTestHelper.java | 4 +- .../java/org/olat/test/AllTestsJunit4.java | 1 + 25 files changed, 1589 insertions(+), 35 deletions(-) create mode 100644 src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/AnalysisController.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/AnalysisReportController.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/AnalysisSegmentsController.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/FilterController.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java create mode 100644 src/main/java/org/olat/modules/quality/analysis/ui/_content/analysis.html create mode 100644 src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java diff --git a/src/main/java/org/olat/modules/curriculum/CurriculumService.java b/src/main/java/org/olat/modules/curriculum/CurriculumService.java index ce1f32f329c..ed16e1612af 100644 --- a/src/main/java/org/olat/modules/curriculum/CurriculumService.java +++ b/src/main/java/org/olat/modules/curriculum/CurriculumService.java @@ -196,7 +196,7 @@ public interface CurriculumService { public CurriculumElement getCurriculumElement(CurriculumElementRef element); - + public List<CurriculumElement> getCurriculumElements(Collection<CurriculumElementRef> elementRefs); public void deleteCurriculumElement(CurriculumElementRef element); @@ -210,6 +210,8 @@ public interface CurriculumService { */ public List<CurriculumElement> getCurriculumElements(CurriculumRef curriculum, CurriculumElementStatus[] status); + public List<CurriculumElement> getCurriculumElementsByCurriculums(Collection<? extends CurriculumRef> curriculumRefs); + /** * Return all the elements of a curriculum, flat, with additional informations * like the number of resources linked to the elements. List element in state diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java index e1963699341..c9abe0b07c4 100644 --- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumElementDAO.java @@ -19,6 +19,8 @@ */ package org.olat.modules.curriculum.manager; +import static java.util.stream.Collectors.toList; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -251,6 +253,21 @@ public class CurriculumElementDAO { .setParameter("entryKey", entry.getKey()) .getResultList(); } + + public List<CurriculumElement> loadElementsByCurriculums(Collection<? extends CurriculumRef> curriculumRefs) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select el"); + sb.append(" from curriculumelement el"); + sb.append(" inner join fetch el.curriculum curriculum"); + sb.append(" left join el.parent parentEl"); + sb.and().append("el.curriculum.key in :curriculumKeys"); + + List<Long> curriculumKeys = curriculumRefs.stream().map(CurriculumRef::getKey).collect(toList()); + return dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), CurriculumElement.class) + .setParameter("curriculumKeys", curriculumKeys) + .getResultList(); + } public List<CurriculumElement> searchElements(String externalId, String identifier, Long key) { StringBuilder sb = new StringBuilder(256); @@ -474,4 +491,5 @@ public class CurriculumElementDAO { return len1 - len2; } } + } diff --git a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java index 7dfd7cc933c..4ab7a4f3058 100644 --- a/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java +++ b/src/main/java/org/olat/modules/curriculum/manager/CurriculumServiceImpl.java @@ -326,6 +326,11 @@ public class CurriculumServiceImpl implements CurriculumService { return curriculumElementDao.getChildren(parentElement); } + @Override + public List<CurriculumElement> getCurriculumElementsByCurriculums(Collection<? extends CurriculumRef> curriculumRefs) { + return curriculumElementDao.loadElementsByCurriculums(curriculumRefs); + } + @Override public List<CurriculumElement> searchCurriculumElements(String externalId, String identifier, Long key) { return curriculumElementDao.searchElements(externalId, identifier, key); diff --git a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java index 2e081cc5748..34080dda064 100644 --- a/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java +++ b/src/main/java/org/olat/modules/forms/model/jpa/RubricStatisticImpl.java @@ -19,7 +19,6 @@ */ package org.olat.modules.forms.model.jpa; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -113,9 +112,9 @@ public class RubricStatisticImpl implements RubricStatistic { } private Long getValue(List<CalculatedLong> calculatedLongs, String identifier, int step) { - String subidentifier = BigDecimal.valueOf((double)step).toPlainString(); for (CalculatedLong calculatedLong: calculatedLongs) { - if (calculatedLong.getIdentifier().equals(identifier) && calculatedLong.getSubIdentifier().equals(subidentifier)) { + int calculatedStep = Double.valueOf(calculatedLong.getSubIdentifier()).intValue(); + if (calculatedLong.getIdentifier().equals(identifier) && calculatedStep == step) { return calculatedLong.getValue(); } } diff --git a/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java b/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java new file mode 100644 index 00000000000..be5e563aec0 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/AnalysisSearchParameter.java @@ -0,0 +1,106 @@ +/** + * <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.analysis; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.olat.core.id.OrganisationRef; +import org.olat.modules.curriculum.CurriculumElementRef; +import org.olat.modules.curriculum.CurriculumRef; +import org.olat.repository.RepositoryEntryRef; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class AnalysisSearchParameter { + + private RepositoryEntryRef formEntryRef; + private Date dateRangeFrom; + private Date dateRangeTo; + private List<? extends OrganisationRef> organisationRefs; + private Collection<? extends CurriculumRef> curriculumRefs; + private List<? extends CurriculumElementRef> curriculumElementRefs; + + public RepositoryEntryRef getFormEntryRef() { + return formEntryRef; + } + + public void setFormEntryRef(RepositoryEntryRef formEntryRef) { + this.formEntryRef = formEntryRef; + } + + public Date getDateRangeFrom() { + return dateRangeFrom; + } + + public void setDateRangeFrom(Date dateRangeFrom) { + this.dateRangeFrom = dateRangeFrom; + } + + public Date getDateRangeTo() { + return dateRangeTo; + } + + public void setDateRangeTo(Date dateRangeTo) { + this.dateRangeTo = dateRangeTo; + } + + public List<? extends OrganisationRef> getOrganisationRefs() { + return organisationRefs; + } + + public void setOrganisationRefs(List<? extends OrganisationRef> organisationRefs) { + this.organisationRefs = organisationRefs; + } + + public Collection<? extends CurriculumRef> getCurriculumRefs() { + return curriculumRefs; + } + + public void setCurriculumRefs(Collection<? extends CurriculumRef> curriculumRefs) { + this.curriculumRefs = curriculumRefs; + } + + public List<? extends CurriculumElementRef> getCurriculumElementRefs() { + return curriculumElementRefs; + } + + public void setCurriculumElementRefs(List<? extends CurriculumElementRef> curriculumElementRefs) { + this.curriculumElementRefs = curriculumElementRefs; + } + + @Override + public AnalysisSearchParameter clone() { + AnalysisSearchParameter clone = new AnalysisSearchParameter(); + clone.formEntryRef = this.formEntryRef; + clone.dateRangeFrom = this.dateRangeFrom; + clone.organisationRefs = this.organisationRefs != null? new ArrayList<>(this.organisationRefs): null; + clone.curriculumRefs = this.curriculumRefs != null? new ArrayList<>(this.curriculumRefs): null; + clone.curriculumElementRefs = this.curriculumElementRefs != null? new ArrayList<>(this.curriculumElementRefs): null; + return clone; + } + +} diff --git a/src/main/java/org/olat/modules/quality/analysis/EvaluationFormView.java b/src/main/java/org/olat/modules/quality/analysis/EvaluationFormView.java index 9f98da50484..fde6dd3909e 100644 --- a/src/main/java/org/olat/modules/quality/analysis/EvaluationFormView.java +++ b/src/main/java/org/olat/modules/quality/analysis/EvaluationFormView.java @@ -21,14 +21,18 @@ package org.olat.modules.quality.analysis; import java.util.Date; +import org.olat.core.id.OLATResourceable; + /** * * Initial date: 03.09.2018<br> * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com * */ -public interface EvaluationFormView { +public interface EvaluationFormView extends OLATResourceable { + public String RESOURCEABLE_TYPE = "form"; + public Long getFormEntryKey(); public Date getFormCreatedDate(); diff --git a/src/main/java/org/olat/modules/quality/analysis/QualityAnalysisService.java b/src/main/java/org/olat/modules/quality/analysis/QualityAnalysisService.java index 6712bf132e8..29de9a219a1 100644 --- a/src/main/java/org/olat/modules/quality/analysis/QualityAnalysisService.java +++ b/src/main/java/org/olat/modules/quality/analysis/QualityAnalysisService.java @@ -21,6 +21,10 @@ package org.olat.modules.quality.analysis; import java.util.List; +import org.olat.core.id.Organisation; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; + /** * * Initial date: 03.09.2018<br> @@ -30,5 +34,13 @@ import java.util.List; public interface QualityAnalysisService { public List<EvaluationFormView> loadEvaluationForms(EvaluationFormViewSearchParams searchParams); + + public List<Organisation> loadFilterOrganisations(AnalysisSearchParameter searchParams); + + public List<Curriculum> loadFilterCurriculums(AnalysisSearchParameter searchParams); + + public List<CurriculumElement> loadFilterCurriculumElements(AnalysisSearchParameter searchParams); + + public Long loadFilterDataCollectionCount(AnalysisSearchParameter searchParams); } 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 new file mode 100644 index 00000000000..042a9d98d75 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAO.java @@ -0,0 +1,199 @@ +/** + * <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.analysis.manager; + +import static java.util.stream.Collectors.toList; + +import java.util.List; + +import javax.persistence.Query; +import javax.persistence.TypedQuery; + +import org.olat.core.commons.persistence.DB; +import org.olat.core.commons.persistence.QueryBuilder; +import org.olat.core.id.Organisation; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumRef; +import org.olat.modules.quality.QualityDataCollectionLight; +import org.olat.modules.quality.QualityDataCollectionStatus; +import org.olat.modules.quality.analysis.AnalysisSearchParameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 05.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +@Service +public class AnalysisFilterDAO { + + @Autowired + private DB dbInstance; + + List<Organisation> loadOrganisations(AnalysisSearchParameter searchParams) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select distinct organisation"); + appendFrom(sb); + appendWhere(sb, searchParams); + sb.and().append("organisation.key is not null"); + + TypedQuery<Organisation> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Organisation.class); + appendParameters(query, searchParams); + return query.getResultList(); + } + + List<Curriculum> loadCurriculums(AnalysisSearchParameter searchParams) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select distinct curriculum"); + appendFrom(sb); + appendWhere(sb, searchParams); + sb.and().append("curriculum.key is not null"); + + TypedQuery<Curriculum> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Curriculum.class); + appendParameters(query, searchParams); + return query.getResultList(); + } + + List<String> loadCurriculumElementPathes(AnalysisSearchParameter searchParams) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select distinct curriculumElement.materializedPathKeys"); + appendFrom(sb); + appendWhere(sb, searchParams); + sb.and().append("curriculumElement.key is not null"); + + TypedQuery<String> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), String.class); + appendParameters(query, searchParams); + return query.getResultList(); + } + + public Long loadFilterDataCollectionCount(AnalysisSearchParameter searchParams) { + QueryBuilder sb = new QueryBuilder(); + sb.append("select count(distinct collection.key)"); + appendFrom(sb); + appendWhere(sb, searchParams); + + TypedQuery<Long> query = dbInstance.getCurrentEntityManager() + .createQuery(sb.toString(), Long.class); + appendParameters(query, searchParams); + return query.getResultList().get(0); + } + + private void appendFrom(QueryBuilder sb) { + sb.append(" from qualitydatacollection collection"); + sb.append(" inner join evaluationformsurvey survey"); + sb.append(" on survey.resName = '").append(QualityDataCollectionLight.RESOURCEABLE_TYPE_NAME).append("'"); + sb.append(" and survey.resId = collection.key"); + sb.append(" left join qualitycontext context"); + sb.append(" on context.dataCollection.key = collection.key"); + sb.append(" left join contexttocurriculum contextToCurriculum"); + sb.append(" on contextToCurriculum.context.key = context.key"); + sb.append(" left join curriculum curriculum"); + sb.append(" on contextToCurriculum.curriculum.key = curriculum.key"); + sb.append(" left join contexttocurriculumelement contextToCurriculumElement"); + sb.append(" on contextToCurriculumElement.context.key = context.key"); + sb.append(" left join curriculumelement curriculumElement"); + sb.append(" on contextToCurriculumElement.curriculumElement.key = curriculumElement.key"); + sb.append(" left join contexttoorganisation contextToOrganisation"); + sb.append(" on contextToOrganisation.context.key = context.key"); + sb.append(" left join organisation organisation"); + sb.append(" on contextToOrganisation.organisation.key = organisation.key"); + } + + private void appendWhere(QueryBuilder sb, AnalysisSearchParameter searchParams) { + sb.and().append("collection.status = '").append(QualityDataCollectionStatus.FINISHED).append("'"); + if (searchParams.getFormEntryRef() != null) { + sb.and().append("survey.formEntry.key = :formEntryKey"); + } + if (searchParams.getDateRangeFrom() != null) { + sb.and().append("collection.deadline >= :dateRangeFrom"); + } + if (searchParams.getDateRangeTo() != null) { + sb.and().append("collection.deadline <= :dateRangeTo"); + } + if (searchParams.getOrganisationRefs() != null) { + sb.and(); + for (int i = 0; i < searchParams.getOrganisationRefs().size(); i++) { + if (i == 0) { + sb.append("("); + } else { + sb.append(" or "); + } + sb.append("organisation.materializedPathKeys like :orgPath").append(i); + if (i == searchParams.getOrganisationRefs().size() - 1) { + sb.append(")"); + } + } + } + if (searchParams.getCurriculumRefs() != null) { + sb.and().append("curriculum.key in :curriculumKeys"); + } + if (searchParams.getCurriculumElementRefs() != null) { + sb.and(); + for (int i = 0; i < searchParams.getCurriculumElementRefs().size(); i++) { + if (i == 0) { + sb.append("("); + } else { + sb.append(" or "); + } + sb.append("curriculumElement.materializedPathKeys like :elePath").append(i); + if (i == searchParams.getCurriculumElementRefs().size() - 1) { + sb.append(")"); + } + } + } + } + + private void appendParameters(Query query, AnalysisSearchParameter searchParams) { + if (searchParams.getFormEntryRef() != null) { + query.setParameter("formEntryKey", searchParams.getFormEntryRef().getKey()); + } + if (searchParams.getDateRangeFrom() != null) { + query.setParameter("dateRangeFrom", searchParams.getDateRangeFrom()); + } + if (searchParams.getDateRangeTo() != null) { + query.setParameter("dateRangeTo", searchParams.getDateRangeTo()); + } + if (searchParams.getOrganisationRefs() != null) { + for (int i = 0; i < searchParams.getOrganisationRefs().size(); i++) { + String parameter = new StringBuilder(12).append("orgPath").append(i).toString(); + Long key = searchParams.getOrganisationRefs().get(i).getKey(); + String value = new StringBuilder(32).append("%/").append(key).append("/%").toString(); + query.setParameter(parameter, value); + } + } + if (searchParams.getCurriculumRefs() != null) { + List<Long> curriculumKeys = searchParams.getCurriculumRefs().stream().map(CurriculumRef::getKey).collect(toList()); + query.setParameter("curriculumKeys", curriculumKeys); + } + if (searchParams.getCurriculumElementRefs() != null) { + for (int i = 0; i < searchParams.getCurriculumElementRefs().size(); i++) { + String parameter = new StringBuilder(12).append("elePath").append(i).toString(); + Long key = searchParams.getCurriculumElementRefs().get(i).getKey(); + String value = new StringBuilder(32).append("%/").append(key).append("/%").toString(); + query.setParameter(parameter, value); + } + } + } +} diff --git a/src/main/java/org/olat/modules/quality/analysis/manager/QualityAnalysisServiceImpl.java b/src/main/java/org/olat/modules/quality/analysis/manager/QualityAnalysisServiceImpl.java index d773b18f5be..90de51fa063 100644 --- a/src/main/java/org/olat/modules/quality/analysis/manager/QualityAnalysisServiceImpl.java +++ b/src/main/java/org/olat/modules/quality/analysis/manager/QualityAnalysisServiceImpl.java @@ -19,8 +19,14 @@ */ package org.olat.modules.quality.analysis.manager; +import java.util.ArrayList; import java.util.List; +import org.olat.core.id.Organisation; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.quality.analysis.AnalysisSearchParameter; import org.olat.modules.quality.analysis.EvaluationFormView; import org.olat.modules.quality.analysis.EvaluationFormViewSearchParams; import org.olat.modules.quality.analysis.QualityAnalysisService; @@ -36,11 +42,52 @@ import org.springframework.stereotype.Service; @Service public class QualityAnalysisServiceImpl implements QualityAnalysisService { + @Autowired + private AnalysisFilterDAO filterDao; @Autowired private EvaluationFormDAO evaluationFromDao; + @Autowired + private CurriculumService curriculumService; @Override public List<EvaluationFormView> loadEvaluationForms(EvaluationFormViewSearchParams searchParams) { return evaluationFromDao.load(searchParams); } + + @Override + public List<Organisation> loadFilterOrganisations(AnalysisSearchParameter searchParams) { + return filterDao.loadOrganisations(searchParams); + } + + @Override + public List<Curriculum> loadFilterCurriculums(AnalysisSearchParameter searchParams) { + return filterDao.loadCurriculums(searchParams); + } + + @Override + public List<CurriculumElement> loadFilterCurriculumElements(AnalysisSearchParameter searchParams) { + if (searchParams == null || searchParams.getCurriculumRefs() == null) { + return new ArrayList<>(0); + } + + List<CurriculumElement> elementsOfCurriculums = curriculumService + .getCurriculumElementsByCurriculums(searchParams.getCurriculumRefs()); + List<String> pathes = filterDao.loadCurriculumElementPathes(searchParams); + elementsOfCurriculums.removeIf(e -> isUnusedLeaf(e, pathes)); + return elementsOfCurriculums; + } + + private boolean isUnusedLeaf(CurriculumElement e, List<String> pathsOfContexts) { + for (String path : pathsOfContexts) { + if (path.contains(e.getMaterializedPathKeys())) { + return false; + } + } + return true; + } + + @Override + public Long loadFilterDataCollectionCount(AnalysisSearchParameter searchParams) { + return filterDao.loadFilterDataCollectionCount(searchParams); + } } diff --git a/src/main/java/org/olat/modules/quality/analysis/model/EvaluationFormViewImpl.java b/src/main/java/org/olat/modules/quality/analysis/model/EvaluationFormViewImpl.java index da93c443833..6903abd7624 100644 --- a/src/main/java/org/olat/modules/quality/analysis/model/EvaluationFormViewImpl.java +++ b/src/main/java/org/olat/modules/quality/analysis/model/EvaluationFormViewImpl.java @@ -50,6 +50,16 @@ public class EvaluationFormViewImpl implements EvaluationFormView { this.numberParticipationsDone = numberParticipationsDone; } + @Override + public String getResourceableTypeName() { + return EvaluationFormView.RESOURCEABLE_TYPE; + } + + @Override + public Long getResourceableId() { + return formEntryKey; + } + @Override public Long getFormEntryKey() { return formEntryKey; diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisController.java b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisController.java new file mode 100644 index 00000000000..331af87d5d1 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisController.java @@ -0,0 +1,102 @@ +/** + * <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.analysis.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.components.velocity.VelocityContainer; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.controller.BasicController; +import org.olat.core.gui.control.controller.BlankController; +import org.olat.modules.quality.QualitySecurityCallback; +import org.olat.modules.quality.analysis.AnalysisSearchParameter; +import org.olat.modules.quality.analysis.EvaluationFormView; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class AnalysisController extends BasicController { + + enum Presentation {REPORT, HEAT_MAP}; + + private VelocityContainer mainVC; + private Controller filterCtrl; + private Controller presentationCtrl; + private final QualitySecurityCallback secCallback; + private final TooledStackedPanel stackPanel; + + private final EvaluationFormView formView; + + protected AnalysisController(UserRequest ureq, WindowControl wControl, QualitySecurityCallback secCallback, + TooledStackedPanel stackPanel, EvaluationFormView formView) { + super(ureq, wControl); + this.secCallback = secCallback; + this.stackPanel = stackPanel; + this.formView = formView; + mainVC = createVelocityContainer("analysis"); + putInitialPanel(mainVC); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + searchParams.setFormEntryRef(() -> formView.getFormEntryKey()); + filterCtrl= new FilterController(ureq, wControl, searchParams); + listenTo(filterCtrl); + mainVC.put("filter", filterCtrl.getInitialComponent()); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + //TODO uh if click on breadcrumb shows blankcontroller + } + + @Override + protected void doDispose() { + removeAsListenerAndDispose(presentationCtrl); + removeAsListenerAndDispose(filterCtrl); + presentationCtrl = null; + filterCtrl = null; + } + + public void setPresentation(UserRequest ureq, Presentation presentation) { + removeAsListenerAndDispose(presentationCtrl); + presentationCtrl = null; + + switch (presentation) { + case REPORT: + presentationCtrl = new AnalysisReportController(ureq, getWindowControl(), secCallback, stackPanel); + break; + case HEAT_MAP: + presentationCtrl = new HeatMapController(ureq, getWindowControl(), secCallback, stackPanel); + break; + default: + presentationCtrl = new BlankController(ureq, getWindowControl()); + break; + } + listenTo(presentationCtrl); + mainVC.put("presentation", presentationCtrl.getInitialComponent()); + mainVC.setDirty(true); + } + +} diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisListController.java b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisListController.java index 2ca9cefa3c1..ab93facb03a 100644 --- a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisListController.java +++ b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisListController.java @@ -27,10 +27,12 @@ import org.olat.basesecurity.OrganisationRoles; import org.olat.basesecurity.OrganisationService; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.form.flexible.FormItem; import org.olat.core.gui.components.form.flexible.FormItemContainer; import org.olat.core.gui.components.form.flexible.elements.FlexiTableElement; import org.olat.core.gui.components.form.flexible.elements.FormLink; 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.elements.table.DefaultFlexiColumnModel; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableCssDelegate; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel; @@ -38,6 +40,7 @@ import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTable import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponentDelegate; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableDataModelFactory; import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableRendererType; +import org.olat.core.gui.components.form.flexible.impl.elements.table.SelectionEvent; import org.olat.core.gui.components.link.Link; import org.olat.core.gui.components.stack.TooledStackedPanel; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -75,6 +78,7 @@ public class AnalysisListController extends FormBasicController implements Flexi private QualityAnalysisService analysisService; @Autowired private OrganisationService organisationService; + private AnalysisSegmentsController analysisCtrl; public AnalysisListController(UserRequest ureq, WindowControl wControl, TooledStackedPanel stackPanel, QualitySecurityCallback secCallback) { @@ -137,7 +141,7 @@ public class AnalysisListController extends FormBasicController implements Flexi private AnalysisRow forgeRow(EvaluationFormView form) { String openLinkId = "open_" + (++counter); - FormLink openLink = uifactory.addFormLink(openLinkId, "analysis.table.open", "analysis.table.open", null, flc, Link.LINK); + FormLink openLink = uifactory.addFormLink(openLinkId, CMD_OPEN, "analysis.table.open", null, flc, Link.LINK); openLink.setElementCssClass("o_qual_ana_open_link"); openLink.setIconRightCSS("o_icon o_icon_start"); @@ -155,6 +159,34 @@ public class AnalysisListController extends FormBasicController implements Flexi } return components; } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == tableEl && event instanceof SelectionEvent) { + SelectionEvent se = (SelectionEvent)event; + String cmd = se.getCommand(); + AnalysisRow row = dataModel.getObject(se.getIndex()); + if (CMD_OPEN.equals(cmd)) { + doOpenAnalysis(ureq, row); + } + } else if (source instanceof FormLink) { + FormLink link = (FormLink)source; + if(CMD_OPEN.equals(link.getCmd())) { + doOpenAnalysis(ureq, (AnalysisRow)link.getUserObject()); + } + } + + super.formInnerEvent(ureq, source, event); + } + + private void doOpenAnalysis(UserRequest ureq, EvaluationFormView formView) { + WindowControl bwControl = addToHistory(ureq, formView, null); + analysisCtrl = new AnalysisSegmentsController(ureq, bwControl, secCallback, stackPanel, formView); + listenTo(analysisCtrl); + String title = formView.getFormTitle(); + stackPanel.pushController(title, analysisCtrl); + analysisCtrl.activate(ureq, null, null); + } @Override protected void formOK(UserRequest ureq) { diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisReportController.java b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisReportController.java new file mode 100644 index 00000000000..12f0b1f5822 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisReportController.java @@ -0,0 +1,72 @@ +/** + * <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.analysis.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.modules.quality.QualitySecurityCallback; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class AnalysisReportController extends FormBasicController { + + private final QualitySecurityCallback secCallback; + private final TooledStackedPanel stackPanel; + + public AnalysisReportController(UserRequest ureq, WindowControl wControl, QualitySecurityCallback secCallback, + TooledStackedPanel stackPanel) { + super(ureq, wControl); + this.secCallback = secCallback; + this.stackPanel = stackPanel; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + + uifactory.addStaticExampleText("re3", "", "Report, report, report,..", formLayout); + + } + + @Override + protected void event(UserRequest ureq, Controller source, Event event) { + super.event(ureq, source, event); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + @Override + protected void doDispose() { + // + } + +} diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisRow.java b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisRow.java index c6545ae496c..e656fa7b0f1 100644 --- a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisRow.java +++ b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisRow.java @@ -30,7 +30,7 @@ import org.olat.modules.quality.analysis.EvaluationFormView; * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com * */ -public class AnalysisRow { +public class AnalysisRow implements EvaluationFormView { private final EvaluationFormView formView; private final FormLink openLink; @@ -39,31 +39,48 @@ public class AnalysisRow { this.formView = formView; this.openLink = openLink; } + + @Override + public String getResourceableTypeName() { + return formView.getResourceableTypeName(); + } + + @Override + public Long getResourceableId() { + return formView.getResourceableId(); + } + @Override public Long getFormEntryKey() { return formView.getFormEntryKey(); } + @Override public Date getFormCreatedDate() { return formView.getFormCreatedDate(); } + @Override public String getFormTitle() { return formView.getFormTitle(); } + @Override public Long getNumberDataCollections() { return formView.getNumberDataCollections(); } + @Override public Date getSoonestDataCollectionDate() { return formView.getSoonestDataCollectionDate(); } + @Override public Date getLatestDataCollectionDate() { return formView.getLatestDataCollectionDate(); } + @Override public Long getNumberParticipationsDone() { return formView.getNumberParticipationsDone(); } diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisSegmentsController.java b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisSegmentsController.java new file mode 100644 index 00000000000..2f690f646ab --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/AnalysisSegmentsController.java @@ -0,0 +1,138 @@ +/** + * <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.analysis.ui; + +import java.util.List; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.Component; +import org.olat.core.gui.components.link.Link; +import org.olat.core.gui.components.link.LinkFactory; +import org.olat.core.gui.components.panel.Panel; +import org.olat.core.gui.components.panel.SimpleStackedPanel; +import org.olat.core.gui.components.panel.StackedPanel; +import org.olat.core.gui.components.stack.ButtonGroupComponent; +import org.olat.core.gui.components.stack.PopEvent; +import org.olat.core.gui.components.stack.TooledController; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.Event; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.gui.control.controller.BasicController; +import org.olat.core.gui.control.generic.dtabs.Activateable2; +import org.olat.core.id.context.ContextEntry; +import org.olat.core.id.context.StateEntry; +import org.olat.modules.quality.QualitySecurityCallback; +import org.olat.modules.quality.analysis.EvaluationFormView; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class AnalysisSegmentsController extends BasicController implements TooledController, Activateable2 { + + private final TooledStackedPanel stackPanel; + private final StackedPanel mainPanel; + private final ButtonGroupComponent segmentButtonsCmp; + private Link reportLink; + private Link heatMapLink; + + private AnalysisController analysisCtrl; + + private final QualitySecurityCallback secCallback; + private final EvaluationFormView formView; + + //TODO uh Wo wird das StackPanel und das SecCallback benätigt + + public AnalysisSegmentsController(UserRequest ureq, WindowControl wControl, QualitySecurityCallback secCallback, + TooledStackedPanel stackPanel, EvaluationFormView formView) { + super(ureq, wControl); + this.secCallback = secCallback; + this.stackPanel = stackPanel; + stackPanel.addListener(this); + this.formView = formView; + + segmentButtonsCmp = new ButtonGroupComponent("segments"); + reportLink = LinkFactory.createLink("segments.report.link", getTranslator(), this); + segmentButtonsCmp.addButton(reportLink, false); + heatMapLink = LinkFactory.createLink("segments.heatmap.link", getTranslator(), this); + segmentButtonsCmp.addButton(heatMapLink, false); + + mainPanel = putInitialPanel(new SimpleStackedPanel("analysisSegments")); + mainPanel.setContent(new Panel("empty")); + } + + @Override + public void activate(UserRequest ureq, List<ContextEntry> entries, StateEntry state) { + doOpenReport(ureq); + } + + @Override + public void initTools() { + stackPanel.addTool(segmentButtonsCmp, true); + } + + @Override + protected void event(UserRequest ureq, Component source, Event event) { + if (reportLink == source) { + doOpenReport(ureq); + } else if(heatMapLink == source) { + doOpenHeatMap(ureq); + } else if (stackPanel == source && stackPanel.getLastController() == this && event instanceof PopEvent) { + PopEvent popEvent = (PopEvent) event; + if (popEvent.isClose()) { + stackPanel.popController(this); + } else { + doOpenReport(ureq); + } + } + } + + private void doOpenAnalysis(UserRequest ureq) { + if (analysisCtrl == null) { + analysisCtrl = new AnalysisController(ureq, getWindowControl(), secCallback, stackPanel, formView); + listenTo(analysisCtrl); + stackPanel.pushController("segments.report.breadcrumb", analysisCtrl); + } + } + + private void doOpenReport(UserRequest ureq) { + doOpenAnalysis(ureq); + analysisCtrl.setPresentation(ureq, AnalysisController.Presentation.REPORT); + stackPanel.changeDisplayname(translate("segments.report.breadcrumb")); + segmentButtonsCmp.setSelectedButton(reportLink); + } + + private void doOpenHeatMap(UserRequest ureq) { + doOpenAnalysis(ureq); + analysisCtrl.setPresentation(ureq, AnalysisController.Presentation.HEAT_MAP); + stackPanel.changeDisplayname(translate("segments.heatmap.breadcrumb")); + segmentButtonsCmp.setSelectedButton(heatMapLink); + } + + @Override + protected void doDispose() { + if(stackPanel != null) { + stackPanel.removeListener(this); + } + } + +} diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/FilterController.java b/src/main/java/org/olat/modules/quality/analysis/ui/FilterController.java new file mode 100644 index 00000000000..d203e0abbcd --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/FilterController.java @@ -0,0 +1,269 @@ +/** + * <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.analysis.ui; + +import static java.util.stream.Collectors.toList; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import org.olat.basesecurity.OrganisationModule; +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItem; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.elements.DateChooser; +import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement; +import org.olat.core.gui.components.form.flexible.elements.StaticTextElement; +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.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.core.id.Organisation; +import org.olat.core.id.OrganisationRef; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumElementRef; +import org.olat.modules.curriculum.CurriculumModule; +import org.olat.modules.curriculum.CurriculumRef; +import org.olat.modules.curriculum.ui.CurriculumTreeModel; +import org.olat.modules.quality.analysis.AnalysisSearchParameter; +import org.olat.modules.quality.analysis.QualityAnalysisService; +import org.olat.modules.quality.ui.QualityUIFactory; +import org.olat.modules.quality.ui.QualityUIFactory.KeysValues; +import org.olat.user.ui.organisation.OrganisationTreeModel; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class FilterController extends FormBasicController { + + private DateChooser dateRangeFromEl; + private DateChooser dateRangeToEl; + private MultipleSelectionElement organisationEl; + private MultipleSelectionElement curriculumEl; + private MultipleSelectionElement curriculumElementEl; + private StaticTextElement countFilteredEl; + + private final AnalysisSearchParameter searchParams; + + @Autowired + private QualityAnalysisService analysisService; + @Autowired + private OrganisationModule organisationModule; + @Autowired + private CurriculumModule curriculumModule; + + public FilterController(UserRequest ureq, WindowControl wControl, AnalysisSearchParameter searchParams) { + super(ureq, wControl); + this.searchParams = searchParams; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + dateRangeFromEl = uifactory.addDateChooser("filter.date.range.from", null, formLayout); + dateRangeFromEl.addActionListener(FormEvent.ONCHANGE); + + dateRangeToEl = uifactory.addDateChooser("filter.date.range.to", null, formLayout); + dateRangeToEl.addActionListener(FormEvent.ONCHANGE); + + organisationEl = uifactory.addCheckboxesDropdown("filter.organisations", formLayout); + organisationEl.addActionListener(FormEvent.ONCLICK); + + curriculumEl = uifactory.addCheckboxesDropdown("filter.curriculums", formLayout); + curriculumEl.addActionListener(FormEvent.ONCLICK); + + curriculumElementEl = uifactory.addCheckboxesDropdown("filter.curriculum.elements", formLayout); + curriculumElementEl.addActionListener(FormEvent.ONCLICK); + + countFilteredEl = uifactory.addStaticTextElement("filter.count", "", formLayout); + + setSelectionValues(); + } + + private void setSelectionValues() { + setOrganisationValues(); + setCurriculumValues(); + setCurriculumElementValues(); + setCountFiltered(); + } + + private void setOrganisationValues() { + if (!organisationModule.isEnabled()) { + organisationEl.setVisible(false); + return; + } + + Collection<String> selectedKeys = organisationEl.getSelectedKeys(); + + AnalysisSearchParameter orgSearchParams = new AnalysisSearchParameter(); + orgSearchParams.setFormEntryRef(searchParams.getFormEntryRef()); + List<Organisation> organisations = analysisService.loadFilterOrganisations(orgSearchParams); + OrganisationTreeModel organisationModel = new OrganisationTreeModel(); + organisationModel.loadTreeModel(organisations); + + KeysValues keysValues = QualityUIFactory.getTopicOrganisationKeysValues(organisationModel, null); + organisationEl.setKeysAndValues(keysValues.getKeys(), keysValues.getValues()); + for (String key: selectedKeys) { + organisationEl.select(key, true); + } + } + + private void setCurriculumValues() { + if (!curriculumModule.isEnabled()) { + curriculumEl.setVisible(false); + return; + } + + Collection<String> selectedKeys = curriculumEl.getSelectedKeys(); + + AnalysisSearchParameter curriculumSearchParams = searchParams.clone(); + curriculumSearchParams.setCurriculumRefs(null); + curriculumSearchParams.setCurriculumElementRefs(null); + List<Curriculum> curriculums = analysisService.loadFilterCurriculums(curriculumSearchParams); + KeysValues keysValues = QualityUIFactory.getCurriculumKeysValues(curriculums, null); + curriculumEl.setKeysAndValues(keysValues.getKeys(), keysValues.getValues()); + for (String key: selectedKeys) { + curriculumEl.select(key, true); + } + } + + private void setCurriculumElementValues() { + if (!curriculumModule.isEnabled()) { + curriculumElementEl.setVisible(false); + return; + } + + Collection<String> selectedKeys = curriculumEl.getSelectedKeys(); + + AnalysisSearchParameter curriculumElementSearchParams = searchParams.clone(); + Collection<String> curriculumKeys = curriculumEl.isAtLeastSelected(1) + ? curriculumEl.getSelectedKeys() + : curriculumEl.getKeys(); + List<? extends CurriculumRef> curriculumRefs = curriculumKeys.stream() + .map(key -> QualityUIFactory.getCurriculumRef(key)) + .collect(toList()); + curriculumElementSearchParams.setCurriculumRefs(curriculumRefs); + curriculumElementSearchParams.setCurriculumElementRefs(null); + List<CurriculumElement> curriculumElements = analysisService.loadFilterCurriculumElements(curriculumElementSearchParams); + + CurriculumTreeModel curriculumTreeModel = new CurriculumTreeModel(); + curriculumTreeModel.loadTreeModel(curriculumElements); + KeysValues keysValues = QualityUIFactory.getCurriculumElementKeysValues(curriculumTreeModel, null); + curriculumElementEl.setKeysAndValues(keysValues.getKeys(), keysValues.getValues()); + for (String key: selectedKeys) { + curriculumElementEl.select(key, true); + } + } + + private void setCountFiltered() { + Long count = analysisService.loadFilterDataCollectionCount(searchParams); + countFilteredEl.setValue(count.toString()); + } + + @Override + protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) { + if (source == dateRangeFromEl) { + doFiltered(); + } else if (source == dateRangeToEl) { + doFiltered(); + } else if (source == organisationEl) { + doFiltered(); + } else if (source == curriculumEl) { + doFiltered(); + } else if (source == curriculumElementEl) { + doFiltered(); + } + super.formInnerEvent(ureq, source, event); + } + + private void doFiltered() { + getSearchParams(); + setSelectionValues(); + //TODO uh filter presentation -> fireEvent + } + + private void getSearchParams() { + getSearchParamOrganisations(); + getSearchParamCurriculums(); + getSearchParamCurriculumElements(); + getSearchParamDateRangeFrom(); + getSearchParamDateRangeTo(); + } + + private void getSearchParamOrganisations() { + if (organisationModule.isEnabled() && organisationEl.isAtLeastSelected(1)) { + List<OrganisationRef> organisationRefs = organisationEl.getSelectedKeys().stream() + .map(key -> QualityUIFactory.getOrganisationRef(key)) + .collect(toList()); + searchParams.setOrganisationRefs(organisationRefs); + } else { + searchParams.setOrganisationRefs(null); + } + } + + private void getSearchParamCurriculums() { + if (curriculumEl.isEnabled() && curriculumEl.isAtLeastSelected(1)) { + Collection<CurriculumRef> curriculumRefs = curriculumEl.getSelectedKeys().stream() + .map(key -> QualityUIFactory.getCurriculumRef(key)) + .collect(toList()); + searchParams.setCurriculumRefs(curriculumRefs); + } else { + searchParams.setCurriculumRefs(null); + } + } + + private void getSearchParamCurriculumElements() { + if (curriculumEl.isEnabled() && curriculumElementEl.isAtLeastSelected(1)) { + List<CurriculumElementRef> curriculumElementRefs = curriculumElementEl.getSelectedKeys().stream() + .map(key -> QualityUIFactory.getCurriculumElementRef(key)) + .collect(toList()); + searchParams.setCurriculumElementRefs(curriculumElementRefs); + } else { + searchParams.setCurriculumElementRefs(null); + } + } + + private void getSearchParamDateRangeFrom() { + Date dateRangeFrom = dateRangeFromEl.getDate(); + searchParams.setDateRangeFrom(dateRangeFrom); + } + + private void getSearchParamDateRangeTo() { + Date dateRangeTo = dateRangeToEl.getDate(); + searchParams.setDateRangeTo(dateRangeTo); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + @Override + protected void doDispose() { + // + } + +} 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 new file mode 100644 index 00000000000..3d1da8e73b2 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/HeatMapController.java @@ -0,0 +1,64 @@ +/** + * <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.analysis.ui; + +import org.olat.core.gui.UserRequest; +import org.olat.core.gui.components.form.flexible.FormItemContainer; +import org.olat.core.gui.components.form.flexible.impl.FormBasicController; +import org.olat.core.gui.components.stack.TooledStackedPanel; +import org.olat.core.gui.control.Controller; +import org.olat.core.gui.control.WindowControl; +import org.olat.modules.quality.QualitySecurityCallback; + +/** + * + * Initial date: 04.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class HeatMapController extends FormBasicController { + + private final QualitySecurityCallback secCallback; + private final TooledStackedPanel stackPanel; + + public HeatMapController(UserRequest ureq, WindowControl wControl, QualitySecurityCallback secCallback, + TooledStackedPanel stackPanel) { + super(ureq, wControl); + this.secCallback = secCallback; + this.stackPanel = stackPanel; + initForm(ureq); + } + + @Override + protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) { + uifactory.addStaticExampleText("hm", "", "HEAT MAP", formLayout); + } + + @Override + protected void formOK(UserRequest ureq) { + // + } + + @Override + protected void doDispose() { + // + } + +} diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/_content/analysis.html b/src/main/java/org/olat/modules/quality/analysis/ui/_content/analysis.html new file mode 100644 index 00000000000..6c8d15d7780 --- /dev/null +++ b/src/main/java/org/olat/modules/quality/analysis/ui/_content/analysis.html @@ -0,0 +1,4 @@ +$r.render("filter") +#if($r.available("presentation")) + $r.render("presentation") +#end diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_de.properties index f01be9fc3f3..d3e2f0e2bd8 100644 --- a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_de.properties @@ -2,8 +2,18 @@ analysis.table.data.collections.latest=Letzte Datenerhebung analysis.table.data.collections.number=Datenerhebungen analysis.table.data.collections.soonest=Erste Datenerhebung analysis.table.empty=Diese Tabelle enth\u00e4hlt keine Daten. -analysis.table.form.created=Erstellt analysis.table.form.created.on=Erstellt am {0} +analysis.table.form.created=Erstellt analysis.table.form.title=Fragebogen -analysis.table.participations.number=Teilnahmen analysis.table.open=\u00d6ffnen +analysis.table.participations.number=Teilnahmen +filter.count=Anzahl Datenerhebungen +filter.curriculum.elements=Curriculumelement +filter.curriculums=Curriculum +filter.date.range.from=Datenerhebungen von +filter.date.range.to=Datenerhebungen to +filter.organisations=Organisationen +segments.heatmap.breadcrumb=Heatmap +segments.heatmap.link=Heatmap +segments.report.breadcrumb=Report +segments.report.link=Report diff --git a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_en.properties index 6977d515fd4..a4ee3901cae 100644 --- a/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/modules/quality/analysis/ui/_i18n/LocalStrings_en.properties @@ -2,8 +2,18 @@ analysis.table.data.collections.latest=Latest data collection analysis.table.data.collections.number=Data collections analysis.table.data.collections.soonest=Soonest data collection analysis.table.empty=This table contains no data. -analysis.table.form.created=Created analysis.table.form.created.on=Created on {0} +analysis.table.form.created=Created analysis.table.form.title=Questionnaire -analysis.table.participations.number=Participations analysis.table.open=Open +analysis.table.participations.number=Participations +filter.count=Number of data collections +filter.curriculum.elements=Curriculumelement +filter.curriculums=Curriculum +filter.date.range.from=Data collections from +filter.date.range.to=Data collections to +filter.organisations=Organisations +segments.heatmap.breadcrumb=Heat map +segments.heatmap.link=Heat map +segments.report.breadcrumb=Report +segments.report.link=Report diff --git a/src/main/java/org/olat/modules/quality/ui/QualityUIFactory.java b/src/main/java/org/olat/modules/quality/ui/QualityUIFactory.java index edd8814368a..885ead38edf 100644 --- a/src/main/java/org/olat/modules/quality/ui/QualityUIFactory.java +++ b/src/main/java/org/olat/modules/quality/ui/QualityUIFactory.java @@ -153,13 +153,7 @@ public class QualityUIFactory { if (StringHelper.containsNonWhitespace(curriculumKey)) { try { Long key = Long.valueOf(curriculumKey); - return new CurriculumRef() { - - @Override - public Long getKey() { - return key; - } - }; + return () -> key; } catch (Exception e) { // } @@ -214,13 +208,7 @@ public class QualityUIFactory { if (StringHelper.containsNonWhitespace(curriculumElementKey)) { try { Long key = Long.valueOf(curriculumElementKey); - return new CurriculumElementRef() { - - @Override - public Long getKey() { - return key; - } - }; + return () -> key; } catch (Exception e) { // } @@ -271,17 +259,11 @@ public class QualityUIFactory { return String.valueOf(organisation.getKey()); } - static OrganisationRef getOrganisationRef(String organisationKey) { + public static OrganisationRef getOrganisationRef(String organisationKey) { if (StringHelper.containsNonWhitespace(organisationKey)) { try { Long key = Long.valueOf(organisationKey); - return new OrganisationRef() { - - @Override - public Long getKey() { - return key; - } - }; + return () -> key; } catch (Exception e) { // } diff --git a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java index 50d86545b77..3da0c2de367 100644 --- a/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java +++ b/src/test/java/org/olat/modules/curriculum/manager/CurriculumElementDAOTest.java @@ -19,7 +19,10 @@ */ package org.olat.modules.curriculum.manager; +import static org.assertj.core.api.Assertions.assertThat; + import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; @@ -34,6 +37,7 @@ import org.olat.modules.curriculum.CurriculumElement; import org.olat.modules.curriculum.CurriculumElementMembership; import org.olat.modules.curriculum.CurriculumElementStatus; import org.olat.modules.curriculum.CurriculumElementType; +import org.olat.modules.curriculum.CurriculumRef; import org.olat.modules.curriculum.CurriculumRoles; import org.olat.modules.curriculum.CurriculumService; import org.olat.modules.curriculum.model.CurriculumElementImpl; @@ -172,6 +176,26 @@ public class CurriculumElementDAOTest extends OlatTestCase { Assert.assertEquals(1, relations.size()); Assert.assertEquals(element, relations.get(0)); } + + @Test + public void loadElementsByCurriculums() { + Curriculum curriculum1 = curriculumDao.createAndPersist("", "", null, null); + CurriculumElement parentElement = curriculumElementDao.createCurriculumElement("", "", null, null, null, null, curriculum1); + CurriculumElement element1 = curriculumElementDao.createCurriculumElement("", "", null, null, parentElement, null, curriculum1); + CurriculumElement element2 = curriculumElementDao.createCurriculumElement("", "", null, null, parentElement, null, curriculum1); + Curriculum curriculum2 = curriculumDao.createAndPersist("", "", null, null); + CurriculumElement parentElement2 = curriculumElementDao.createCurriculumElement("", "", null, null, null, null, curriculum2); + Curriculum otherCurriculum = curriculumDao.createAndPersist("", "", null, null); + CurriculumElement otherElement = curriculumElementDao.createCurriculumElement("", "", null, null, null, null, otherCurriculum); + dbInstance.commitAndCloseSession(); + + Collection<CurriculumRef> curriculumRefs = Arrays.asList(curriculum1, curriculum2); + List<CurriculumElement> elements = curriculumElementDao.loadElementsByCurriculums(curriculumRefs); + + assertThat(elements) + .containsExactlyInAnyOrder(parentElement, element1, element2, parentElement2) + .doesNotContain(otherElement); + } @Test public void createCurriculumElementParentChildren() { 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 new file mode 100644 index 00000000000..9d9c52c32e3 --- /dev/null +++ b/src/test/java/org/olat/modules/quality/analysis/manager/AnalysisFilterDAOTest.java @@ -0,0 +1,427 @@ +/** + * <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.analysis.manager; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.olat.basesecurity.OrganisationService; +import org.olat.core.commons.persistence.DB; +import org.olat.core.id.Identity; +import org.olat.core.id.Organisation; +import org.olat.modules.curriculum.Curriculum; +import org.olat.modules.curriculum.CurriculumElement; +import org.olat.modules.curriculum.CurriculumService; +import org.olat.modules.forms.EvaluationFormParticipation; +import org.olat.modules.quality.QualityContextBuilder; +import org.olat.modules.quality.QualityDataCollection; +import org.olat.modules.quality.QualityDataCollectionStatus; +import org.olat.modules.quality.QualityService; +import org.olat.modules.quality.analysis.AnalysisSearchParameter; +import org.olat.modules.quality.manager.QualityTestHelper; +import org.olat.repository.RepositoryEntry; +import org.olat.test.JunitTestHelper; +import org.olat.test.OlatTestCase; +import org.springframework.beans.factory.annotation.Autowired; + + +/** + * + * Initial date: 05.09.2018<br> + * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com + * + */ +public class AnalysisFilterDAOTest extends OlatTestCase { + + @Autowired + private DB dbInstance; + @Autowired + private QualityTestHelper qualityTestHelper; + @Autowired + private QualityService qualityService; + @Autowired + private OrganisationService organisationService; + @Autowired + private CurriculumService curriculumService; + + @Autowired + private AnalysisFilterDAO sut; + + @Before + public void cleanUp() { + qualityTestHelper.deleteAll(); + } + + @Test + public void shouldLoadDistinctOrganisations() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Identity executor = JunitTestHelper.createAndPersistIdentityAsUser(""); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + Organisation organisation1 = qualityTestHelper.createOrganisation(); + Organisation organisation2 = 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.addOrganisation(organisation1).addOrganisation(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.addOrganisation(organisation2).build(); + // Participation without organisation + QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityService.addParticipations(dcNull, Collections.singletonList(executor)); + finish(asList(dc1, dc2, dcNull)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + List<Organisation> filtered = sut.loadOrganisations(searchParams); + + assertThat(filtered) + .containsExactlyInAnyOrder(organisation1, organisation2) + .doesNotContainNull(); + } + + @Test + public void shouldLoadDistinctCurriculum() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Identity executor = JunitTestHelper.createAndPersistIdentityAsUser(""); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + Curriculum curriculum1 = qualityTestHelper.createCurriculum(); + Curriculum curriculum2 = qualityTestHelper.createCurriculum(); + // 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.addCurriculum(curriculum1).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.addCurriculum(curriculum2).build(); + // Second participation with curriculum (to test distinct) + QualityDataCollection dcDistinct = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + List<EvaluationFormParticipation> participationsDistinct = qualityService.addParticipations(dcDistinct, Collections.singletonList(executor)); + QualityContextBuilder contextBuilderDistinct = qualityService.createContextBuilder(dcDistinct, participationsDistinct.get(0)); + contextBuilderDistinct.addCurriculum(curriculum1).build(); + // Participation without curriculum (to test no nulls) + QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityService.addParticipations(dcNull, Collections.singletonList(executor)); + finish(asList(dc1, dc2, dcDistinct, dcNull)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + List<Curriculum> filtered = sut.loadCurriculums(searchParams); + + assertThat(filtered) + .containsExactlyInAnyOrder(curriculum1, curriculum2) + .doesNotContainNull(); + } + + @Test + public void shouldLoadDistinctCurriculumElementPathes() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Identity executor = JunitTestHelper.createAndPersistIdentityAsUser(""); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + CurriculumElement element1 = qualityTestHelper.createCurriculumElement(); + CurriculumElement element2 = 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(); + // Second participation with curriculum element (to test distinct) + QualityDataCollection dcDistinct = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + List<EvaluationFormParticipation> participationsDistinct = qualityService.addParticipations(dcDistinct, Collections.singletonList(executor)); + QualityContextBuilder contextBuilderDistinct = qualityService.createContextBuilder(dcDistinct, participationsDistinct.get(0)); + contextBuilderDistinct.addCurriculumElement(element1).build(); + // Participation without curriculum (to test no nulls) + QualityDataCollection dcNull = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityService.addParticipations(dcNull, Collections.singletonList(executor)); + finish(asList(dc1, dc2, dcDistinct, dcNull)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + List<String> filtered = sut.loadCurriculumElementPathes(searchParams); + + assertThat(filtered) + .containsExactlyInAnyOrder(element1.getMaterializedPathKeys(), element2.getMaterializedPathKeys()) + .doesNotContainNull(); + } + + @Test + public void shouldLoadDataCollectionCount() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Identity executor1 = JunitTestHelper.createAndPersistIdentityAsUser(""); + Identity executor2 = JunitTestHelper.createAndPersistIdentityAsUser(""); + Identity executor3 = JunitTestHelper.createAndPersistIdentityAsUser(""); + Organisation dcOrganisation = organisationService.createOrganisation("", "", null, null, null); + // Data collection with three participations + QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityService.addParticipations(dc1, asList(executor1, executor2, executor3)); + // Another data collection with three participations + QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityService.addParticipations(dc2, asList(executor1, executor2, executor3)); + // Data collection without participation + QualityDataCollection dcWithout = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + finish(asList(dc1, dc2, dcWithout)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(3); + } + + @Test + public void shouldFilterByFinishedDataCollections() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + QualityDataCollection dcFinished1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityTestHelper.updateStatus(dcFinished1, QualityDataCollectionStatus.FINISHED); + QualityDataCollection dcFinishe2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityTestHelper.updateStatus(dcFinishe2, QualityDataCollectionStatus.FINISHED); + QualityDataCollection dcRunning = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + qualityTestHelper.updateStatus(dcRunning, QualityDataCollectionStatus.RUNNING); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(2); + } + + @Test + public void shouldFilterByFormEntry() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + RepositoryEntry otherFormEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + QualityDataCollection dc3 = qualityService.createDataCollection(asList(dcOrganisation), otherFormEntry); + finish(asList(dc1, dc2, dc3)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + searchParams.setFormEntryRef(formEntry); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(2); + } + + @Test + public void shouldFilterByDateRangeFrom() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + Date now = new Date(); + QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dc1.setDeadline(addDays(now, 1)); + qualityService.updateDataCollection(dc1); + QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dc2.setDeadline(addDays(now, 20)); + qualityService.updateDataCollection(dc2); + QualityDataCollection dcToEarly = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dcToEarly.setDeadline(addDays(now, -2)); + qualityService.updateDataCollection(dcToEarly); + finish(asList(dc1, dc2, dcToEarly)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + searchParams.setDateRangeFrom(now); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(2); + } + + @Test + public void shouldFilterByDateRangeTo() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + Date now = new Date(); + QualityDataCollection dc1 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dc1.setDeadline(addDays(now, -1)); + qualityService.updateDataCollection(dc1); + QualityDataCollection dc2 = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dc2.setDeadline(addDays(now, -20)); + qualityService.updateDataCollection(dc2); + QualityDataCollection dcToEarly = qualityService.createDataCollection(asList(dcOrganisation), formEntry); + dcToEarly.setDeadline(addDays(now, 2)); + qualityService.updateDataCollection(dcToEarly); + finish(asList(dc1, dc2, dcToEarly)); + dbInstance.commitAndCloseSession(); + + AnalysisSearchParameter searchParams = new AnalysisSearchParameter(); + searchParams.setDateRangeTo(now); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(2); + } + + @Test + public void shouldFilterByCurriculums() { + RepositoryEntry formEntry = JunitTestHelper.createAndPersistRepositoryEntry(); + Identity executor = JunitTestHelper.createAndPersistIdentityAsUser(""); + Organisation dcOrganisation = qualityTestHelper.createOrganisation(); + Curriculum curriculum1 = qualityTestHelper.createCurriculum(); + Curriculum curriculum2 = qualityTestHelper.createCurriculum(); + Curriculum otherCurriculum = qualityTestHelper.createCurriculum(); + // 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.addCurriculum(curriculum1).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.addCurriculum(curriculum2).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.addCurriculum(otherCurriculum).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.setCurriculumRefs(asList(curriculum1, curriculum2)); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + assertThat(count).isEqualTo(2); + } + + @Test + public void shouldFilterByCurriculumElements() { + 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, 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.setCurriculumElementRefs(asList(element1, element2)); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + long expected = asList(element1, element2, subElement).size(); + assertThat(count).isEqualTo(expected); + } + + @Test + public void shouldFilterByOrganisations() { + 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.addOrganisation(organisation1).addOrganisation(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.addOrganisation(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.addOrganisation(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.addOrganisation(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.setOrganisationRefs(asList(organisation1, organisation2)); + Long count = sut.loadFilterDataCollectionCount(searchParams); + + long expected = asList(organisation1, organisation2, subOrganisation).size(); + assertThat(count).isEqualTo(expected); + } + + private void finish(Collection<QualityDataCollection> dataCollections) { + for (QualityDataCollection dataCollection: dataCollections) { + qualityTestHelper.updateStatus(dataCollection, QualityDataCollectionStatus.FINISHED); + } + } + + private static Date addDays(Date date, int days) { + Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(Calendar.DATE, days); + return c.getTime(); + } + +} diff --git a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java index 83eb156fe49..8dd9a8b9d96 100644 --- a/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java +++ b/src/test/java/org/olat/modules/quality/manager/QualityTestHelper.java @@ -215,11 +215,11 @@ public class QualityTestHelper { return organisationService.createOrganisation(UUID.randomUUID().toString(), UUID.randomUUID().toString(), null, null, null); } - Curriculum createCurriculum() { + public Curriculum createCurriculum() { return curriculumService.createCurriculum("i", "d", "d", createOrganisation()); } - CurriculumElement createCurriculumElement() { + public CurriculumElement createCurriculumElement() { return curriculumService.createCurriculumElement("i", "d", null, null, null, null, createCurriculum()); } diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java index 53b34352d2a..807ae74f876 100644 --- a/src/test/java/org/olat/test/AllTestsJunit4.java +++ b/src/test/java/org/olat/test/AllTestsJunit4.java @@ -217,6 +217,7 @@ import org.junit.runners.Suite; org.olat.modules.portfolio.manager.SharedWithMeQueriesTest.class, org.olat.modules.portfolio.manager.PortfolioServiceTest.class, org.olat.modules.portfolio.manager.BinderUserInformationsDAOTest.class, + org.olat.modules.quality.analysis.manager.AnalysisFilterDAOTest.class, org.olat.modules.quality.analysis.manager.EvaluationFormDAOTest.class, org.olat.modules.quality.generator.manager.QualityGeneratorDAOTest.class, org.olat.modules.quality.generator.manager.QualityGeneratorConfigDAOTest.class, -- GitLab