diff --git a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java index 38ea1053917df3daa245665f7c0d470f1c376bc0..644b3d5216ba07934d3b898777d1affbeff784cb 100644 --- a/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java +++ b/src/main/java/org/olat/course/assessment/ui/tool/AssessmentCourseTreeController.java @@ -123,6 +123,7 @@ public class AssessmentCourseTreeController extends BasicController implements A view = View.users; } doSelectCourseNode(ureq, rootNode, (CourseNode)rootNode.getUserObject()); + menuTree.setSelectedNode(rootNode); } } else { ContextEntry entry = entries.get(0); @@ -136,6 +137,7 @@ public class AssessmentCourseTreeController extends BasicController implements A if(ctrl instanceof Activateable2) { ((Activateable2)ctrl).activate(ureq, entries, null); } + menuTree.setSelectedNode(treeNode); } } else if("Node".equalsIgnoreCase(resourceTypeName) || "CourseNode".equalsIgnoreCase(resourceTypeName)) { Long nodeIdent = entries.get(0).getOLATResourceable().getResourceableId(); @@ -150,6 +152,7 @@ public class AssessmentCourseTreeController extends BasicController implements A List<ContextEntry> subEntries = entries.subList(1, entries.size()); ((Activateable2)ctrl).activate(ureq, subEntries, entry.getTransientState()); } + menuTree.setSelectedNode(treeNode); } } } diff --git a/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java index c88f11fb2d980789422802c072d3bf2bb6c96a5a..f567098a29b28c8d3f3078c035319f539adf3a10 100644 --- a/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java +++ b/src/main/java/org/olat/ims/qti21/AssessmentTestSession.java @@ -22,6 +22,8 @@ package org.olat.ims.qti21; import java.math.BigDecimal; import java.util.Date; +import javax.persistence.Transient; + import org.olat.core.id.CreateInfo; import org.olat.core.id.Identity; import org.olat.core.id.ModifiedInfo; @@ -58,6 +60,21 @@ public interface AssessmentTestSession extends CreateInfo, ModifiedInfo { public void setManualScore(BigDecimal manualScore); + /** + * @return The score + manual score + */ + @Transient + public default BigDecimal getFinalScore() { + BigDecimal finalScore = getScore(); + BigDecimal manualScore = getManualScore(); + if(finalScore == null) { + finalScore = manualScore; + } else if(manualScore != null) { + finalScore = finalScore.add(manualScore); + } + return finalScore; + } + /** * Return the duration in milliseconds * @return diff --git a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java index 4f872e5eb9a100b5c0155684904b5a86414eed7f..fc33613af462f12ae580a40224b68f8485fd7b20 100644 --- a/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java +++ b/src/main/java/org/olat/ims/qti21/manager/archive/QTI21ArchiveFormat.java @@ -35,6 +35,7 @@ import java.util.zip.ZipOutputStream; import org.olat.core.CoreSpringFactory; import org.olat.core.gui.media.MediaResource; import org.olat.core.gui.translator.Translator; +import org.olat.core.id.Identity; import org.olat.core.id.User; import org.olat.core.id.context.BusinessControlFactory; import org.olat.core.id.context.ContextEntry; @@ -52,6 +53,7 @@ import org.olat.core.util.openxml.workbookstyle.CellStyle; import org.olat.course.CourseFactory; import org.olat.course.ICourse; import org.olat.course.archiver.ExportFormat; +import org.olat.course.nodes.AssessableCourseNode; import org.olat.course.nodes.CourseNode; import org.olat.fileresource.FileResourceManager; import org.olat.ims.qti.export.QTIArchiver; @@ -137,6 +139,7 @@ public class QTI21ArchiveFormat { private final QTI21StatisticSearchParams searchParams; private ExportFormat exportConfig; + private CourseNode courseNode; private List<ItemInfos> itemInfos; private final Map<String, InteractionArchive> interactionArchiveMap = new HashMap<>(); @@ -200,7 +203,7 @@ public class QTI21ArchiveFormat { */ public void exportCourseElement(ZipOutputStream exportStream) { ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry()); - CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); + courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); String label = StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + "_" + Formatter.formatDatetimeWithMinutes(new Date()) + ".xlsx"; @@ -209,7 +212,7 @@ public class QTI21ArchiveFormat { public void exportCourseElement(String label, ZipOutputStream exportStream) { ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry()); - CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); + courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); if("iqself".equals(courseNode.getType())) { anonymizerCallback = course.getCourseEnvironment().getCoursePropertyManager(); } @@ -259,7 +262,7 @@ public class QTI21ArchiveFormat { resolvedAssessmentTest = qtiService.loadAndResolveAssessmentTest(unzippedDirRoot, false, false); ICourse course = CourseFactory.loadCourse(searchParams.getCourseEntry()); - CourseNode courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); + courseNode = course.getRunStructure().getNode(searchParams.getNodeIdent()); String label = courseNode.getType() + "_" + StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName()) + "_" + Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis())) @@ -290,19 +293,39 @@ public class QTI21ArchiveFormat { private void writeHeaders_1(OpenXMLWorksheet exportSheet, OpenXMLWorkbook workbook) { + CellStyle headerStyle = workbook.getStyles().getHeaderStyle(); //first header Row header1Row = exportSheet.newRow(); int col = 1; if(anonymizerCallback != null) { - col += 4;// anonymized name -> test duration + col += 0;// anonymized name -> test duration } else { for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { if (userPropertyHandler != null) { col++; } } - col += 5;// homepage -> test duration + col += 1;// homepage -> test duration + } + + // course node points and passed + if(courseNode instanceof AssessableCourseNode) { + AssessableCourseNode assessableCourseNode = (AssessableCourseNode)courseNode; + if(assessableCourseNode.hasScoreConfigured()) { + header1Row.addCell(col++, translator.translate("archive.table.header.node"), headerStyle); + } + if(assessableCourseNode.hasPassedConfigured()) { + if(assessableCourseNode.hasScoreConfigured()) { + col++; + } else { + header1Row.addCell(col++, translator.translate("archive.table.header.node"), headerStyle); + } + } } + + // test points, passed and dates + header1Row.addCell(col++, translator.translate("archive.table.header.test"), headerStyle); + col += 5; List<ItemInfos> infos = getItemInfos(); for(int i=0; i<infos.size(); i++) { @@ -353,8 +376,21 @@ public class QTI21ArchiveFormat { // add other user and session information header2Row.addCell(col++, translator.translate("column.header.homepage"), headerStyle); } + + // course node points and passed + if(courseNode instanceof AssessableCourseNode) { + AssessableCourseNode assessableCourseNode = (AssessableCourseNode)courseNode; + if(assessableCourseNode.hasScoreConfigured()) { + header2Row.addCell(col++, translator.translate("archive.table.header.node.points"), headerStyle); + } + if(assessableCourseNode.hasPassedConfigured()) { + header2Row.addCell(col++, translator.translate("archive.table.header.node.passed"), headerStyle); + } + } - header2Row.addCell(col++, translator.translate("column.header.assesspoints"), headerStyle); + header2Row.addCell(col++, translator.translate("archive.table.header.points"), headerStyle); + header2Row.addCell(col++, translator.translate("archive.table.header.manual.points"), headerStyle); + header2Row.addCell(col++, translator.translate("archive.table.header.final.points"), headerStyle); header2Row.addCell(col++, translator.translate("column.header.passed"), headerStyle); if (anonymizerCallback == null){ header2Row.addCell(col++, translator.translate("column.header.date"), headerStyle); @@ -416,9 +452,10 @@ public class QTI21ArchiveFormat { AssessmentTestSession testSession = responses.getTestSession(); AssessmentEntry entry = testSession.getAssessmentEntry(); + Identity assessedIdentity = entry.getIdentity(); //user properties - if(entry.getIdentity() == null) { + if(assessedIdentity == null) { for (UserPropertyHandler userPropertyHandler:userPropertyHandlers) { if (userPropertyHandler != null) { if(userPropertyHandlers.get(0) == userPropertyHandler) { @@ -429,10 +466,10 @@ public class QTI21ArchiveFormat { } } } else if(anonymizerCallback != null) { - String anonymizedName = anonymizerCallback.getAnonymizedUserName(entry.getIdentity()); + String anonymizedName = anonymizerCallback.getAnonymizedUserName(assessedIdentity); dataRow.addCell(col++, anonymizedName, null); } else { - User assessedUser = entry.getIdentity().getUser(); + User assessedUser = assessedIdentity.getUser(); for (UserPropertyHandler userPropertyHandler : userPropertyHandlers) { if (userPropertyHandler != null) { String property = userPropertyHandler.getUserProperty(assessedUser, translator.getLocale()); @@ -453,12 +490,41 @@ public class QTI21ArchiveFormat { dataRow.addCell(col++, homepage, null); } + // course node points and passed + if(courseNode instanceof AssessableCourseNode) { + AssessableCourseNode assessableCourseNode = (AssessableCourseNode)courseNode; + if(assessableCourseNode.hasScoreConfigured()) { + if(entry.getScore() != null) { + dataRow.addCell(col++, entry.getScore(), null); + } else { + col++; + } + } + if(assessableCourseNode.hasPassedConfigured()) { + if(entry.getPassed() != null) { + dataRow.addCell(col++, entry.getPassed().toString(), null); + } else { + col++; + } + } + } + //assesspoints, passed, ipaddress, date, duration if(testSession.getScore() != null) { dataRow.addCell(col++, testSession.getScore(), null); } else { col++; } + if(testSession.getManualScore() != null) { + dataRow.addCell(col++, testSession.getManualScore(), null); + } else { + col++; + } + if(testSession.getFinalScore() != null) { + dataRow.addCell(col++, testSession.getFinalScore(), null); + } else { + col++; + } if(testSession.getPassed() != null) { dataRow.addCell(col++, testSession.getPassed().toString(), null); } else { diff --git a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java index fb303e50deac53f0d743cf7a514b9df9eec22979..583e12e1c5fb8851a7f468c3dcdb1c3477b08cb1 100644 --- a/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java +++ b/src/main/java/org/olat/ims/qti21/model/jpa/AssessmentTestSessionImpl.java @@ -298,10 +298,12 @@ public class AssessmentTestSessionImpl implements AssessmentTestSession, Persist this.score = score; } + @Override public BigDecimal getManualScore() { return manualScore; } + @Override public void setManualScore(BigDecimal manualScore) { this.manualScore = manualScore; } diff --git a/src/main/java/org/olat/ims/qti21/resultexport/AssessedMember.java b/src/main/java/org/olat/ims/qti21/resultexport/AssessedMember.java new file mode 100644 index 0000000000000000000000000000000000000000..78358a152c4ceb451da69d0bac317643eeb4385a --- /dev/null +++ b/src/main/java/org/olat/ims/qti21/resultexport/AssessedMember.java @@ -0,0 +1,93 @@ +/** + * <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.ims.qti21.resultexport; + +import java.math.BigDecimal; + +import org.olat.course.assessment.AssessmentHelper; + +/** + * + * Initial date: 29 nov. 2017<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class AssessedMember { + + private String username; + private String name; + private String lastname; + private String firstname; + private String email; + + private int attempts; + private Boolean passed; + private BigDecimal score; + private String href; + + public AssessedMember(String username, String lastname, String firstname, String email, + int attempts, Boolean passed, BigDecimal score, String href) { + this.username = username; + this.lastname = lastname; + this.firstname = firstname; + this.email = email; + this.attempts = attempts; + this.passed = passed; + this.score = score; + this.href = href; + } + + public String getName() { + return name; + } + + public String getUsername() { + return username; + } + + public String getLastname() { + return lastname; + } + + public String getFirstname() { + return firstname; + } + + public String getEmail() { + return email; + } + + public int getAttempts() { + return attempts; + } + + public String getPassedIcon() { + return QTI21ResultsExportMediaResource.createPassedIcons(passed); + } + + public String getScoreString() { + String val = AssessmentHelper.getRoundedScore(score); + return val == null ? "" : val; + } + + public String getHref() { + return href; + } +} diff --git a/src/main/java/org/olat/ims/qti21/resultexport/QTI21ResultsExportMediaResource.java b/src/main/java/org/olat/ims/qti21/resultexport/QTI21ResultsExportMediaResource.java index 067e0e10cc733554337ee068f02978533f6b04b9..ffd364285406a4a178d37bebda8547fdfad53f45 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/QTI21ResultsExportMediaResource.java +++ b/src/main/java/org/olat/ims/qti21/resultexport/QTI21ResultsExportMediaResource.java @@ -24,14 +24,17 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.TimeZone; import java.util.zip.ZipEntry; @@ -61,7 +64,6 @@ import org.olat.core.gui.util.SyntheticUserRequest; import org.olat.core.gui.util.WindowControlMocker; import org.olat.core.id.Identity; import org.olat.core.id.Roles; -import org.olat.core.id.UserConstants; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.FileUtils; @@ -75,13 +77,13 @@ import org.olat.course.nodes.ArchiveOptions; import org.olat.course.nodes.QTICourseNode; import org.olat.course.run.environment.CourseEnvironment; import org.olat.fileresource.FileResourceManager; -import org.olat.ims.qti.resultexport.AssessedMember; import org.olat.ims.qti21.AssessmentTestSession; import org.olat.ims.qti21.QTI21AssessmentResultsOptions; import org.olat.ims.qti21.QTI21Service; import org.olat.ims.qti21.manager.archive.QTI21ArchiveFormat; import org.olat.ims.qti21.model.QTI21StatisticSearchParams; import org.olat.ims.qti21.ui.AssessmentResultController; +import org.olat.modules.assessment.AssessmentEntry; import org.olat.repository.RepositoryEntry; import org.olat.user.UserManager; @@ -101,19 +103,22 @@ public class QTI21ResultsExportMediaResource implements MediaResource { private List<Identity> identities; private QTICourseNode courseNode; - private QTI21Service qtiService; - private String title, exportFolderName; + private String exportFolderName; private Translator translator; private RepositoryEntry entry; + private final CourseEnvironment courseEnv; private UserRequest ureq; private final Set<RepositoryEntry> testEntries = new HashSet<>(); + private final UserManager userManager; + private final QTI21Service qtiService; + public QTI21ResultsExportMediaResource(CourseEnvironment courseEnv, List<Identity> identities, - QTICourseNode courseNode, Locale locale) { - this.title = "qti21export"; + QTICourseNode courseNode, Locale locale) { this.courseNode = courseNode; this.identities = identities; + this.courseEnv = courseEnv; ureq = new SyntheticUserRequest(new TransientIdentity(), locale, new UserSession()); Roles roles = new Roles(false, false, false, false, false, false, false); @@ -121,6 +126,7 @@ public class QTI21ResultsExportMediaResource implements MediaResource { velocityHelper = VelocityHelper.getInstance(); qtiService = CoreSpringFactory.getImpl(QTI21Service.class); + userManager = CoreSpringFactory.getImpl(UserManager.class); entry = courseEnv.getCourseGroupManager().getCourseEntry(); translator = Util.createPackageTranslator(QTI21ResultsExportMediaResource.class, locale); exportFolderName = translator.translate("export.folder.name"); @@ -153,11 +159,9 @@ public class QTI21ResultsExportMediaResource implements MediaResource { @Override public void prepare(HttpServletResponse hres) { - String label = StringHelper.transformDisplayNameToFileSystemName(title); - if (label != null && !label.toLowerCase().endsWith(".zip")) { - label += ".zip"; - } - + String label = StringHelper.transformDisplayNameToFileSystemName(courseNode.getShortName() + "_" + entry.getDisplayname()) + + "_" + Formatter.formatDatetimeWithMinutes(new Date()) + + ".zip"; String urlEncodedLabel = StringHelper.urlEncodeUTF8(label); hres.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + urlEncodedLabel); hres.setHeader("Content-Description", urlEncodedLabel); @@ -262,34 +266,37 @@ public class QTI21ResultsExportMediaResource implements MediaResource { } private List<AssessedMember> createAssessedMembersDetail(ZipOutputStream zout) throws IOException { + List<AssessmentEntry> assessmentEntries = courseEnv.getAssessmentManager().getAssessmentEntries(courseNode); + Map<Identity,AssessmentEntry> assessmentEntryMap = new HashMap<>(); + for(AssessmentEntry assessmentEntry:assessmentEntries) { + assessmentEntryMap.put(assessmentEntry.getIdentity(), assessmentEntry); + } + List<AssessedMember> assessedMembers = new ArrayList<>(); - for (Identity identity : identities) { - + for(Identity identity : identities) { String idDir = exportFolderName + "/" + DATA + identity.getName(); idDir = idDir.endsWith(SEP) ? idDir : idDir + SEP; createZipDirectory(zout, idDir); - //content of single assessed member - String userName = identity.getName(); - String firstName = identity.getUser().getProperty(UserConstants.FIRSTNAME, null); - String lastName = identity.getUser().getProperty(UserConstants.LASTNAME, null); - String memberEmail = UserManager.getInstance().getUserDisplayEmail(identity, ureq.getLocale()); - AssessedMember assessedMember = new AssessedMember (userName, lastName, firstName, memberEmail, null); - - List<ResultDetail> assessments = createResultDetail(identity, zout, idDir); + //content of single assessed member + List<ResultDetail> assessments = createResultDetail(identity, zout, idDir); + Boolean passed = null; + BigDecimal score = null; + if(assessmentEntryMap.containsKey(identity)) { + AssessmentEntry assessmentEntry = assessmentEntryMap.get(identity); + passed = assessmentEntry.getPassed(); + score = assessmentEntry.getScore(); + } - String singleUserInfoHTML = createResultListingHTML(assessments, assessedMember); - convertToZipEntry(zout, exportFolderName + "/" + DATA + identity.getName() + "/index.html", singleUserInfoHTML); + String linkToUser = idDir.replace(exportFolderName + "/", "") + "index.html"; + String memberEmail = userManager.getUserDisplayEmail(identity, ureq.getLocale()); + AssessedMember member = new AssessedMember(identity.getName(), + identity.getUser().getLastName(), identity.getUser().getFirstName(), memberEmail, + assessments.size(), passed, score, linkToUser); - String linkToUser = idDir.replace(exportFolderName + "/", "") + "index.html"; - //content of assessed members table - AssessedMember member = new AssessedMember(); - member.setUsername(createLink(identity.getName(), linkToUser, false)); - member.setLastname(createLink(identity.getUser().getProperty(UserConstants.LASTNAME, null),linkToUser,false)); - member.setFirstname(createLink(identity.getUser().getProperty(UserConstants.FIRSTNAME, null),linkToUser,false)); - member.setTries(String.valueOf(assessments.size())); + String singleUserInfoHTML = createResultListingHTML(assessments, member); + convertToZipEntry(zout, exportFolderName + "/" + DATA + identity.getName() + "/index.html", singleUserInfoHTML); assessedMembers.add(member); - } return assessedMembers; } @@ -301,18 +308,14 @@ public class QTI21ResultsExportMediaResource implements MediaResource { ZipUtil.addDirectoryToZip(fUnzippedDirRoot.toPath(), baseDir, zout); } - private String createLink(String name, String href, boolean userview) { - String targetLink = userview ? "_blank" : "_self"; - return "<a href='" + href + "' target='" + targetLink + "' class='userLink'>" + name + "</a>"; - } - - private String createPassedIcons(boolean passed) { - String icon = passed ? "<i class='o_icon o_passed o_icon_passed text-success'></i>" + protected static String createPassedIcons(Boolean passed) { + if(passed == null) return ""; + + return passed.booleanValue() ? "<i class='o_icon o_passed o_icon_passed text-success'></i>" : "<i class='o_icon o_failed o_icon_failed text-danger'></i>"; - return icon; } - private String createResultHTML (Component results){ + private String createResultHTML(Component results) { StringOutput sb = new StringOutput(32000); String pagePath = Util.getPackageVelocityRoot(this.getClass()) + "/qti21results.html"; URLBuilder ubu = new URLBuilder("auth", "1", "0"); @@ -329,7 +332,7 @@ public class QTI21ResultsExportMediaResource implements MediaResource { return sb.toString(); } - private String createResultListingHTML (List<ResultDetail> assessments, AssessedMember assessedMember) { + private String createResultListingHTML(List<ResultDetail> assessments, AssessedMember assessedMember) { // now put values to velocityContext VelocityContext ctx = new VelocityContext(); ctx.put("t", translator); diff --git a/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiListing.html b/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiListing.html index 39465d709062c8a60d29d58a74e8b5c520a3bdc1..3d8dee15a9016cf9314d8db29f763260b8f7fcdd 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiListing.html +++ b/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiListing.html @@ -27,6 +27,9 @@ <th>$t.translate("table.all.lastname")</th> <th>$t.translate("table.all.firstname")</th> <th>$t.translate("table.user.email")</th> + <th>$t.translate("table.all.node.score")</th> + <th>$t.translate("table.all.node.passed")</th> + <th>$t.translate("table.all.tries")</th> </tr> </thead> <tbody> @@ -35,6 +38,9 @@ <td>$assessedMember.lastname</td> <td>$assessedMember.firstname</td> <td>$assessedMember.email</td> + <td>$assessedMember.scoreString</td> + <td>$assessedMember.passedIcon</td> + <td>$assessedMember.attempts</td> </tr> </tbody> </table> @@ -43,6 +49,9 @@ <div> <table id='results' class='table table-striped table-hover'> <thead> + <tr> + <th colspan="8">$t.translate("table.test.sessions")</th> + </tr> <tr> <th>$t.translate("table.user.id")</th> <th>$t.translate("table.user.date")</th> diff --git a/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiUserlisting.html b/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiUserlisting.html index 56702445b8a386b28341760e38106fac1aa12c0f..32839a6d851781d6496eb93033f40a3074e3913b 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiUserlisting.html +++ b/src/main/java/org/olat/ims/qti21/resultexport/_content/qtiUserlisting.html @@ -14,16 +14,20 @@ <th>$t.translate("table.all.username")</th> <th>$t.translate("table.all.lastname")</th> <th>$t.translate("table.all.firstname")</th> + <th>$t.translate("table.all.score")</th> + <th>$t.translate("table.all.passed")</th> <th>$t.translate("table.all.tries")</th> </tr> </thead> <tbody> #foreach( $member in $assessedMembers ) <tr> - <td>$member.username</td> - <td>$member.lastname</td> - <td>$member.firstname</td> - <td>$member.tries</td> + <td><a href="${member.href}" target="_self" class="userLink">$member.username</a></td> + <td><a href="${member.href}" target="_self" class="userLink">$member.lastname</a></td> + <td><a href="${member.href}" target="_self" class="userLink">$member.firstname</a></td> + <td>$member.scoreString</td> + <td>$member.passedIcon</td> + <td>$member.attempts</td> </tr> #end </tbody> diff --git a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_de.properties index 512810489733b600eedc3eb9ee555f54173f7607..a8f71b2ceed5b143ce9f219c0656530df0eae229 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_de.properties @@ -7,8 +7,11 @@ error.no.assessed.users=Keine Bewertungen vorhanden table.all.firstname=Vorname table.all.id=ID table.all.lastname=Nachname +table.all.node.score=Kursbaustein Punkte +table.all.node.passed=Kursbaustein Bestanden table.all.passed=Bestanden table.all.tries=Versuche +table.all.score=Punkte table.all.username=Benutzername table.grading=Bewertung table.overview=\u00DCbersicht @@ -21,4 +24,5 @@ table.user.id=ID table.user.manualScore=Manuell Punkte table.user.score=Punkte table.user.trial=Versuch +table.test.sessions=Test Versuche export.folder.name=Resultate \ No newline at end of file diff --git a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_en.properties index 048952628e98766cffe7db7b31369a38437cd1c3..2f89c6200fd02d8ac42f97c4360ae1c2db15145a 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_en.properties @@ -1,23 +1,28 @@ +#Thu Nov 30 09:37:59 CET 2017 button.export=Export Results button.return=Return button.show=Display -table.overview=Overview -table.grading=Grading detail.results=Detailed Results -table.user.id=ID -table.user.email=Email +error.no.assessed.users=No Assessments available +export.folder.name=Results +table.all.firstname=Firstname +table.all.id=ID +table.all.lastname=Lastname +table.all.node.passed=Passed course element +table.all.node.score=Score course element +table.all.passed=Passed +table.all.score=Score +table.all.tries=Attempts +table.all.username=Username +table.grading=Grading +table.overview=Overview +table.test.sessions=Test attempts +table.user.attempt=Attempt_ table.user.date=Date table.user.duration=Duration +table.user.email=Email table.user.finalScore=Final score +table.user.id=ID table.user.manualScore=Manual score -table.user.trial=Attempt table.user.score=Score -table.all.id=ID -table.all.username=Username -table.all.firstname=Firstname -table.all.lastname=Lastname -table.all.tries=Attempts -table.user.attempt=Attempt_ -table.all.passed=Passed -error.no.assessed.users=No Assessments available -export.folder.name=Results \ No newline at end of file +table.user.trial=Attempt diff --git a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_fr.properties index 5468c3d5f268a21888e8cac388781af47a0db9b6..69e44960f76103de8e6463919a3bd3c36483f280 100644 --- a/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/ims/qti21/resultexport/_i18n/LocalStrings_fr.properties @@ -1,4 +1,4 @@ -#Wed Mar 08 10:51:23 CET 2017 +#Thu Nov 30 09:42:59 CET 2017 button.export=Exporter les r\u00E9sultats button.return=Retour button.show=Afficher @@ -8,11 +8,15 @@ export.folder.name=R\u00E9sultats table.all.firstname=Pr\u00E9nom table.all.id=ID table.all.lastname=Nom +table.all.node.passed=Element de cours r\u00E9ussi +table.all.node.score=Points \u00E9l\u00E9ment de cours table.all.passed=R\u00E9ussi +table.all.score=Points table.all.tries=Essais table.all.username=Nom d'utilisateur table.grading=Evaluation table.overview=Aper\u00E7u +table.test.sessions=Tentatives du test table.user.attempt=Essai_ table.user.date=Date table.user.duration=Dur\u00E9e diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java index 11c6728fd9fff522c877fc75e9f9b161f00f44df..b4882807253ae6f4f46dd4b91cf9aa3261cbe9d7 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentDetailsController.java @@ -370,7 +370,7 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private void doUpdateCourseNode(AssessmentTestSession session) { ScoreEvaluation scoreEval = courseNode.getUserScoreEvaluation(assessedUserCourseEnv); - BigDecimal finalScore = calculateFinalScore(session); + BigDecimal finalScore = session.getFinalScore(); Float score = finalScore == null ? null : finalScore.floatValue(); ScoreEvaluation manualScoreEval = new ScoreEvaluation(score, scoreEval.getPassed(), scoreEval.getAssessmentStatus(), null, scoreEval.getFullyAssessed(), session.getKey()); @@ -379,21 +379,11 @@ public class QTI21AssessmentDetailsController extends FormBasicController { private void doUpdateEntry(AssessmentTestSession session) { AssessmentEntry assessmentEntry = assessmentService.loadAssessmentEntry(assessedIdentity, entry, null, entry); - BigDecimal finalScore = calculateFinalScore(session); - assessmentEntry.setScore(finalScore); + assessmentEntry.setScore(session.getFinalScore()); assessmentEntry.setAssessmentId(session.getKey()); assessmentService.updateAssessmentEntry(assessmentEntry); } - private BigDecimal calculateFinalScore(AssessmentTestSession session) { - BigDecimal finalScore = session.getScore(); - if(finalScore == null) { - finalScore = session.getManualScore(); - } else if(session.getManualScore() != null) { - finalScore = finalScore.add(session.getManualScore()); - } - return finalScore; - } private void doConfirmPullSession(UserRequest ureq, AssessmentTestSession session) { String title = translate("pull"); diff --git a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentTestSessionTableModel.java b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentTestSessionTableModel.java index a0e73d033291d90847cea56d2546bd45d361f2f5..e66fbb760b8208f0f817907e38931a9a7a9587d6 100644 --- a/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentTestSessionTableModel.java +++ b/src/main/java/org/olat/ims/qti21/ui/QTI21AssessmentTestSessionTableModel.java @@ -19,6 +19,7 @@ */ package org.olat.ims.qti21.ui; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -97,14 +98,8 @@ public class QTI21AssessmentTestSessionTableModel extends DefaultFlexiTableDataM } case finalScore: { if(session.getTestSession().getFinishTime() != null) { - double score = 0.0d; - if(session.getTestSession().getScore() != null) { - score += session.getTestSession().getScore().doubleValue(); - } - if(session.getTestSession().getManualScore() != null) { - score += session.getTestSession().getManualScore().doubleValue(); - } - return AssessmentHelper.getRoundedScore(score); + BigDecimal finalScore = session.getTestSession().getFinalScore(); + return AssessmentHelper.getRoundedScore(finalScore); } return ""; } diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties index 5904bc053931dffa0d998f9ebff68d336991e760..ce965d6af35e77e191646440eb2609cca4d73b4d 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_de.properties @@ -10,6 +10,13 @@ anonym.user=Anonym Benutzer answer.correct=$org.olat.ims.qti.statistics.ui\:answer.correct answer.false=$org.olat.ims.qti.statistics.ui\:answer.false answer.noanswer=$org.olat.ims.qti.statistics.ui\:answer.noanswer +archive.table.header.points=$\:table.header.score +archive.table.header.manual.points=$\:table.header.manualScore +archive.table.header.final.points=$\:table.header.finalScore +archive.table.header.node.points=Kursbaustein Punkte +archive.table.header.node.passed=Kursbaustein bestanden +archive.table.header.node=Kurs +archive.table.header.test=Test assessment.comment.legend=Pers\u00F6nliche Notizen assessment.item.status.answered=Beantwortet assessment.item.status.finished=Erledigt diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties index 0fb6e51c6f9617581201e189bd8233fa36f2f655..198c7c59ca8d4f0342cafff026bcbc889990d6e3 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_en.properties @@ -1,4 +1,4 @@ -#Mon Sep 04 18:16:12 CEST 2017 +#Thu Nov 30 09:38:35 CET 2017 actualPoints=$org.olat.modules.iq\:actualPoints admin.12.title=QTI 1.2 settings admin.menu.title=QTI 2.1 @@ -10,6 +10,13 @@ anonym.user=Anonymous user answer.correct=$org.olat.ims.qti.statistics.ui\:answer.correct answer.false=$org.olat.ims.qti.statistics.ui\:answer.false answer.noanswer=$org.olat.ims.qti.statistics.ui\:answer.noanswer +archive.table.header.final.points=$\:table.header.finalScore +archive.table.header.manual.points=$\:table.header.manualScore +archive.table.header.node=Course +archive.table.header.node.passed=Passed course element +archive.table.header.node.points=Score course element +archive.table.header.points=$\:table.header.score +archive.table.header.test=Test assessment.comment.legend=Please use the following text box if you need to provide any additional information, comments or feedback during this test\: assessment.item.mark=Add personal marking as a reminder to review this question assessment.item.status.answered=Answered @@ -103,8 +110,8 @@ error.input.extendedText.min=Write a text with at least {0} words. error.input.extendedText.min.max=Write a text with at least {0} and at the most {1} words. error.integer=Need to be an integer. Example\: 15, 5, 1 error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType -error.required.format=Your input is not of the required format. error.reload.question=An unexpected error occurred. Please check the answer to your question and save it again. +error.required.format=Your input is not of the required format. error.textEntry.invalid=Your input is not valid error.textEntry.invalid.float=Your input must be a number error.textEntry.invalid.record=Your input must be a valid number diff --git a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_fr.properties index 5c89d575567bbed7cb319953be0de207849ae01b..1fbcf595876465a300e4292259206b06ad1d6496 100644 --- a/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/ims/qti21/ui/_i18n/LocalStrings_fr.properties @@ -1,4 +1,4 @@ -#Wed Nov 15 22:23:50 CET 2017 +#Thu Nov 30 09:44:19 CET 2017 actualPoints=$org.olat.modules.iq\:actualPoints admin.12.title=Configuration QTI 1.2 admin.menu.title=QTI 2.1 @@ -10,6 +10,13 @@ anonym.user=Utilisateur anonyme answer.correct=$org.olat.ims.qti.statistics.ui\:answer.correct answer.false=$org.olat.ims.qti.statistics.ui\:answer.false answer.noanswer=$org.olat.ims.qti.statistics.ui\:answer.noanswer +archive.table.header.final.points=$\:table.header.finalScore +archive.table.header.manual.points=$\:table.header.manualScore +archive.table.header.node=Cours +archive.table.header.node.passed=Element de cours r\u00E9ussi +archive.table.header.node.points=Points \u00E9l\u00E9ment de cours +archive.table.header.points=$\:table.header.score +archive.table.header.test=Test assessment.comment.legend=Notes personelles assessment.item.mark=Ajouter un signet pour revoir cette question plus tard assessment.item.status.answered=R\u00E9pondu @@ -103,6 +110,7 @@ error.input.extendedText.min=Ecrivez un texte avec au moins {0} mots. error.input.extendedText.min.max=Ecrivez un texte avec au moins {0} mots et au plus {1}. error.integer=Format de nombre inad\u00E9quat. Exemple\: 15, 5, 1 error.mimetype=$org.olat.core.commons.modules.bc\:WrongMimeType +error.reload.question=Une erreur inattendue s'est produite. Veuillez v\u00E9rifiez la r\u00E9ponse que vous avez donn\u00E9 \u00E0 la question et sauvez-la \u00E0 nouveau. error.required.format=Votre entr\u00E9e ne correspond pas au format requis. error.textEntry.invalid=Votre entr\u00E9e doit \u00EAtre un {0} valide. error.textEntry.invalid.float=Votre entr\u00E9e doit \u00EAtre un nombre {0} valide avec une virgule ou un point comme s\u00E9parateur d\u00E9cimal. diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestOverviewDataModel.java b/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestOverviewDataModel.java index 40c4f71d8175e8be86d5c60ed04e4b700d8f8710..6e18f5bf5539d1c3c2a24da4726a405c8f7f8cd3 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestOverviewDataModel.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/IdentitiesAssessmentTestOverviewDataModel.java @@ -19,7 +19,6 @@ */ package org.olat.ims.qti21.ui.assessment; -import java.math.BigDecimal; import java.util.List; import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel; @@ -83,16 +82,7 @@ public class IdentitiesAssessmentTestOverviewDataModel extends DefaultFlexiTable } case score: return correction.getScore(); case manualScore: return correction.getManualScore(); - case finalScore: { - BigDecimal finalScore = correction.getScore(); - BigDecimal manualScore = correction.getManualScore(); - if(finalScore == null) { - finalScore = manualScore; - } else if(manualScore != null) { - finalScore = finalScore.add(manualScore); - } - return finalScore; - } + case finalScore: return correction.getFinalScore(); } } diff --git a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java b/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java index 4f9369b4f2568534f88db56a394cba0a7fd2204d..eaed4a133cb3c80554b1647b6660cc8e86ff2853 100644 --- a/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java +++ b/src/main/java/org/olat/ims/qti21/ui/assessment/QTI21CorrectionToolController.java @@ -129,13 +129,7 @@ public class QTI21CorrectionToolController extends BasicController { .createAndInitUserCourseEnvironment(testSession.getIdentity(), courseEnv); ScoreEvaluation scoreEval = courseNode.getUserScoreEvaluation(assessedUserCourseEnv); - BigDecimal finalScore = testSession.getScore(); - if(finalScore == null) { - finalScore = testSession.getManualScore(); - } else if(testSession.getManualScore() != null) { - finalScore = finalScore.add(testSession.getManualScore()); - } - + BigDecimal finalScore = testSession.getFinalScore(); Float score = finalScore == null ? null : finalScore.floatValue(); ScoreEvaluation manualScoreEval = new ScoreEvaluation(score, scoreEval.getPassed(), scoreEval.getAssessmentStatus(), scoreEval.getUserVisible(), scoreEval.getFullyAssessed(), testSession.getKey()); diff --git a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_fr.properties index 4448bc9ff158aebbb267bf9d260f8365b8cbd1ea..db02c5363318558810f2f63d95b5d19eeea48d7c 100644 --- a/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/edubase/ui/_i18n/LocalStrings_fr.properties @@ -1,5 +1,6 @@ -#Thu Nov 16 19:05:31 CET 2017 +#Thu Nov 30 09:53:14 CET 2017 admin.edubase.coverver.url=URL du service "coverver" +admin.edubase.description=Edubase est une solution compl\u00E8te pour les publications digitales et imprim\u00E9es. Avec le module OpenOLAT Edubase, l0Edubase Reader peut \u00EAtre int\u00E9gr\u00E9 \u00E0 un cours. Vous trouverez de plus amples informations sur le site Edubase <a href\="http\://www.edubase.ch" target\=_blank>http\://www.edubase.ch</a>. admin.edubase.enabled=El\u00E9ment de cours "Edubase" admin.edubase.infover.url=URL du service "infover" admin.edubase.lti.launch.url=URL de d\u00E9marrage LTI diff --git a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties index 50d0d36d211a6f779afd1a4348103b16bf95cf3d..560be8cd795f7285f93976603e96ee264d5b5c7f 100644 --- a/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/lecture/ui/_i18n/LocalStrings_fr.properties @@ -1,4 +1,4 @@ -#Fri Nov 17 12:10:38 CET 2017 +#Thu Nov 30 10:01:22 CET 2017 active=Actif add.lecture=Cr\u00E9er un nouveau cours bloc add.reason=Cr\u00E9er une justification @@ -11,6 +11,7 @@ all.teachers.switch=Tous les charg\u00E9s de cours all.teachers.switch.tooltip.off=Montrer tous les cours blocs all.teachers.switch.tooltip.on=Montrer seulement mes cours blocs appeal=Recours +appeal.body=<p>Ch\u00E8re / Cher {1}</p><p>J'aimerais signaler que les absences de {2} ont \u00E9t\u00E9 \u00E0 mon avis saisies de mani\u00E8re incorrecte.</p><p>Raisons\:</p><p><span style\="color\: \#ff0000;">(entrez les raisons ici)</span></p><p>Merci d'avance de les v\u00E9rifier et pour les \u00E9ventuelles corrections.</p><p>Avec mes salutations distingu\u00E9es</p> appeal.closed=Termin\u00E9 appeal.contact.list=Charg\u00E9 de cours appeal.from=Depuis le {0} diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties index e8d7f5a323b2536d288413ade7f2b7fc51ff9c3b..f41c1c25365f33f9776c8661ea17a0561a0f2320 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_de.properties @@ -21,7 +21,7 @@ confirm.removed.competence=Die Kompetenz-en wurde erfolgreich entfert. confirmation.delete.level=Wollen Sie wirklich die Ebene "{0}" l\u00F6schen? confirmation.delete.level.title=Ebene l\u00F6schen confirmation.delete.type=Wollen Sie wirklich den Ebenentyp "{0}" l\u00F6schen? -confirmation.delete.type.title= +confirmation.delete.type.title=Ebenentyp "{0}" l\u00F6schen confirmation.remove.competence=Wollen Sie wirklich die Kompetenz "{0}" f\u00FCr den Level "{1}" entfern? confirmation.remove.competence.title=Kompetenz entfernen create.taxonomy=Neue Taxonomy erstellen diff --git a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_fr.properties index 78815657975d6e889a70963aadd2ae86517384f0..7ff112f86edd9f511a48c72222dbe354ab720917 100644 --- a/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_fr.properties +++ b/src/main/java/org/olat/modules/taxonomy/ui/_i18n/LocalStrings_fr.properties @@ -21,6 +21,7 @@ confirm.removed.competence=La ou les comp\u00E9tences ont \u00E9t\u00E9 effac\u0 confirmation.delete.level=Voulez-vous vraiment effacer le niveau "{0}" de la taxonomie? confirmation.delete.level.title=Effacer un niveau de la taxonomie confirmation.delete.type=Voulez-vous vraiment effacer le type de niveau "{0}"? +confirmation.delete.type.title=Effacer le type de niveau "{0}" confirmation.remove.competence=Voulez-vous vraiment enlever la comp\u00E9tence "{0}" du niveau "{1}" de la taxonomie? confirmation.remove.competence.title=Enlever une comp\u00E9tence create.taxonomy=Cr\u00E9er une taxonomie