示例#1
0
def interactive_extend(_file, _out_file):

    if not _out_file:
        _out_file = 'modified' + _file

    breaks = dict()
    date = ''
    for line in open(_file, 'r'):
        break_it = False
        for match in re.finditer("""<Id>(.+?)<""", line):
            date = time_seconds(match.group(1))
            break_it = True
        if break_it:
            break

    while True:
        local_time = raw_input('Break time from start (hours:minutes:seconds): ')
        if not local_time:
            break

        _s = [int(s) for s in local_time.split(':')]
        local_time_seconds = _s[0] * 60 * 60 + _s[1] * 60 + _s[2]
        local_time_seconds = date + local_time_seconds

        length_seconds = raw_input('Break length (minutes:seconds): ')
        if not length_seconds:
            break
        _l = [int(s) for s in length_seconds.split(':')]
        length_seconds = _l[0] * 60 + _l[1]

        breaks.update({local_time_seconds: length_seconds})

    tcx = etree.parse(_file)
    tcx_root = tcx.getroot()

    default_ns = """http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"""
    seconds_ns = """http://www.garmin.com/xmlschemas/ActivityExtension/v2"""
    maker = ElementMaker(nsmap=tcx_root.nsmap, namespace=default_ns)
    maker2 = ElementMaker(nsmap=tcx_root.nsmap, namespace=seconds_ns)

    def make_track_points(_tcx_root, _breaks_dict):
        """

        :param etree._Element _tcx_root:
        :param dict[int, int] _breaks_dict:
        :return:
        """
        source_track_points = _tcx_root.findall(add_ns('.//Trackpoint', default_ns))
        result_track_points = []
        for source_track_point in source_track_points:
            timestamp = source_track_point.find(add_ns('./Time', default_ns)).text
            result_track_point = maker.Trackpoint(
                maker.Time(
                    add_breaks_to_timestamp(timestamp, _breaks_dict)
                ),
                maker.AltitudeMeters(
                    source_track_point.find(add_ns('./AltitudeMeters', default_ns)).text
                ),
                maker.DistanceMeters(
                    source_track_point.find(add_ns('./DistanceMeters', default_ns)).text
                ),
                maker.HeartRateBpm(
                    maker.Value(source_track_point.find(add_ns('./HeartRateBpm/Value', default_ns)).text),
                    type='HeartRateInBeatsPerMinute_t'
                ),
                maker.Cadence(
                    source_track_point.find(add_ns('./Cadence', default_ns)).text
                ),
                maker.Extensions(
                    maker2.TPX(
                        maker2.Speed(
                            source_track_point.find(
                                add_ns('./Extensions', default_ns) + add_ns('/TPX/Speed', seconds_ns)).text
                        )
                    )
                )
            )
            result_track_points.append(result_track_point)
        return result_track_points

    tcx_res = maker.TrainingCenterDatabase(
        maker.Activities(
            maker.Activity(
                maker.Id(
                    tcx_root.find(add_ns('.//Activity/Id', default_ns)).text
                ),
                maker.Lap(
                    maker.Track(
                        *make_track_points(tcx_root, breaks)
                    ),
                    maker.TotalTimeSeconds(
                        tcx_root.find(add_ns('.//Lap/TotalTimeSeconds', default_ns)).text
                    ),
                    maker.DistanceMeters(
                        tcx_root.find(add_ns('.//Lap/DistanceMeters', default_ns)).text
                    ),
                    maker.MaximumSpeed(
                        tcx_root.find(add_ns('.//Lap/MaximumSpeed', default_ns)).text
                    ),
                    maker.Calories(
                        tcx_root.find(add_ns('.//Lap/Calories', default_ns)).text
                    ),
                    maker.Intensity(
                        tcx_root.find(add_ns('.//Lap/Intensity', default_ns)).text
                    ),
                    maker.AverageHeartRateBpm(
                        maker.Value(
                            tcx_root.find(add_ns('.//Lap/AverageHeartRateBpm/Value', default_ns)).text
                        ),
                        type='HeartRateInBeatsPerMinute_t'
                    ),
                    maker.MaximumHeartRateBpm(
                        maker.Value(
                            tcx_root.find(add_ns('.//Lap/MaximumHeartRateBpm/Value', default_ns)).text
                        ),
                        type='HeartRateInBeatsPerMinute_t'
                    ),
                    maker.Cadence(
                        tcx_root.find(add_ns('.//Lap/Cadence', default_ns)).text
                    ),
                    maker.TriggerMethod(
                        'Manual'
                    ),
                    StartTime=tcx_root.find(add_ns('.//Lap', default_ns)).attrib['StartTime']
                ),
                Sport='Biking'
            )
        )
    )

    with open(_out_file, 'w+') as o:
        o.write(etree.tostring(tcx_res, xml_declaration=True, pretty_print=True, encoding='UTF-8'))
