コード例 #1
0
    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
コード例 #2
0
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))
コード例 #3
0
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))
コード例 #4
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
def test_headers():
    t = Toggl("*****@*****.**", "secret", 99)
    assert t.headers == {
        "Authorization": "Basic c2VjcmV0OmFwaV90b2tlbg==",
        "Content-Type": "application/json",
        "Accept": "*/*",
        "User-Agent": "python/urllib",
    }
コード例 #5
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
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
コード例 #6
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"
コード例 #7
0
    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())
コード例 #8
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"
コード例 #9
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
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
コード例 #10
0
ファイル: toggl-normalize.py プロジェクト: gdeOo/toggl-utils
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
コード例 #11
0
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
コード例 #12
0
 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+)')
コード例 #13
0
ファイル: detailed_report.py プロジェクト: dadwin/Toggl.py
            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:
コード例 #14
0
 def __init__(self):
     Application.__init__(self)
     self.log = logging.getLogger("TogglApp")
     self.toggl = Toggl()
コード例 #15
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
def test_repr():
    t = Toggl("*****@*****.**", "secret", 99)
    assert (
        str(t) ==
        "Toggl([email protected], api_key=secret, workspace=99, verbose=False)"
    )
コード例 #16
0
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)
コード例 #17
0
ファイル: detailed_report.py プロジェクト: dadwin/Toggl.py
        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:
コード例 #18
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
def test_raise_error_if_email_alone():
    with pytest.raises(RuntimeError):
        Toggl(email="foo")
コード例 #19
0
    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
コード例 #20
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
def test_params():
    t = Toggl("*****@*****.**", "secret", 99)
    assert t.params == {"user_agent": "*****@*****.**", "workspace_id": 99}
コード例 #21
0
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))
コード例 #22
0
ファイル: test_toggl.py プロジェクト: bhcastleton/toggl_api
def test_raise_error_if_api_key_alone():
    with pytest.raises(RuntimeError):
        Toggl(api_key="bar")
コード例 #23
0
 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+)')
コード例 #24
0
 def __init__(self):
     Application.__init__(self)
     self.log = logging.getLogger("TogglApp")
     self.toggl = Toggl()
コード例 #25
0
ファイル: example.py プロジェクト: bhcastleton/toggl_api
"""
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)