예제 #1
0
def garmin_upload(player_id, activity):
    try:
        from garmin_uploader.workflow import Workflow
    except ImportError:
        logger.warn(
            "garmin_uploader is not installed. Skipping Garmin upload attempt."
        )
        return
    profile_dir = '%s/%s' % (STORAGE_DIR, player_id)
    try:
        with open('%s/garmin_credentials.txt' % profile_dir, 'r') as f:
            username = f.readline().rstrip('\r\n')
            password = f.readline().rstrip('\r\n')
    except:
        logger.warn(
            "Failed to read %s/garmin_credentials.txt. Skipping Garmin upload attempt."
            % profile_dir)
        return
    try:
        with open('%s/last_activity.fit' % profile_dir, 'wb') as f:
            f.write(activity.fit)
    except:
        logger.warn("Failed to save fit file. Skipping Garmin upload attempt.")
        return
    try:
        w = Workflow(['%s/last_activity.fit' % profile_dir],
                     activity_name=activity.name,
                     username=username,
                     password=password)
        w.run()
    except:
        logger.warn("Garmin upload failed. No internet?")
예제 #2
0
def uploadToGarmin(paths, garminUsername, garminPassword, activityType,
                   activityName):
    try:
        workflow = Workflow(paths, garminUsername, garminPassword,
                            activityType, activityName)
        workflow.run()
    except Exception as e:
        logger.error("Failed to upload to Garmin Connect. - {}".format(e))
        raise e
예제 #3
0
def test_listing(activities_dir):
    """
    Test the activities listing used in CLI
    """
    from garmin_uploader.workflow import Workflow

    # Test directory
    w = Workflow([activities_dir], username='******', password='******')
    activities = dict([(repr(a), a) for a in w.activities])
    assert 'a.fit' in activities
    assert 'a.tcx' in activities
    assert 'invalid.txt' not in activities

    # Test csv + name
    w = Workflow([activities_dir + '/list.csv'],
                 username='******',
                 password='******')  # noqa
    activities = dict([(repr(a), a) for a in w.activities])
    assert len(activities) == 1  # no nope
    assert 'AAAA' in activities
    a = activities['AAAA']
    assert a.filename == 'a.fit'
    assert a.type == 'running'

    # Test simple file + name + type
    w = Workflow([activities_dir + '/a.tcx'],
                 activity_name='Test TCX',
                 activity_type='cycling',
                 username='******',
                 password='******')  # noqa
    assert len(w.activities) == 1
    a = w.activities[0]
    assert a.name == 'Test TCX'
    assert a.type == 'cycling'

    # Test simple files + name + type
    # Name must be skipped here
    files = [
        activities_dir + '/a.tcx',
        activities_dir + '/a.fit',
    ]
    w = Workflow(files,
                 activity_name='Test 2 files',
                 activity_type='cycling',
                 username='******',
                 password='******')  # noqa
    activities = dict([(repr(x), x) for x in w.activities])
    assert len(activities) == 2
    assert 'a.fit' in activities
    assert 'a.tcx' in activities
    assert activities['a.fit'].name is None
    assert activities['a.tcx'].name is None
    assert activities['a.fit'].type == 'cycling'
    assert activities['a.tcx'].type == 'cycling'
예제 #4
0
def upload_to_garmin(paths, username=None, password=None, activity_name=None, activity_type=None, verbose=2):
    workflow = Workflow(activity_name=activity_name, activity_type=activity_type, password=password, paths=paths, username=username, verbose=2)
    workflow.run()
예제 #5
0
def main():
    """
    CLI Entry point
    """
    base_dir = os.path.realpath(os.path.dirname(__file__))
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='A script to upload .TCX, .GPX, and .FIT'
        'files to the Garmin Connect web site.',
        epilog=open(os.path.join(base_dir, 'help.txt')).read(),
    )

    parser.add_argument(
        'paths',
        type=str,
        nargs='+',
        help='Path and name of file(s) to upload, list file name, or directory'
        'name containing fitness files.')
    parser.add_argument(
        '-a',
        '--name',
        dest='activity_name',
        type=str,
        help='Sets the activity name for the upload file. This option is'
        'ignored if multiple upload files are given.')
    parser.add_argument(
        '-t',
        '--type',
        dest='activity_type',
        type=str,
        help='Sets activity type for ALL files in filename list, except files'
        'described inside a csv list file.')
    parser.add_argument('-u',
                        '--username',
                        dest='username',
                        type=str,
                        help='Garmin Connect user login')
    parser.add_argument('-p',
                        '--password',
                        dest='password',
                        type=str,
                        help='Garmin Connect user password')
    parser.add_argument(
        '-v',
        '--verbose',
        dest='verbose',
        type=int,
        default=2,
        choices=[1, 2, 3, 4, 5],
        help='Verbose - select level of verbosity. 1=DEBUG(most verbose),'
        ' 2=INFO, 3=WARNING, 4=ERROR, 5= CRITICAL(least verbose).'
        ' [default=2]')

    # Run workflow with these options
    options = parser.parse_args()
    try:
        workflow = Workflow(**vars(options))
        workflow.run()
    except Exception as e:
        print('Error: {}'.format(e))
        return 1  # erroneous exit code

    return 0
