Passed
Push — master ( e0d3b6...7c5751 )
by benoit
01:32
created

main()   A

Complexity

Conditions 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
1
"""
2
Module that contains the command line app.
3
4
Why does this file exist, and why not put this in __main__?
5
6
  You might be tempted to import things from __main__ later, but that will cause
7
  problems: the code will get executed twice:
8
9
  - When you run `python -mgols` python will execute
10
    ``__main__.py`` as a script. That means there won't be any
11
    ``gols.__main__`` in ``sys.modules``.
12
  - When you import __main__ it will get executed again (as a module) because
13
    there's no ``gols.__main__`` in ``sys.modules``.
14
15
  Also see (1) from http://click.pocoo.org/5/setuptools/#setuptools-integration
16
"""
17
18
import logging
19
import os
20
import shutil
21
22
import click
23
import requests
24
25
logger = logging.getLogger(__name__)
26
logging.basicConfig()
27
28
29
@click.group()
30
@click.option('--debug/--no_debug', default=False,
31
              help='Set to true to see debug logs on top of info')
32
def main(debug):
33
    if debug:
34
        requests_log = logging.getLogger("requests.packages.urllib3")
35
        requests_log.setLevel(logging.DEBUG)
36
        requests_log.propagate = True
37
        logging.root.setLevel(level=logging.DEBUG)
38
        logger.info('Debug level set on')
39
    else:
40
        logger.setLevel(level=logging.INFO)
41
42
43
@main.command(short_help='uploads .fit files to your garmin connect account')
44
@click.option('--directory_fit', '-d', required=True,
45
              type=click.Path(exists=True, file_okay=False),
46
              help='Path of your .fit files on your watch mount path')
47
@click.option('--move/--no_move', '-m', default=False,
48
              help='Move files upon upload')
49
@click.option('--username', '-u', required=True, prompt=True,
50
              default=lambda: os.environ.get('GARMINCONNECT_USERNAME', ''),
51
              help='The GARMINCONNECT_USERNAME environment variable should you have one set')  # noqa
52
@click.option('--password', '-p', required=True, prompt=True,
53
              default=lambda: os.environ.get('GARMINCONNECT_PASSWORD', ''),
54
              help='The GARMINCONNECT_PASSWORD environment variable should you have one set ')  # noqa
55
@click.option('--conf_dir_fit', '-c', required=True,
56
               type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True, readable=True))  # noqa
57
def upload(directory_fit, move, username, password, conf_dir_fit):
58
    logger.info('Uplading stuff')
59
    headers = {
60
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:51.0) Gecko/20100101 Firefox/51.0',
61
    }
62
    params_login = {
63
        'service': 'https://connect.garmin.com/modern/',
64
        'webhost': 'olaxpw-conctmodern011',
65
        'source': 'https://connect.garmin.com/en-US/signin',
66
        'redirectAfterAccountLoginUrl': 'https://connect.garmin.com/modern/',
67
        'redirectAfterAccountCreationUrl': 'https://connect.garmin.com/modern/',
68
        'gauthHost': 'https://sso.garmin.com/sso',
69
        'locale': 'en_US',
70
        'id': 'gauth-widget',
71
        'cssUrl': 'https://static.garmincdn.com/com.garmin.connect/ui/css/gauth-custom-v1.2-min.css',
72
        'clientId': 'GarminConnect',
73
        'rememberMeShown': 'true',
74
        'rememberMeChecked': 'false',
75
        'createAccountShown': 'true',
76
        'openCreateAccount': 'false',
77
        'usernameShown': 'false',
78
        'displayNameShown': 'true',
79
        'consumeServiceTicket': 'false',
80
        'initialFocus': 'true',
81
        'embedWidget': 'false',
82
        'generateExtraServiceTicket': 'false',
83
        'globalOptInShown': 'false',
84
        'globalOptInChecked': 'false',
85
        'connectLegalTerms': 'true',
86
    }
87
    data_login = {
88
        'username': username,
89
        'password': password,
90
        'embed': 'true',
91
        'lt': 'e1s1',
92
        '_eventId': 'submit',
93
        'displayNameRequired': 'false',
94
        'rememberme': 'on',
95
    }
96
97
    # begin session with headers because, requests client isn't an option, dunno if Icewasel is still banned...
98
    logger.info('Login into Garmin connect')
99
    s = requests.session()
100
    s.headers.update(headers)
101
    # we need the cookies from the login page before we can post the user/pass
102
    url_login = 'https://sso.garmin.com/sso/login'
103
    req_login = s.get(url_login, params=params_login)
104
    if req_login.status_code != 200:
105
        logger.info('issue with {}, you can turn on debug for more info'.format(
106
            req_login))
107
    req_login2 = s.post(url_login, data=data_login)
108
    if req_login2.status_code != 200:
109
        logger.info('issue with {}, you can turn on debug for more info'.format(
110
            req_login2))
111
    # we need that to authenticate further, kind like a weird way to login but...
112
    t = req_login2.cookies.get('CASTGC')
113
    t = 'ST-0' + t[4:]
114
    # now the auth with the cookies we got
115
    # url_post_auth = 'https://connect.garmin.com/modern' this one I still don't know how to get it
116
    url_post_auth = 'https://connect.garmin.com/post-auth/login'
117
    params_post_auth = {'ticket': t}
118
    req_post_auth = s.get(url_post_auth, params=params_post_auth)
119
    if req_post_auth.status_code != 200:
120
        logger.info('issue with {}, you can turn on debug for more info'.format(
121
            req_post_auth))
122
    logger.info('Let\'s upload stuff now')
123
    # login should be done we upload now
124
125
    # url_upload = 'https://connect.garmin.com/proxy/upload-service-1.1/json/upload/.fit'
126
    url_upload = 'https://connect.garmin.com/modern/proxy/upload-service/upload/.fit'
127
    if len(os.listdir(directory_fit)):
128
        logger.debug([f for f in os.listdir(directory_fit) if os.path.isfile(os.path.join(directory_fit, f))])
129
        for filename in [f for f in os.listdir(directory_fit) if os.path.isfile(os.path.join(directory_fit, f))]:
130
            logger.info('uploading:  {}'.format(filename))
131
            files = {'data': (filename,
132
                              open(os.path.join(directory_fit, filename), 'rb'),
133
                              'application/octet-stream')
134
                     }
135
            s.headers.update({'Referer': 'https://connect.garmin.com/modern/import-data', 'NK': 'NT'})
136
            req5 = s.post(url_upload, files=files)
137
            if req5.status_code != 200:
138
                logger.info(
139
                    'issue with {}, you can turn on debug for more info'.format(
140
                        req5))
141
142
            # fn = req5.json()['detailedImportResult']['fileName']
143
            if 'failures' in req5.json()['detailedImportResult']:
144
                for failure in req5.json()['detailedImportResult']['failures']:
145
                    m_failures = failure['messages'][0]['content']
146
                    logger.info(m_failures)
147
            if 'successes' in req5.json()['detailedImportResult']:
148
                for successes in req5.json()['detailedImportResult']['successes']:
149
                    m_success = 'https://connect.garmin.com/modern/activity/' + str(
150
                        successes['internalId'])
151
                    logger.info(m_success)
152
153
            if move:
154
                shutil.move(os.path.join(directory_fit, filename),
155
                            os.path.join(conf_dir_fit, filename))
156
157
        logger.info('Done uploading')
158
    else:
159
        logger.info('No file found in {}'.format(directory_fit))
160
    logger.info('Finished')
161