aboutsummaryrefslogtreecommitdiffstats
path: root/harvester_submit_week_for_approval.py
blob: 6f9e0477249d749937cb9cd66adf5c0f62bfa956 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python

import argparse
from datetime import datetime, timedelta
import subprocess

from selenium.webdriver import Firefox
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.support import expected_conditions as expected
from selenium.webdriver.support.wait import WebDriverWait


def login(driver, wait, email, password):
    driver.get('https://id.getharvest.com/harvest/sign_in')

    wait.until(
        expected.visibility_of_element_located((By.NAME, 'email'))
    ).send_keys(email)
    driver.find_element_by_name('password').send_keys(password + Keys.ENTER)

    wait.until(
        expected.visibility_of_element_located((By.ID, 'user-dropdown-toggle'))
    )

    return driver

def most_recent_friday():
    friday = 4
    now = datetime.now()

    if now.weekday() >= friday:
        return now - timedelta(days=now.weekday() - friday)
    else:
        return now - timedelta(weeks=1) + timedelta(days=friday - now.weekday())

def submit_week_for_approval(driver, wait, subdomain):
    friday = most_recent_friday()

    url = 'https://{subdomain}.harvestapp.com/time/week/{year}/{month}/{day}/'.format(
        subdomain=subdomain,
        year=friday.year,
        month=friday.month,
        day=friday.day)

    driver.get(url)

    # Wait for page to load
    wait.until(expected.text_to_be_present_in_element(
        (By.CLASS_NAME, 'timesheet-header'),
        str(friday.year)))
    wait.until(expected.visibility_of_element_located(
        (
            By.XPATH,
            '//a[@aria-label="{}"]'.format(friday.strftime('%A, %d %B'))
        )))

    approval_button = driver.find_element_by_css_selector(
        '.approval-button .submit-link')

    # Don't submit if the time sheet has already been submitted
    if approval_button.text == 'Resubmit Week for Approval':
        return driver

    # Click "Submit Week for Approval" button
    approval_button.click()

    # Click "Yes, Submit Timesheet" button
    wait.until(
        expected.visibility_of_element_located(
            (
                By.CSS_SELECTOR,
                '.approval-confirmation .js-submit-for-approval',
            )
        )
    ).click()

    # Wait for success message
    wait.until(expected.text_to_be_present_in_element(
        (By.ID, 'status_message'),
        'Timesheet has been submitted for approval.'))

    return driver

def get_password(command):
    result = subprocess.run(['sh', '-c', command], stdout=subprocess.PIPE)

    return str(result.stdout, 'utf-8').rstrip()


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='Submit the most recent Harvest timesheet for approval.')
    parser.add_argument(
        '--email',
        required=True,
        help='Harvest email address')
    parser.add_argument(
        '--password-command',
        required=True,
        help='Shell command to get Harvest password')
    parser.add_argument(
        '--subdomain',
        required=True,
        help='Harvest subdomain (acme in acme.harvestapp.com)')

    args = parser.parse_args()

    options = Options()
    options.add_argument('-headless')
    driver = Firefox(
        executable_path='./geckodriver',
        firefox_binary='/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
        firefox_options=options)

    wait = WebDriverWait(driver, timeout=10)

    driver = login(
        driver,
        wait,
        args.email,
        get_password(args.password_command))
    driver = submit_week_for_approval(driver, wait, args.subdomain)

    driver.quit()