def __parse_files(base_path): try: f_settings = open(base_path + ".todocalrc", 'r') data_path = base_path + ".todocal/" f_colorcode = open(data_path + "color-code", 'r') f_yearly = open(data_path + "yearly", 'r') f_monthly = open(data_path + "monthly", 'r') f_weekly = open(data_path + "weekly", 'r') f_daily = open(data_path + "daily", 'r') f_norepeat = open(data_path + "no-repeat", 'r') except: tc_meta.raise_ERROR("TodoCal: data file path invalid.") raw_settings = [line.strip() for line in f_settings] raw_colorcode = [line.strip() for line in f_colorcode] raw_yearly = [line.strip() for line in f_yearly] raw_monthly = [line.strip() for line in f_monthly] raw_weekly = [line.strip() for line in f_weekly] raw_daily = [line.strip() for line in f_daily] raw_norepeat = [line.strip() for line in f_norepeat] # filter out empty lines and commented lines filter_settings = [s for s in raw_settings if len(s) != 0 and s[0] != '#'] filter_colorcode = [ s for s in raw_colorcode if len(s) != 0 and s[0] != '#' ] filter_yearly = [s for s in raw_yearly if len(s) != 0 and s[0] != '#'] filter_monthly = [s for s in raw_monthly if len(s) != 0 and s[0] != '#'] filter_weekly = [s for s in raw_weekly if len(s) != 0 and s[0] != '#'] filter_daily = [s for s in raw_daily if len(s) != 0 and s[0] != '#'] filter_norepeat = [s for s in raw_norepeat if len(s) != 0 and s[0] != '#'] return (filter_settings, filter_colorcode, filter_yearly, filter_monthly, filter_weekly, filter_daily, filter_norepeat)
def get_categorizers(L): try: default_index = L.index("__default") todo_index = L.index("__todo") except: tc_meta.raise_ERROR("TodoCal: event data storage problem.") return (default_index, todo_index)
def __prepare_events( E, TIME_INFO): # synthesize event information, return new list synthesis = [] current_year = TIME_INFO["year"] for e in E: new_info = {} new_info["name"] = e["name"] # new_info -> name if e["mark"] != '': new_info["name"] += (' ' + e["mark"] ) # include todo mark in event name event_date_object = datetime.datetime(year=current_year, month=int(e["month"]), day=int(e["day"])) weekday = event_date_object.isoweekday() if weekday == 7: weekday = 0 weekday = str(weekday) new_info["weekday"] = int(weekday) # new_info -> weekday (int) new_info["color-code"] = e["color"] # new_info -> color-code (str) # code segment copy --------------------------------------------------- if ' ' not in e["hour"]: e["hour"] += " 00" e["time-start-code"] = int(e["hour"].replace(' ', '')) try: start_time = [int(s) for s in e["hour"].split(' ')] start_hour = start_time[0] start_minute = start_time[1] except: tc_meta.raise_ERROR("TodoCal: time convertion error.") start_time_object = datetime.time(hour=start_hour, minute=start_minute) start_time_object = datetime.datetime( year=2000, month=1, day=1, hour=start_time_object.hour, minute=start_time_object.minute) # convert time to datetime try: if e["length"] == "": end_time_object = start_time_object else: end_time_object = start_time_object + datetime.timedelta( minutes=int(e["length"])) except: tc_meta.raise_ERROR("TodoCal: time calculation error.") time_end_code = end_time_object.hour * 100 + end_time_object.minute if time_end_code < e["time-start-code"]: time_end_code = 2359 if time_end_code != 0 and time_end_code % 100 == 0: time_end_code -= 41 # reduce to past hour e["time-end-code"] = time_end_code # code segment copy end ----------------------------------------------- new_info["time-start-code"] = e["time-start-code"] new_info["time-end-code"] = e["time-end-code"] # new_info -> name, weekday (int), color-code (str), time-start-code (int), time-end-code (int) synthesis.append(new_info) return synthesis # synthesized new event list
def parse_event(line): if line[0] != '{' or line[-1] != '}': tc_meta.raise_ERROR("TodoCal: event data storage corrupted.") line = line[1:-1].strip() # remove line container kv_pairs = [s.strip() for s in line.split(',')] E = {} for kv in kv_pairs: kv_l = [s.strip() for s in kv.split(':')] key = kv_l[0] value = kv_l[1] E[key] = value return E
def make_main_display(col_width, span_height, DICT, T): # ensure span_height allow all events normal display for (span_hour, E) in DICT.items(): if len(E) > span_height: tc_meta.raise_ERROR("TodoCal: calendar height too small.") else: continue COL = [] # make list of strings for this column display for span_hour in sorted(DICT): # access span hour event lists in order E = DICT[span_hour] # access to event list if len(E) == 0: for _ in range( span_height): # fill span hour height with white space COL.append(' ' * col_width) continue # handle actual events individual_span = span_height // (len(E)) assert (individual_span >= 1) count_use_span = 0 for e in E: if '\u001b[37m\u2713\u001b[0m' in e["name"]: e["name"] = e["name"].replace('\u001b[37m\u2713\u001b[0m', '^') # forbidden character 1 if '\u001b[31m\u2717\u001b[0m' in e["name"]: e["name"] = e["name"].replace('\u001b[31m\u2717\u001b[0m', '%') # forbidden character 2 e_display = T.wrap( e["name"]) # return string list of <= col_width if len(e_display) > individual_span: # event overflows height e_display = e_display[:individual_span] # slice excess last_line = e_display[-1] if ' ' not in last_line: last_line = "..." else: white_space_index = last_line.rfind(' ') last_line = last_line[:white_space_index] + "..." e_display[-1] = last_line # update last line in display assert (len(e_display) == individual_span) for line in e_display: COL.append('\u001b[38;5;' + e["color-code"] + 'm' + ('{line_str:<' + str(col_width) + '}').format( line_str=line) + tc_meta.color_reset) count_use_span += 1 for _ in range(span_height - count_use_span): # fill un-used height COL.append(' ' * col_width) continue assert (len(COL) == span_height * len(spans)) return COL # encoded with color information
def __get_dimension_info(raw_info): # dimension raw info passed from pass_info try: cell_width = int(raw_info["cell-width"]) except: try: cell_width = int(raw_info["max-width"]) // 8 except: tc_meta.raise_ERROR("Error: calendar dimensions not well defined.") if cell_width <= 7: tc_meta.raise_ERROR("TodoCal: unimplemented situation.") # assert (cell_width >= 8) time_col_width = 7 # allow full time display between_col_width = 1 col_width = cell_width calendar_width = time_col_width + col_width * 7 + between_col_width * 7 try: max_height = int(raw_info["max-height"]) except: tc_meta.raise_ERROR("Error: calendar dimensions not well defined.") return (calendar_width, max_height, (time_col_width, between_col_width, col_width))
def __get_calendar_height(max_height, E): # max_height passed from __get_dimension_info # information in e in E (passed from pass_info) # name, month, day, hour, length, (done), mark (0/1 char), color (str) for e in E: # code segment first copy --------------------------------------------- if ' ' not in e["hour"]: e["hour"] += " 00" e["time-start-code"] = int(e["hour"].replace(' ', '')) try: start_time = [int(s) for s in e["hour"].split(' ')] start_hour = start_time[0] start_minute = start_time[1] except: tc_meta.raise_ERROR("TodoCal: time convertion error.") start_time_object = datetime.time(hour=start_hour, minute=start_minute) start_time_object = datetime.datetime( year=2000, month=1, day=1, hour=start_time_object.hour, minute=start_time_object.minute) # convert time to datetime try: if e["length"] == "": end_time_object = start_time_object else: end_time_object = start_time_object + datetime.timedelta( minutes=int(e["length"])) except: tc_meta.raise_ERROR("TodoCal: time calculation error.") time_end_code = end_time_object.hour * 100 + end_time_object.minute if time_end_code < e["time-start-code"]: time_end_code = 2359 if time_end_code != 0 and time_end_code % 100 == 0: time_end_code -= 41 # reduce to past hour e["time-end-code"] = time_end_code # code segment end ---------------------------------------------------- # e.g. hour = "15 20", length = "30" # time-start-code = 1520, time-end-code = 1550 span_mark = [] for _ in range(24): span_mark.append(0) # span_mark [i] => time from i hour to (i + 1) hour for e in E: for i in range(e["time-start-code"] // 100, e["time-end-code"] // 100 + 1): span_mark[i] = 1 spans = [] for i in range(24): if span_mark[i] == 1: spans.append(i) # e.g. spans = [0, 7, 8, 10, 15] # => exist events in 0-1, 7-9, 10-11, 15-16 available_height = max_height - 3 # reserve top rows for calendar header span_height = available_height // (len(spans)) if span_height <= 1: tc_meta.raise_ERROR("TodoCal: unimplemented situation.") # assert (span_height >= 2) calendar_height = span_height * (len(spans)) + 3 return (calendar_height, span_height, spans)
def __parse_DATA(base_path): (settings, colorcode, yearly, monthly, weekly, daily, norepeat) = __parse_files(base_path) DATA = { "meta": { "color-code": {}, "color": {}, "dimension": {} }, "events": { "yearly": { "default": [], "todo": [] }, "monthly": { "default": [], "todo": [] }, "weekly": { "default": [], "todo": [] }, "daily": { "default": [], "todo": [] }, "no-repeat": { "default": [], "todo": [] } } } # initialize DATA # parse settings try: color_settings_index = settings.index("__color") except: tc_meta.raise_ERROR("TodoCal: color settings not defined.") try: dimension_settings_index = settings.index("__dimension") except: tc_meta.raise_ERROR("TodoCal: dimension settings not defined.") for i in range(color_settings_index + 1, dimension_settings_index): line = settings[i] (key, value) = [s.strip() for s in line.split(':')] DATA["meta"]["color"][key] = value for i in range(dimension_settings_index + 1, len(settings)): line = settings[i] (key, value) = [s.strip() for s in line.split(':')] DATA["meta"]["dimension"][key] = value # regularized event parser def parse_event(line): if line[0] != '{' or line[-1] != '}': tc_meta.raise_ERROR("TodoCal: event data storage corrupted.") line = line[1:-1].strip() # remove line container kv_pairs = [s.strip() for s in line.split(',')] E = {} for kv in kv_pairs: kv_l = [s.strip() for s in kv.split(':')] key = kv_l[0] value = kv_l[1] E[key] = value return E # categorize events def get_categorizers(L): try: default_index = L.index("__default") todo_index = L.index("__todo") except: tc_meta.raise_ERROR("TodoCal: event data storage problem.") return (default_index, todo_index) y_indices = get_categorizers(yearly) m_indices = get_categorizers(monthly) w_indices = get_categorizers(weekly) d_indices = get_categorizers(daily) n_indices = get_categorizers(norepeat) (yearly_default, yearly_todo) = (yearly[y_indices[0] + 1:y_indices[1]], yearly[y_indices[1] + 1:len(yearly)]) (monthly_default, monthly_todo) = (monthly[m_indices[0] + 1:m_indices[1]], monthly[m_indices[1] + 1:len(monthly)]) (weekly_default, weekly_todo) = (weekly[w_indices[0] + 1:w_indices[1]], weekly[w_indices[1] + 1:len(weekly)]) (daily_default, daily_todo) = (daily[d_indices[0] + 1:d_indices[1]], daily[d_indices[1] + 1:len(daily)]) (norepeat_default, norepeat_todo) = (norepeat[n_indices[0] + 1:n_indices[1]], norepeat[n_indices[1] + 1:len(norepeat)]) # parse events and update DATA DATA["events"]["yearly"]["default"] = [ parse_event(line) for line in yearly_default ] DATA["events"]["monthly"]["default"] = [ parse_event(line) for line in monthly_default ] DATA["events"]["weekly"]["default"] = [ parse_event(line) for line in weekly_default ] DATA["events"]["daily"]["default"] = [ parse_event(line) for line in daily_default ] DATA["events"]["no-repeat"]["default"] = [ parse_event(line) for line in norepeat_default ] DATA["events"]["yearly"]["todo"] = [ parse_event(line) for line in yearly_todo ] DATA["events"]["monthly"]["todo"] = [ parse_event(line) for line in monthly_todo ] DATA["events"]["weekly"]["todo"] = [ parse_event(line) for line in weekly_todo ] DATA["events"]["daily"]["todo"] = [ parse_event(line) for line in daily_todo ] DATA["events"]["no-repeat"]["todo"] = [ parse_event(line) for line in norepeat_todo ] # parse color codes for line in colorcode: kv_l = [s.strip() for s in line.split(':')] DATA["meta"]["color-code"][kv_l[0]] = kv_l[1] return DATA