Skip to content

Commit

Permalink
Code refactoring - ticket #5
Browse files Browse the repository at this point in the history
  • Loading branch information
dgranda committed Mar 6, 2013
1 parent a0da00d commit d4d1b2b
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 170 deletions.
26 changes: 14 additions & 12 deletions imports/file_garminfit.py
Expand Up @@ -95,7 +95,7 @@ def buildActivitiesSummary(self):
self.activities = self.getActivities()
for activity in self.activities:
startTime = self.getDateTime(self.getStartTimeFromActivity(activity))
inDatabase = self.inDatabase(activity, startTime)
inDatabase = self.inDatabase(startTime)
sport = self.getSport(activity)
distance, duration = self.getDetails(activity, startTime)
distance = distance / 1000.0
Expand All @@ -109,15 +109,14 @@ def buildActivitiesSummary(self):

def testFile(self, filename):
logging.debug('>>')
logging.debug("Testing " + filename)
logging.debug("Testing %s" %filename)
try:
# Original file is in fit format, so first it will be translated into tcxv2 and then parsed
xmldoc = etree.fromstring(self.fromFIT2TCXv2(filename))
valid_xml = self.validate(xmldoc, "schemas/GarminTrainingCenterDatabase_v2.xsd")
if (valid_xml):
logging.debug("FIT file converted to valid TCXv2")
logging.debug("FIT file %s converted to valid TCXv2" %filename)
self.xmldoc = xmldoc
#Possibly multiple entries in file
self.buildActivitiesSummary()
return True
except:
Expand All @@ -131,16 +130,19 @@ def getActivities(self):
activities = self.xmldoc.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
return activities

def inDatabase(self, activity, startTime):
def inDatabase(self, startTime):
#comparing date and start time (sport may have been changed in DB after import)
time = startTime
if time is None:
return False
time = time[0].strftime("%Y-%m-%dT%H:%M:%SZ")
if self.parent.parent.ddbb.select("records","*","date_time_utc=\"%s\"" % (time)):
return True
logging.debug('>>')
result = False
if startTime is not None:
logging.info("Checking if activity from %s exists in db" % startTime[0])
time = startTime[0].strftime("%Y-%m-%dT%H:%M:%SZ")
if self.parent.parent.ddbb.select("records","*","date_time_utc=\"%s\"" % (time)):
result = True
else:
return False
logging.info("No start time provided, nothing to check")
logging.debug('<<')
return result

def getSport(self, activity):
try:
Expand Down
322 changes: 164 additions & 158 deletions imports/file_garmintcxv2.py
Expand Up @@ -26,162 +26,168 @@
from pytrainer.lib.xmlUtils import XMLParser

class garmintcxv2():
def __init__(self, parent = None, data_path = None):
self.parent = parent
self.pytrainer_main = parent.parent
self.tmpdir = self.pytrainer_main.profile.tmpdir
self.main_data_path = data_path
self.data_path = os.path.dirname(__file__)
self.xmldoc = None
self.activitiesSummary = []
self.activities = []

def getXmldoc(self):
''' Function to return parsed xmlfile '''
return self.xmldoc

def getFileType(self):
return _("Garmin training center database file version 2")

def getActivitiesSummary(self):
return self.activitiesSummary

