Пример #1
0
 def choice(evt):
     if as_date(evt.start) == nowdate:
         return isinstance(evt.end, datetime) and evt.start >= now
     else:
         return evt.recurring
Пример #2
0
 def evt2short(evt):
     if as_date(evt.start) > as_date(now):
         dark = evt.recurring
     else:
         dark = as_datetime(evt.end) <= now
     return _evt2short(evt, dark=dark)
Пример #3
0
 def _evt2short(evt):
     if as_date(evt.start) > as_date(self.now):
         dark = dark_recurring and evt.recurring
     else:
         dark = as_datetime(evt.end) <= self.now
     return evt2short(evt, dark=dark)
Пример #4
0
def fourweek(
    todate,
    calendars,
    termsize=None,
    objs=None,
    zero_offset=False,
    table_height=4,
    no_recurring=False,
):
    table_width = 7

    inner_width = (termsize.columns - (table_width + 1)) // table_width
    inner_height = (termsize.lines - (table_height + 1)) // table_height

    table = []

    offset = todate.isoweekday() % 7
    rev_offset = 0
    if zero_offset:
        rev_offset = (7 - offset) % 7
        offset = 0

    def do_row(fill, left, mid=None, right=None, thick=None):
        if mid is None:
            mid = left
        if right is None:
            right = left
        if thick is None:
            thick = mid
        line = left
        for _ in range(table_width):
            line += fill * inner_width + mid
        if rev_offset:
            index = rev_offset * (inner_width + 1)
            line = line[:index] + thick + line[index + 1:]
        line = line[:-1] + right
        return LGRAY + line + RESET

    obj, _evt2short = objs
    now = datetime.now(tzlocal())
    nowdate = now.date()

    def evt2short(evt):
        if as_date(evt.start) > as_date(now):
            dark = evt.recurring
        else:
            dark = as_datetime(evt.end) <= now
        return _evt2short(evt, dark=dark)

    def choice(evt):
        if as_date(evt.start) == nowdate:
            return isinstance(evt.end, datetime) and evt.start >= now
        else:
            return evt.recurring

    callist = filter_calendars(obj, calendars)

    OPEN = object()
    CLOSED = object()
    weekcells = [[ddict(lambda: OPEN) for _ in range(table_width)]
                 for _ in range(table_height)]
    cells = [(list(), list()) for _ in range(table_width * table_height)]

    calstart = todate - t(days=offset)
    events = get_events(
        obj,
        todate - t(days=offset),
        table_width * table_height,
        callist,
        local_recurring=True,
    )

    running_width = 0
    ftime_len = len(ftime()) + 1
    for evt in events:
        # XXX do DRY with following loop
        if no_recurring and evt.recurring:
            continue
        cellnum = (as_date(evt.start) - calstart).days
        cellend = (as_date(evt.end) - calstart).days
        if (0 <= cellnum < len(cells)) or (0 < cellend <= len(cells)):
            length = len(evt.summary)
            if isinstance(evt.start, datetime):
                length += ftime_len
            running_width = max(length, running_width)
    if termsize.columns_auto:
        inner_width = min(inner_width, running_width)

    for evt in events:
        if no_recurring and evt.recurring:
            continue
        cellnum = (as_date(evt.start) - calstart).days
        cellend = (as_date(evt.end) - calstart).days
        if (0 <= cellnum < len(cells)) or (0 < cellend <= len(cells)):
            cellnum = max(0, cellnum)
            if isinstance(evt.start, datetime):
                text = ftime(evt.start) + ' ' + evt.summary
                text = shorten(text, inner_width)
                text = fg(evt2short(evt)) + text + RESET
                cells[cellnum][choice(evt)].append(text)
            else:
                # full-day event
                # XXX code copied from weekview, need to DRY
                start_week = cellnum // table_width
                end_week = (cellnum +
                            (evt.end - evt.start).days - 1) // table_width
                end_week = min(end_week, table_height - 1)
                for week in range(start_week, end_week + 1):
                    text = ' ' + evt.summary
                    week_start = calstart + t(days=(week * table_width))
                    week_end = week_start + t(days=table_width)

                    incellnum = (max(evt.start, week_start) - week_start).days
                    excellnum = (min(evt.end, week_end) - week_start).days
                    ndays = excellnum - incellnum

                    daycells = weekcells[week]
                    subcells = daycells[incellnum:excellnum]
                    topslot = max(map(len, subcells))
                    for j in range(topslot):
                        if all(j >= len(daycell) or daycell[j] is OPEN
                               for daycell in subcells):
                            # found an empty slot range
                            break
                    else:
                        j = topslot

                    if evt.start < week_start:
                        text = '┄' + text

                    if (evt.end - evt.start).days > 1:
                        outlen = ndays * (inner_width + 1) - 1
                        text += (' ' + DASH * (outlen - len(text) - 2) +
                                 ('┄' if evt.end > week_end else '>'))
                        text = shorten(text, outlen)
                    else:
                        text = shorten(text, inner_width)

                    daycell = subcells[0]
                    assert daycell[j] is OPEN
                    daycell[j] = fg(evt2short(evt)) + text + RESET

                    for daycell in subcells[1:]:
                        assert daycell[j] is OPEN
                        daycell[j] = CLOSED

    filled_cells = []

    for i in range(table_height):
        daycells = weekcells[i]
        for j in range(table_width):
            daycell = daycells[j]
            cell, cell_recurring = cells[i * table_width + j]
            filled_cell = []
            for k in range(max(daycell.keys(), default=-1) + 1):
                text = daycell[k]
                if text is OPEN:
                    text = ' ' * inner_width
                elif text is CLOSED:
                    text = None
                filled_cell.append(text)
            filled_cell.extend(cell)
            if filled_cell:
                filled_cell.append(' ' * inner_width)
            filled_cell.extend(cell_recurring)
            filled_cells.append(filled_cell)

    max_inner_height = max(len(cell) for cell in filled_cells) + 2
    if termsize.lines_auto:
        inner_height = min(inner_height, max_inner_height)

    # set up table borders
    table.append(do_row(DASH, *CORNERS[0]))
    for _ in range(table_height):
        for _ in range(inner_height):
            table.append([])
        table.append(do_row(DASH, *CORNERS[1]))
    table.pop()
    table.append(do_row(DASH, *CORNERS[2]))

    # overwrite table with content of cells
    for i in range(table_height):
        for j in range(table_width):
            cell = filled_cells[i * table_width + j]
            for k in range(inner_height):
                lineIndex = i * (inner_height + 1) + k + 1
                text = ' ' * inner_width
                if k == 0:
                    celldate = todate + t(days=(i * table_width + j - offset))
                    datetext = dtime(celldate)
                    dcolor = LGRAY
                    if celldate == now.date():
                        datetext = f'> {datetext} <'
                        dcolor = WBOLD
                    text = (dcolor + shorten(f'{datetext:^{inner_width}}',
                                             inner_width) + RESET)
                elif k == 1:
                    text = LGRAY + DASH * inner_width + RESET
                else:
                    k -= 2
                    if k + 2 == inner_height - 1 and len(cell) > k + 1:
                        text = shorten(
                            format('... more ...', f'^{inner_width}'),
                            inner_width)
                        text = fg(4) + text + RESET
                    elif k < len(cell):
                        text = cell[k]
                table[lineIndex].append(text)

    newtable = []
    for line in table:
        if isinstance(line, list):
            text = ''
            for i, segment in enumerate(line):
                if segment is not None:
                    if rev_offset and rev_offset == i:
                        text += LGRAY + THICK + RESET
                    else:
                        text += LGRAY + PIPE + RESET
                    text += segment
            text += LGRAY + PIPE + RESET
            newtable.append(text)
        else:
            newtable.append(line)
    if outofdate := string_outofdate(obj, now):
        newtable[0] = place(outofdate, 2, newtable[0])