示例#2
0
def merge(file1, file2):
    """Merging 2 tcx files

    :param file1:
    :param file2:
    :return:
    """
    logger = logging.getLogger(__name__)
    lh = logging.StreamHandler()
    lh.setFormatter(
        logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
    lh.setLevel(logging.DEBUG)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(lh)

    assert os.path.isfile(file1)
    assert os.path.isfile(file2)

    tcx1_root = etree.parse(file1).getroot()
    tcx2_root = etree.parse(file2).getroot()
    default_ns = """http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2"""

    e = ElementMaker(nsmap=tcx1_root.nsmap, namespace=default_ns)

    header_tags = {
        'TotalTimeSeconds', 'DistanceMeters', 'MaximumSpeed', 'Calories',
        'AverageHeartRateBpm', 'MaximumHeartRateBpm', 'Intensity'
    }
    time_format = '%Y-%m-%dT%H:%M:%SZ'
    time_format2 = '%Y-%m-%dT%H:%M:%S.%fZ'

    def get_stats(tcx_root):
        """
        :param etree._Element tcx_root: root element of tcx file
        :rtype: dict[str, etree._Element]
        """
        return dict([
            (_tag, tcx_root.find(add_ns('.//Lap/{0}'.format(_tag),
                                        default_ns))) for _tag in header_tags
        ])

    stats1 = get_stats(tcx1_root)
    stats2 = get_stats(tcx2_root)
    header = dict()

    def text(_el):
        if not _el:
            return ''
        if not _el.getchildren():
            return _el.text
        return _el.getchildren()[0].text

    for tag_key in header_tags:
        logger.debug('%s: %s', stats1[tag_key], text(stats1[tag_key]))
        logger.debug('%s: %s', stats2[tag_key], text(stats2[tag_key]))
        t_res = max(stats1[tag_key], stats2[tag_key], key=text)
        logger.debug('res: %s', text(t_res))
        header.update({tag_key: text(t_res)})

    logger.debug(r"""Merged header will contain: {0}""".format(header))

    def get_track_points(tcx_root):
        """
        :param etree._Element tcx_root:
        :return:
        :rtype: list[etree._Element]
        """
        track_points = tcx_root.findall(
            add_ns(""".//Lap/Track/Trackpoint""", default_ns))
        logger.debug('{0} track points found in track {1}'.format(
            len(track_points), tcx_root))
        return track_points

    def strp_dif_formats(_time):
        try:
            res = calendar.timegm(time.strptime(_time, time_format))
        except ValueError:
            res = calendar.timegm(time.strptime(_time, time_format2))
        return res

    def get_point_time(point):
        """
        :param etree._Element point:
        :rtype: int
        """
        _time = point.find(add_ns('./Time', default_ns)).text
        seconds = strp_dif_formats(_time)
        return seconds

    points = get_track_points(tcx1_root) + get_track_points(tcx2_root)
    points.sort(key=get_point_time)  # points sorted by time

    def get_lap_time(_lap):
        """
        :param etree._Element _lap:
        :rtype: int
        """
        _time = _lap.attrib['StartTime']
        logger.debug('Lap with start time %s', _time)
        return strp_dif_formats(_time)

    def format_time(seconds):
        """
        :param int seconds:
        :rtype: str
        """
        return time.strftime(time_format, time.gmtime(seconds))

    lap_path = add_ns('.//Lap', default_ns)
    lap_time = min(get_lap_time(tcx1_root.find(lap_path)),
                   get_lap_time(tcx2_root.find(lap_path)))
    activity_id = format_time(get_point_time(points[0]) -
                              60)  # not duplicating activity in strava
    lap_start = format_time(lap_time)

    head = []
    for h in header:
        el = etree.Element(h)
        el.text = header[h]
        if el.tag not in ('MaximumHeartRateBpm', 'AverageHeartRateBpm'):
            head.append(el)

    head.extend(points)

    basic = e.TrainingCenterDatabase(
        e.Activities(
            e.Activity(e.Id(activity_id),
                       e.Lap(e.MaximumHeartRateBpm(
                           e.Value(header['MaximumHeartRateBpm']),
                           type='HeartRateInBeatsPerMinute_t'),
                             e.AverageHeartRateBpm(
                                 e.Value(header['AverageHeartRateBpm']),
                                 type='HeartRateInBeatsPerMinute_t'),
                             e.TriggerMethod('Manual'),
                             *head,
                             StartTime=lap_start),
                       Sport='Biking')))

    basic = etree.tostring(basic,
                           pretty_print=True,
                           encoding="UTF-8",
                           xml_declaration=True).replace('xsi:type', 'type')

    with open('output.xml', 'w+') as o:
        o.write(basic)