def __init__(self): self.config = Config() self.slackbot = SlackerAdapter() self.logger = Logger().get_logger() self.toggl = Toggl() self.toggl.setAPIKey(self.config.open_api['toggl']['TOKEN']) wid = self.toggl.getWorkspace( name=self.config.open_api['toggl']['WORKSPACE_NAME'])['id'] self.toggl.setWorkspaceId(wid) self.entity = TogglProjectEntity().entity
class TimeTracking(object): def __init__(self, toggl_api_key=TOGGL_API_KEY, redmine_api_key=REDMINE_API_KEY): self.toggle_api = Toggl(toggl_api_key) self.redmine_api = Redmine(REDMINE_URL, key=redmine_api_key) self.issue_re = re.compile(r'#(?P<issue_id>\w+)') def set_time_entries(self, working_date): """ Get time reports from toggl and tracks the task time to an specific redmine issue """ today = working_date.strftime("%Y-%m-%dT%H:%M:%S+00:00") tomorrow = (working_date + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%S+00:00") print ("tracking time entries for date {}".format(today)) entries = self.toggle_api.get_time_entries(today, tomorrow) for entry in entries: entry_text_issue = self.issue_re.match(entry['description']) if entry_text_issue: issue_id = entry_text_issue.groups()[0] hours = entry['duration'] / 3600.0 time_entry = self.redmine_api.time_entry.create( issue_id=issue_id, hours=hours, activity_id=DEFAULT_ACTIVITY_ID, comments='' ) if time_entry: print ("succesfuly tracked time for issue {} ({} hours)".format(issue_id, hours)) else: print ("failed tracking issue {}".format(issue_id))
class TimeTracking(object): def __init__(self, toggl_api_key=TOGGL_API_KEY, redmine_api_key=REDMINE_API_KEY): self.toggle_api = Toggl(toggl_api_key) self.redmine_api = Redmine(REDMINE_URL, key=redmine_api_key) self.issue_re = re.compile(r'#(?P<issue_id>\w+)') def set_time_entries(self, working_date): """ Get time reports from toggl and tracks the task time to an specific redmine issue """ today = working_date.strftime("%Y-%m-%dT%H:%M:%S+00:00") tomorrow = (working_date + timedelta(1)).strftime("%Y-%m-%dT%H:%M:%S+00:00") print("tracking time entries for date {}".format(today)) entries = self.toggle_api.get_time_entries(today, tomorrow) for entry in entries: entry_text_issue = self.issue_re.match(entry['description']) if entry_text_issue: issue_id = entry_text_issue.groups()[0] hours = entry['duration'] / 3600.0 time_entry = self.redmine_api.time_entry.create( issue_id=issue_id, hours=hours, activity_id=DEFAULT_ACTIVITY_ID, comments='') if time_entry: print("succesfuly tracked time for issue {} ({} hours)". format(issue_id, hours)) else: print("failed tracking issue {}".format(issue_id))
def test_headers(): t = Toggl("*****@*****.**", "secret", 99) assert t.headers == { "Authorization": "Basic c2VjcmV0OmFwaV90b2tlbg==", "Content-Type": "application/json", "Accept": "*/*", "User-Agent": "python/urllib", }
def test_default_state(): t = Toggl("*****@*****.**", "secret", 99) assert t.email == "*****@*****.**" assert t._api_key == "secret" assert t._verbose is False assert t.workspace == 99 assert t._current_page == 1 assert t._pages == 1 assert t._current_records_acquired == 0
class TogglApp(Application): def __init__(self): Application.__init__(self) self.log = logging.getLogger("TogglApp") self.toggl = Toggl() def task_cmd(self, args=None): self.log.debug("Running the \'task\' sub command") def list_cmd(self, args=None): self.log.debug("Running the \'list\' sub command") if args.projects: projects = self.toggl.get_projects() for project in projects: print "%s\t%s" % (project['id'], project['name']) elif args.tags: raise NotImplementedError elif args.entries: entries = self.toggl.get_range_entries() for entry in entries: project = None try: project = entry['pid'] except: pass desc = "(No description)" try: desc = entry['description'] except: pass print project, desc def report_cmd(self, args=None): self.log.debug("Running the \'report\' sub command") if args.current: current = self.toggl.get_current_entry() if current: try: print current['description'] except: pass print current['start'] else: print "No timer currently running"
def __init__(self): # timeframe for report end_date = datetime.datetime.now() start_date = end_date - settings.timeframe # create report report_builder = Toggl(settings.api_token) workspaces = report_builder.get_workspaces() reports = [] for ws_name, ws_id in workspaces: if ws_name in settings.workspace2meta.keys(): metaproject = settings.workspace2meta[ws_name] for record in report_builder.detailed_report( ws_id, start_date, end_date): # record duration is in milliseconds # divide by 3600000 to convert to hours reports.append({ 'user': record['user'], 'team': ws_name, 'project': metaproject, 'subproject': record['project'], # example of record['start']: 2015-05-29T16:07:20+03:00 'start': record['start'][:19], 'duration': round(float(record['dur']) / 3600000, 2) }) self.df = pd.DataFrame(reports) self.df['start'] = pd.to_datetime(self.df['start']) self.total_weeks = set(self.df.set_index('start').index.week) self.first_timestamp = self.df.min(axis=1) self.projects = list( self.df.set_index('project').index.get_level_values(0).unique()) self.users = list( self.df.set_index('user').index.get_level_values(0).unique())
def test_reset_pagination(): t = Toggl("*****@*****.**", "secret", 99) t._current_records_acquired = 33 t._current_page = 2 t._pages = 2 t._reset_instance_pagination() assert t._current_page == 1 assert t._pages == 1 assert t._current_records_acquired == 0
def main(): # print a banner print " _ _ _ _ " print "| |_ ___ __ _ __ _| | _ __ ___ _ __ _ __ ___ __ _| (_)_______ _ __ " print "| __/ _ \ / _` |/ _` | | | '_ \ / _ \| '__| '_ ` _ \ / _` | | |_ / _ \ '__|" print "| || (_) | (_| | (_| | | | | | | (_) | | | | | | | | (_| | | |/ / __/ | " print " \__\___/ \__, |\__, |_| |_| |_|\___/|_| |_| |_| |_|\__,_|_|_/___\___|_| " print " |___/ |___/ " print "" try: # read and print options opts = options() print "" print "Days: from {0} to {1}.".format(opts.start_date, opts.end_date) print "Hours per day: {0}".format(common.secs_to_hms_str(int(round(opts.hours_per_day*60*60)))) # test toggl API connection toggl = Toggl() print "" print "Testing toggl connection...", toggl.login() print "Ok." print "" # iterate through each day from start_date to end_date and normalize each one current_day = opts.start_date while current_day <= opts.end_date: normalize_day(toggl, current_day) current_day += timedelta(days=1) print "" print "Done." except Exception, e: print "" print "Error:", e raise
def main(): try: # read and print options opts = options() print "" print "Days: from {0} to {1}.".format(opts.start_date, opts.end_date) print "Csv report: \"{0}\"".format(opts.csv_file) # test toggl API connection toggl = Toggl() print "" print "Testing toggl connection...", toggl.login() print "Ok." print "" # iterate through each day from start_date to end_date and report on each one daily_reports = [] current_day = opts.start_date while current_day <= opts.end_date: report = day_report(toggl, current_day) daily_reports.append((current_day, report)) current_day += timedelta(days=1) # generate the csv report print "" print "Generating report...", generate_report(opts.csv_file, daily_reports) print "Ok." print "" print "Done." except Exception: print "" print "Error:" raise
def __init__(self, toggl_api_key=TOGGL_API_KEY, redmine_api_key=REDMINE_API_KEY): self.toggle_api = Toggl(toggl_api_key) self.redmine_api = Redmine(REDMINE_URL, key=redmine_api_key) self.issue_re = re.compile(r'#(?P<issue_id>\w+)')
parser.exit(1, "Invalid date\n") start_date = datetime.datetime.strptime(settings.start_date, settings.date_format) end_date = datetime.datetime.strptime(settings.end_date, settings.date_format) if today < start_date or today > end_date: parser.exit(1, "Date {0} is out of the {1}..{2} range.\n Check dates in" " settings.py\n".format( today.strftime(settings.date_format), settings.start_date, settings.end_date)) # create report report_builder = Toggl(settings.api_token) workspaces = report_builder.get_workspaces() weeks = week_list(start_date, end_date) last_records = {} # last_records[user] = last_record report_writer = csv.DictWriter( sys.stdout, ['user', 'team', 'project', 'start', 'duration']) report_writer.writeheader() for (monday, sunday) in weeks: if sunday > today: break for ws_name, ws_id in workspaces:
def __init__(self): Application.__init__(self) self.log = logging.getLogger("TogglApp") self.toggl = Toggl()
def test_repr(): t = Toggl("*****@*****.**", "secret", 99) assert ( str(t) == "Toggl([email protected], api_key=secret, workspace=99, verbose=False)" )
class TogglManager(object): def __init__(self): self.config = Config() self.slackbot = SlackerAdapter() self.logger = Logger().get_logger() self.toggl = Toggl() self.toggl.setAPIKey(self.config.open_api['toggl']['TOKEN']) wid = self.toggl.getWorkspace( name=self.config.open_api['toggl']['WORKSPACE_NAME'])['id'] self.toggl.setWorkspaceId(wid) self.entity = TogglProjectEntity().entity def timer(self, description=None): state = State() state.check() advice_rest_time = state.current.get(state.REST, {}).get('time', None) if advice_rest_time is not None: advice_rest_time = arrow.get(advice_rest_time) if advice_rest_time > arrow.now(): self.slackbot.send_message(text=MsgResource.TOGGL_ADVICE_REST( advice_rest_time.format('HH:mm'))) return current_timer = self.toggl.currentRunningTimeEntry()['data'] if current_timer is None: if description is None or description == "": pid = None else: # matching name lower_description = description.lower() name = None for key, value_list in self.entity['project'].items(): if any(v in lower_description for v in value_list): name = key pid = self.__get_pid(name=name) break else: pid = None self.toggl.startTimeEntry(description=description, pid=pid) self.slackbot.send_message(text=MsgResource.TOGGL_START) else: stop = self.toggl.stopTimeEntry(current_timer['id']) description = stop['data'].get('description', 'no description') diff_min = ArrowUtil.get_curr_time_diff( start=stop['data']['start'], stop=stop['data']['stop']) self.slackbot.send_message(text=MsgResource.TOGGL_STOP) self.slackbot.send_message( text=MsgResource.TOGGL_STOP_SUMMARY(description, diff_min)) todoist = TodoistManager() todoist.complete_by_toggl(description, int(diff_min)) state.advice_rest(diff_min) def __get_pid(self, name=None): project = self.toggl.getWorkspaceProject(name=name) if project is None: pid = None else: pid = project['id'] return pid def check_toggl_timer(self): current_timer = self.toggl.currentRunningTimeEntry()['data'] self.logger.info(str(current_timer)) if current_timer is None: return diff_min = ArrowUtil.get_curr_time_diff(start=current_timer['start']) self.logger.info("diff_min: " + str(diff_min)) diff_min_divide_10 = int(diff_min / 10) if diff_min > 100: self.slackbot.send_message(text=MsgResource.TOGGL_NOTI_RELAY) else: for i in range(3, 10, 3): if diff_min_divide_10 == i: self.slackbot.send_message( text=MsgResource.TOGGL_TIMER_CHECK(diff_min)) break q_ratio = random.randint(1, 10) if q_ratio > 7: attention = AttentionQuestion() attention.question() def report(self, kind="chart", timely="weekly"): now = arrow.now() if timely == "daily": before_days = now.replace(days=0) elif timely == "weekly": before_days = now.replace(days=-6) data = { 'since': before_days.format('YYYY-MM-DD'), 'until': now.format('YYYY-MM-DD'), 'calculate': 'time' } if kind == "basic": f_name = "basic-report.pdf" self.toggl.getWeeklyReportPDF(data, f_name) self.slackbot.file_upload(f_name, title=timely + " 기본 리포트", comment=MsgResource.TOGGL_REPORT) elif kind == "chart": f_name = "chart-report.pdf" self.toggl.getSummaryReportPDF(data, f_name) self.slackbot.file_upload(f_name, title=timely + " 차트 리포트", comment=MsgResource.TOGGL_REPORT) elif kind == "detail": f_name = "detail-report.pdf" self.toggl.getDetailedReportPDF(data, f_name) self.slackbot.file_upload(f_name, title=timely + " 상세 리포트", comment=MsgResource.TOGGL_REPORT) def get_point(self): now = arrow.now() data = { 'since': now.format('YYYY-MM-DD'), 'until': now.format('YYYY-MM-DD'), 'calculate': 'time' } today = self.toggl.getDetailedReport(data) if today['total_grand']: total_hours = round(today['total_grand'] / 60 / 60 / 10) else: total_hours = 0 return Score.percent(total_hours, 100, 800)
except ValueError: parser.exit(1, "Invalid date\n") start_date = datetime.datetime.strptime(settings.start_date, settings.date_format) end_date = datetime.datetime.strptime(settings.end_date, settings.date_format) if today < start_date or today > end_date: parser.exit( 1, "Date {0} is out of the {1}..{2} range.\n Check dates in" " settings.py\n".format(today.strftime(settings.date_format), settings.start_date, settings.end_date)) # create report report_builder = Toggl(settings.api_token) workspaces = report_builder.get_workspaces() weeks = week_list(start_date, end_date) last_records = {} # last_records[user] = last_record report_writer = csv.DictWriter( sys.stdout, ['user', 'team', 'project', 'start', 'duration']) report_writer.writeheader() for (monday, sunday) in weeks: if sunday > today: break for ws_name, ws_id in workspaces:
def test_raise_error_if_email_alone(): with pytest.raises(RuntimeError): Toggl(email="foo")
if args.date is None: logging.debug("No date specified, using system date: %s", today.strftime(date_format)) else: try: today = datetime.datetime.strptime(args.date, date_format) except ValueError: parser.exit(1, "Invalid date\n") if today < start_date: parser.exit( 1, "Start date ({0}) has not yet come.\n Check dates in" "the settings.py\n".format(start_date)) # create report toggl = Toggl(settings.api_token) workspaces = [(w['name'], w['id']) for w in toggl.get_workspaces()] weeks = week_list(start_date, today) report_writer = csv.DictWriter( args.output, ['user', 'team', 'project', 'start', 'duration']) report_writer.writeheader() for (monday, sunday) in weeks: if sunday > today: break for ws_name, ws_id in workspaces: inactive_users = set() if args.all else \ set(u['name'] for u in
def test_params(): t = Toggl("*****@*****.**", "secret", 99) assert t.params == {"user_agent": "*****@*****.**", "workspace_id": 99}
def main(): args = parse_args() toggl = Toggl(settings.TOGGL_TOKEN) startDate = (datetime.now(timezone.utc) - timedelta(days=90)).isoformat() endDate = datetime.now(timezone.utc).isoformat() timeEntries = toggl.getTimeEntries(startDate, endDate) entriesByWeekday = {str(k): {} for k in range(6)} for te in timeEntries: startDate = dateutil.parser.parse(te['start']) te['startDate'] = startDate weekDay = str(startDate.weekday()) key = ("%s!%s!%s!%s" % (te['description'], startDate.hour, startDate.minute, te['duration'])) if weekDay not in entriesByWeekday: entriesByWeekday[weekDay] = {} if key not in entriesByWeekday[weekDay]: entriesByWeekday[weekDay][key] = { 'wid': te['wid'], 'pid': te.get('pid', None), 'description': te['description'], 'start': time(te['startDate'].hour, te['startDate'].minute, tzinfo=timezone.utc), 'duration': te['duration'], 'tags': te.get('tags', []), 'count': 0 } entriesByWeekday[weekDay][key]['count'] += 1 for day, entries in entriesByWeekday.items(): entriesByWeekday[day] = { k: entries[k] for k in entries if entries[k]['count'] > 1 } weekday = str(args.date.weekday()) entries = sorted(entriesByWeekday[weekday].values(), key=lambda entry: entry['start']) for te in entries: startDate = datetime.combine(args.date, te['start']) endDate = startDate + timedelta(seconds=te['duration']) prompt = "Add {} from {} to {} (duration {} sec)?".format( te['description'], startDate, endDate, te['duration']) newTe = copy.deepcopy(te) newTe['start'] = startDate if query_yes_no(prompt, "no"): res = toggl.createTimeEntry(newTe) if 'data' in res and 'id' in res['data'] and res['data']['id'] > 0: eprint("OK!") else: eprint("WARNING: Check output - " + str(res))
def test_raise_error_if_api_key_alone(): with pytest.raises(RuntimeError): Toggl(api_key="bar")
""" Toggl Example. This example assumes you have a `config.yml` file. Usage: `python example.py` """ from toggl import Toggl toggl = Toggl(verbose=True) toggl.report(start="2018-08-10") # detailed_report = toggl.detailed_report(start='2018-01-01', end='2018-01-15') # simple_report = toggl.report(start='2018-01-01', end='2018-01-15') # intacct_format = toggl.intacct_report(start='2018-01-01', end='2018-01-15') # # print('\n\ndetailed_report\n') # print(detailed_report) # # print('\n\nsimple_report\n') # print(simple_report) # # print('\n\nintacct_format\n') # print(intacct_format)