예제 #6
0
def sync(csv_file, garmin_username, garmin_password,
         fromdate, todate, no_upload, verbose):

    def verbose_print(s):
        if verbose:
            if no_upload:
                sys.stderr.write(s)
            else:
                sys.stdout.write(s)

    if csv_file == '':
        verbose_print("--csv_file is required\n")
        exit(1)
    if not(os.path.isfile(csv_file)):
        verbose_print("File " + csv_file + " not found\n")
        exit(1)

    # OpenScale CSV
    osc = OpenScaleCSV()
    osc.load(csv_file)
    startdate = int(time.mktime(fromdate.timetuple()))
    enddate = int(time.mktime(todate.timetuple())) + 86399
    groups = []
    for ix in range(0, osc.records()):
        item = osc.record(ix)
        dt = int(time.mktime(item['dateTime'].timetuple()))
        if dt < startdate:
            continue
        if dt > enddate:
            continue
        groups.append(item)

    # create fit file
    verbose_print('generating fit file...\n')
    fit = FitEncoder_Weight()
    fit.write_file_info()
    fit.write_file_creator()

    for group in groups:
        dt = group['dateTime']
        weight = group['weight']
        fat_ratio = group['fat']
        fit.write_device_info(timestamp=dt)
        fit.write_weight_scale(timestamp=dt, weight=weight, percent_fat=fat_ratio)
        verbose_print('appending weight scale record... %s %skg %s%%\n' % (dt, weight, fat_ratio))
    fit.finish()

    if no_upload:
        sys.stdout.write(fit.getvalue())
        return

    # create temporary file
    fi, fn = mkstemp()
    fn = fn + '.fit'
    fp = open(fn, 'w')
    fp.write(fit.getvalue())
    fp.close()

    # garmin connect
    verbose_print('attempting to upload fit file...\n')
    workflow = Workflow(paths=[fn], username=garmin_username, password=garmin_password)
    workflow.run()
예제 #7
0
def main():
    args = parse_arguments()
    files = sorted(set(args.files))
    file_rex = re.compile(".*tr(\d{4})(\d{2})(\d{2})(\d{2})(\d{2}).csv",
                          re.IGNORECASE)
    laps = []
    lap = None
    for f in files:
        m = file_rex.match(f)
        if not m:
            raise KeyError("Error: unknown file naming format: {}".format(f))
        starttime = datetime.datetime(int(m.group(1)), int(m.group(2)),
                                      int(m.group(3)), int(m.group(4)),
                                      int(m.group(5)))

        # Collapse close (5sec) or overlapping laps
        if laps:
            tdelta = starttime - laps[-1].EndTime()
            lap = laps.pop() if tdelta.total_seconds() < 5 else None
        if not lap:
            lap = Lap(starttime)

        with open(f, newline="") as csvfile:
            reader = csv.reader(csvfile, delimiter=",")
            for row in reader:
                if row_is_header(row):
                    continue
                if row_is_totals(row):
                    break
                if row_is_incomplete(row):
                    continue
                values = dict(zip(stat_keys, [int(n) for n in row]))
                lap.XTrainerSample(values)

        # Use lap but only if it has data
        if len(lap):
            laps.append(lap)

    # Split into sessions (more than 20 minutes in between laps)
    lapsplit = [
        idx for idx, (l1, l2) in enumerate(pairwise(laps), 1)
        if l2.StartTime() - l1.EndTime() > datetime.timedelta(minutes=20)
    ]
    lapsplit = [0] + lapsplit + [len(laps)]
    sessions = [laps[i:j] for i, j in pairwise(lapsplit)]

    tcx_files = []
    for laps in sessions:
        # Add rest laps
        restlaps = []
        for l1, l2 in pairwise(laps):
            restlap = Lap(l1.EndTime() + datetime.timedelta(seconds=1), False)
            restlap.RestSample(l2.StartTime() - datetime.timedelta(seconds=1),
                               l1.HeartRateBpm())
            restlaps.append(restlap)
        for idx, restlap in enumerate(restlaps, 1):
            laps.insert(idx * 2 - 1, restlap)

        # Recalculate distance and altitude
        start_distance = start_altitude = 0.0
        for lap in laps:
            start_distance = lap.UpdateDistance(start_distance)
            start_altitude = lap.UpdateAltitude(start_altitude)

        # Garmin altitude graph does not like altitudes below -500m, hence
        # adjust altitude to have 10m as lowest point
        start_altitude = 10.0 - min([lap.MinimumAltitude() for lap in laps])
        for lap in laps:
            start_altitude = lap.UpdateAltitude(start_altitude)

        tcx_file = write_xml(laps)
        tcx_files.append(tcx_file)
        totsec = sum([l.TotalTimeSeconds() for l in laps])
        print("active time: {}".format(datetime.timedelta(seconds=totsec)))

        totdist = sum([l.DistanceMeters() for l in laps])
        print("total distance: {0:.2f}km".format(totdist / 1000))

        totwatt = sum([l.Sum('watt') for l in laps])
        totlen = sum([len(l) for l in laps])
        print("avg watt: {0:.2f}W".format(totwatt / totlen))

        for i in [1, 10, 30, 60, 120]:
            print("max {}s watt: {:.0f}W".format(
                i, max([l.MaximumWatts(i) for l in laps])))
        print("max speed: {}km/t".format(max([l.MaximumSpeed()
                                              for l in laps])))

    if args.upload and tcx_files:
        for tcx in tcx_files:
            try:
                workflow = Workflow([tcx],
                                    args.username,
                                    args.password,
                                    activity_type="indoor_cycling",
                                    activity_name="X-trainer indoor cycling",
                                    verbose=5 if args.verbose else 2)
                workflow.run()
            except Exception as e:
                print("Error: {}".format(str(e)))
                sys.exit(1)