diff --git a/src/main/java/org/olat/course/condition/interpreter/GetLastAttemptDateFunction.java b/src/main/java/org/olat/course/condition/interpreter/GetLastAttemptDateFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..21ea20cd0ffbab9ca0b72bdd9ca6630d307205b3 --- /dev/null +++ b/src/main/java/org/olat/course/condition/interpreter/GetLastAttemptDateFunction.java @@ -0,0 +1,125 @@ +/** + * <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> + * 04.10.2013 by frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.course.condition.interpreter; + +import java.util.Date; + +import org.olat.core.id.Identity; +import org.olat.course.assessment.AssessmentManager; +import org.olat.course.editor.CourseEditorEnv; +import org.olat.course.nodes.CourseNode; +import org.olat.course.run.userview.UserCourseEnvironment; + +/** + * + * Description:<br> + * Function to get the date of the last attempt a user tried to solve a test, do + * a questionnaire and alike. Meaning may be different on the various course + * building blocks. + * <p> + * + * @author gnaegi + * + * Initial Date: Oct 04, 2013 + */ +public class GetLastAttemptDateFunction extends AbstractFunction { + + public static final String name = "getLastAttemptDate"; + + /** + * Default constructor to use the get attempts object + * + * @param userCourseEnv + */ + public GetLastAttemptDateFunction(UserCourseEnvironment userCourseEnv) { + super(userCourseEnv); + } + + /** + * @see com.neemsoft.jmep.FunctionCB#call(java.lang.Object[]) + */ + public Object call(Object[] inStack) { + /* + * argument check + */ + if (inStack.length > 1) { + return handleException(new ArgumentParseException( + ArgumentParseException.NEEDS_FEWER_ARGUMENTS, name, "", + "error.fewerargs", "solution.provideone.nodereference")); + } else if (inStack.length < 1) { + return handleException(new ArgumentParseException( + ArgumentParseException.NEEDS_MORE_ARGUMENTS, name, "", + "error.moreargs", "solution.provideone.nodereference")); + } + /* + * argument type check + */ + if (!(inStack[0] instanceof String)) + return handleException(new ArgumentParseException( + ArgumentParseException.WRONG_ARGUMENT_FORMAT, name, "", + "error.argtype.coursnodeidexpeted", + "solution.example.node.infunction")); + String nodeId = (String) inStack[0]; + /* + * check reference integrity + */ + CourseEditorEnv cev = getUserCourseEnv().getCourseEditorEnv(); + if (cev != null) { + if (!cev.existsNode(nodeId)) { + return handleException(new ArgumentParseException( + ArgumentParseException.REFERENCE_NOT_FOUND, name, + nodeId, "error.notfound.coursenodeid", + "solution.copypastenodeid")); + } + // Remember the reference to the node id for this condition for cycle testing. + // Allow self-referencing but do not allow dependencies to parents as they create cycles. + if (!nodeId.equals(cev.getCurrentCourseNodeId())) { + cev.addSoftReference("courseNodeId", nodeId); + } + // return a valid value to continue with condition evaluation test + return defaultValue(); + } + + /* + * the real function evaluation which is used during run time + */ + CourseNode node = getUserCourseEnv().getCourseEnvironment().getRunStructure().getNode(nodeId); + AssessmentManager am = getUserCourseEnv().getCourseEnvironment().getAssessmentManager(); + Identity identity = getUserCourseEnv().getIdentityEnvironment().getIdentity(); + + // use the last modified date from the course node + Date lastModified = am.getScoreLastModifiedDate(node, identity); + + if (lastModified != null && lastModified.getTime() > 0) { + return Double.valueOf(lastModified.getTime()); + } else { + // what to do in case of no date available??? -> return date in the future + return new Double(Double.POSITIVE_INFINITY); + } + } + + /** + * @see org.olat.course.condition.interpreter.AbstractFunction#defaultValue() + */ + protected Object defaultValue() { + return new Double(Double.MIN_VALUE); + } + +} \ No newline at end of file diff --git a/src/main/java/org/olat/course/editor/_chelp/ced-expert-eg.html b/src/main/java/org/olat/course/editor/_chelp/ced-expert-eg.html index 224bcde6b32b1b895b9df25b33025aad31f7f5c1..342ebcfd3abdb522ffa4bf7af22ad18928beec82 100644 --- a/src/main/java/org/olat/course/editor/_chelp/ced-expert-eg.html +++ b/src/main/java/org/olat/course/editor/_chelp/ced-expert-eg.html @@ -55,6 +55,9 @@ <tr> <td><b>getAttempts("70323524635734") <= 3 </b> <br/>$r.translate("chelp.egR12") <br/><br/></td> </tr> + <tr> + <td><b>getLastAttemptDate("70323524635734") + 24h > now </b> <br/>$r.translate("chelp.egLastAttempt") <br/><br/></td> + </tr> <tr> <td><b>getInitialEnrollmentDate("70323786958847") <= date("26.5.2005 18:00")</b> <br/>$r.translate("chelp.egR13") <br/><br/></td> </tr> diff --git a/src/main/java/org/olat/course/editor/_chelp/ced-expert-expl.html b/src/main/java/org/olat/course/editor/_chelp/ced-expert-expl.html index eda4b898abf81d77cdf4f40dc98732292253cfe3..ade2b0761fe2cab8fe9e56332feefd5b68d09f2e 100644 --- a/src/main/java/org/olat/course/editor/_chelp/ced-expert-expl.html +++ b/src/main/java/org/olat/course/editor/_chelp/ced-expert-expl.html @@ -25,7 +25,7 @@ $r.contextHelpRelativeLink("ced-expert-eg.html") <td>$r.translate("chelp.signNow")</td> </tr> <tr> - <td rowspan="21"><b>$r.translate("chelp.typeFunc")</b></td> + <td rowspan="24"><b>$r.translate("chelp.typeFunc")</b></td> <td><i>$r.translate("chelp.funcDate")</i></td> <td>$r.translate("chelp.signDate")</td> </tr> @@ -98,6 +98,10 @@ $r.contextHelpRelativeLink("ced-expert-eg.html") <td>$r.translate("chelp.funcAtte")</td> <td>$r.translate("chelp.signAtte")</td> </tr> + <tr> + <td>$r.translate("chelp.funcLastAtte")</td> + <td>$r.translate("chelp.signLastAtte")</td> + </tr> <tr> <td>$r.translate("chelp.funcInit")</td> <td>$r.translate("chelp.signInit")</td> diff --git a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties index 4e578a2dafe6bec7dcf5fcce3e000666d36e0e57..eed3c6eb23c6997d60661b868c59059a05934970 100644 --- a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties +++ b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_de.properties @@ -176,6 +176,7 @@ chelp.egR6=Ausschliesslich Studierende der Universit\u00E4t Z\u00FCrich haben Zu chelp.egR7=Gibt $\:chelp.wordTrue f\u00FCr alle Personen, deren Attribut <i>$\:chelp.aaiSN</i> die Buchstabenfolge "Mue" enthaltet. Gibt z.B. $\:chelp.wordTrue f\u00FCr den Wert "Mueller" oder "Muehlebacher" chelp.egR8=Gibt $\:chelp.wordTrue f\u00FCr alle Personen, deren Attribut <i>$\:chelp.aaiEPE</i> den Wert "http\://vam.uzh.ch" enthaltet. Gibt z.B. auch $\:chelp.wordTrue f\u00FCr den Wert "http\://vam.uzh.ch/surgery" chelp.egR9=Diese Regel wird im Tab $\:chelp.pntsF -> $\:chelp.pntsProcF des Bausteins $\:chelp.st gesetzt. Der Baustein $\:chelp.st zeigt 10 Punkte, wenn einer der Tests (Kursbaustein-IDs "69742969114730", "69742969115733" oder "69742969118009") bestanden wurde, sonst 0 Punkte. +chelp.egLastAttempt=Gibt $\:chelp.wordTrue, wenn der letzte Testversuch 24 Stunden zurĆ¼ckliegt. chelp.egThead=Beispiele f\u00FCr Expertenregeln in den Tabs $\:chelp.visF, $\:chelp.accF und $\:chelp.pntsF (Struktur-Baustein) chelp.en=<i>$org.olat.course.nodes\:title_en</i> chelp.end=<b>$org.olat.course.condition\:form.easy.edate</b> @@ -208,6 +209,7 @@ chelp.fldstAccWikiRw=$org.olat.course.nodes.wiki\:condition.editable.title chelp.funcAdm=isCourseAdministrator(0) chelp.funcArea=<i>inLearningArea("</i>$\:chelp.string<i>")</i> chelp.funcAtte=<i>getAttempts("</i>$\:chelp.int<i>")</i> +chelp.funcLastAtte=<i>getLastAttemptDate("</i>$\:chelp.int<i>")</i> chelp.funcAuth=isGlobalAuthor(0) chelp.funcCoach=isCourseCoach(0) chelp.funcParticipant=isCourseParticipant(0) @@ -256,6 +258,7 @@ chelp.signAdm=Gibt $\:chelp.wordTrue f\u00FCr alle Besitzer Ihres Kurses (Lernre chelp.signAnd=Logisches UND chelp.signArea=Gibt $\:chelp.wordTrue f\u00FCr alle Mitglieder der Gruppen im Lernbereich $\:chelp.string chelp.signAtte=Gibt vom Kursbaustein mit spezifizierter ID die Anzahl abgeschlossener Versuche zur\u00FCck. Kann auf Kursbausteine vom Typ $\:chelp.iqtest, $\:chelp.iqself, $\:chelp.iqsurv (m\u00F6gliche R\u00FCckgabewerte 0 oder 1) und $\:chelp.ta (R\u00FCckgabewert \= Anzahl abgegebener Dateien) angewendet werden. +chelp.signLastAtte=Gibt vom Kursbaustein mit spezifizierter ID das Datum des letzen Versuches zur\u00FCck. Die Anwendung ist gleich wie die getAttempts Methode. chelp.signAuth=Gibt $\:chelp.wordTrue f\u00FCr alle Mitglieder der OLAT-Autorengruppe chelp.signCoach=Gibt $\:chelp.wordTrue f\u00FCr alle Benutzer, die eine Lerngruppe oder den gesamten Kurs betreuen chelp.signParticipant=Gibt $\:chelp.wordTrue f\u00FCr alle Teilnehmer des Kurses diff --git a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_en.properties index e9ed83f4572366ac8dbbd1b66ec56a71d8fb32fc..f3a007a17eddb8a669d51d8921fd010cac66c86b 100644 --- a/src/main/java/org/olat/course/editor/_i18n/LocalStrings_en.properties +++ b/src/main/java/org/olat/course/editor/_i18n/LocalStrings_en.properties @@ -163,6 +163,7 @@ chelp.egR6=Only students of the University of Zurich have access to this course chelp.egR7=Generates $\:chelp.wordTrue for all persons whose attribute <i>$\:chelp.aaiSN</i> contains the letter sequence "Mue". E.g. gives $\:chelp.wordTrue for the value "Mueller" or "Muehlebacher" chelp.egR8=Generates $\:chelp.wordTrue for all persons whose attribute <i>$\:chelp.aaiEPE</i> contains the value "http\://vam.uzh.ch". E.g. gives $\:chelp.wordTrue for the value "http\://vam.uzh.ch/surgery" chelp.egR9=This rule is set in the tab $\:chelp.pntsF -> $\:chelp.pntsProcF of the course element $\:chelp.st . The course element $\:chelp.st shows 10 points if one of the tests (course element IDs "69742969114730", "69742969115733" or "69742969118009") was passed. Otherwise 0 points. +chelp.egLastAttempt=Generates $\:chelp.wordTrue when the last test attempt is older than 24 hours chelp.egThead=Examples of expert rules in the tabs $\:chelp.visF, $\:chelp.accF and $\:chelp.pntsF (structural elements) chelp.en=<i>$org.olat.course.nodes\:title_en</i> chelp.end=<b>$org.olat.course.condition\:form.easy.edate</b> @@ -195,6 +196,7 @@ chelp.fldstAccWikiRw=$org.olat.course.nodes.wiki\:condition.editable.title chelp.funcAdm=isCourseAdministrator(0) chelp.funcArea=<i>inLearningArea("</i>$\:chelp.string<i>")</i> chelp.funcAtte=<i>getAttempts("</i>$\:chelp.int<i>")</i> +chelp.funcLastAtte=<i>getLastAttemptDate("</i>$\:chelp.int<i>")</i> chelp.funcAuth=isGlobalAuthor(0) chelp.funcCoach=isCourseCoach(0) chelp.funcCourPass=<i>getPassedWithCourseId("</i>$\:chelp.int1<i>","</i>$\:chelp.int2<i>")</i> @@ -244,6 +246,7 @@ chelp.signAdm=Generates $\:chelp.wordTrue for all owners of a course (learning r chelp.signAnd=Logical AND chelp.signArea=Generates $\:chelp.wordTrue for all group members in the learning area $\:chelp.string chelp.signAtte=Generates the number of completed attempts from a course element with specified ID. Can be applied to course elements of the type $\:chelp.iqtest, $\:chelp.iqself, $\:chelp.iqsurv (possible return values 0 or 1) and $\:chelp.ta (return value \= number of files handed in). +chelp.signLastAtte=Generates the date of the last attempt from a course element with the specified ID. Can be applied like the getAttempts method. chelp.signAuth=Generates $\:chelp.wordTrue for all members of the OLAT author group chelp.signCoach=Generates $\:chelp.wordTrue for all users supervising a learning group chelp.signCourPass=Generates the Boolean $\:chelp.wordTrue (\=Passed) or $\:chelp.wordFalse (\=Failed) from the course element ID\=$\:chelp.int2 of the course ID\=$\:chelp.int1