def main(): if len(sys.argv) == 1: cmd_handler(['-h']) elif sys.argv[1] == 'commit': # commits should not be handled by argparse because the command # string must remain intact if sys.argv[2] in ('-h', '--help'): # however, argparse makes a nice help screen, so we'll use # argparse here only if the user asks for help parser = argparse.ArgumentParser( description='Run a regular git commit, but also log your time.') parser.add_argument('--fake', help='Add this commit to the invoice, but don\'t make an actual commit.', action='store_true') parser.add_argument('--hours', help='Manually enter the time worked instead of using the timer.', nargs='?', default='argparse.SUPPRESS') args = parser.parse_args() else: commands.commit_main(sys.argv) else: # every other command gets handled by argparse try: cmd_handler() except Exception as e: gitime.fprintf( "GITIME ERROR: Something has caused gitime to crash. " "If the issue persists, please seek help by opening a " "ticket on Github at " "<https://github.com/jongoodnow/gitime/issues>. " "Please include the following error message: ", file=sys.stderr) fprintf(e, file=sys.stderr)
def commit_main(args): # commits are NOT handled by argparse. `args` are passed to this function # as they are from sys.argv. u = User() invid = u.active_invoice_rowid if invid == 0: fprintf( "GITIME ERROR: You do not have an active invoice set. " "You won't be able to record your hours without one. " "Create an invoice with the command: `gitime invoice -n <invoice " "name>` first. Your commit has NOT been made.", file=sys.stderr) sys.exit() inv = Invoice(invid) raw_hours = parse_hours_flag(args) if raw_hours is not False: hours = round(raw_hours / inv.rounding) * inv.rounding else: hours = u.time_tracked(inv) if hours <= 0: fprintf( "GITIME ERROR: You didn't specify a number of hours, and the " "timer hasn't recorded anything. Run this command with the " "`--hours <hour count>` flag, or use the timer to track your " "time. Your commit has NOT been made."), file=sys.stderr) sys.exit() u.reset_timer() message = parse_commit_message(args) if not message: fprintf("GITIME ERROR: Could not find a message in your commit.", file=sys.stderr) sys.exit()
def timer_main(args): u = User() if not args.force: if u.active_invoice_rowid == 0: fprintf( "WARNING: You do not have an active invoice set. " "You won't be able to record your hours without one. " "Create an invoice with the command: `gitime invoice -n " "<invoice name>` first, or suppress this warning by running " "the timer with the --force flag.", file=sys.stderr) sys.exit() if args.action == 'start': u.start_timer() fprintf('Timer started at %s' %str(datetime.now())) elif args.action == 'pause': u.pause_timer() fprintf('Timer paused at %s' %str(datetime.now())) elif args.action == 'reset': u.reset_timer() elif args.action == 'status': inv = Invoice(u.active_invoice_rowid) if u.timer_running: status = 'has been running since %s.' %str( datetime.fromtimestamp(u.timer_start)) else: status = 'is not running.' fprintf('The timer %s' %status) fprintf('Total hours tracked: %.2f' %(u.time_tracked(inv)))
def set_rate(self, r): try: r = float(r) except ValueError: fprintf('Rates must be provided in the form of a number.', file=sys.stderr) sys.exit() self.rate = r db.update(lambda c: c.execute(""" UPDATE invoice SET rate=? WHERE rowid=? """, (self.rate, self.rowid)))
def invoice_main(args): u = User() if hasattr(args, 'name'): if args.new: kwargs = {'new': True} if hasattr(args, 'rate'): kwargs['rate'] = args.rate if hasattr(args, 'round'): kwargs['rounding'] = args.round inv = Invoice(args.name, **kwargs) inv.set_active() fprintf("Future commits will now be sent to the invoice %s." %inv.name) else: try: inv = Invoice(args.name) except InvoiceNotFound: if raw_input( "That invoice doesn't exist. Make a new one? [Y/n] " ) == 'n': sys.exit() inv = Invoice(args.name, new=True) if hasattr(args, 'rate'): inv.set_rate(args.rate) if hasattr(args, 'round'): inv.set_rounding(args.round) if u.active_invoice_rowid != inv.rowid: inv.set_active() fprintf("Future commits will now be sent to the invoice %s." %inv.name) else: if db.invoice_count() == 0: fprintf("You do not have any invoices yet! Create one with `gitime " "invoice -n <invoice name>`.") else: inv = Invoice(u.active_invoice_rowid) if hasattr(args, 'rate'): inv.set_rate(args.rate) if hasattr(args, 'round'): inv.set_rounding(args.round) if args.list: count = db.invoice_count() noun = 'invoice' if count == 1 else 'invoices' fprintf("You have %d %s:" %(count, noun)) for invoice in db.query_all_invoices(): if invoice[3] == u.active_invoice_rowid: active = " (active)" else: active = "" fprintf(invoice[0], active)
def export_invoice_main(args): if hasattr(args, 'invoice'): try: inv = Invoice(args.invoice) except InvoiceNotFound: fprintf("That invoice does not exist.", file=sys.stderr) sys.exit() else: u = User() if u.active_invoice_rowid == 0: fprintf("You do not have an active invoice set. Create one with " "`gitime invoice -n <invoice name> first.", file=sys.stderr) sys.exit() inv = Invoice(u.active_invoice_rowid)
def parse_hours_flag(args): """ If the `--hours` flag was used, get the time, and remove the flag and the time from the args list. Return FALSE if the flag was not present. """ try: pos = args.index('--hours') except ValueError: return False else: hours = args[pos + 1] try: hours = float(hours) except ValueError: fprintf("GITIME ERROR: %s is not a valid amount of hours. Your " "commit was NOT made. Try again." %hours, file=sys.stderr) sys.exit() else: del args[pos + 1] del args[pos] return hours
def status_main(args): if hasattr(args, 'invoice'): inv = Invoice(args.invoice) else: u = User() invid = u.active_invoice_rowid if invid == 0: fprintf("You do not have any invoices yet! Create one with `gitime " "invoice -n <invoice name>`.") sys.exit() inv = Invoice(u.active_invoice_rowid) total_hours = inv.total_hours() hourstr = 'hour' if total_hours == 1 else 'hours' print(textwrap.dedent("""\ On invoice %s Total Time Worked: %g %s Total Charges: $%.2f Charges:""" %(inv.name, total_hours, hourstr, inv.total_earnings()))) commits = inv.get_commit_meta() if not commits: fprintf("No charges yet!") else: for com in commits: date = (datetime.fromtimestamp(com[1])).strftime('%m-%d-%Y') wspace1 = (17 - len(date)) * " " hourstr = 'hour' if com[2] == 1 else 'hours' hours = "%g %s" %(com[2], hourstr) wspace2 = (14 - len(hours)) * " " message = com[0] fprintf(date, wspace1, hours, wspace2, message)
def parse_commit_message(args): """ Find the commit message in the commit command. Messages may be formatted like: * `-m "some message"` * `--message "some message"` * `--message="some message"` Single quotes are allowed too. `args` is a list that should be formatted exactly as sys.argv """ reg = re.compile( r"(\-\w*m\w*|\-\-message)(\s*|(='(?:\\.|[^'])*'|\"(?:\\.|[^\"])*\"))") scan = [index for index, val in enumerate(args) for mat in [reg.search(val)] if mat] if not scan: return False if len(scan) > 1: fprintf("GITIME ERROR: Your commit contains more than one message. " "Your commit was NOT made. Try again.", file=sys.stderr) sys.exit() index = scan[0] # check if the format is `--message="some message"` format_reg = re.search(r"='((\\.|[^'])*?)'|\"((\\.|[^\"])*?)\"", args[index]) if format_reg is not None: return format_reg.group().strip('=').strip('"').strip("'") elif len(args) < index + 2: fprintf("GITIME ERROR: Your commit doesn't contain a message. Your " "commit was NOT made. Try again.", file=sys.stderr) sys.exit() else: ret = args[index + 1] # escape unescaped quotes so that we can add quotes around the message args[index + 1] = re.sub(r'(?<!\\)(?:\\\\)*"', '\\"', args[index + 1]) # add quotes to the message so that git can understand it # when the command is run args[index + 1] = "".join(['"', args[index + 1], '"']) return ret
def __init__(self, unique, new=False, rate=None, rounding=None, userid=1): """ Invoices can be instantiated from a rowid or a name. If the name isn't already in the database a new one will be created if `new` is true. Otherwise the program halts. If instantiated from a rowid, using a rowid that does not exist will throw an exception. `rate` and `rounding` args unnecessary if querying by rowid. `rate` and `rounding` default to user values. """ self.user = User(userid) meta = db.query_invoice(unique) if meta: if new: print("An invoice with the name %s already exists. You can't " "make a new one with that name." %unique, file=sys.stderr) sys.exit() self.name = meta[0] self.rate = meta[1] self.rounding = meta[2] self.rowid = meta[3] if len(meta) == 4 else unique else: # this will only happen if `unique` is a name # if `unique` is a rowid, an exception is raised # on the query. if not new: raise InvoiceNotFound self.name = unique if rate is None and self.user.rate == 0: fprintf("WARNING: Your default hourly rate is set to zero. This" " means that no earnings will be recorded. You can set your" " default rate with `gitime set -r <rate>` or set the rate " "for this invoice with `gitime invoice <invoice name> -r " "<rate>`.") self.rate = rate if rate is not None else self.user.rate self.rounding = (rounding if rounding is not None else self.user.rounding) self.rowid = db.insert_invoice(self.name, self.rate, self.rounding)
def settings_main(args): u = User() if hasattr(args, 'rate'): u.set_rate(args.rate) fprintf("The rate of $%s per hour will be applied to all new invoices." %args.rate) if hasattr(args, 'round'): u.set_rounding(args.round) fprintf("Hours will be rounded to the nearest %s on all new invoices" %args.round) if args.list: fprintf(textwrap.dedent("""\ Your default values for all invoices created in the future: Hourly Rate: $%.2f Round hours to the nearest %g""" %(u.rate, u.rounding)))
fprintf( "GITIME ERROR: You didn't specify a number of hours, and the " "timer hasn't recorded anything. Run this command with the " "`--hours <hour count>` flag, or use the timer to track your " "time. Your commit has NOT been made."), file=sys.stderr) sys.exit() u.reset_timer() message = parse_commit_message(args) if not message: fprintf("GITIME ERROR: Could not find a message in your commit.", file=sys.stderr) sys.exit() com = Commit(message=message, hours=hours, invoice=u.active_invoice_rowid) fprintf("GITIME: Your commit has been logged in invoice %s." %inv.name) if '--fake' not in args: fprintf("GITIME: Running your commit now...") args[0] = 'git' os.system(" ".join(args)) def export_invoice_main(args): if hasattr(args, 'invoice'): try: inv = Invoice(args.invoice) except InvoiceNotFound: fprintf("That invoice does not exist.", file=sys.stderr) sys.exit() else: u = User()