diff --git a/inkycal/modules/inkycal_calendar.py b/inkycal/modules/inkycal_calendar.py index 8d01330..92ff6dd 100755 --- a/inkycal/modules/inkycal_calendar.py +++ b/inkycal/modules/inkycal_calendar.py @@ -4,11 +4,13 @@ Inkycal Calendar Module Copyright by aceisace """ -from inkycal.modules.template import inkycal_module -from inkycal.custom import * + +# pylint: disable=logging-fstring-interpolation import calendar as cal import arrow +from inkycal.modules.template import inkycal_module +from inkycal.custom import * logger = logging.getLogger(__name__) @@ -21,39 +23,32 @@ class Calendar(inkycal_module): name = "Calendar - Show monthly calendar with events from iCalendars" optional = { - "week_starts_on": { "label": "When does your week start? (default=Monday)", "options": ["Monday", "Sunday"], - "default": "Monday" + "default": "Monday", }, - "show_events": { "label": "Show parsed events? (default = True)", "options": [True, False], - "default": True + "default": True, }, - "ical_urls": { "label": "iCalendar URL/s, separate multiple ones with a comma", }, - "ical_files": { "label": "iCalendar filepaths, separated with a comma", }, - "date_format": { - "label": "Use an arrow-supported token for custom date formatting " + - "see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. D MMM", + "label": "Use an arrow-supported token for custom date formatting " + + "see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. D MMM", "default": "D MMM", }, - "time_format": { - "label": "Use an arrow-supported token for custom time formatting " + - "see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. HH:mm", - "default": "HH:mm" + "label": "Use an arrow-supported token for custom time formatting " + + "see https://arrow.readthedocs.io/en/stable/#supported-tokens, e.g. HH:mm", + "default": "HH:mm", }, - } def __init__(self, config): @@ -62,6 +57,11 @@ class Calendar(inkycal_module): super().__init__(config) config = config['config'] + self.ical = None + self.month_events = None + self._upcoming_events = None + self._days_with_events = None + # optional parameters self.weekstart = config['week_starts_on'] self.show_events = config['show_events'] @@ -82,11 +82,17 @@ class Calendar(inkycal_module): # additional configuration self.timezone = get_system_tz() self.num_font = ImageFont.truetype( - fonts['NotoSans-SemiCondensed'], size=self.fontsize) + fonts['NotoSans-SemiCondensed'], size=self.fontsize + ) # give an OK message print(f'{__name__} loaded') + @staticmethod + def flatten(values): + """Flatten the values.""" + return [x for y in values for x in y] + def generate_image(self): """Generate image for this module""" @@ -94,6 +100,7 @@ class Calendar(inkycal_module): im_width = int(self.width - (2 * self.padding_left)) im_height = int(self.height - (2 * self.padding_top)) im_size = im_width, im_height + events_height = 0 logger.info(f'Image size: {im_size}') @@ -110,7 +117,9 @@ class Calendar(inkycal_module): if self.show_events: logger.debug("Allocating space for events") calendar_height = int(im_height * 0.6) - events_height = im_height - month_name_height - weekdays_height - calendar_height + events_height = ( + im_height - month_name_height - weekdays_height - calendar_height + ) logger.debug(f'calendar-section size: {im_width} x {calendar_height} px') logger.debug(f'events-section size: {im_width} x {events_height} px') else: @@ -132,49 +141,60 @@ class Calendar(inkycal_module): logger.debug(f"y_spacing_calendar :{y_spacing_calendar}") # Calculate positions for days of month - grid_start_y = (month_name_height + weekdays_height + y_spacing_calendar) + grid_start_y = month_name_height + weekdays_height + y_spacing_calendar grid_start_x = x_spacing_calendar - grid_coordinates = [(grid_start_x + icon_width * x, grid_start_y + icon_height * y) - for y in range(calendar_rows) for x in range(calendar_cols)] + grid_coordinates = [ + (grid_start_x + icon_width * x, grid_start_y + icon_height * y) + for y in range(calendar_rows) + for x in range(calendar_cols) + ] - weekday_pos = [(grid_start_x + icon_width * _, month_name_height) for _ in - range(calendar_cols)] + weekday_pos = [ + (grid_start_x + icon_width * _, month_name_height) + for _ in range(calendar_cols) + ] now = arrow.now(tz=self.timezone) # Set weekstart of calendar to specified weekstart if self.weekstart == "Monday": cal.setfirstweekday(cal.MONDAY) - weekstart = now.shift(days=- now.weekday()) + weekstart = now.shift(days=-now.weekday()) else: cal.setfirstweekday(cal.SUNDAY) - weekstart = now.shift(days=- now.isoweekday()) + weekstart = now.shift(days=-now.isoweekday()) # Write the name of current month - write(im_black, (0, 0), (im_width, month_name_height), - str(now.format('MMMM', locale=self.language)), font=self.font, - autofit=True) + write( + im_black, + (0, 0), + (im_width, month_name_height), + str(now.format('MMMM', locale=self.language)), + font=self.font, + autofit=True, + ) # Set up weeknames in local language and add to main section - weekday_names = [weekstart.shift(days=+_).format('ddd', locale=self.language) - for _ in range(7)] + weekday_names = [ + weekstart.shift(days=+_).format('ddd', locale=self.language) + for _ in range(7) + ] logger.debug(f'weekday names: {weekday_names}') - for _ in range(len(weekday_pos)): + for idx, weekday in enumerate(weekday_pos): write( im_black, - weekday_pos[_], + weekday, (icon_width, weekdays_height), - weekday_names[_], + weekday_names[idx], font=self.font, autofit=True, - fill_height=1.0 + fill_height=1.0, ) # Create a calendar template and flatten (remove nestings) - flatten = lambda z: [x for y in z for x in y] - calendar_flat = flatten(cal.monthcalendar(now.year, now.month)) + calendar_flat = self.flatten(cal.monthcalendar(now.year, now.month)) # logger.debug(f" calendar_flat: {calendar_flat}") # Map days of month to co-ordinates of grid -> 3: (row2_x,col3_y) @@ -190,8 +210,15 @@ class Calendar(inkycal_module): # Add the numbers on the correct positions for number in calendar_flat: if number != int(now.day): - write(im_black, grid[number], (icon_width, icon_height), - str(number), font=self.num_font, fill_height=0.5, fill_width=0.5) + write( + im_black, + grid[number], + (icon_width, icon_height), + str(number), + font=self.num_font, + fill_height=0.5, + fill_width=0.5, + ) # Draw a red/black circle with the current day of month in white icon = Image.new('RGBA', (icon_width, icon_height)) @@ -199,10 +226,24 @@ class Calendar(inkycal_module): x_circle, y_circle = int(icon_width / 2), int(icon_height / 2) radius = int(icon_width * 0.2) ImageDraw.Draw(icon).ellipse( - (x_circle - radius, y_circle - radius, x_circle + radius, y_circle + radius), - fill='black', outline=None) - write(icon, (0, 0), (icon_width, icon_height), str(now.day), - font=self.num_font, fill_height=0.5, colour='white') + ( + x_circle - radius, + y_circle - radius, + x_circle + radius, + y_circle + radius, + ), + fill='black', + outline=None, + ) + write( + icon, + (0, 0), + (icon_width, icon_height), + str(now.day), + font=self.num_font, + fill_height=0.5, + colour='white', + ) im_colour.paste(icon, current_day_pos, icon) # If events should be loaded and shown... @@ -217,17 +258,21 @@ class Calendar(inkycal_module): events_height += icon_height * 2 # import the ical-parser + # pylint: disable=import-outside-toplevel from inkycal.modules.ical_parser import iCalendar # find out how many lines can fit at max in the event section line_spacing = 0 - max_event_lines = events_height // (self.font.getsize('hg')[1] + - line_spacing) + max_event_lines = events_height // ( + self.font.getsize('hg')[1] + line_spacing + ) # generate list of coordinates for each line events_offset = im_height - events_height - event_lines = [(0, events_offset + int(events_height / max_event_lines * _)) - for _ in range(max_event_lines)] + event_lines = [ + (0, events_offset + int(events_height / max_event_lines * _)) + for _ in range(max_event_lines) + ] # logger.debug(f"event_lines {event_lines}") @@ -250,8 +295,9 @@ class Calendar(inkycal_module): self.month_events = month_events # find out on which days of this month events are taking place - days_with_events = [int(events['begin'].format('D')) for events in - month_events] + days_with_events = [ + int(events['begin'].format('D')) for events in month_events + ] # remove duplicates (more than one event in a single day) list(set(days_with_events)).sort() @@ -266,13 +312,12 @@ class Calendar(inkycal_module): (icon_width, icon_height), radius=6, thickness=1, - shrinkage=(0.4, 0.2) + shrinkage=(0.4, 0.2), ) # Filter upcoming events until 4 weeks in the future parser.clear_events() - upcoming_events = parser.get_events(now, now.shift(weeks=4), - self.timezone) + upcoming_events = parser.get_events(now, now.shift(weeks=4), self.timezone) self._upcoming_events = upcoming_events # delete events which won't be able to fit (more events than lines) @@ -284,56 +329,97 @@ class Calendar(inkycal_module): # Find out how much space (width) the date format requires lang = self.language - date_width = int(max([self.font.getsize( - events['begin'].format(self.date_format, locale=lang))[0] - for events in upcoming_events]) * 1.1) + date_width = int( + max( + ( + self.font.getsize( + events['begin'].format(self.date_format, locale=lang) + )[0] + for events in upcoming_events + ) + ) + * 1.1 + ) - time_width = int(max([self.font.getsize( - events['begin'].format(self.time_format, locale=lang))[0] - for events in upcoming_events]) * 1.1) + time_width = int( + max( + ( + self.font.getsize( + events['begin'].format(self.time_format, locale=lang) + )[0] + for events in upcoming_events + ) + ) + * 1.1 + ) line_height = self.font.getsize('hg')[1] + line_spacing event_width_s = im_width - date_width - time_width event_width_l = im_width - date_width - # Display upcoming events below calendar - tomorrow = now.shift(days=1).floor('day') - in_two_days = now.shift(days=2).floor('day') + # Display upcoming events below calendar TODO: not used? + # tomorrow = now.shift(days=1).floor('day') + # in_two_days = now.shift(days=2).floor('day') cursor = 0 for event in upcoming_events: if cursor < len(event_lines): - name = event['title'] - date = event['begin'].format(self.date_format, locale=lang) - time = event['begin'].format(self.time_format, locale=lang) - # logger.debug(f"name:{name} date:{date} time:{time}") + the_name = event['title'] + the_date = event['begin'].format(self.date_format, locale=lang) + the_time = event['begin'].format(self.time_format, locale=lang) + # logger.debug(f"name:{the_name} date:{the_date} time:{the_time}") if now < event['end']: - write(im_colour, event_lines[cursor], (date_width, line_height), - date, font=self.font, alignment='left') + write( + im_colour, + event_lines[cursor], + (date_width, line_height), + the_date, + font=self.font, + alignment='left', + ) # Check if event is all day if parser.all_day(event): - write(im_black, (date_width, event_lines[cursor][1]), - (event_width_l, line_height), name, font=self.font, - alignment='left') + write( + im_black, + (date_width, event_lines[cursor][1]), + (event_width_l, line_height), + the_name, + font=self.font, + alignment='left', + ) else: - write(im_black, (date_width, event_lines[cursor][1]), - (time_width, line_height), time, font=self.font, - alignment='left') + write( + im_black, + (date_width, event_lines[cursor][1]), + (time_width, line_height), + the_time, + font=self.font, + alignment='left', + ) - write(im_black, (date_width + time_width, event_lines[cursor][1]), - (event_width_s, line_height), name, font=self.font, - alignment='left') + write( + im_black, + (date_width + time_width, event_lines[cursor][1]), + (event_width_s, line_height), + the_name, + font=self.font, + alignment='left', + ) cursor += 1 else: symbol = '- ' while self.font.getsize(symbol)[0] < im_width * 0.9: symbol += ' -' - write(im_black, event_lines[0], - (im_width, self.font.getsize(symbol)[1]), symbol, - font=self.font) + write( + im_black, + event_lines[0], + (im_width, self.font.getsize(symbol)[1]), + symbol, + font=self.font, + ) # return the images ready for the display return im_black, im_colour