def getDetails(self, activity, startTime):
logging.debug(">>")
distance = 0
duration = 0
laps = activity.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Lap")
if laps:
for lap in laps:
lap_duration = float(lap.findtext(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}TotalTimeSeconds"))
lap_distance = float(lap.findtext(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}DistanceMeters"))
logging.debug("Lap distance (m): %f | duration (s): %f" % (lap_distance, lap_duration))
distance += lap_distance
duration += lap_duration
hours = int(duration)//3600
minutes = (int(duration)/60)%60
seconds = int(duration)%60
duration_hhmmss = "%02d:%02d:%02d" % (hours, minutes, seconds)
logging.debug("Activity distance (m): %f | duration (hh:mm:ss - s): %s - %f" % (distance, duration_hhmmss, duration))
else:
points = activity.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Trackpoint")
while True:
lastPoint = points[-1]
try:
distance = lastPoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}DistanceMeters")
if distance is None:
points = points[:-1]
continue
time = lastPoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Time")
distance = distance.text
time = time.text
break
except:
#Try again without the last point (i.e work from end until find time and distance)
points = points[:-1]
continue
duration_hhmmss = self.getDateTime(time)[0]-startTime[0]
logging.debug("Activity distance (m): %f | duration (hh:mm:ss): %s" % (distance, duration_hhmmss))
logging.debug("<<")
return float(distance), duration_hhmmss

def testFile(self, filename):
logging.debug('>>')
logging.debug("Testing " + filename)
try:
#parse filename as xml
xmldoc = etree.parse(filename)
#Parse XML schema
xmlschema_doc = etree.parse(self.main_data_path+"schemas/GarminTrainingCenterDatabase_v2.xsd")
xmlschema = etree.XMLSchema(xmlschema_doc)
if (xmlschema.validate(xmldoc)):
#Valid file
self.xmldoc = xmldoc
#Possibly multiple entries in file
self.activities = self.getActivities(xmldoc)
for activity in self.activities:
startTime = self.getDateTime(self.getStartTimeFromActivity(activity))
inDatabase = self.inDatabase(activity, startTime)
sport = self.getSport(activity)

distance, duration = self.getDetails(activity, startTime)
distance = distance / 1000.0
self.activitiesSummary.append( (self.activities.index(activity),
inDatabase,
startTime[1].strftime("%Y-%m-%dT%H:%M:%S"),
"%0.2f" % distance ,
str(duration),
sport,
) )
#print self.activitiesSummary
return True
except:
logging.debug("Traceback: %s" % traceback.format_exc())
return False
return False

def getActivities(self, tree):
'''Function to return all activities in Garmin training center version 2 file
'''
root = tree.getroot()
activities = root.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
return activities

def inDatabase(self, activity, startTime):
#comparing date and start time (sport may have been changed in DB after import)
time = startTime
if time is None:
return False
time = time[0].strftime("%Y-%m-%dT%H:%M:%SZ")
if self.parent.parent.ddbb.select("records","*","date_time_utc=\"%s\"" % (time)):
return True
else:
return False

def getSport(self, activity):
#return sport from activity
#sportElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
try:
sport = activity.get("Sport")
except:
sport = "import"
return sport

def getStartTimeFromActivity(self, activity):
timeElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Id")
if timeElement is None:
return None
else:
return timeElement.text

def getDateTime(self, time_):
return Date().getDateTime(time_)

def getGPXFile(self, ID, file_id):
"""
Generate GPX file based on activity ID
Returns (sport, GPX filename)
"""
sport = None
gpxFile = None
activityID = int(ID)
activitiesCount = len(self.activities)
if activitiesCount > 0 and activityID < activitiesCount:
gpxFile = "%s/garmin-tcxv2-%s-%d.gpx" % (self.tmpdir, file_id, activityID)
activity = self.activities[int(activityID)]
sport = self.getSport(activity)
self.createGPXfile(gpxFile, activity)
return sport, gpxFile


def createGPXfile(self, gpxfile, activity):
""" Function to transform a Garmin Training Center v2 Track to a valid GPX+ file
"""
xslt_doc = etree.parse(self.data_path+"/translate_garmintcxv2.xsl")
transform = etree.XSLT(xslt_doc)
#xml_doc = etree.parse(filename)
xml_doc = activity
result_tree = transform(xml_doc)
result_tree.write(gpxfile, xml_declaration=True, encoding='UTF-8')
def __init__(self, parent = None, data_path = None):
if parent is not None:
self.parent = parent
self.pytrainer_main = parent.parent
self.tmpdir = self.pytrainer_main.profile.tmpdir
if data_path is not None:
self.main_data_path = data_path
self.data_path = os.path.dirname(__file__)
self.xmldoc = None
self.activitiesSummary = []
self.activities = []

def getXmldoc(self):
''' Function to return parsed xmlfile '''
return self.xmldoc

def getFileType(self):
return _("Garmin training center database file version 2")

def getActivitiesSummary(self):
return self.activitiesSummary

def getDetails(self, activity, startTime):
logging.debug(">>")
distance = 0
duration = 0
laps = activity.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Lap")
if laps:
for lap in laps:
lap_duration = float(lap.findtext(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}TotalTimeSeconds"))
lap_distance = float(lap.findtext(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}DistanceMeters"))
logging.debug("Lap distance (m): %f | duration (s): %f" % (lap_distance, lap_duration))
distance += lap_distance
duration += lap_duration
hours = int(duration)//3600
minutes = (int(duration)/60)%60
seconds = int(duration)%60
duration_hhmmss = "%02d:%02d:%02d" % (hours, minutes, seconds)
logging.debug("Activity distance (m): %f | duration (hh:mm:ss - s): %s - %f" % (distance, duration_hhmmss, duration))
else:
points = activity.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Trackpoint")
while True:
lastPoint = points[-1]
try:
distance = lastPoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}DistanceMeters")
if distance is None:
points = points[:-1]
continue
time = lastPoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Time")
distance = distance.text
time = time.text
break
except:
#Try again without the last point (i.e work from end until find time and distance)
points = points[:-1]
continue
duration_hhmmss = self.getDateTime(time)[0]-startTime[0]
logging.debug("Activity distance (m): %f | duration (hh:mm:ss): %s" % (distance, duration_hhmmss))
logging.debug("<<")
return float(distance), duration_hhmmss

def validate(self, xmldoc, schema):
logging.debug(">>")
xmlschema_doc = etree.parse(self.main_data_path + schema)
xmlschema = etree.XMLSchema(xmlschema_doc)
logging.debug("<<")
return xmlschema.validate(xmldoc)

def buildActivitiesSummary(self):
logging.debug(">>")
self.activities = self.getActivities()
for activity in self.activities:
startTime = self.getDateTime(self.getStartTimeFromActivity(activity))
inDatabase = self.inDatabase(startTime)
sport = self.getSport(activity)
distance, duration = self.getDetails(activity, startTime)
distance = distance / 1000.0
self.activitiesSummary.append((self.activities.index(activity),
inDatabase,
startTime[1].strftime("%Y-%m-%dT%H:%M:%S"),
"%0.2f" % distance ,
str(duration),
sport,
))
logging.debug("<<")

def testFile(self, filename):
'''Check if file is valid TCXv2 one and if yes, retrieve activities from it'''
logging.debug('>>')
logging.debug("Testing %s" %filename)
result = False
try:
xmldoc = etree.parse(filename)
valid_xml = self.validate(xmldoc, "schemas/GarminTrainingCenterDatabase_v2.xsd")
if (valid_xml):
logging.debug("Valid TCXv2 file (%s)" %filename)
self.xmldoc = xmldoc
self.buildActivitiesSummary()
result = True
except:
logging.debug("Traceback: %s" % traceback.format_exc())
logging.debug('<<')
return result

def getActivities(self):
'''Function to return all activities in Garmin training center version 2 file'''
logging.debug('>>')
activities = self.xmldoc.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Activity")
logging.debug('<<')
return activities

def inDatabase(self, startTime):
#comparing date and start time (sport may have been changed in DB after import)
logging.debug('>>')
result = False
if startTime is not None:
logging.info("Checking if activity from %s exists in db" % startTime[0]) # 2012-10-14 10:02:42+00:00
time = startTime[0].strftime("%Y-%m-%dT%H:%M:%SZ")
if self.parent.parent.ddbb.select("records","*","date_time_utc=\"%s\"" % (time)):
result = True
else:
logging.info("No start time provided, nothing to check")
logging.debug('<<')
return result

def getSport(self, activity):
try:
sport = activity.get("Sport")
except:
sport = "import"
return sport

def getStartTimeFromActivity(self, activity):
timeElement = activity.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Id")
if timeElement is None:
return None
else:
return timeElement.text

def getDateTime(self, time_):
return Date().getDateTime(time_)

def getGPXFile(self, ID, file_id):
""" Generate GPX file based on activity ID
Returns (sport, GPX filename)
"""
sport = None
gpxFile = None
activityID = int(ID)
activitiesCount = len(self.activities)
if activitiesCount > 0 and activityID < activitiesCount:
gpxFile = "%s/garmin-tcxv2-%s-%d.gpx" % (self.tmpdir, file_id, activityID)
activity = self.activities[int(activityID)]
sport = self.getSport(activity)
self.createGPXfile(gpxFile, activity)
return sport, gpxFile

def createGPXfile(self, gpxfile, activity):
""" Function to transform a Garmin Training Center v2 Track to a valid GPX+ file"""
xslt_doc = etree.parse(self.data_path+"/translate_garmintcxv2.xsl")
transform = etree.XSLT(xslt_doc)
xml_doc = activity
result_tree = transform(xml_doc)
result_tree.write(gpxfile, xml_declaration=True, encoding='UTF-8')

0 comments on commit d4d1b2b

Please sign in to comment.