Beispiel #1
0
 def test_get_laps_old(self):
     orig_laps = [("1264.66", "42.84154594", "-2.68554166", "426",
                   "5000.71875", "42.83547375", "-2.68631422", "active",
                   "170", "177", "4.93775940", "manual"),
                  ("1279.71", "42.86093295", "-2.66849270", "445",
                   "5162.37109", "42.84155038", "-2.68552473", "active",
                   "176", "179", "5.10653210", "manual"),
                  ("1263.54", "42.83505499", "-2.67709371", "423",
                   "4882.18457", "42.86094376", "-2.66848792", "active",
                   "176", "179", "4.37805939", "manual"),
                  ("1525.68", "42.84018606", "-2.68670272", "426",
                   "4973.64746", "42.83504661", "-2.67710888", "active",
                   "167", "181", "4.52464294", "manual"),
                  ("374.23", "42.83771038", "-2.68647373", "96",
                   "1098.94531", "42.84018849", "-2.68670733", "active",
                   "159", "163", "4.30066299", "manual")]
     try:
         xml_file = os.path.dirname(
             os.path.abspath(__file__)) + "/gpxplus_sample_old.gpx"
         gpx = Gpx(None, None)  # avoid launching _getValues
         gpx.tree = etree.ElementTree(file=xml_file).getroot()
         gpx_laps = gpx.getLaps()
         self.assertEqual(orig_laps, gpx_laps)
     except ():
         self.fail()
Beispiel #2
0
 def test_get_laps_old(self):
     orig_laps = [
         ("1264.66","42.84154594","-2.68554166","426","5000.71875","42.83547375","-2.68631422","active","170","177","4.93775940","manual"),
         ("1279.71","42.86093295","-2.66849270","445","5162.37109","42.84155038","-2.68552473","active","176","179","5.10653210","manual"),
         ("1263.54","42.83505499","-2.67709371","423","4882.18457","42.86094376","-2.66848792","active","176","179","4.37805939","manual"),
         ("1525.68","42.84018606","-2.68670272","426","4973.64746","42.83504661","-2.67710888","active","167","181","4.52464294","manual"),
         ("374.23","42.83771038","-2.68647373","96","1098.94531","42.84018849","-2.68670733","active","159","163","4.30066299","manual")]
     try:
         xml_file = os.path.dirname(__file__) + "/gpxplus_sample_old.gpx"
         gpx = Gpx(None, None) # avoid launching _getValues
         gpx.tree = etree.ElementTree(file = xml_file).getroot()
         gpx_laps = gpx.getLaps()
         self.assertEquals(orig_laps, gpx_laps)
     except():
         self.fail()
Beispiel #3
0
 def gpx(self):
     '''
     Get activity information from the GPX file
     '''
     logging.debug(">>")
     if self._gpx:
         logging.debug("Return pre-created GPX")
         return self._gpx
     elif self.gpx_file:
         logging.debug("Parse GPX")
         #Parse GPX file
         #print "Activity initing GPX.. ",
         self._gpx = Gpx(
             filename=self.gpx_file)  #TODO change GPX code to do less....
         logging.info(
             "GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s"
             % (self.gpx.total_dist, self.gpx.total_dist_trkpts,
                self.gpx.total_time, self.gpx.total_time_trkpts))
         time_diff = self.gpx.total_time_trkpts - self.gpx.total_time
         acceptable_lapse = 4  # number of seconds that duration calculated using lap and trkpts data can differ
         if time_diff > acceptable_lapse:
             self.time_pause = time_diff
             logging.debug("Identified non active time: %s s" %
                           self.time_pause)
         return self._gpx
     else:
         logging.debug("No GPX file found")
         return None
     logging.debug("<<")
Beispiel #4
0
 def test_get_laps(self):
     orig_laps = [("311.31", "43.53781521", "-5.63955233", "81",
                   "1000.000000", "43.54065232", "-5.65094300", "active",
                   "158", "178", "3.52099586", "distance"),
                  ("337.85", "43.53220135", "-5.63737772", "83",
                   "1000.000000", "43.53780859", "-5.63955157", "active",
                   "149", "153", "4.07694530", "distance"),
                  ("342.13", "43.52516323", "-5.64443462", "87",
                   "1000.000000", "43.53218752", "-5.63737328", "active",
                   "150", "154", "3.13006544", "distance"),
                  ("353.81", "43.52035671", "-5.65329663", "86",
                   "1000.000000", "43.52515301", "-5.64443881", "active",
                   "146", "150", "3.00786400", "distance"),
                  ("352.61", "43.51314962", "-5.65532908", "87",
                   "1000.000000", "43.52035412", "-5.65329814", "active",
                   "148", "158", "3.17764997", "distance"),
                  ("354.17", "43.52061689", "-5.65409191", "87",
                   "1000.000000", "43.51314115", "-5.65533193", "active",
                   "142", "149", "3.52461219", "distance"),
                  ("343.65", "43.52592498", "-5.64510651", "86",
                   "1000.000000", "43.52062519", "-5.65408990", "active",
                   "144", "147", "3.04636431", "distance"),
                  ("366.95", "43.53079587", "-5.63821390", "83",
                   "1000.000000", "43.52592733", "-5.64509553", "active",
                   "142", "150", "3.21967506", "distance"),
                  ("345.69", "43.53726536", "-5.63784711", "87",
                   "1000.000000", "43.53081406", "-5.63820661", "active",
                   "146", "150", "4.38874722", "distance"),
                  ("330.64", "43.54042768", "-5.64873822", "86",
                   "1000.000000", "43.53726494", "-5.63783269", "active",
                   "149", "154", "3.56236672", "distance"),
                  ("41.96", "43.54054570", "-5.65028653", "11",
                   "132.227539", "43.54043892", "-5.64874199", "active",
                   "150", "152", "3.40324497", "manual")]
     try:
         xml_file = os.path.dirname(
             os.path.abspath(__file__)) + "/gpxplus_sample.gpx"
         gpx = Gpx(None, None)  # avoid launching _getValues
         gpx.tree = etree.ElementTree(file=xml_file).getroot()
         gpx_laps = gpx.getLaps()
         self.assertEqual(orig_laps, gpx_laps)
     except ():
         self.fail()
Beispiel #5
0
 def test_get_laps(self):
     orig_laps = [
         ("311.31","43.53781521","-5.63955233","81","1000.000000","43.54065232","-5.65094300","active","158","178","3.52099586","distance"),
         ("337.85","43.53220135","-5.63737772","83","1000.000000","43.53780859","-5.63955157","active","149","153","4.07694530","distance"),
         ("342.13","43.52516323","-5.64443462","87","1000.000000","43.53218752","-5.63737328","active","150","154","3.13006544","distance"),
         ("353.81","43.52035671","-5.65329663","86","1000.000000","43.52515301","-5.64443881","active","146","150","3.00786400","distance"),
         ("352.61","43.51314962","-5.65532908","87","1000.000000","43.52035412","-5.65329814","active","148","158","3.17764997","distance"),
         ("354.17","43.52061689","-5.65409191","87","1000.000000","43.51314115","-5.65533193","active","142","149","3.52461219","distance"),
         ("343.65","43.52592498","-5.64510651","86","1000.000000","43.52062519","-5.65408990","active","144","147","3.04636431","distance"),
         ("366.95","43.53079587","-5.63821390","83","1000.000000","43.52592733","-5.64509553","active","142","150","3.21967506","distance"),
         ("345.69","43.53726536","-5.63784711","87","1000.000000","43.53081406","-5.63820661","active","146","150","4.38874722","distance"),
         ("330.64","43.54042768","-5.64873822","86","1000.000000","43.53726494","-5.63783269","active","149","154","3.56236672","distance"),
         ("41.96","43.54054570","-5.65028653","11","132.227539","43.54043892","-5.64874199","active","150","152","3.40324497","manual")]
     try:
         xml_file = os.path.dirname(__file__) + "/gpxplus_sample.gpx"
         gpx = Gpx(None, None) # avoid launching _getValues
         gpx.tree = etree.ElementTree(file = xml_file).getroot()
         gpx_laps = gpx.getLaps()
         self.assertEquals(orig_laps, gpx_laps)
     except():
         self.fail()
Beispiel #6
0
    def test_missing_tracks(self):
        trkdata = """<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" creator="" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
</gpx>
"""

        # Write a GPX file with no tracks
        with NamedTemporaryFile(mode='w', delete=False) as tmpf:
            tmpf.write(trkdata)
        self.tmp_files.append(tmpf.name)

        try:
            g = Gpx(filename=tmpf.name)
        except IndexError:
            self.fail("Gpx parser crashed on file without tracks")
