Esempio n. 1
0
    def getGradeAdjustment(self, score, total, timestamp, allowNeg=True):
        """
        Given the submission's score, assignment total, and timestamp,
        returns the adjustment based on this policy. Respects 
        tamarin.GRADE_PRECISION.  
        
        Normally, returns the adjustment even if this is a penalty greater than
        the score itself.  If allowNeg is False, any computed penalty is 
        capped at -score.
        
        Remember, if passed a non-numeric score, any $ rules will convert to
        % instead.  This way, a numeric value can always be returned.  
                
        """
        import tamarin
        if self.span_sign == '+':
            assert self.deadline < timestamp
            timestamp = min(timestamp, self.end)
            timespan = (tamarin.convertTimestampToTime(timestamp) -
                        tamarin.convertTimestampToTime(self.deadline)) 
        else:
            assert timestamp <= self.deadline
            timestamp = max(timestamp, self.end)
            timespan = (tamarin.convertTimestampToTime(self.deadline) -
                        tamarin.convertTimestampToTime(timestamp)) 

        if not self.rule_sign:
            #no rule, which means do nothing to the grade
            return 0 
            
        if not self.rule_unit:
            modifier = self.rule_value 
        elif self.rule_unit == '$' and \
                (isinstance(score, int) or isinstance(score, float)):
            modifier = (self.rule_value * score) / 100
        else:   # self.rule_unit == '%', or '$' with non-numeric 
            modifier = (self.rule_value * total) / 100
                    
        if self.rule_sign == '-':
            modifier *= -1
        
        times = 1
        if self.rule_repeater and not self.rule_sign == '=':
            days = int(self.rule_day[:-1]) if self.rule_day else 0
            hours = int(self.rule_hour[:-1]) if self.rule_hour else 0
            minutes = int(self.rule_minute[:-1]) if self.rule_minute else 0
            repeater = datetime.timedelta(days=days, hours=hours, 
                                        minutes=minutes)
            times = timespan / repeater
            if self.span_sign == '+':
                times = math.ceil(times)  #include any partial repeater span
            else:
                times = math.floor(times) #only complete repeater span
            
        gradeAdj = round(modifier * times, tamarin.GRADE_PRECISION)        
        if not allowNeg and score + gradeAdj < 0:
            gradeAdj = -score
        return gradeAdj
