summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--perc586
-rw-r--r--perc.cfg264
2 files changed, 585 insertions, 265 deletions
diff --git a/perc b/perc
index c906b42..b125b3a 100644
--- a/perc
+++ b/perc
@@ -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()
diff --git a/perc.cfg b/perc.cfg
index a3f02b6..018f379 100644
--- a/perc.cfg
+++ b/perc.cfg
@@ -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"
+ }
+ }
}