示例#1
0
 def _reschedule_duration(self):
     for entry, successor in window(self._entries):
         duration = successor.start_dt - entry.start_dt - self.gap
         if duration <= timedelta(0):
             raise UserValueError(_('The chosen time gap would result in an entry with a duration of less than a '
                                    'minute. Please choose a smaller gap between entries.'))
         entry.object.duration = duration
示例#2
0
def _get_category_score(user, categ, attended_events, debug=False):
    if debug:
        print(repr(categ))
    # We care about events in the whole timespan where the user attended some events.
    # However, this might result in some missed events e.g. if the user was not working for
    # a year and then returned. So we throw away old blocks (or rather adjust the start time
    # to the start time of the newest block)
    first_event_date = attended_events[0].start_dt.replace(hour=0, minute=0)
    last_event_date = attended_events[-1].start_dt.replace(
        hour=0, minute=0) + timedelta(days=1)
    blocks = _get_blocks(
        _query_categ_events(categ, first_event_date, last_event_date),
        attended_events)
    for a, b in window(blocks):
        # More than 3 months between blocks? Ignore the old block!
        if b[0].start_dt - a[-1].start_dt > timedelta(weeks=12):
            first_event_date = b[0].start_dt.replace(hour=0, minute=0)

    # Favorite categories get a higher base score
    score = int(categ in user.favorite_categories)
    if debug:
        print(f'{score:+.3f} - initial')
    # if there is a favorite event in the category
    if any(e.category == categ for e in user.favorite_events):
        score += 0.1
    if debug:
        print(f'{score:+.3f} - favorite events')
    # Attendance percentage goes to the score directly. If the attendance is high chances are good that the user
    # is either very interested in whatever goes on in the category or it's something he has to attend regularily.
    total = _query_categ_events(categ, first_event_date,
                                last_event_date).count()
    if total:
        attended_block_event_count = sum(1 for e in attended_events
                                         if e.start_dt >= first_event_date)
        score += attended_block_event_count / total
    if debug:
        print(f'{score:+.3f} - attendance')
    # If there are lots/few unattended events after the last attended one we also update the score with that
    total_after = _query_categ_events(categ,
                                      last_event_date + timedelta(days=1),
                                      None).count()
    if total_after < total * 0.05:
        score += 0.25
    elif total_after > total * 0.25:
        score -= 0.5
    if debug:
        print(f'{score:+.3f} - unattended new events')
    # Lower the score based on how long ago the last attended event was if there are no future events
    # We start applying this modifier only if the event has been more than 40 days in the past to avoid
    # it from happening in case of monthly events that are not created early enough.
    days_since_last_event = (date.today() - last_event_date.date()).days
    if days_since_last_event > 40:
        score -= 0.025 * days_since_last_event
    if debug:
        print(f'{score:+.3f} - days since last event')
    # For events in the future however we raise the score
    now_local = utc_to_server(now_utc())
    attending_future = (_query_categ_events(
        categ, now_local,
        last_event_date).filter(Event.id.in_(e.id
                                             for e in attended_events)).all())
    if attending_future:
        score += 0.25 * len(attending_future)
        if debug:
            print(f'{score:+.3f} - future event count')
        days_to_future_event = (attending_future[0].start_dt.date() -
                                date.today()).days
        score += max(0.1,
                     -(max(0, days_to_future_event - 2) / 4)**(1 / 3) + 2.5)
        if debug:
            print(f'{score:+.3f} - days to next future event')
    return score