Esempio n. 2
0
 def getLateOffset(self, submittedTimestamp=None):
     """
     Returns a string in the format of '+#d #h #m' showing the lateness of
     the given Tamarin timestamp.
     
     If a timestamp is not given, will use the current time instead.
     If the given timestamp is before this assignment's deadline, will
     return a negative offset showing how early the submission is.
     """
     # convert to datetimes
     import tamarin
     submitted = tamarin.convertTimestampToTime(submittedTimestamp)
     deadline = tamarin.convertTimestampToTime(self.due)
     # to avoid dealing with negative timedeltas (which are weird)
     if submitted <= deadline:
         offset = deadline - submitted
         sign = '-'
     else:
         offset = submitted - deadline
         sign = '+'
     hours = offset.seconds // 3600
     minutes = (offset.seconds // 60) - (hours * 60)
     return '{0}{1}d {2}h {3:02}m'.format(sign, offset.days, hours, minutes)
Esempio n. 3
0
    def __init__(self, policy, deadline):
        """
        Constructs a LatePolicy according to the given policy string.

        The deadline should be in Tamarin timestamp format.  
                
        Each policy should be a string in the form of: 'range:rule'
        
        A range has the following format: [+|-][[#d][#h][#m]|timestamp]
        
        That is, the range may optionally begin with either + or -.  
        If the sign is omitted, + is assumed.  
        
        The sign may then be followed by a span specified in days, 
        hours, and minutes.  This span is relative to the assignment deadline,
        either earlier (if sign is -) or later (if sign is +).  A particular
        unit may be omitted if unused; it will be assumed to be 0.  If more 
        than 23 hours or 59 minutes are specified, the corresponding overflow 
        into days and hours will be calculated.  At least one unit must be
        given though: d for day, h for hours, or m for minutes.  If more
        than one is used, they must be in that order.
        
        As an alternative to specifying a relative span, a specific timestamp
        may be given. The sign is ignored in these cases. (Since LatePolicies 
        usually apply across multiple assignments, specific timestamps are 
        rare.  However, they can be handy to specify a final cutoff date for 
        all assignments--such as the last day of the course.)  
        
        It is an error to have a 0 length span, either because the relative
        span is of size 0 or if the timestamp given is equal to the deadline.
        
        If neither span nor timestamp is given, the end of the span is 
        treated as year 0 for a - sign or year 9999 for a positive sign.
                
        The range is then separated from the associated rule by a colon (:).
        
        The rule is of the form: [+|-|=][#[%|$]][/[#d][#h][#m]]

        That is, the rule may begin with a sign: +, -, or =.  If omitted,
        = is assumed.  The sign specifies whether the grade should be 
        increased by (+), decreased (-) by, or set equal to (=) the following
        value. 
        
        The sign is followed by an integer value, which may be appended by 
        either % or $.  Without a modifier, the value is treated as a raw 
        point modifier.  If appended with a %, this is a percentage of the 
        assignment's total possible score.  If appended with a $, 
        it is a percentage of the submission's actual score.  (If the actual
        score to be modified is not numeric, will default to % instead.)
        
        Optionally, the modifier may be followed by a / and relative span.
        At least one value--whether day, hour, or minutes--must be given
        after a /.  When such a span is given, the modifier is applied for 
        each span that the assignment is late.  For example, /1d means apply
        the modifier for each day.  Such as span does nothing if applied to 
        an = modifier.  If this rule is associated with a -timespan ("bonus 
        policy"), it is applied for each full span. Otherwise, it is applied 
        for each partial span.
        
        The rule may be omitted.  In this case, the submission is marked late
        (or early), but no grade change is applied.
                
        Examples:
        +5d:-1/1d - For up to 5 days after the deadline, submissions suffer 
        a penalty of 1 point off for each (partial) day.
        
        +48h:-10% - For 2 days after the deadline, any submission loses 10% 
        of the assignment total.  (So, for a 5 point assignment, this would 
        be -1 point.)  Equivalent to '2d:-10%' (if relying on the assumed 
        + sign on the timespan).
        
        -3d:+5$/1d - An example of an "early bonus" policy, this grants a 
        cumulative bonus of 5 percent of the submission's score for each full 
        day early.
        
        20121221-1200:40$ - Using a timestamp to define the span, anything 
        submitted after the deadline but before noon on 21 Dec 2012 will 
        receive 40% of the score it would otherwise have received.  
        (:=40$ or :-60$ would also have been equivalent rules, since = is 
        assumed on rules without a sign.)
        
        1d: - Anything submitted for up to 1 day past the deadline is marked
        as late but its grade is unaffected.
        
        Given only the policies above, any submissions later than the deadline
        plus the given late period timespan would be refused by Tamarin.
        
        +:10% - Anything submitted after the deadline, no matter how late or 
        how bad, is given 10% of the total possible assignment value.  
        
        : - Anything submitted after the deadline is late but still accepted.
        
        """
        import tamarin
        self.raw = policy
        self.deadline = deadline
        assert re.match(r'\d{8}-\d{4}', self.deadline)
                
        span_re = r'(\+|-)?((\d{8}-\d{4})|((\d+d)?(\d+h)?(\d+m)?))'
        rule_re = r'(\+|-|=)?(\d*)([%$])?(/(\d+d)?(\d+h)?(\d+m)?)?'
        parsed = re.match(span_re + ':' + rule_re, policy)
        if not parsed:
            raise TamarinError('INVALID_LATE_POLICY', policy)

        (self.span_sign, unused_offset, self.span_timestamp, 
         relative, self.span_day, self.span_hour, self.span_minute,
         self.rule_sign, self.rule_value, self.rule_unit, 
         self.rule_repeater, 
         self.rule_day, self.rule_hour, self.rule_minute) = parsed.groups()
        
        #apply defaults and constrains
        if not self.span_sign:
            self.span_sign = '+'
        
        if self.span_timestamp:
            self.end = self.span_timestamp
        elif relative:
            day = hour = minute = 0
            sign = -1 if self.span_sign == '-' else 1           
            if self.span_day:
                day = int(self.span_day[:-1]) * sign
            if self.span_hour:
                hour = int(self.span_hour[:-1]) * sign
            if self.span_minute:
                minute = int(self.span_minute[:-1]) * sign
            endDate = tamarin.convertTimestampToTime(deadline)            
            shift = datetime.timedelta(days=day, hours=hour, minutes=minute)
            self.end = tamarin.convertTimeToTimestamp(endDate + shift)
        else:
            if self.span_sign == '-':
                self.end = '00000101-0000'
            else: 
                self.end = '99991231-2359'
        
        if self.end == self.deadline:
            raise TamarinError('INVALID_LATE_POLICY', 
                               "Parsable, but deadline == endpoint.")

        if self.rule_sign or self.rule_value or self.rule_unit or \
                self.rule_repeater:
            #some part of a rule given, so lets see if it's all valid
            if not self.rule_sign:
                self.rule_sign = '='
            if not self.rule_value:
                raise TamarinError('INVALID_LATE_POLICY', 
                                   "No grade change value given.")
            self.rule_value = int(self.rule_value)
            if self.rule_repeater and not self.rule_day and \
                    not self.rule_hour and not self.rule_minute:
                raise TamarinError('INVALID_LATE_POLICY', 
                                   "Empty /modifier given: " + self.raw)