'Check minimum intervals between consecutive registrations
of the same event type
@param person_id: the person record ID
@param type_id: check only this event type (rather than all types)
@return: a dict with blocked event types
{type_id: (error_message, blocked_until_datetime)}'

def check_intervals(self, person_id, type_id=None):

    T = current.T
    db = current.db
    s3db = current.s3db
    now = current.request.utcnow
    day_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
    next_day = (day_start + datetime.timedelta(days=1))
    output = {}
    table = s3db.dvr_case_event
    event_type_id = table.type_id
    event_types = self.get_event_types()
    etable = s3db.dvr_case_event_exclusion
    query = (((((table.person_id == person_id) & (table.date >= day_start)) & (table.deleted == False)) & (etable.excluded_by_id == table.type_id)) & (etable.deleted == False))
    if (type_id and event_types.get(type_id)):
        query &= (etable.type_id == type_id)
    rows = db(query).select(etable.type_id, etable.excluded_by_id)
    excluded = {}
    for row in rows:
        tid = row.type_id
        if (tid in excluded):
            excluded[tid].append(row.excluded_by_id)
        else:
            excluded[tid] = [row.excluded_by_id]
    for (tid, excluded_by_ids) in excluded.items():
        event_type = event_types.get(tid)
        if (not event_type):
            continue
        excluded_by_names = []
        seen = set()
        for excluded_by_id in excluded_by_ids:
            if (excluded_by_id in seen):
                continue
            else:
                seen.add(excluded_by_id)
            excluded_by_type = event_types.get(excluded_by_id)
            if (not excluded_by_type):
                continue
            excluded_by_names.append(s3_str(T(excluded_by_type.name)))
        if excluded_by_names:
            msg = (T('%(event)s    already    registered    today,    not    combinable') % {'event': ',    '.join(excluded_by_names)})
            output[tid] = (msg, next_day)
    def type_query(items):
        if (len(items) == 1):
            return (event_type_id == items[0])
        elif items:
            return event_type_id.belongs(set(items))
        else:
            return None
    q = None
    if type_id:
        event_type = event_types.get(type_id)
        if (event_type and event_type.max_per_day and (type_id not in output)):
            q = type_query((type_id,))
    else:
        check = [tid for (tid, row) in event_types.items() if (row.max_per_day and (tid != '_default') and (tid not in output))]
        q = type_query(check)
    if (q is not None):
        cnt = table.id.count()
        query = ((((table.person_id == person_id) & q) & (table.date >= day_start)) & (table.deleted != True))
        rows = db(query).select(event_type_id, cnt, groupby=event_type_id)
        for row in rows:
            number = row[cnt]
            tid = row[event_type_id]
            event_type = event_types[tid]
            limit = event_type.max_per_day
            if (number >= limit):
                if (number > 1):
                    msg = (T('%(event)s    already    registered    %(number)s    times    today') % {'event': T(event_type.name), 'number': number})
                else:
                    msg = (T('%(event)s    already    registered    today') % {'event': T(event_type.name)})
                output[tid] = (msg, next_day)
    q = None
    if type_id:
        event_type = event_types.get(type_id)
        if (event_type and event_type.min_interval and (type_id not in output)):
            q = type_query((type_id,))
    else:
        check = [tid for (tid, row) in event_types.items() if (row.min_interval and (tid != '_default') and (tid not in output))]
        q = type_query(check)
    if (q is not None):
        query = (((table.person_id == person_id) & q) & (table.deleted != True))
        timestamp = table.date.max()
        rows = db(query).select(event_type_id, timestamp, groupby=event_type_id)
        represent = table.date.represent
        for row in rows:
            latest = row[timestamp]
            tid = row[event_type_id]
            event_type = event_types[tid]
            interval = event_type.min_interval
            if latest:
                earliest = (latest + datetime.timedelta(hours=interval))
                if (earliest > now):
                    msg = (T('%(event)s    already    registered    on    %(timestamp)s') % {'event': T(event_type.name), 'timestamp': represent(latest)})
                    output[tid] = (msg, earliest)
    return output
