diff options
-rw-r--r-- | perc | 586 | ||||
-rw-r--r-- | perc.cfg | 264 |
2 files changed, 585 insertions, 265 deletions
@@ -1,6 +1,7 @@ #! /usr/bin/env python -# McAuthor: CarpNet +# McAuthor: CarpNet (thanks to a paradigm shifting idea by Bikeman) + # TODO 1: some crazy AI heuristics stuff, see The Terminator and Skynet for ideas. # TODO 2: Add time travel capability (note: mind the butterfly effect and for god's sake don't @@ -14,6 +15,8 @@ # TODO 5: Extend chart to be different types (bar, pie, raised-arm-man etc.) # Raised arm man chart: \o/ = 1 hour, \ = 20 minutes, \o = 40 minutes etc. # TODO 6: Web trawling important date finder +# TODO 7: Start and end dates to be accepted +# TODO 8: Birthdays # CHANGE LOG: # 3.21: - Bug fix for option --NICK. Changed option back to --nick @@ -21,8 +24,15 @@ # 3.22: - Fix bug fix to fix the fix that was fixed in the last bug fix # 3.23: - Assumes end time if given one argument, start and end if given two. # - Restructuring of code in parseArgs function - -import datetime +# - Added "halloween" as an IMPORTANT TEMPORAL EVENT +# 3.3: +# - Added 'mode' as second argument, choices are 'user' and 'admin': +# - Users are allowed to check times and dates +# - Admins can create and remove custom events +# - Allows creation and removal of custom temporal events (stored in perc.cfg) +# - Maintains a list of admins allowed to perform admin actions + +import copy import decimal import json import optparse @@ -30,13 +40,21 @@ import re import string import sys +from datetime import datetime, date + PROG = "perc" VERSION = "3.23" CONFIG_FILE = "perc.cfg" +NOW = datetime.now() + +MODES = ["user", "admin"] + +USER = None NICK = "default" CONFIG = {} +CONFIG_IS_DIRTY = False MONDAY = 0 TUESDAY = 1 @@ -52,17 +70,21 @@ WEEK_LENGTH = 7 WEEKEND = [ SATURDAY, SUNDAY ] WORK_WEEK = [ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY ] -IMPORTANT_TEMPORAL_EVENTS = ["tomorrow", "friday", "new year", "newyear", "christmas", "xmas", "weekend", "midday", "tg", "intense", "lunch", "easter", "halloween"] +IMPORTANT_TEMPORAL_EVENTS = ["tomorrow", "friday", "new year", "newyear", "christmas", "xmas", "weekend", "midday", "noon", "tg", "intense", "lunch", "easter", "halloween"] OUTPUT_TYPES = ["perc", "bar", "ram", "pint"] -parser = optparse.OptionParser("{0} <finish> [options] | {0} <start> <finish> [options]".format(PROG)) -parser.add_option("-v", "--version", action="store_true", dest="version", help="Display the version of perc") -parser.add_option("-s", "--start", action="store", dest="start", help="The start time of your particular day") -parser.add_option("-f", "--finish", action="store", dest="finish", help="The finish time of your particular day") -parser.add_option("-n", "--nick", action="store", dest="nick", help="Nickname of someone (e.g. fuckwad, l_bratch, bikeman or something)") -parser.add_option("-o", "--output", action="store", type="choice", choices=OUTPUT_TYPES, dest="output", help="Output display type. Can be a simple percentage or different types of chart. Options are %s" % ", ".join(OUTPUT_TYPES)) -parser.add_option("-m", "--mobile", action="store_true", default=False, dest="mobile", help="Indicate this should be displayed appropriately for a mobile device (i.e. smaller character display width)") +VERB_FORMS = ["is", "is not", "should be", "could be", "would be", "ought to be", "might be", "may become"] + +main_parser = optparse.OptionParser("{0} <finish> [options] | {0} <start> <finish> [options]".format(PROG)) +main_parser.add_option("-v", "--version", action="store_true", dest="version", help="Display the version of perc") +main_parser.add_option("-s", "--start", action="store", dest="start", help="The start time of your particular day") +main_parser.add_option("-f", "--finish", action="store", dest="finish", help="The finish time of your particular day") +main_parser.add_option("-n", "--nick", action="store", dest="nick", help="Nickname of someone (e.g. fuckwad, l_bratch, bikeman or something)") +main_parser.add_option("-o", "--output", action="store", type="choice", choices=OUTPUT_TYPES, dest="output", help="Output display type. Can be a simple percentage or different types of chart. Options are %s" % ", ".join(OUTPUT_TYPES)) +main_parser.add_option("-m", "--mobile", action="store_true", default=False, dest="mobile", help="Indicate this should be displayed appropriately for a mobile device (i.e. smaller character display width)") +main_parser.add_option("-a", "--add-event", action="store", dest="add_event") +main_parser.add_option("-r", "--remove-event", action="store", dest="remove_event") def clamp(value, min_value, max_value): if value < min_value: @@ -93,7 +115,7 @@ def delta_in_seconds(delta): def calculate_ratio(start, finish): total = float(delta_in_seconds(finish - start)) - done = float(delta_in_seconds(datetime.datetime.now() - start)) + done = float(delta_in_seconds(NOW - start)) ratio = 0.0 if total != 0.0: @@ -111,7 +133,7 @@ def round_to_sigfig(number, sigfig): except TypeError: d = float_to_decimal(float(number)) - sign , digits,exponent = d.as_tuple() + sign, digits, exponent = d.as_tuple() if len(digits) < sigfig: digits = list(digits) @@ -146,47 +168,26 @@ def round_to_sigfig(number, sigfig): return ''.join(result) -def format_time(time, standard): - formatstr = CONFIG["FORMATTING"][standard] - - period = "" - - if "{period}" in formatstr: - period = "am" if time.hour < 12 else "pm" - - if time.hour > 12: - time = time.replace(hour=time.hour - 12) - - return formatstr.format(hour=time.hour, minute=time.minute, period=period) - def parseTime(timestr): - format = "civilian" - - for key, value in CONFIG["STANDARDS"].items(): - m = re.search(value, timestr) - - if m: - hour = int(m.group(1)) - minute = int(m.group(2)) if m.group(2) else 0 - second = 0 - - period = "" - if m.group(3): - period = m.group(3).upper() - if period == "PM" and hour < 12: - hour += 12 - - if hour < 0 or hour > 23 or minute < 0 or minute > 59 or second < 0 or second > 59: - print "Not a possible time '%s'" % timestr - sys.exit(1) - - return datetime.datetime.now().replace(hour=hour, minute=minute, second=second, microsecond=0), key - - print "Not a correctly formatted time/day: '%s'" % timestr + for key, value in CONFIG["FORMAT"]["time"].items(): + try: + t = datetime.strptime(timestr, value) + return NOW.replace(hour=t.hour, minute=t.minute, second=t.second, microsecond=t.microsecond), value + except ValueError: + pass + + for key, value in CONFIG["FORMAT"]["date"].items(): + try: + t = datetime.strptime(timestr, value) + return NOW.replace(year=t.year, month=t.month, day=t.day, hour=t.hour, minute=t.minute, second=t.second, microsecond=t.microsecond), value + except ValueError: + pass + + print "Not a correctly formatted time/date: '%s'" % timestr sys.exit(1) def isImportantTemporalEvent(event): - return event in IMPORTANT_TEMPORAL_EVENTS + return event in IMPORTANT_TEMPORAL_EVENTS + CONFIG["CUSTOM_EVENTS"].keys() def isTime(timestr): for key, value in CONFIG["STANDARDS"].items(): @@ -198,36 +199,38 @@ def isTime(timestr): return False def getDayOfWeek(day): - now = datetime.datetime.now() - - return (now - day_of_week() * datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0) + day * datetime.timedelta(days=1) + return (NOW - day_of_week() * datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0) + day * datetime.timedelta(days=1) def parseTemporalEvent(event): start, finish = None, None - - now = datetime.datetime.now() + + work_start = datetime.combine(NOW.date(), datetime.strptime(CONFIG["DEFAULTS"][NICK]["start"], "%H:%M").time()) + work_finish = datetime.combine(NOW.date(), datetime.strptime(CONFIG["DEFAULTS"][NICK]["finish"], "%H:%M").time()) if event == "tomorrow": - start = now.replace(hour=0, minute=0, second=0, microsecond=0) - finish = now.replace(day=now.day + 1, hour=0, minute=0, second=0, microsecond=0) + start = NOW.replace(hour=0, minute=0, second=0, microsecond=0) + finish = NOW.replace(day=NOW.day + 1, hour=0, minute=0, second=0, microsecond=0) start_formatted = WEEK_DAY_NAMES[day_of_week()] - finish_formatted = WEEK_DAY_NAMES[day_of_week(datetime.date.today() + datetime.timedelta(days=1))] + finish_formatted = WEEK_DAY_NAMES[day_of_week(date.today() + datetime.timedelta(days=1))] elif event in ["christmas", "xmas"]: - start = now.replace(year=now.year - 1, month=12, day=25, hour=0, minute=0, second=0, microsecond=0) - finish = now.replace(year=now.year, month=12, day=25, hour=0, minute=0, second=0, microsecond=0) + start = NOW.replace(year=NOW.year - 1, month=12, day=25, hour=0, minute=0, second=0, microsecond=0) + finish = NOW.replace(year=NOW.year, month=12, day=25, hour=0, minute=0, second=0, microsecond=0) start_formatted = "%s %d" % (string.capwords(event), start.year) finish_formatted = "%s %d" % (string.capwords(event), finish.year) elif event in ["new year", "newyear"]: - start = now.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0) - finish = now.replace(year=now.year + 1, month=1, day=1, hour=0, minute=0, second=0, microsecond=0) + start = NOW.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0) + finish = NOW.replace(year=NOW.year + 1, month=1, day=1, hour=0, minute=0, second=0, microsecond=0) start_formatted = str(start.year) finish_formatted = str(finish.year) elif event == "weekend": start = getDayOfWeek(MONDAY) - finish = getDayOfWeek(SATURDAY) + finish = getDayOfWeek(FRIDAY) + + start = datetime.combine(start.date(), work_start.time()) + finish = datetime.combine(finish.date(), work_finish.time()) start_formatted = "Monday" finish_formatted = "Weekend" @@ -235,48 +238,47 @@ def parseTemporalEvent(event): start = getDayOfWeek(MONDAY) finish = getDayOfWeek(FRIDAY) + start = datetime.combine(start.date(), work_start.time()) + start_formatted = "Monday" finish_formatted = "Friday" - elif event == "midday": - start = now.replace(hour=0, minute=0, second=0, microsecond=0) - finish = now.replace(hour=12, minute=0, second=0, microsecond=0) + elif event in ["midday", "noon"]: + start = NOW.replace(hour=0, minute=0, second=0, microsecond=0) + finish = NOW.replace(hour=12, minute=0, second=0, microsecond=0) start_formatted = "Midnight" - finish_formatted = "Midday" + finish_formatted = string.capwords(event) elif event == "halloween": - start = now.replace(year=now.year - 1, month=10, day=31, hour=0, minute=0, second=0, microsecond=0) - finish = now.replace(year=now.year, month=10, day=31, hour=0, minute=0, second=0, microsecond=0) + start = NOW.replace(year=NOW.year - 1, month=10, day=31, hour=0, minute=0, second=0, microsecond=0) + finish = NOW.replace(year=NOW.year, month=10, day=31, hour=0, minute=0, second=0, microsecond=0) start_formatted = "Halloween %d" % start.year finish_formatted = "Halloween %d" % finish.year - elif event == "intense": - start = now.replace(year=2011, month=9, day=4, hour=18, minute=0, second=0, microsecond=0) - finish = now.replace(year=2011, month=11, day=11, hour=20, minute=0, second=0, microsecond=0) - - start_formatted = "last intense" - finish_formatted = "next intense (minibirthday)" - elif event == "tg": - start = now.replace(year=2011, month=4, day=24, hour=10, minute=0, second=0, microsecond=0) - finish = now.replace(year=2012, month=4, day=4, hour=12, minute=0, second=0, microsecond=0) - - start_formatted = "TG %d" % start.year - finish_formatted = "TG %d" % finish.year elif event == "lunch": - lunch_start = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["lunch"]["start"]) - lunch_finish = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["lunch"]["finish"]) - if now < lunch_start: - start = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["start"]) + lunch_start = datetime.combine(NOW.date(), datetime.strptime(CONFIG["DEFAULTS"][NICK]["lunch"]["start"]).time()) + lunch_finish = datetime.combine(NOW.date(), datetime.strptime(CONFIG["DEFAULTS"][NICK]["lunch"]["finish"]).time()) + format = CONFIG["DEFAULTS"][NICK]["format"] + + if NOW < lunch_start: + start = work_start finish = lunch_start - start_formatted = format_time(start, CONFIG["DEFAULTS"][NICK]["standard"]) - finish_formatted = "lunch (%s)" % format_time(finish, CONFIG["DEFAULTS"][NICK]["standard"]) + finish_formatted = "Lunch (%s)" % finish.strftime(format) else: start = lunch_start finish = lunch_finish - start_formatted = format_time(start, CONFIG["DEFAULTS"][NICK]["standard"]) - finish_formatted = format_time(finish, CONFIG["DEFAULTS"][NICK]["standard"]) + finish_formatted = finish.strftime(format) + + start_formatted = start.strftime(format) + elif event in CONFIG["CUSTOM_EVENTS"]: + start = datetime.strptime(CONFIG["CUSTOM_EVENTS"][event]["start"], "%Y-%m-%d %H:%M") + finish = datetime.strptime(CONFIG["CUSTOM_EVENTS"][event]["finish"], "%Y-%m-%d %H:%M") + format = CONFIG["CUSTOM_EVENTS"][event]["format"] + + start_formatted = start.strftime(format).format(name=event) + finish_formatted = finish.strftime(format).format(name=event) return start, finish, start_formatted, finish_formatted @@ -284,90 +286,133 @@ def readConfig(): global CONFIG with open(CONFIG_FILE, "r") as f: CONFIG = json.load(f) + +def day_of_week(day=date.today()): + return date.weekday(day) + +def isWeekend(): + return day_of_week() in WEEKEND + +def combination_pairs(first, second): + combined = [] - if not CONFIG: - print "Config file '%s' not found. Exiting..." % CONFIG_FILE - sys.exit(1) + for x in first: + for y in second: + combined.append("%s %s" % (x, y)) + combined.append("%s %s" % (y, x)) - if "STANDARDS" not in CONFIG: - print "No STANDARDS specified in config file: '%s'" % CONFIG_FILE + return combined + +def parseDatetime(datetimestring): + date_formats = CONFIG["FORMAT"]["date"].values() + time_formats = CONFIG["FORMAT"]["time"].values() + combined_formats = combination_pairs(date_formats, time_formats) + + for format in date_formats + time_formats + combined_formats: + try: + t = datetime.strptime(datetimestring, format) + return t, format + except ValueError: + pass + + print "Not a recognised date/time '%s'" % datetimestring + sys.exit(1) + +def setUser(name, start=None, finish=None, format=None, output=None): + user = {} + + if name in CONFIG["DEFAULTS"]: + if NICK != name: + print "Cannot edit someone else's perc settings!" + sys.exit(1) + + user = CONFIG["DEFAULTS"][name] + + if start: + user["start"] = start.strftime("%H:%M") + if finish: + user["finish"] = finish.strftime("%H:%M") + if format: + user["format"] = format + if output: + user["output"] = output + + CONFIG["DEFAULTS"][name] = user + +def setUserLunch(name, start=None, finish=None): + if name not in CONFIG["DEFAULTS"]: + print "User must be added to the config first using -u command" sys.exit(1) - if "FORMATTING" not in CONFIG: - print "No FORMATTING options specified in config file: '%s'" % CONFIG_FILE + if NICK != name: + print "Cannot edit someone else's perc settings!" sys.exit(1) - # TODO: can do set comparison here (possibly disjoint?) - for key in CONFIG["STANDARDS"]: - if key not in CONFIG["FORMATTING"]: - print "No format specified for standard '%s'" % key - sys.exit(1) - -def day_of_week(day=datetime.date.today()): - return datetime.date.weekday(day) + lunch = CONFIG["DEFAULTS"][name]["lunch"] + if start: + lunch["start"] = start + if finish: + lunch["finish"] = finish + + CONFIG["DEFAULTS"][name]["lunch"] = lunch -def isWeekend(): - return day_of_week() in WEEKEND +def addEvent(name, start, finish, format="{name} %Y"): + if start > finish: + print "Event cannot have a start time later than the finish" + sys.exit(1) + + if name in CONFIG["CUSTOM_EVENTS"]: + print "Removing existing event with same name" + + event = { + "start": start.strftime("%Y-%m-%d %H:%M"), + "finish": finish.strftime("%Y-%m-%d %H:%M"), + "format": format + } + + CONFIG["CUSTOM_EVENTS"][name] = event + + global CONFIG_IS_DIRTY + CONFIG_IS_DIRTY = True -def parseArgs(args): - global NICK - NICK = args[0].lower() if args else "default" - if options.nick: - NICK = options.nick.lower() - if NICK not in CONFIG["DEFAULTS"]: - NICK = "default" +def removeEvent(name): + if name in CONFIG["CUSTOM_EVENTS"]: + del CONFIG["CUSTOM_EVENTS"][name] - start, finish = None, None - start_formatted, finish_formatted = None, None - now = datetime.datetime.now() + global CONFIG_IS_DIRTY + CONFIG_IS_DIRTY = True + +def parseUserArgs(args, user): + start, finish, start_formatted, finish_formatted = user.start, user.finish, user.start.strftime(user.format), user.finish.strftime(user.format) - if len(args) >= 2 and isImportantTemporalEvent(args[1].lower()): - start, finish, start_formatted, finish_formatted = parseTemporalEvent(args[1].lower()) - elif len(args) >= 3: - start, standard = parseTime(args[1]) - if options.start: - start, standard = parseTime(options.start) - - finish, standard = parseTime(args[2]) - if options.finish: - finish, standard = parseTime(options.finish) - - if finish < start: - finish = finish.replace(day=finish.day + 1) - - start_formatted = format_time(start, standard) - finish_formatted = format_time(finish, standard) - elif len(args) >= 2: - start = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["start"]) + if args and isImportantTemporalEvent(args[0].lower()): + start, finish, start_formatted, finish_formatted = parseTemporalEvent(args[0].lower()) + else: + format = None + times = args[0:1] - finish, standard = parseTime(args[1]) - if options.finish: - finish, standard = parseTime(options.finish) + default_start = datetime.strptime(CONFIG["DEFAULTS"][NICK]["start"], "%H:%M") + default_finish = datetime.strptime(CONFIG["DEFAULTS"][NICK]["finish"], "%H:%M") - if finish < start: - finish = finish.replace(day=finish.day + 1) + # Set start and finish to defaults + start = datetime.combine(NOW.date(), default_start.time()) + finish = datetime.combine(NOW.date(), default_finish.time()) - start_formatted = format_time(start, standard) - finish_formatted = format_time(finish, standard) - else: - standard = None + if len(times) == 1: + finish, format = parseDatetime(times[0])#parseTime(times[0]) + elif len(times) >= 2: + start, format = parseDatetime(times[0])#parseTime(times[0]) + finish, format = parseDatetime(times[1])#parseTime(times[1]) - start = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["start"]) if options.start: - start, standard = parseTime(options.start) - - finish = now.replace(hour=0, minute=0, second=0, microsecond=0).replace(**CONFIG["DEFAULTS"][NICK]["finish"]) + start, format = parseDatetime(options.start)#parseTime(options.start) if options.finish: - finish, standard = parseTime(options.finish) - - if standard is None: - standard = CONFIG["DEFAULTS"][NICK]["standard"] + finish, format = parseDatetime(options.finish)#parseTime(options.finish) + if format is None: + format = CONFIG["DEFAULTS"][NICK]["format"] - if finish < start: - finish = finish.replace(day=finish.day + 1) - - start_formatted = format_time(start, standard) - finish_formatted = format_time(finish, standard) + start_formatted = start.strftime(format) + finish_formatted = finish.strftime(format) output_type = options.output if options.output else CONFIG["DEFAULTS"][NICK]["output"] @@ -375,14 +420,29 @@ def parseArgs(args): return start_formatted, finish_formatted, ratio, output_type +class Event(object): + + Time = property(fget=lambda self: self._time) + Format = property(fget=lambda self: self._format) + + def __init__(self, time, format=None): + self._time = time + self._format = format + + def __str__(self): + return self._time.strftime(self._format) + +class EventImportant(Event): + pass + class Output(object): def perc(self, ratio, start, finish): - print CONFIG["FORMATTING"]["PERCENTAGE"].format(ratio=ratio, start=start, finish=finish) + print CONFIG["OUTPUT"]["PERCENTAGE"].format(ratio=ratio, start=start, finish=finish) def bar(self, ratio, start, finish): - length = CONFIG["FORMATTING"]["BAR"]["length"] if not options.mobile else CONFIG["FORMATTING"]["BAR"]["length_mobile"] - char = CONFIG["FORMATTING"]["BAR"]["character"] + length = CONFIG["OUTPUT"]["BAR"]["length"] if not options.mobile else CONFIG["OUTPUT"]["BAR"]["length_mobile"] + char = CONFIG["OUTPUT"]["BAR"]["character"] current = clamp(int(ratio * length), 0, length) @@ -398,34 +458,224 @@ class Output(object): # Raised arm men parts (3 parts per raised arm man and 10 of them) ram_parts = 1.0 / (3.0 * 10.0) + + print "Not implemented" def pint(self, ratio, start, finish): - pass + print "Not implemented" def output(self, type, ratio, start, finish): func = getattr(self, type, self.perc) func(ratio, start, finish) -if __name__ == "__main__": - readConfig() +class User(object): + + Name = property(fget=lambda self: self._name) + + def __init__(self, name, data=None): + self._name = name + if data: + self.data = copy.deepcopy(data) + else: + self.data = {} + + def commit(self): + if self.Name in CONFIG["DEFAULTS"] and USER.Name != self.Name: + print "Cannot edit someone else's perc settings!" + sys.exit(1) + + if self.Name == "default": + print "Cannot edit default user" + sys.exit(1) + + CONFIG["DEFAULTS"][self.Name] = self.data + + @property + def start(self): + try: + return datetime.combine(NOW.date(), datetime.strptime(self.data["start"], "%H:%M").time()) + except KeyError: + return CONFIG["DEFAULTS"]["default"]["start"] + + @start.setter + def start(self, value): + self.data["start"] = value + + @start.deleter + def start(self): + del self.data["start"] + + @property + def finish(self): + try: + return datetime.combine(NOW.date(), datetime.strptime(self.data["finish"], "%H:%M").time()) + except KeyError: + return CONFIG["DEFAULTS"]["default"]["finish"] + + @finish.setter + def finish(self, value): + self.data["finish"] = value + + @finish.deleter + def finish(self): + del self.data["finish"] + + @property + def format(self): + try: + return self.data["format"] + except KeyError: + return CONFIG["DEFAULTS"]["default"]["format"] + + @format.setter + def format(self, value): + self.data["format"] = value + + @format.deleter + def format(self): + del self.data["format"] + + @property + def output(self): + try: + return self.data["output"] + except KeyError: + return CONFIG["DEFAULTS"]["default"]["output"] + + @output.setter + def output(self, value): + self.data["output"] = value + + @output.deleter + def output(self): + del self.data["output"] + + @property + def lunch(self): + try: + return self.data["lunch"] + except KeyError: + return CONFIG["DEFAULTS"]["default"]["lunch"] + + @lunch.setter + def lunch(self, value): + self.data["lunch"] = value + + @lunch.deleter + def lunch(self): + del self.data["lunch"] + +def admin(args): + if NICK not in CONFIG["ADMINS"]: + print "You do not have admin permissions" + sys.exit(1) global options - (options, args) = parser.parse_args() + parser = optparse.OptionParser() + parser.add_option("-v", "--version", action="store_true", dest="version", help="Display the version of %s" % PROG) + parser.add_option("-a", "--add-event", action="store", dest="add_event", help="Add the named event to {prog} to be used in the usual !{prog} <event_name> way".format(prog=PROG)) + parser.add_option("-u", "--user", action="store", dest="user", help="Reference the named user. Used in tandem with -l and -s and -f commands. If only used with -s and/or -f then sets start and finish times of entire day for user. If used with -l and -s and/or -f commands sets the lunch times of that user.") + parser.add_option("-l", "--lunch", action="store_true", default=False, dest="lunch", help="Indicate that lunch times are to be set.") + parser.add_option("-r", "--remove-event", action="store", dest="remove_event", help="Remove a named event from %s" % PROG) + parser.add_option("-s", "--start", action="store", dest="start", help="Used in conjunction with -a/--add-event option. The start date/time of the event.") + parser.add_option("-f", "--finish", action="store", dest="finish", help="Used in conjunction with -a/--add-event option. The finish date/time of the event.") + parser.add_option("-p", "--format", action="store", default="{name} %Y", dest="format", help="Used in conjunction with -a/--add-event option. The format of the printed event date/time, uses python strftime formatting and accepts {name} keyword to display name of event.") + parser.add_option("-o", "--output", action="store", type="choice", choices=OUTPUT_TYPES, dest="output", help="The output display type for the user. Options are %s" % ", ".join(OUTPUT_TYPES)) + + (options, args) = parser.parse_args(args) if options.version: print "v%s" % VERSION sys.exit(0) - if isWeekend(): - print "It's the freekin' Weekend!!!!" + if options.user: + + else + if options.add_event: + if not options.start or not options.finish: + print "--add-event option requires --start and --finish date/time options to be set" + sys.exit(1) + + name = options.add_event + start, f = parseDatetime(options.start) + finish, f = parseDatetime(options.finish) + format = options.format + + addEvent(name, start, finish, format) + + if options.remove_event: + removeEvent(options.remove_event) + + if CONFIG_IS_DIRTY: + with open(CONFIG_FILE, "w") as f: + json.dump(CONFIG, f, indent=4) + +def user(args): + global options + + parser = optparse.OptionParser("{0} <finish> [options] | {0} <start> <finish> [options]".format(PROG)) + parser.add_option("-v", "--version", action="store_true", dest="version", help="Display the version of %s" % PROG) + parser.add_option("-s", "--start", action="store", dest="start", help="The start time of your particular day") + parser.add_option("-f", "--finish", action="store", dest="finish", help="The finish time of your particular day") + parser.add_option("-n", "--nick", action="store", dest="nick", help="Nickname of someone (e.g. fuckwad, l_bratch, bikeman or something)") + parser.add_option("-o", "--output", action="store", type="choice", choices=OUTPUT_TYPES, dest="output", help="Output display type. Options are %s" % ", ".join(OUTPUT_TYPES)) + parser.add_option("-m", "--mobile", action="store_true", default=False, dest="mobile", help="Indicate this should be displayed appropriately for a mobile device (i.e. smaller character display width)") + + (options, args) = parser.parse_args(args) + + if options.version: + print "v%s" % VERSION sys.exit(0) - if "perc" in args: - args.remove("perc") + user = USER + if options.nick: + nick = options.nick.lower() + user = User(nick, CONFIG["DEFAULTS"].get(nick)) - start, finish, ratio, output_type = parseArgs(args) + start, finish, ratio, output_type = parseUserArgs(args, user) output = Output() output.output(output_type, ratio, start, finish) + +def main(): + readConfig() + + if isWeekend(): + print "It's the freekin' Weekend!!!!" + sys.exit(0) + + args = sys.argv[1:] + if "perc" in args: + args.remove("perc") + +# global NICK +# if args: +# NICK = args.pop(0).lower() +# else: +# NICK = "default" +# +# if NICK not in CONFIG["DEFAULTS"]: +# NICK = "default" + global USER + USER = User("default") + if args: + nick = args.pop(0).lower() + USER = User(nick, CONFIG["DEFAULTS"].get(nick)) + + mode = "user" + if args: + if args[0] in MODES: + mode = args.pop(0) + + if mode == "user": + user(args) + elif mode == "admin": + admin(args) + else: + print "Not a recognised mode '%s'" % mode + sys.exit(1) + +if __name__ == "__main__": + main() @@ -1,99 +1,169 @@ { - "RATIO_SIGFIGS": 3, - - "STANDARDS": { - "civilian": "^(\\d+):(\\d{2})()$", - "military": "(\\d+)(\\d{2})()$", - "12hour": "^(\\d+):(\\d{2})([a-zA-Z]{2})$", - "12houronly": "^(\\d+)()([a-zA-Z]{2})$", - "24houronly": "^(\\d+)()()$" - }, - - "FORMATTING": { - "civilian": "{hour:02d}:{minute:02d}", - "military": "{hour:02d}{minute:02d}", - "12hour": "{hour:02d}:{minute:02d}{period}", - "12houronly": "{hour}{period}", - "24houronly": "{hour}", - - "PERCENTAGE": "{ratio:.2%} {finish} ({start} start)", - "BAR": { - "character": "-", - "length": 40, - "length_mobile": 30 - } - }, - - "DEFAULTS": { - "default": { - "start": { "hour": 9 }, - "finish": { "hour": 17 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "carpnet": { - "start": { "hour": 10 }, - "finish": { "hour": 19 }, - "standard": "12houronly", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "l_bratch": { - "start": { "hour": 8, "minute": 30 }, - "finish": { "hour": 17 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "bikeman": { - "start": { "hour": 9 }, - "finish": { "hour": 17 }, - "standard": "12houronly", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "otherlw": { - "start": { "hour": 10 }, - "finish": { "hour": 19 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "wjoe": { - "start": { "hour": 9 }, - "finish": { "hour": 17 }, - "standard": "12houronly", - "output": "bar", - "lunch": { "start": { "hour": 12 }, "finish": { "hour": 13 } } - }, - "jagw": { - "start": { "hour": 9 }, - "finish": { "hour": 18 }, - "standard": "12houronly", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "miniwork": { - "start": { "hour": 9 }, - "finish": { "hour": 17, "minute": 30 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "bottlecap": { - "start": { "hour": 9 }, - "finish": { "hour": 17, "minute": 30 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - }, - "sbeans": { - "start": { "hour": 9 }, - "finish": { "hour": 17, "minute": 30 }, - "standard": "civilian", - "output": "bar", - "lunch": { "start": { "hour": 13 }, "finish": { "hour": 14 } } - } - } + "FORMAT": { + "date": { + "slashes": "%Y/%m/%d", + "hyphens": "%Y-%m-%d" + }, + "time": { + "12hour": "%I:%M%p", + "civilian": "%H:%M", + "12houronly": "%I%p", + "military": "%H%M" + } + }, + "RATIO_SIGFIGS": 3, + "ADMINS": [ + "bikeman", + "bondroid", + "bottlecap", + "carpnet", + "daveg", + "dhm", + "dsk", + "fbeans", + "sbeans", + "jagw", + "l_bratch", + "miniwork", + "otherlw", + "otherlp", + "wjoe" + ], + "DEFAULTS": { + "daveg": { + "start": "07:30", + "format": "%H:%M", + "finish": "18:00", + "lunch": { + "start": "12:30", + "finish": "13:00" + }, + "output": "bar" + }, + "bikeman": { + "start": "09:00", + "format": "%I%p", + "finish": "17:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "wjoe": { + "start": "09:00", + "format": "%I%p", + "finish": "17:00", + "lunch": { + "start": "12:00", + "finish": "13:00" + }, + "output": "bar" + }, + "default": { + "start": "09:00", + "format": "%H:%M", + "finish": "17:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "l_bratch": { + "start": "08:30", + "format": "%H:%M", + "finish": "17:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "bottlecap": { + "start": "09:00", + "format": "%H:%M", + "finish": "17:30", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "carpnet": { + "start": "10:00", + "format": "%H:%M", + "finish": "19:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "miniwork": { + "start": "09:00", + "format": "%H:%M", + "finish": "17:30", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "otherlw": { + "start": "10:00", + "format": "%H:%M", + "finish": "19:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "jagw": { + "start": "09:00", + "format": "%I%p", + "finish": "18:00", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + }, + "sbeans": { + "start": "09:00", + "format": "%H:%M", + "finish": "17:30", + "lunch": { + "start": "13:00", + "finish": "14:00" + }, + "output": "bar" + } + }, + "OUTPUT": { + "PERCENTAGE": "{ratio:.2%} {finish} ({start} start)", + "BAR": { + "character": "-", + "length": 40, + "length_mobile": 30 + } + }, + "CUSTOM_EVENTS": { + "tg": { + "start": "2010-04-24 10:00", + "finish": "2011-04-04 12:00", + "format": "{name} %Y" + }, + "intense": { + "start": "2011-09-04 18:00", + "finish": "2011-11-11 20:00", + "format": "{name} (%y-%m-%d %H:%M)" + }, + "birth": { + "start": "2011-03-28 10:00", + "finish": "2012-03-28 00:00", + "format": "{name} %Y" + } + } } |