Beispiel #7
0
    def _init_from_gpx_file(self):
        '''
		Get activity information from the GPX file
		'''
        logging.debug(">>")
        #Parse GPX file
        #print "Activity initing GPX.. ",
        self.gpx = Gpx(
            filename=self.gpx_file)  #TODO change GPX code to do less....
        self.tree = self.gpx.tree
        self.tracks = self.gpx.getTrackList(
        )  #TODO fix - this should removed and replaced with self.tracklist functionality
        self.tracklist = self.gpx.trkpoints
        self.gpx_distance = self.gpx.total_dist
        logging.info(
            "GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s"
            % (self.gpx_distance, self.gpx.total_dist_trkpts,
               self.gpx.total_time, self.gpx.total_time_trkpts))
        time_diff = self.gpx.total_time_trkpts - self.gpx.total_time
        acceptable_lapse = 4  # number of seconds that duration calculated using lap and trkpts data can differ
        if time_diff > acceptable_lapse:
            self.time_pause = time_diff
            logging.debug("Identified non active time: %s s" % self.time_pause)
        logging.debug("<<")
Beispiel #8
0
    def test_missing_tracks(self):
        trkdata = """<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" creator="" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
</gpx>
"""

        # Write a GPX file with no tracks
        file_name = "test-missing.gpx"
        tmpf = file(file_name, 'w')
        tmpf.write(trkdata)
        tmpf.close()
        self.tmp_files.append(file_name)

        try:
            g = Gpx(filename=file_name)
        except IndexError:
            self.fail("Gpx parser crashed on file without tracks")
Beispiel #9
0
	def _init_from_gpx_file(self):
		'''
		Get activity information from the GPX file
		'''
		logging.debug(">>")
		#Parse GPX file
		#print "Activity initing GPX.. ",
		self.gpx = Gpx(filename = self.gpx_file) #TODO change GPX code to do less....
		self.tree = self.gpx.tree
		self.tracks = self.gpx.getTrackList() #TODO fix - this should removed and replaced with self.tracklist functionality
		self.tracklist = self.gpx.trkpoints
		self.gpx_distance = self.gpx.total_dist
		logging.info("GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s" % (self.gpx_distance, self.gpx.total_dist_trkpts, self.gpx.total_time, self.gpx.total_time_trkpts))
		time_diff = self.gpx.total_time_trkpts - self.gpx.total_time
		acceptable_lapse = 4 # number of seconds that duration calculated using lap and trkpts data can differ
		if time_diff > acceptable_lapse:
			self.time_pause = time_diff
			logging.debug("Identified non active time: %s s" % self.time_pause)
		logging.debug("<<")