Пример #5
0
class Agenda:
    def __init__(self,
                 calendars,
                 objs=None,
                 dark_recurring=False,
                 interval=None):
        self.now = datetime.now(tzlocal())
        self.obj, evt2short = objs

        def _evt2short(evt):
            if as_date(evt.start) > as_date(self.now):
                dark = dark_recurring and evt.recurring
            else:
                dark = as_datetime(evt.end) <= self.now
            return evt2short(evt, dark=dark)

        self.evt2short = _evt2short
        self.callist = filter_calendars(self.obj, calendars)
        self.interval = t(minutes=(interval or 15))
        self.seen_events = set()

    # assumes times with granularity at minutes
    def quantize(self, thetime, endtime=False):
        if endtime:
            return self.quantize(thetime - t(minutes=1))
        minutes = self.interval.seconds // 60
        theminutes = (thetime.hour * 60 + thetime.minute) // minutes * minutes
        return datetime(
            thetime.year,
            thetime.month,
            thetime.day,
            theminutes // 60,
            theminutes % 60,
            tzinfo=thetime.tzinfo,
        )

    def agenda_table(self,
                     todate,
                     ndays=None,
                     starttime=None,
                     print_warning=True):
        self.todate = todate
        self.has_later = False

        if starttime is None:
            starttime = time(tzinfo=tzlocal())

        # table[0] is the time column
        # table[n] is the event column for day n
        # each column is a map keyed by tick
        # special key None is for full-day events
        # also table[0][None] is reserved for status messages (like the outofdate string)
        #
        # table[0][None]: list[str]  (status messages that should print before the table)
        # table[0][tick]: bool       (whether there is an event starting at this time anywhere in the table
        # table[n][None]: list[evt]  (list of full-day events)
        # table[n][tick]: list[evt]  (list of events starting at that tick)
        actual_ndays = ndays or 1
        table = [ddict(lambda: None)]
        table[0][None] = []
        for _ in range(actual_ndays):
            table.append(ddict(list))

        if print_warning:
            if outofdate := string_outofdate(self.obj, self.now):
                table[0][None].append(outofdate)

        events = get_events(self.obj,
                            todate,
                            actual_ndays,
                            self.callist,
                            local_recurring=True)

        self.longest_summary = max((len(evt.summary) for evt in events),
                                   default=0)

        for evt in events:
            # get column (1-indexed)
            # accounts for events that start before the first day
            startindex = (as_date(evt.start) - todate).days
            index = max(startindex, 0) + 1
            if not isinstance(evt.start, datetime):
                # full-day event
                if evt.id not in self.seen_events:
                    table[index][None].append(evt)
                    self.seen_events.add(evt.id)
                continue
            # timeblock event
            if startindex >= 0:
                tickt = self.quantize(evt.start).time()
            else:
                tickt = starttime
            table[0][tickt] = tickt
            table[index][tickt].append(evt)

        return table