source: OpenWorkouts-current/ow/utilities.py @ 119412d

currentfeature/docs
Last change on this file since 119412d was 119412d, checked in by borja <borja@…>, 5 years ago

Modify create_blob() so we can tell if we want to create a blob from binary

content or not.

  • Property mode set to 100644
File size: 5.5 KB
Line 
1import re
2from datetime import datetime
3from decimal import Decimal
4from shutil import copyfileobj
5
6from unidecode import unidecode
7from xml.dom import minidom
8from ZODB.blob import Blob
9
10
11def slugify(text, delim=u'-'):
12    """
13    Generates an ASCII-only slug.
14    from http://flask.pocoo.org/snippets/5/
15    """
16    _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')
17    result = []
18    text = unidecode(text)
19    for word in _punct_re.split(text.lower()):
20        result.extend(word.split())
21    return delim.join(result)
22
23
24class GPXMinidomParser(object):
25    """
26    GPX parser, using minidom from the base library.
27
28    We need this as a workaround, as gpxpy does not handle GPX 1.1 extensions
29    correctly right now (and we have not been able to fix it).
30
31    This method is inspired by this blog post:
32
33    http://castfortwo.blogspot.com.au/2014/06/
34    parsing-strava-gpx-file-with-python.html
35    """
36
37    def __init__(self, gpx_path):
38        self.gpx_path = gpx_path
39        self.gpx = None
40        self.tracks = {}
41
42    def load_gpx(self):
43        """
44        Load the given gpx file into a minidom doc, normalize it and set
45        self.gpx to the document root so we can reuse it later on
46        """
47        doc = minidom.parse(self.gpx_path)
48        doc.normalize()
49        self.gpx = doc.documentElement
50
51    def parse_tracks(self):
52        """
53        Loop over all the tracks found in the gpx, parsing them
54        """
55        for trk in self.gpx.getElementsByTagName('trk'):
56            self.parse_track(trk)
57
58    def parse_track(self, trk):
59        """
60        Parse the given track, extracting all the information and putting it
61        into a dict where the key is the track name and the value is a list
62        of data for the the different segments and points in the track.
63
64        All the data is saved in self.tracks
65        """
66        name = trk.getElementsByTagName('name')[0].firstChild.data
67        if name not in self.tracks:
68            self.tracks[name] = []
69
70        for trkseg in trk.getElementsByTagName('trkseg'):
71            for trkpt in trkseg.getElementsByTagName('trkpt'):
72                lat = Decimal(trkpt.getAttribute('lat'))
73                lon = Decimal(trkpt.getAttribute('lon'))
74
75                # There could happen there is no elevation data
76                ele = trkpt.getElementsByTagName('ele')
77                if ele:
78                    ele = Decimal(ele[0].firstChild.data)
79                else:
80                    ele = None
81
82                rfc3339 = trkpt.getElementsByTagName('time')[0].firstChild.data
83                try:
84                    t = datetime.strptime(
85                        rfc3339, '%Y-%m-%dT%H:%M:%S.%fZ')
86                except ValueError:
87                    t = datetime.strptime(
88                        rfc3339, '%Y-%m-%dT%H:%M:%SZ')
89
90                hr = None
91                cad = None
92                atemp = None
93                extensions = trkpt.getElementsByTagName('extensions')
94                if extensions:
95                    extensions = extensions[0]
96                    trkPtExt = extensions.getElementsByTagName(
97                        'gpxtpx:TrackPointExtension')[0]
98                    if trkPtExt:
99                        hr_ext = trkPtExt.getElementsByTagName('gpxtpx:hr')
100                        cad_ext = trkPtExt.getElementsByTagName('gpxtpx:cad')
101                        atemp_ext = trkPtExt.getElementsByTagName(
102                            'gpxtpx:atemp')
103                        if hr_ext:
104                            hr = Decimal(hr_ext[0].firstChild.data)
105                        if cad_ext:
106                            cad = Decimal(cad_ext[0].firstChild.data)
107                        if atemp_ext:
108                            atemp = Decimal(atemp_ext[0].firstChild.data)
109
110                self.tracks[name].append({
111                    'lat': lat,
112                    'lon': lon,
113                    'ele': ele,
114                    'time': t,
115                    'hr': hr,
116                    'cad': cad,
117                    'atemp': atemp})
118
119
120def semicircles_to_degrees(semicircles):
121    return semicircles * (180 / pow(2, 31))
122
123
124def degrees_to_semicircles(degrees):
125    return degrees * (pow(2, 31) / 180)
126
127
128def miles_to_kms(miles):
129    factor = 0.62137119
130    return miles / factor
131
132
133def kms_to_miles(kms):
134    factor = 0.62137119
135    return kms * factor
136
137
138def meters_to_kms(meters):
139    return meters / 1000
140
141
142def kms_to_meters(kms):
143    return kms * 1000
144
145
146def mps_to_kmph(mps):
147    """
148    Transform a value from meters-per-second to kilometers-per-hour
149    """
150    return mps * 3.6
151
152
153def kmph_to_mps(kmph):
154    """
155    Transform a value from kilometers-per-hour to meters-per-second
156    """
157    return kmph * 0.277778
158
159
160def copy_blob(blob):
161    """
162    Create a copy of a blob object, returning another blob object that is
163    the copy of the given blob file.
164    """
165    new_blob = Blob()
166    if getattr(blob, 'file_extension', None):
167        new_blob.file_extension = blob.file_extension
168    with blob.open('r') as orig_blob, new_blob.open('w') as dest_blob:
169        orig_blob.seek(0)
170        copyfileobj(orig_blob, dest_blob)
171    return new_blob
172
173
174def create_blob(data, file_extension, binary=False):
175    """
176    Create a ZODB blob file from some data, return the blob object
177    """
178    blob = Blob()
179    blob.file_extension = file_extension
180    with blob.open('w') as open_blob:
181        # use .encode() to convert the string to bytes if needed
182        if not binary and not isinstance(data, bytes):
183            data = data.encode('utf-8')
184        open_blob.write(data)
185    return blob
Note: See TracBrowser for help on using the repository browser.