Beispiel #10
0
class Activity:
    '''
	Class that knows everything about a particular activity

	All values are stored in the class (and DB) in metric and are converted as needed

	tracks			- (list) tracklist from gpx
	tracklist		- (list of dict) trackpoint data from gpx
	laps			- (list of dict) lap list
	tree			- (ElementTree) parsed xml of gpx file
	us_system		- (bool) True: imperial measurement False: metric measurement
	distance_unit	- (string) unit to use for distance
	speed_unit		- (string) unit to use for speed
	distance_data	- (dict of graphdata classes) contains the graph data with x axis distance
	time_data		- (dict of graphdata classes) contains the graph data with x axis time
	height_unit		- (string) unit to use for height
	pace_unit		- (string) unit to use for pace
	gpx_file		- (string) gpx file name
	gpx				- (Gpx class) actual gpx instance
	sport_name		- (string) sport name
	sport_id		- (string) id for sport in sports table
	title			- (string) title of activity
	date			- (string) date of activity
	time			- (int) activity duration in seconds
	time_tuple		- (tuple) activity duration as hours, min, secs tuple
	beats			- (int) average heartrate for activity
	maxbeats 		- (int) maximum heartrate for activity
	comments		- (string) activity comments
	calories		- (int) calories of activity
	id_record		- (string) id for activity in records table
	date_time_local	- (string) date and time of activity in local timezone
	date_time_utc	- (string) date and time of activity in UTC timezone
	date_time		- (datetime) date and time of activity in local timezone
	starttime		- (string)
	distance 		- (float) activity distance
	average			- (float) average speed of activity
	upositive 		- (float) height climbed during activity
	unegative		- (float) height decended during activity
	maxspeed 		- (float) maximum speed obtained during activity
	maxpace 		- (float) maxium pace obtained during activity
	pace			- (float) average pace for activity
	has_data		- (bool) true if instance has data populated
	x_axis			- (string) distance or time, determines what will be graphed on x axis
	x_limits		- (tuple of float) start, end limits of x axis (as determined by matplotlib)
	y1_limits		- (tuple of float) start, end limits of y1 axis (as determined by matplotlib)
	y2_limits		- (tuple of float) start, end limits of y2 axis (as determined by matplotlib)
	x_limits_u		- (tuple of float) start, end limits of x axis (as requested by user)
	y1_limits_u		- (tuple of float) start, end limits of y1 axis (as requested by user)
	y2_limits_u		- (tuple of float) start, end limits of y2 axis (as requested by user)
	show_laps		- (bool) display laps on graphs
	lap_distance	- (graphdata)
	lap_time		- (graphdata)
	pace_limit		- (int) maximum pace that is valid for this activity
	'''
    def __init__(self, pytrainer_main=None, id=None):
        logging.debug(">>")
        self.id = id
        #It is an error to try to initialise with no id
        if self.id is None:
            return
        #It is an error to try to initialise with no reference to pytrainer_main
        if pytrainer_main is None:
            print(
                "Error - must initialise with a reference to the main pytrainer class"
            )
            return
        self.pytrainer_main = pytrainer_main
        self.tracks = None
        self.tracklist = None
        self.laps = None
        self.tree = None
        self.has_data = False
        self.distance_data = {}
        self.time_data = {}
        self.time_pause = 0
        self.pace_limit = None
        self.starttime = None
        self.gpx_distance = None
        #self.upositive = 0
        #self.unegative = 0
        if self.pytrainer_main.profile.getValue("pytraining",
                                                "prf_us_system") == "True":
            self.us_system = True
        else:
            self.us_system = False
        self._set_units()
        self.gpx_file = "%s/%s.gpx" % (self.pytrainer_main.profile.gpxdir, id)
        #It is OK to not have a GPX file for an activity - this just limits us to information in the DB
        if not os.path.isfile(self.gpx_file):
            self.gpx_file = None
            logging.debug("No GPX file found for record id: %s" % id)
        if self.gpx_file is not None:
            self._init_from_gpx_file()
        self._init_from_db()
        self._init_graph_data()
        self._generate_per_lap_graphs()
        self.x_axis = "distance"
        self.x_limits = (None, None)
        self.y1_limits = (None, None)
        self.y2_limits = (None, None)
        self.x_limits_u = (None, None)
        self.y1_limits_u = (None, None)
        self.y2_limits_u = (None, None)
        self.y1_grid = False
        self.y2_grid = False
        self.x_grid = False
        self.show_laps = False
        logging.debug("<<")

    def __str__(self):
        return '''
        tracks (%s)
		tracklist (%s)
		laps (%s)
		tree (%s)
		us_system (%s)
		distance_unit (%s)
		speed_unit (%s)
		distance_data (%s)
		time_data (%s)
		height_unit (%s)
		pace_unit (%s)
		gpx_file (%s)
		gpx (%s)
		sport_name (%s)
		sport_id (%s)
		title (%s)
		date (%s)
		time (%s)
		time_tuple (%s)
		beats (%s)
		maxbeats (%s)
		comments (%s)
		calories (%s)
		id_record (%s)
		date_time_local (%s)
		date_time_utc (%s)
		date_time (%s)
		starttime (%s)
		distance (%s)
		average (%s)
		upositive (%s)
		unegative (%s)
		maxspeed (%s)
		maxpace (%s)
		pace (%s)
		has_data (%s)
		x_axis (%s)
		x_limits (%s)
		y1_limits (%s)
		y2_limits (%s)
		x_limits_u (%s)
		y1_limits_u (%s)
		y2_limits_u (%s)
		show_laps (%s)
		lap_distance (%s)
		lap_time (%s)
		pace_limit (%s)
        ''' % ('self.tracks', self.tracklist, self.laps, self.tree,
               self.us_system, self.distance_unit, self.speed_unit,
               self.distance_data, self.time_data, self.height_unit,
               self.pace_unit, self.gpx_file, self.gpx, self.sport_name,
               self.sport_id, self.title, self.date, self.time,
               self.time_tuple, self.beats, self.maxbeats, self.comments,
               self.calories, self.id_record, self.date_time_local,
               self.date_time_utc, self.date_time, self.starttime,
               self.distance, self.average, self.upositive, self.unegative,
               self.maxspeed, self.maxpace, self.pace, self.has_data,
               self.x_axis, self.x_limits, self.y1_limits, self.y2_limits,
               self.x_limits_u, self.y1_limits_u, self.y2_limits_u,
               self.show_laps, self.lap_distance, self.lap_time,
               self.pace_limit)

    def _set_units(self):
        if self.us_system:
            self.distance_unit = _("miles")
            self.speed_unit = _("miles/h")
            self.pace_unit = _("min/mile")
            self.height_unit = _("feet")
        else:
            self.distance_unit = _("km")
            self.speed_unit = _("km/h")
            self.pace_unit = _("min/km")
            self.height_unit = _("m")
        self.units = {
            'distance': self.distance_unit,
            'average': self.speed_unit,
            'upositive': self.height_unit,
            'unegative': self.height_unit,
            'maxspeed': self.speed_unit,
            'pace': self.pace_unit,
            'maxpace': self.pace_unit
        }

    def _init_from_gpx_file(self):
        '''
		Get activity information from the GPX file
		'''
        logging.debug(">>")
        #Parse GPX file
        #print "Activity initing GPX.. ",
        self.gpx = Gpx(
            filename=self.gpx_file)  #TODO change GPX code to do less....
        self.tree = self.gpx.tree
        self.tracks = self.gpx.getTrackList(
        )  #TODO fix - this should removed and replaced with self.tracklist functionality
        self.tracklist = self.gpx.trkpoints
        self.gpx_distance = self.gpx.total_dist
        logging.info(
            "GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s"
            % (self.gpx_distance, self.gpx.total_dist_trkpts,
               self.gpx.total_time, self.gpx.total_time_trkpts))
        time_diff = self.gpx.total_time_trkpts - self.gpx.total_time
        acceptable_lapse = 4  # number of seconds that duration calculated using lap and trkpts data can differ
        if time_diff > acceptable_lapse:
            self.time_pause = time_diff
            logging.debug("Identified non active time: %s s" % self.time_pause)
        logging.debug("<<")

    def _init_from_db(self):
        '''
		Get activity information from the DB
		'''
        logging.debug(">>")
        #Get base information
        cols = ("sports.name", "id_sports", "date", "distance", "time",
                "beats", "comments", "average", "calories", "id_record",
                "title", "upositive", "unegative", "maxspeed", "maxpace",
                "pace", "maxbeats", "date_time_utc", "date_time_local",
                "sports.max_pace")
        # outer join on sport id to workaround bug where sport reference is null on records from GPX import
        db_result = self.pytrainer_main.ddbb.select(
            "records left outer join sports on records.sport=sports.id_sports",
            ", ".join(cols), "id_record=\"%s\" " % self.id)
        if len(db_result) == 1:
            row = db_result[0]
            self.sport_name = row[cols.index('sports.name')]
            if self.sport_name == None:
                self.sport_name = ""
            self.sport_id = row[cols.index('id_sports')]
            self.pace_limit = row[cols.index('sports.max_pace')]
            if self.pace_limit == 0 or self.pace_limit == "":
                self.pace_limit = None
            self.title = row[cols.index('title')]
            if self.title is None:
                self.title = ""
            self.date = row[cols.index('date')]
            self.time = self._int(row[cols.index('time')])
            self.time_tuple = Date().second2time(self.time)
            self.beats = self._int(row[cols.index('beats')])
            self.comments = row[cols.index('comments')]
            if self.comments is None:
                self.comments = ""
            self.calories = self._int(row[cols.index('calories')])
            self.id_record = row[cols.index('id_record')]
            self.maxbeats = self._int(row[cols.index('maxbeats')])
            #Sort time....
            # ... use local time if available otherwise use date_time_utc and create a local datetime...
            self.date_time_local = row[cols.index('date_time_local')]
            self.date_time_utc = row[cols.index('date_time_utc')]
            if self.date_time_local is not None:  #Have a local time stored in DB
                self.date_time = dateutil.parser.parse(self.date_time_local)
                self.starttime = self.date_time.strftime("%X")
            else:  #No local time in DB
                tmpDateTime = dateutil.parser.parse(self.date_time_utc)
                self.date_time = tmpDateTime.astimezone(tzlocal(
                ))  #datetime with localtime offset (using value from OS)
                self.starttime = self.date_time.strftime("%X")
            #Sort data that changes for the US etc
            #if self.us_system:
            #	self.distance = km2miles(self._float(row[cols.index('distance')]))
            #	self.average = km2miles(self._float(row[cols.index('average')]))
            #	self.upositive = m2feet(self._float(row[cols.index('upositive')]))
            #	self.unegative = m2feet(self._float(row[cols.index('unegative')]))
            #	self.maxspeed = km2miles(self._float(row[cols.index('maxspeed')]))
            #	self.maxpace = pacekm2miles(self._float(row[cols.index('maxpace')]))
            #	self.pace = pacekm2miles(self._float(row[cols.index('pace')]))
            #else:
            self.distance = self._float(row[cols.index('distance')])
            if not self.distance:
                self.distance = self.gpx_distance
            self.average = self._float(row[cols.index('average')])
            self.upositive = self._float(row[cols.index('upositive')])
            self.unegative = self._float(row[cols.index('unegative')])
            self.maxspeed = self._float(row[cols.index('maxspeed')])
            self.maxpace = self._float(row[cols.index('maxpace')])
            self.pace = self._float(row[cols.index('pace')])
            self.has_data = True
        else:
            raise Exception("Error - multiple results from DB for id: %s" %
                            self.id)
        #Get lap information
        laps = self.pytrainer_main.ddbb.select_dict(
            "laps", ("id_lap", "record", "elapsed_time", "distance",
                     "start_lat", "start_lon", "end_lat", "end_lon",
                     "calories", "lap_number", "intensity", "avg_hr", "max_hr",
                     "max_speed", "laptrigger", "comments"),
            "record=\"%s\"" % self.id)
        if laps is None or laps == [] or len(laps) < 1:  #No laps found
            logging.debug("No laps in DB for record %d" % self.id)
            if self.gpx_file is not None:
                laps = self._get_laps_from_gpx()
        self.laps = laps
        logging.debug("<<")

    def _generate_per_lap_graphs(self):
        '''Build lap based graphs...'''
        logging.debug(">>")
        if self.laps is None:
            logging.debug("No laps to generate graphs from")
            logging.debug("<<")
            return
        #Lap columns
        self.lap_distance = GraphData()
        self.lap_distance.set_color('#CCFF00', '#CCFF00')
        self.lap_distance.graphType = "vspan"
        self.lap_time = GraphData()
        self.lap_time.set_color('#CCFF00', '#CCFF00')
        self.lap_time.graphType = "vspan"
        #Pace
        title = _("Pace by Lap")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Pace'), self.pace_unit)
        self.distance_data['pace_lap'] = GraphData(title=title,
                                                   xlabel=xlabel,
                                                   ylabel=ylabel)
        self.distance_data['pace_lap'].set_color('#99CCFF', '#99CCFF')
        self.distance_data['pace_lap'].graphType = "bar"
        xlabel = _("Time (seconds)")
        self.time_data['pace_lap'] = GraphData(title=title,
                                               xlabel=xlabel,
                                               ylabel=ylabel)
        self.time_data['pace_lap'].set_color('#99CCFF', '#99CCFF')
        self.time_data['pace_lap'].graphType = "bar"
        #Speed
        title = _("Speed by Lap")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Speed'), self.speed_unit)
        self.distance_data['speed_lap'] = GraphData(title=title,
                                                    xlabel=xlabel,
                                                    ylabel=ylabel)
        self.distance_data['speed_lap'].set_color('#336633', '#336633')
        self.distance_data['speed_lap'].graphType = "bar"
        xlabel = _("Time (seconds)")
        self.time_data['speed_lap'] = GraphData(title=title,
                                                xlabel=xlabel,
                                                ylabel=ylabel)
        self.time_data['speed_lap'].set_color('#336633', '#336633')
        self.time_data['speed_lap'].graphType = "bar"
        for lap in self.laps:
            time = float(lap['elapsed_time'].decode(
                'utf-8'))  # time in sql is a unicode string
            dist = lap['distance'] / 1000  #distance in km
            try:
                pace = time / (60 * dist)  #min/km
            except ZeroDivisionError:
                pace = 0.0
            try:
                avg_speed = dist / (time / 3600)  # km/hr
            except:
                avg_speed = 0.0
            if self.pace_limit is not None and pace > self.pace_limit:
                logging.debug("Pace (%s) exceeds limit (%s). Setting to 0" %
                              (str(pace), str(self.pace_limit)))
                pace = 0.0
            logging.debug("Time: %f, Dist: %f, Pace: %f, Speed: %f" %
                          (time, dist, pace, avg_speed))
            self.lap_time.addBars(x=time, y=10)
            if self.us_system:
                self.lap_distance.addBars(x=km2miles(dist), y=10)
                self.distance_data['pace_lap'].addBars(x=km2miles(dist),
                                                       y=pacekm2miles(pace))
                self.time_data['pace_lap'].addBars(x=time,
                                                   y=pacekm2miles(pace))
                self.distance_data['speed_lap'].addBars(x=km2miles(dist),
                                                        y=km2miles(avg_speed))
                self.time_data['speed_lap'].addBars(x=time,
                                                    y=km2miles(avg_speed))
            else:
                self.lap_distance.addBars(x=dist, y=10)
                self.distance_data['pace_lap'].addBars(x=dist, y=pace)
                self.time_data['pace_lap'].addBars(x=time, y=pace)
                self.distance_data['speed_lap'].addBars(x=dist, y=avg_speed)
                self.time_data['speed_lap'].addBars(x=time, y=avg_speed)
        logging.debug("<<")

    def _get_laps_from_gpx(self):
        logging.debug(">>")
        laps = []
        gpxLaps = self.gpx.getLaps()
        for lap in gpxLaps:
            lap_number = gpxLaps.index(lap)
            tmp_lap = {}
            tmp_lap['record'] = self.id
            tmp_lap['lap_number'] = lap_number
            tmp_lap['elapsed_time'] = lap[0]
            tmp_lap['distance'] = lap[4]
            tmp_lap['start_lat'] = lap[5]
            tmp_lap['start_lon'] = lap[6]
            tmp_lap['end_lat'] = lap[1]
            tmp_lap['end_lon'] = lap[2]
            tmp_lap['calories'] = lap[3]
            laps.append(tmp_lap)
        if laps is not None:
            for lap in laps:
                lap_keys = ", ".join(map(str, lap.keys()))
                lap_values = lap.values()
                self.pytrainer_main.record.insertLaps(lap_keys, lap.values())
        logging.debug("<<")
        return laps

    def _init_graph_data(self):
        logging.debug(">>")
        if self.tracklist is None:
            logging.debug("No tracklist in activity")
            logging.debug("<<")
            return
        #Profile
        title = _("Elevation")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Elevation'), self.height_unit)
        self.distance_data['elevation'] = GraphData(title=title,
                                                    xlabel=xlabel,
                                                    ylabel=ylabel)
        self.distance_data['elevation'].set_color('#ff0000', '#ff0000')
        self.distance_data[
            'elevation'].show_on_y1 = True  #Make graph show elevation by default
        xlabel = _("Time (seconds)")
        self.time_data['elevation'] = GraphData(title=title,
                                                xlabel=xlabel,
                                                ylabel=ylabel)
        self.time_data['elevation'].set_color('#ff0000', '#ff0000')
        self.time_data[
            'elevation'].show_on_y1 = True  #Make graph show elevation by default
        #Corrected Elevation...
        title = _("Corrected Elevation")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Corrected Elevation'), self.height_unit)
        self.distance_data['cor_elevation'] = GraphData(title=title,
                                                        xlabel=xlabel,
                                                        ylabel=ylabel)
        self.distance_data['cor_elevation'].set_color('#993333', '#993333')
        xlabel = _("Time (seconds)")
        self.time_data['cor_elevation'] = GraphData(title=title,
                                                    xlabel=xlabel,
                                                    ylabel=ylabel)
        self.time_data['cor_elevation'].set_color('#993333', '#993333')
        #Speed
        title = _("Speed")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Speed'), self.speed_unit)
        self.distance_data['speed'] = GraphData(title=title,
                                                xlabel=xlabel,
                                                ylabel=ylabel)
        self.distance_data['speed'].set_color('#000000', '#000000')
        xlabel = _("Time (seconds)")
        self.time_data['speed'] = GraphData(title=title,
                                            xlabel=xlabel,
                                            ylabel=ylabel)
        self.time_data['speed'].set_color('#000000', '#000000')
        #Pace
        title = _("Pace")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Pace'), self.pace_unit)
        self.distance_data['pace'] = GraphData(title=title,
                                               xlabel=xlabel,
                                               ylabel=ylabel)
        self.distance_data['pace'].set_color('#0000ff', '#0000ff')
        xlabel = _("Time (seconds)")
        self.time_data['pace'] = GraphData(title=title,
                                           xlabel=xlabel,
                                           ylabel=ylabel)
        self.time_data['pace'].set_color('#0000ff', '#0000ff')
        #Heartrate
        title = _("Heart Rate")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Heart Rate'), _('bpm'))
        self.distance_data['hr'] = GraphData(title=title,
                                             xlabel=xlabel,
                                             ylabel=ylabel)
        self.distance_data['hr'].set_color('#00ff00', '#00ff00')
        xlabel = _("Time (seconds)")
        self.time_data['hr'] = GraphData(title=title,
                                         xlabel=xlabel,
                                         ylabel=ylabel)
        self.time_data['hr'].set_color('#00ff00', '#00ff00')
        #Heartrate as %
        maxhr = self.pytrainer_main.profile.getMaxHR()
        title = _("Heart Rate (% of max)")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Heart Rate'), _('%'))
        self.distance_data['hr_p'] = GraphData(title=title,
                                               xlabel=xlabel,
                                               ylabel=ylabel)
        self.distance_data['hr_p'].set_color('#00ff00', '#00ff00')
        xlabel = _("Time (seconds)")
        self.time_data['hr_p'] = GraphData(title=title,
                                           xlabel=xlabel,
                                           ylabel=ylabel)
        self.time_data['hr_p'].set_color('#00ff00', '#00ff00')
        #Cadence
        title = _("Cadence")
        xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
        ylabel = "%s (%s)" % (_('Cadence'), _('rpm'))
        self.distance_data['cadence'] = GraphData(title=title,
                                                  xlabel=xlabel,
                                                  ylabel=ylabel)
        self.distance_data['cadence'].set_color('#cc00ff', '#cc00ff')
        xlabel = _("Time (seconds)")
        self.time_data['cadence'] = GraphData(title=title,
                                              xlabel=xlabel,
                                              ylabel=ylabel)
        self.time_data['cadence'].set_color('#cc00ff', '#cc00ff')
        for track in self.tracklist:
            try:
                pace = 60 / track['velocity']
                if self.pace_limit is not None and pace > self.pace_limit:
                    logging.debug(
                        "Pace (%s) exceeds limit (%s). Setting to 0" %
                        (str(pace), str(self.pace_limit)))
                    pace = 0  #TODO this should be None when we move to newgraph...
            except Exception as e:
                #print type(e), e
                pace = 0
            try:
                hr_p = float(track['hr']) / maxhr * 100
            except:
                hr_p = 0
            if self.us_system:
                self.distance_data['elevation'].addPoints(
                    x=km2miles(track['elapsed_distance']),
                    y=m2feet(track['ele']))
                self.distance_data['cor_elevation'].addPoints(
                    x=km2miles(track['elapsed_distance']),
                    y=m2feet(track['correctedElevation']))
                self.distance_data['speed'].addPoints(
                    x=km2miles(track['elapsed_distance']),
                    y=km2miles(track['velocity']))
                self.distance_data['pace'].addPoints(x=km2miles(
                    track['elapsed_distance']),
                                                     y=pacekm2miles(pace))
                self.distance_data['hr'].addPoints(x=km2miles(
                    track['elapsed_distance']),
                                                   y=track['hr'])
                self.distance_data['hr_p'].addPoints(x=km2miles(
                    track['elapsed_distance']),
                                                     y=hr_p)
                self.distance_data['cadence'].addPoints(x=km2miles(
                    track['elapsed_distance']),
                                                        y=track['cadence'])
                self.time_data['elevation'].addPoints(x=track['time_elapsed'],
                                                      y=m2feet(track['ele']))
                self.time_data['cor_elevation'].addPoints(
                    x=track['time_elapsed'],
                    y=m2feet(track['correctedElevation']))
                self.time_data['speed'].addPoints(x=track['time_elapsed'],
                                                  y=km2miles(
                                                      track['velocity']))
                self.time_data['pace'].addPoints(x=track['time_elapsed'],
                                                 y=pacekm2miles(pace))
            else:
                self.distance_data['elevation'].addPoints(
                    x=track['elapsed_distance'], y=track['ele'])
                self.distance_data['cor_elevation'].addPoints(
                    x=track['elapsed_distance'], y=track['correctedElevation'])
                self.distance_data['speed'].addPoints(
                    x=track['elapsed_distance'], y=track['velocity'])
                self.distance_data['pace'].addPoints(
                    x=track['elapsed_distance'], y=pace)
                self.distance_data['hr'].addPoints(x=track['elapsed_distance'],
                                                   y=track['hr'])
                self.distance_data['hr_p'].addPoints(
                    x=track['elapsed_distance'], y=hr_p)
                self.distance_data['cadence'].addPoints(
                    x=track['elapsed_distance'], y=track['cadence'])
                self.time_data['elevation'].addPoints(x=track['time_elapsed'],
                                                      y=track['ele'])
                self.time_data['cor_elevation'].addPoints(
                    x=track['time_elapsed'], y=track['correctedElevation'])
                self.time_data['speed'].addPoints(x=track['time_elapsed'],
                                                  y=track['velocity'])
                self.time_data['pace'].addPoints(x=track['time_elapsed'],
                                                 y=pace)
            self.time_data['hr'].addPoints(x=track['time_elapsed'],
                                           y=track['hr'])
            self.time_data['hr_p'].addPoints(x=track['time_elapsed'], y=hr_p)
            self.time_data['cadence'].addPoints(x=track['time_elapsed'],
                                                y=track['cadence'])
        #Remove data with no values
        for item in self.distance_data.keys():
            if len(self.distance_data[item]) == 0:
                logging.debug("No values for %s. Removing...." % item)
                del self.distance_data[item]
        for item in self.time_data.keys():
            if len(self.time_data[item]) == 0:
                logging.debug("No values for %s. Removing...." % item)
                del self.time_data[item]
        logging.debug("<<")
        #Add Heartrate zones graphs
        if 'hr' in self.distance_data:
            zones = self.pytrainer_main.profile.getZones()
            title = _("Heart Rate zone")
            xlabel = "%s (%s)" % (_('Distance'), self.distance_unit)
            ylabel = "%s (%s)" % (_('Heart Rate'), _('bpm'))
            self.distance_data['hr_z'] = GraphData(title=title,
                                                   xlabel=xlabel,
                                                   ylabel=ylabel)
            self.distance_data['hr_z'].graphType = "hspan"
            self.distance_data['hr_z'].set_color(None, None)
            xlabel = _("Time (seconds)")
            self.time_data['hr_z'] = GraphData(title=title,
                                               xlabel=xlabel,
                                               ylabel=ylabel)
            self.time_data['hr_z'].set_color(None, None)
            for zone in zones:
                self.distance_data['hr_z'].addPoints(x=zone[0],
                                                     y=zone[1],
                                                     label=zone[3],
                                                     color=zone[2])
                self.time_data['hr_z'].addPoints(x=zone[0],
                                                 y=zone[1],
                                                 label=zone[3],
                                                 color=zone[2])

    def _float(self, value):
        try:
            result = float(value)
        except:
            result = 0.0
        return result

    def _int(self, value):
        try:
            result = int(value)
        except:
            result = 0
        return result

    def get_value_f(self, param, format=None, with_units=False):
        ''' Function to return a value formated as a string
			- takes into account US/metric
			- also appends units if required
		'''
        value = self.get_value(param)
        if not value:
            #Return blank string if value is None or 0
            return ""
        if format is not None:
            result = format % value
        else:
            result = str(value)
        if with_units:
            if param in self.units:
                result += self.units[param]
        #print "activity: 509", result
        return result

    def get_value(self, param):
        ''' Function to get the value of various params in this activity instance
			Automatically returns values converted to imperial if needed
		'''
        if param == 'distance':
            if self.us_system:
                return km2miles(self.distance)
            else:
                return self.distance
        elif param == 'average':
            if self.us_system:
                return km2miles(self.average)
            else:
                return self.average
        elif param == 'upositive':
            if self.us_system:
                return m2feet(self.upositive)
            else:
                return self.upositive
        elif param == 'unegative':
            if self.us_system:
                return m2feet(self.unegative)
            else:
                return self.unegative
        elif param == 'maxspeed':
            if self.us_system:
                return km2miles(self.maxspeed)
            else:
                return self.maxspeed
        elif param == 'maxpace':
            if self.us_system:
                return self.pace_from_float(pacekm2miles(self.maxpace))
            else:
                return self.pace_from_float(self.maxpace)
        elif param == 'pace':
            if self.us_system:
                return self.pace_from_float(pacekm2miles(self.pace))
            else:
                return self.pace_from_float(self.pace)
        elif param == 'calories':
            return self.calories
        elif param == 'time':
            if not self.time:
                return ""
            _hour, _min, _sec = self.pytrainer_main.date.second2time(self.time)
            if _hour == 0:
                return "%02d:%02d" % (_min, _sec)
            else:
                return "%0d:%02d:%02d" % (_hour, _min, _sec)
        else:
            print "Unable to provide value for unknown parameter (%s) for activity" % param
            return None

    def set_value(self, param, value):
        ''' Function to set the value of various params in this activity instance
			Automatically converts from imperial if using them
		'''
        _value = _float(value)
        if param == 'distance':
            if self.us_system:
                self.distance = miles2mk(_value)
            else:
                self.distance = _value
        elif param == 'average':
            if self.us_system:
                self.average = miles2mk(_value)
            else:
                self.average = _value
        elif param == 'upositive':
            if self.us_system:
                self.upositive = feet2m(_value)
            else:
                self.upositive = _value
        elif param == 'unegative':
            if self.us_system:
                self.unegative = feet2m(_value)
            else:
                self.unegative = _value
        elif param == 'maxspeed':
            if self.us_system:
                self.maxspeed = miles2mk(_value)
            else:
                self.maxspeed = _value
        elif param == 'maxpace':
            if self.us_system:
                _maxpace = pacemiles2mk(_value)
            else:
                _maxpace = _value
            self.maxpace = self.pace_to_float(_maxpace)
        elif param == 'pace':
            if self.us_system:
                _pace = pacemiles2mk(_value)
            else:
                _pace = _value
            self.pace = self.pace_to_float(_pace)
        else:
            print "Unable to set value (%s) for unknown parameter (%s) for activity" % (
                str(value), param)

    def pace_to_float(self, value):
        '''Take a mm:ss or mm.ss and return float'''
        value = value.replace(':', '.')
        try:
            value = float(value)
        except ValueError:
            value = None
        return value

    def pace_from_float(self, value):
        '''Helper to generate mm:ss from float representation mm.ss (or mm,ss?)'''
        #Check that value supplied is a float
        if not value:
            return ""
        try:
            _value = "%0.2f" % float(value)
        except ValueError:
            _value = str(value)
        return _value.replace('.', ':')
Beispiel #11
0
class Activity:
	'''
	Class that knows everything about a particular activity

	All values are stored in the class (and DB) in metric and are converted as needed

	tracks			- (list) tracklist from gpx
	tracklist		- (list of dict) trackpoint data from gpx
	laps			- (list of dict) lap list
	tree			- (ElementTree) parsed xml of gpx file
	us_system		- (bool) True: imperial measurement False: metric measurement
	distance_unit	- (string) unit to use for distance
	speed_unit		- (string) unit to use for speed
	distance_data	- (dict of graphdata classes) contains the graph data with x axis distance
	time_data		- (dict of graphdata classes) contains the graph data with x axis time
	height_unit		- (string) unit to use for height
	pace_unit		- (string) unit to use for pace
	gpx_file		- (string) gpx file name
	gpx				- (Gpx class) actual gpx instance
	sport_name		- (string) sport name
	sport_id		- (string) id for sport in sports table
	title			- (string) title of activity
	date			- (string) date of activity
	time			- (int) activity duration in seconds
	time_tuple		- (tuple) activity duration as hours, min, secs tuple
	beats			- (int) average heartrate for activity
	maxbeats 		- (int) maximum heartrate for activity
	comments		- (string) activity comments
	calories		- (int) calories of activity
	id_record		- (string) id for activity in records table
	date_time_local	- (string) date and time of activity in local timezone
	date_time_utc	- (string) date and time of activity in UTC timezone
	date_time		- (datetime) date and time of activity in local timezone
	starttime		- (string)
	distance 		- (float) activity distance
	average			- (float) average speed of activity
	upositive 		- (float) height climbed during activity
	unegative		- (float) height decended during activity
	maxspeed 		- (float) maximum speed obtained during activity
	maxpace 		- (float) maxium pace obtained during activity
	pace			- (float) average pace for activity
	has_data		- (bool) true if instance has data populated
	x_axis			- (string) distance or time, determines what will be graphed on x axis
	x_limits		- (tuple of float) start, end limits of x axis (as determined by matplotlib)
	y1_limits		- (tuple of float) start, end limits of y1 axis (as determined by matplotlib)
	y2_limits		- (tuple of float) start, end limits of y2 axis (as determined by matplotlib)
	x_limits_u		- (tuple of float) start, end limits of x axis (as requested by user)
	y1_limits_u		- (tuple of float) start, end limits of y1 axis (as requested by user)
	y2_limits_u		- (tuple of float) start, end limits of y2 axis (as requested by user)
	show_laps		- (bool) display laps on graphs
	lap_distance	- (graphdata)
	lap_time		- (graphdata)
	pace_limit		- (int) maximum pace that is valid for this activity
	'''
	def __init__(self, pytrainer_main = None, id = None):
		logging.debug(">>")
		self.id = id
		#It is an error to try to initialise with no id
		if self.id is None:
			return
		#It is an error to try to initialise with no reference to pytrainer_main
		if pytrainer_main is None:
			print("Error - must initialise with a reference to the main pytrainer class")
			return
		self.pytrainer_main = pytrainer_main
		self.tracks = None
		self.tracklist = None
		self.laps = None
		self.tree = None
		self.has_data = False
		self.distance_data = {}
		self.time_data = {}
		self.time_pause = 0
		self.pace_limit = None
		self.starttime = None
		self.gpx_distance = None
		#self.upositive = 0
		#self.unegative = 0
		if self.pytrainer_main.profile.getValue("pytraining","prf_us_system") == "True":
			self.us_system = True
		else:
			self.us_system = False
		self._set_units()
		self.gpx_file = "%s/%s.gpx" % (self.pytrainer_main.profile.gpxdir, id)
		#It is OK to not have a GPX file for an activity - this just limits us to information in the DB
		if not os.path.isfile(self.gpx_file):
			self.gpx_file = None
			logging.debug("No GPX file found for record id: %s" % id)
		if self.gpx_file is not None:
			self._init_from_gpx_file()
		self._init_from_db()
		self._init_graph_data()
		self._generate_per_lap_graphs()
		self.x_axis = "distance"
		self.x_limits = (None, None)
		self.y1_limits = (None, None)
		self.y2_limits = (None, None)
		self.x_limits_u = (None, None)
		self.y1_limits_u = (None, None)
		self.y2_limits_u = (None, None)
		self.y1_grid = False
		self.y2_grid = False
		self.x_grid = False
		self.show_laps = False
		logging.debug("<<")

	def __str__(self):
		return '''
        tracks (%s)
		tracklist (%s)
		laps (%s)
		tree (%s)
		us_system (%s)
		distance_unit (%s)
		speed_unit (%s)
		distance_data (%s)
		time_data (%s)
		height_unit (%s)
		pace_unit (%s)
		gpx_file (%s)
		gpx (%s)
		sport_name (%s)
		sport_id (%s)
		title (%s)
		date (%s)
		time (%s)
		time_tuple (%s)
		beats (%s)
		maxbeats (%s)
		comments (%s)
		calories (%s)
		id_record (%s)
		date_time_local (%s)
		date_time_utc (%s)
		date_time (%s)
		starttime (%s)
		distance (%s)
		average (%s)
		upositive (%s)
		unegative (%s)
		maxspeed (%s)
		maxpace (%s)
		pace (%s)
		has_data (%s)
		x_axis (%s)
		x_limits (%s)
		y1_limits (%s)
		y2_limits (%s)
		x_limits_u (%s)
		y1_limits_u (%s)
		y2_limits_u (%s)
		show_laps (%s)
		lap_distance (%s)
		lap_time (%s)
		pace_limit (%s)
        ''' % ('self.tracks', self.tracklist, self.laps, self.tree, self.us_system,
			self.distance_unit, self.speed_unit, self.distance_data, self.time_data,
			self.height_unit, self.pace_unit, self.gpx_file, self.gpx, self.sport_name,
			self.sport_id, self.title, self.date, self.time, self.time_tuple, self.beats,
			self.maxbeats, self.comments, self.calories, self.id_record, self.date_time_local,
			self.date_time_utc, self.date_time, self.starttime, self.distance, self.average,
			self.upositive, self.unegative, self.maxspeed, self.maxpace, self.pace, self.has_data,
			self.x_axis, self.x_limits, self.y1_limits, self.y2_limits, self.x_limits_u, self.y1_limits_u,
			self.y2_limits_u, self.show_laps, self.lap_distance, self.lap_time, self.pace_limit)

	def _set_units(self):
		if self.us_system:
			self.distance_unit = _("miles")
			self.speed_unit = _("miles/h")
			self.pace_unit = _("min/mile")
			self.height_unit = _("feet")
		else:
			self.distance_unit = _("km")
			self.speed_unit = _("km/h")
			self.pace_unit = _("min/km")
			self.height_unit = _("m")
		self.units = { 'distance': self.distance_unit, 'average': self.speed_unit, 'upositive': self.height_unit, 'unegative': self.height_unit, 'maxspeed': self.speed_unit, 'pace': self.pace_unit, 'maxpace': self.pace_unit }

	def _init_from_gpx_file(self):
		'''
		Get activity information from the GPX file
		'''
		logging.debug(">>")
		#Parse GPX file
		#print "Activity initing GPX.. ",
		self.gpx = Gpx(filename = self.gpx_file) #TODO change GPX code to do less....
		self.tree = self.gpx.tree
		self.tracks = self.gpx.getTrackList() #TODO fix - this should removed and replaced with self.tracklist functionality
		self.tracklist = self.gpx.trkpoints
		self.gpx_distance = self.gpx.total_dist
		logging.info("GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s" % (self.gpx_distance, self.gpx.total_dist_trkpts, self.gpx.total_time, self.gpx.total_time_trkpts))
		time_diff = self.gpx.total_time_trkpts - self.gpx.total_time
		acceptable_lapse = 4 # number of seconds that duration calculated using lap and trkpts data can differ
		if time_diff > acceptable_lapse:
			self.time_pause = time_diff
			logging.debug("Identified non active time: %s s" % self.time_pause)
		logging.debug("<<")

	def _init_from_db(self):
		'''
		Get activity information from the DB
		'''
		logging.debug(">>")
		#Get base information
		cols = ("sports.name","id_sports", "date","distance","time","beats","comments",
						"average","calories","id_record","title","upositive","unegative",
						"maxspeed","maxpace","pace","maxbeats","date_time_utc","date_time_local", "sports.max_pace")
		# outer join on sport id to workaround bug where sport reference is null on records from GPX import
		db_result = self.pytrainer_main.ddbb.select("records left outer join sports on records.sport=sports.id_sports",
					", ".join(cols),
					"id_record=\"%s\" " %self.id)
		if len(db_result) == 1:
			row = db_result[0]
			self.sport_name = row[cols.index('sports.name')]
			if self.sport_name == None:
				self.sport_name = ""
			self.sport_id = row[cols.index('id_sports')]
			self.pace_limit = row[cols.index('sports.max_pace')]
			if self.pace_limit == 0 or self.pace_limit == "":
				self.pace_limit = None
			self.title = row[cols.index('title')]
			if self.title is None:
				self.title = ""
			self.date = row[cols.index('date')]
			self.time = self._int(row[cols.index('time')])
			self.time_tuple = Date().second2time(self.time)
			self.beats = self._int(row[cols.index('beats')])
			self.comments = row[cols.index('comments')]
			if self.comments is None:
				self.comments = ""
			self.calories = self._int(row[cols.index('calories')])
			self.id_record = row[cols.index('id_record')]
			self.maxbeats = self._int(row[cols.index('maxbeats')])
			#Sort time....
			# ... use local time if available otherwise use date_time_utc and create a local datetime...
			self.date_time_local = row[cols.index('date_time_local')]
			self.date_time_utc = row[cols.index('date_time_utc')]
			if self.date_time_local is not None: #Have a local time stored in DB
				self.date_time = dateutil.parser.parse(self.date_time_local)
				self.starttime = self.date_time.strftime("%X")
			else: #No local time in DB
				tmpDateTime = dateutil.parser.parse(self.date_time_utc)
				self.date_time = tmpDateTime.astimezone(tzlocal()) #datetime with localtime offset (using value from OS)
				self.starttime = self.date_time.strftime("%X")
			#Sort data that changes for the US etc
			#if self.us_system:
			#	self.distance = km2miles(self._float(row[cols.index('distance')]))
			#	self.average = km2miles(self._float(row[cols.index('average')]))
			#	self.upositive = m2feet(self._float(row[cols.index('upositive')]))
			#	self.unegative = m2feet(self._float(row[cols.index('unegative')]))
			#	self.maxspeed = km2miles(self._float(row[cols.index('maxspeed')]))
			#	self.maxpace = pacekm2miles(self._float(row[cols.index('maxpace')]))
			#	self.pace = pacekm2miles(self._float(row[cols.index('pace')]))
			#else:
			self.distance = self._float(row[cols.index('distance')])
			if not self.distance:
				self.distance = self.gpx_distance
			self.average = self._float(row[cols.index('average')])
			self.upositive = self._float(row[cols.index('upositive')])
			self.unegative = self._float(row[cols.index('unegative')])
			self.maxspeed = self._float(row[cols.index('maxspeed')])
			self.maxpace = self._float(row[cols.index('maxpace')])
			self.pace = self._float(row[cols.index('pace')])
			self.has_data = True
		else:
			raise Exception( "Error - multiple results from DB for id: %s" % self.id )
		#Get lap information
		laps = self.pytrainer_main.ddbb.select_dict("laps",
					("id_lap", "record", "elapsed_time", "distance", "start_lat", "start_lon", "end_lat", "end_lon", "calories", "lap_number", "intensity", "avg_hr", "max_hr", "max_speed", "laptrigger", "comments"),
					"record=\"%s\"" % self.id)
		if laps is None or laps == [] or len(laps) < 1:  #No laps found
			logging.debug("No laps in DB for record %d" % self.id)
			if self.gpx_file is not None:
				laps = self._get_laps_from_gpx()
		self.laps = laps
		logging.debug("<<")

	def _generate_per_lap_graphs(self):
		'''Build lap based graphs...'''
		logging.debug(">>")
		if self.laps is None:
			logging.debug("No laps to generate graphs from")
			logging.debug("<<")
			return
		#Lap columns
		self.lap_distance = GraphData()
		self.lap_distance.set_color('#CCFF00', '#CCFF00')
		self.lap_distance.graphType = "vspan"
		self.lap_time = GraphData()
		self.lap_time.set_color('#CCFF00', '#CCFF00')
		self.lap_time.graphType = "vspan"
		#Pace
		title=_("Pace by Lap")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Pace'), self.pace_unit)
		self.distance_data['pace_lap'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['pace_lap'].set_color('#99CCFF', '#99CCFF')
		self.distance_data['pace_lap'].graphType = "bar"
		xlabel=_("Time (seconds)")
		self.time_data['pace_lap'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.time_data['pace_lap'].set_color('#99CCFF', '#99CCFF')
		self.time_data['pace_lap'].graphType = "bar"
		#Speed
		title=_("Speed by Lap")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Speed'), self.speed_unit)
		self.distance_data['speed_lap'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['speed_lap'].set_color('#336633', '#336633')
		self.distance_data['speed_lap'].graphType = "bar"
		xlabel=_("Time (seconds)")
		self.time_data['speed_lap'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.time_data['speed_lap'].set_color('#336633', '#336633')
		self.time_data['speed_lap'].graphType = "bar"
		for lap in self.laps:
			time = float( lap['elapsed_time'].decode('utf-8') ) # time in sql is a unicode string
			dist = lap['distance']/1000 #distance in km
			try:
				pace = time/(60*dist) #min/km
			except ZeroDivisionError:
				pace = 0.0
			try:
				avg_speed = dist/(time/3600) # km/hr
			except:
				avg_speed = 0.0
			if self.pace_limit is not None and pace > self.pace_limit:
				logging.debug("Pace (%s) exceeds limit (%s). Setting to 0" % (str(pace), str(self.pace_limit)))
				pace = 0.0
			logging.debug("Time: %f, Dist: %f, Pace: %f, Speed: %f" % (time, dist, pace, avg_speed) )
			self.lap_time.addBars(x=time, y=10)
			if self.us_system:
				self.lap_distance.addBars(x=km2miles(dist), y=10)
				self.distance_data['pace_lap'].addBars(x=km2miles(dist), y=pacekm2miles(pace))
				self.time_data['pace_lap'].addBars(x=time, y=pacekm2miles(pace))
				self.distance_data['speed_lap'].addBars(x=km2miles(dist), y=km2miles(avg_speed))
				self.time_data['speed_lap'].addBars(x=time, y=km2miles(avg_speed))
			else:
				self.lap_distance.addBars(x=dist, y=10)
				self.distance_data['pace_lap'].addBars(x=dist, y=pace)
				self.time_data['pace_lap'].addBars(x=time, y=pace)
				self.distance_data['speed_lap'].addBars(x=dist, y=avg_speed)
				self.time_data['speed_lap'].addBars(x=time, y=avg_speed)
		logging.debug("<<")

	def _get_laps_from_gpx(self):
		logging.debug(">>")
		laps = []
		gpxLaps = self.gpx.getLaps()
		for lap in gpxLaps:
			lap_number = gpxLaps.index(lap)
			tmp_lap = {}
			tmp_lap['record'] = self.id
			tmp_lap['lap_number'] = lap_number
			tmp_lap['elapsed_time'] = lap[0]
			tmp_lap['distance'] = lap[4]
			tmp_lap['start_lat'] = lap[5]
			tmp_lap['start_lon'] = lap[6]
			tmp_lap['end_lat'] = lap[1]
			tmp_lap['end_lon'] = lap[2]
			tmp_lap['calories'] = lap[3]
			laps.append(tmp_lap)
		if laps is not None:
			for lap in laps:
				lap_keys = ", ".join(map(str, lap.keys()))
				lap_values = lap.values()
				self.pytrainer_main.record.insertLaps(lap_keys,lap.values())
		logging.debug("<<")
		return laps

	def _init_graph_data(self):
		logging.debug(">>")
		if self.tracklist is None:
			logging.debug("No tracklist in activity")
			logging.debug("<<")
			return
		#Profile
		title=_("Elevation")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Elevation'), self.height_unit)
		self.distance_data['elevation'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['elevation'].set_color('#ff0000', '#ff0000')
		self.distance_data['elevation'].show_on_y1 = True #Make graph show elevation by default
		xlabel=_("Time (seconds)")
		self.time_data['elevation'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['elevation'].set_color('#ff0000', '#ff0000')
		self.time_data['elevation'].show_on_y1 = True #Make graph show elevation by default
		#Corrected Elevation...
		title=_("Corrected Elevation")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Corrected Elevation'), self.height_unit)
		self.distance_data['cor_elevation'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['cor_elevation'].set_color('#993333', '#993333')
		xlabel=_("Time (seconds)")
		self.time_data['cor_elevation'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['cor_elevation'].set_color('#993333', '#993333')
		#Speed
		title=_("Speed")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Speed'), self.speed_unit)
		self.distance_data['speed'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['speed'].set_color('#000000', '#000000')
		xlabel=_("Time (seconds)")
		self.time_data['speed'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['speed'].set_color('#000000', '#000000')
		#Pace
		title=_("Pace")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Pace'), self.pace_unit)
		self.distance_data['pace'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['pace'].set_color('#0000ff', '#0000ff')
		xlabel=_("Time (seconds)")
		self.time_data['pace'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['pace'].set_color('#0000ff', '#0000ff')
		#Heartrate
		title=_("Heart Rate")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Heart Rate'), _('bpm'))
		self.distance_data['hr'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['hr'].set_color('#00ff00', '#00ff00')
		xlabel=_("Time (seconds)")
		self.time_data['hr'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['hr'].set_color('#00ff00', '#00ff00')
		#Heartrate as %
		maxhr = self.pytrainer_main.profile.getMaxHR()
		title=_("Heart Rate (% of max)")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Heart Rate'), _('%'))
		self.distance_data['hr_p'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['hr_p'].set_color('#00ff00', '#00ff00')
		xlabel=_("Time (seconds)")
		self.time_data['hr_p'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['hr_p'].set_color('#00ff00', '#00ff00')
		#Cadence
		title=_("Cadence")
		xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
		ylabel="%s (%s)" % (_('Cadence'), _('rpm'))
		self.distance_data['cadence'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
		self.distance_data['cadence'].set_color('#cc00ff', '#cc00ff')
		xlabel=_("Time (seconds)")
		self.time_data['cadence'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
		self.time_data['cadence'].set_color('#cc00ff', '#cc00ff')
		for track in self.tracklist:
			try:
				pace = 60/track['velocity']
				if self.pace_limit is not None and pace > self.pace_limit:
					logging.debug("Pace (%s) exceeds limit (%s). Setting to 0" % (str(pace), str(self.pace_limit)))
					pace = 0  #TODO this should be None when we move to newgraph...
			except Exception as e:
				#print type(e), e
				pace = 0
			try:
				hr_p = float(track['hr'])/maxhr*100
			except:
				hr_p = 0
			if self.us_system:
				self.distance_data['elevation'].addPoints(x=km2miles(track['elapsed_distance']), y=m2feet(track['ele']))
				self.distance_data['cor_elevation'].addPoints(x=km2miles(track['elapsed_distance']), y=m2feet(track['correctedElevation']))
				self.distance_data['speed'].addPoints(x=km2miles(track['elapsed_distance']), y=km2miles(track['velocity']))
				self.distance_data['pace'].addPoints(x=km2miles(track['elapsed_distance']), y=pacekm2miles(pace))
				self.distance_data['hr'].addPoints(x=km2miles(track['elapsed_distance']), y=track['hr'])
				self.distance_data['hr_p'].addPoints(x=km2miles(track['elapsed_distance']), y=hr_p)
				self.distance_data['cadence'].addPoints(x=km2miles(track['elapsed_distance']), y=track['cadence'])
				self.time_data['elevation'].addPoints(x=track['time_elapsed'], y=m2feet(track['ele']))
				self.time_data['cor_elevation'].addPoints(x=track['time_elapsed'], y=m2feet(track['correctedElevation']))
				self.time_data['speed'].addPoints(x=track['time_elapsed'], y=km2miles(track['velocity']))
				self.time_data['pace'].addPoints(x=track['time_elapsed'], y=pacekm2miles(pace))
			else:
				self.distance_data['elevation'].addPoints(x=track['elapsed_distance'], y=track['ele'])
				self.distance_data['cor_elevation'].addPoints(x=track['elapsed_distance'], y=track['correctedElevation'])
				self.distance_data['speed'].addPoints(x=track['elapsed_distance'], y=track['velocity'])
				self.distance_data['pace'].addPoints(x=track['elapsed_distance'], y=pace)
				self.distance_data['hr'].addPoints(x=track['elapsed_distance'], y=track['hr'])
				self.distance_data['hr_p'].addPoints(x=track['elapsed_distance'], y=hr_p)
				self.distance_data['cadence'].addPoints(x=track['elapsed_distance'], y=track['cadence'])
				self.time_data['elevation'].addPoints(x=track['time_elapsed'], y=track['ele'])
				self.time_data['cor_elevation'].addPoints(x=track['time_elapsed'], y=track['correctedElevation'])
				self.time_data['speed'].addPoints(x=track['time_elapsed'], y=track['velocity'])
				self.time_data['pace'].addPoints(x=track['time_elapsed'], y=pace)
			self.time_data['hr'].addPoints(x=track['time_elapsed'], y=track['hr'])
			self.time_data['hr_p'].addPoints(x=track['time_elapsed'], y=hr_p)
			self.time_data['cadence'].addPoints(x=track['time_elapsed'], y=track['cadence'])
		#Remove data with no values
		for item in self.distance_data.keys():
			if len(self.distance_data[item]) == 0:
				logging.debug( "No values for %s. Removing...." % item )
				del self.distance_data[item]
		for item in self.time_data.keys():
			if len(self.time_data[item]) == 0:
				logging.debug( "No values for %s. Removing...." % item )
				del self.time_data[item]
		logging.debug("<<")
		#Add Heartrate zones graphs
		if 'hr' in self.distance_data:
			zones = self.pytrainer_main.profile.getZones()		
			title=_("Heart Rate zone")
			xlabel="%s (%s)" % (_('Distance'), self.distance_unit)
			ylabel="%s (%s)" % (_('Heart Rate'), _('bpm'))
			self.distance_data['hr_z'] = GraphData(title=title, xlabel=xlabel, ylabel=ylabel)
			self.distance_data['hr_z'].graphType = "hspan"
			self.distance_data['hr_z'].set_color(None, None)
			xlabel=_("Time (seconds)")
			self.time_data['hr_z'] = GraphData(title=title,xlabel=xlabel, ylabel=ylabel)
			self.time_data['hr_z'].set_color(None, None)
			for zone in zones:
				self.distance_data['hr_z'].addPoints(x=zone[0], y=zone[1], label=zone[3], color=zone[2])
				self.time_data['hr_z'].addPoints(x=zone[0], y=zone[1], label=zone[3], color=zone[2])

	def _float(self, value):
		try:
			result = float(value)
		except:
			result = 0.0
		return result

	def _int(self, value):
		try:
			result = int(value)
		except:
			result = 0
		return result

	def get_value_f(self, param, format=None, with_units=False):
		''' Function to return a value formated as a string
			- takes into account US/metric
			- also appends units if required
		'''
		value = self.get_value(param)
		if not value:
			#Return blank string if value is None or 0
			return ""
		if format is not None:
			result = format % value
		else:
			result = str(value)
		if with_units:
			if param in self.units:
				result += self.units[param]
		#print "activity: 509", result
		return result

	def get_value(self, param):
		''' Function to get the value of various params in this activity instance
			Automatically returns values converted to imperial if needed
		'''
		if param == 'distance':
			if self.us_system:
				return km2miles(self.distance)
			else:
				return self.distance
		elif param == 'average':
			if self.us_system:
				return km2miles(self.average)
			else:
				return self.average
		elif param == 'upositive':
			if self.us_system:
				return m2feet(self.upositive)
			else:
				return self.upositive
		elif param == 'unegative':
			if self.us_system:
				return m2feet(self.unegative)
			else:
				return self.unegative
		elif param == 'maxspeed':
			if self.us_system:
				return km2miles(self.maxspeed)
			else:
				return self.maxspeed
		elif param == 'maxpace':
			if self.us_system:
				return self.pace_from_float(pacekm2miles(self.maxpace))
			else:
				return self.pace_from_float(self.maxpace)
		elif param == 'pace':
			if self.us_system:
				return self.pace_from_float(pacekm2miles(self.pace))
			else:
				return self.pace_from_float(self.pace)
		elif param == 'calories':
			return self.calories
		elif param == 'time':
			if not self.time:
				return ""
			_hour,_min,_sec=self.pytrainer_main.date.second2time(self.time)
			if _hour == 0:
				return "%02d:%02d" % (_min, _sec)
			else:
				return "%0d:%02d:%02d" % (_hour, _min, _sec)
		else:
			print "Unable to provide value for unknown parameter (%s) for activity" % param
			return None

	def set_value(self, param, value):
		''' Function to set the value of various params in this activity instance
			Automatically converts from imperial if using them
		'''
		_value = _float(value)
		if param == 'distance':
			if self.us_system:
				self.distance = miles2mk(_value)
			else:
				self.distance = _value
		elif param == 'average':
			if self.us_system:
				self.average = miles2mk(_value)
			else:
				self.average = _value
		elif param == 'upositive':
			if self.us_system:
				self.upositive = feet2m(_value)
			else:
				self.upositive = _value
		elif param == 'unegative':
			if self.us_system:
				self.unegative = feet2m(_value)
			else:
				self.unegative = _value
		elif param == 'maxspeed':
			if self.us_system:
				self.maxspeed = miles2mk(_value)
			else:
				self.maxspeed = _value
		elif param == 'maxpace':
			if self.us_system:
				_maxpace = pacemiles2mk(_value)
			else:
				_maxpace = _value
			self.maxpace = self.pace_to_float(_maxpace)
		elif param == 'pace':
			if self.us_system:
				_pace = pacemiles2mk(_value)
			else:
				_pace = _value
			self.pace = self.pace_to_float(_pace)
		else:
			print "Unable to set value (%s) for unknown parameter (%s) for activity" % (str(value), param)


	def pace_to_float(self, value):
		'''Take a mm:ss or mm.ss and return float'''
		value = value.replace(':', '.')
		try:
			value = float(value)
		except ValueError:
			value = None
		return value

	def pace_from_float(self, value):
		'''Helper to generate mm:ss from float representation mm.ss (or mm,ss?)'''
		#Check that value supplied is a float
		if not value:
			return ""
		try:
			_value = "%0.2f" % float(value)
		except ValueError:
			_value = str(value)
		return _value.replace('.',':')