Beispiel #1
0
 def _initialize(self):
     logging.debug(">>")
     self.environment = Environment()
     self.uc = uc.UC()
     self.profile = Profile()
     self.has_data = True
     self._distance_data = {}
     self._time_data = {}
     self._lap_time = None
     self._lap_distance = None
     self.time_pause = 0
     self.pace_limit = None
     self._gpx = None
     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("<<")
Beispiel #2
0
 def _initialize(self):
     logging.debug(">>")
     self.environment = Environment()
     self.uc = uc.UC()
     self.profile = Profile()
     self.has_data = True
     self._distance_data = {}
     self._time_data = {}
     self._lap_time = None
     self._lap_distance = None
     self.time_pause = 0
     self.pace_limit = None
     self._gpx = None
     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("<<")
Beispiel #3
0
 def setUp(self):
     self.ddbb = DDBB()
     main = Mock()
     main.ddbb = self.ddbb
     main.profile = Profile()
     main.ddbb.connect()
     main.ddbb.create_tables(add_default=False)
     self.athlete = Athlete(parent=main)
Beispiel #4
0
 def setUp(self):
     self.ddbb = DDBB()
     main = Mock()
     main.ddbb = self.ddbb
     main.profile = Profile()
     main.ddbb.connect()
     main.ddbb.create_tables(add_default=True)  # We need a sport
     self.uc = UC()
     self.uc.set_us(False)
     self.service = ActivityService(pytrainer_main=main)
     records_table = DeclarativeBase.metadata.tables['records']
     self.ddbb.session.execute(
         records_table.insert({
             'distance': 46.18,
             'maxspeed': 44.6695617695,
             'maxpace': 1.2,
             'title': u'test activity',
             'unegative': 564.08076273,
             'upositive': 553.05993673,
             'average': 22.3882142185,
             'date_time_local': u'2016-07-24 12:58:23+0300',
             'calories': 1462,
             'beats': 115.0,
             'comments': u'test comment',
             'pace': 2.4,
             'date_time_utc': u'2016-07-24T09:58:23Z',
             'date': datetime.date(2016, 7, 24),
             'duration': 7426,
             'sport': 1,
             'maxbeats': 120.0
         }))
     laps_table = DeclarativeBase.metadata.tables['laps']
     self.ddbb.session.execute(
         laps_table.insert({
             'distance': 46181.9,
             'lap_number': 0,
             'calories': 1462,
             'elapsed_time': u'7426.0',
             'record': 1,
             'intensity': u'active',
             'avg_hr': 136,
             'max_hr': 173,
             'laptrigger': u'manual'
         }))
     self.activity = self.service.get_activity(1)
Beispiel #5
0
 def setUp(self):
     env = Environment()
     env.data_path = os.curdir
     env.create_directories()
     self.main = mock.Mock()
     self.main.ddbb = DDBB()
     self.main.ddbb.connect()
     self.main.ddbb.create_tables()
     self.main.profile = Profile()
     self.main.uc = UC()
     self.sport_service = SportService(self.main.ddbb)
     non_linear_sport = self.sport_service.get_sport(1)
     self.main.ddbb.session.delete(non_linear_sport)
     self.main.ddbb.session.commit()
     make_transient(non_linear_sport)
     non_linear_sport.id = None
     self.main.ddbb.session.add(non_linear_sport)
     self.main.ddbb.session.commit()
     self.main.record = Record(self.sport_service, parent=self.main)
     self.parent = Main(self.sport_service, parent=self.main)
     self.main.ddbb.session.add(
         Activity(sport=non_linear_sport,
                  date=datetime.datetime(2018, 5, 6, 10, 0, 0),
                  duration=1000,
                  distance=25))
     self.main.ddbb.session.add(
         Activity(sport=non_linear_sport,
                  date=datetime.datetime(2018, 1, 6, 10, 0, 0),
                  duration=10000,
                  distance=150))
     self.main.ddbb.session.add(
         Activity(sport=self.sport_service.get_sport(2),
                  date=datetime.datetime(2018, 1, 6, 10, 0, 0),
                  duration=10000,
                  distance=100))
     self.main.ddbb.session.commit()
Beispiel #6
0
class Activity(DeclarativeBase):
    '''
    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
    us_system               - (bool) True: imperial measurement False: metric measurement
    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
    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                      - (int) 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
    '''
    __tablename__ = 'records'
    average = Column(Float)
    beats = Column(Float)
    calories = Column(ForcedInteger)
    comments = Column(UnicodeText)
    date = Column(Date)
    date_time_local = Column(String(length=40))
    date_time_utc = Column(String(length=40))
    distance = Column(Float)
    duration = Column(ForcedInteger)
    gpslog = deferred(Column(String(length=200)))
    id = Column("id_record", Integer, primary_key=True)
    maxbeats = Column(Float)
    maxpace = Column(Float)
    maxspeed = Column(Float)
    pace = Column(Float)
    sport_id = Column("sport",
                      Integer,
                      ForeignKey('sports.id_sports'),
                      index=True,
                      nullable=False)
    title = Column(Unicode(length=200))
    unegative = Column(Float)
    upositive = Column(Float)

    #relation definitions
    sport = relationship("Sport",
                         backref=backref("activities",
                                         order_by=date,
                                         cascade='all, delete-orphan'))
    equipment = relationship("Equipment",
                             secondary=record_to_equipment,
                             backref=backref("activities", order_by=date))
    Laps = relationship('Lap',
                        backref=backref('activity'),
                        order_by='Lap.lap_number',
                        cascade='all, delete-orphan')

    def __init__(self, **kwargs):
        self._initialize()
        super(Activity, self).__init__(**kwargs)

    @reconstructor
    def _initialize(self):
        logging.debug(">>")
        self.environment = Environment()
        self.uc = uc.UC()
        self.profile = Profile()
        self.has_data = True
        self._distance_data = {}
        self._time_data = {}
        self._lap_time = None
        self._lap_distance = None
        self.time_pause = 0
        self.pace_limit = None
        self._gpx = None
        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("<<")

    @property
    def gpx_file(self):
        if self.id:
            filename = "%s/%s.gpx" % (self.environment.gpx_dir, self.id)
            #It is OK to not have a GPX file for an activity - this just limits us to information in the DB
            if os.path.isfile(filename):
                return filename
        logging.debug("No GPX file found for record id: %s", self.id)
        return None

    @property
    def tracks(self):
        if self.gpx:
            return self.gpx.getTrackList()
        else:
            return None

    @property
    def tracklist(self):
        if self.gpx:
            return self.gpx.trkpoints
        else:
            return None

    @property
    def distance_data(self):
        if not self._distance_data:
            self._init_graph_data()
        return self._distance_data

    @property
    def time_data(self):
        if not self._time_data:
            self._init_graph_data()
        return self._time_data

    @property
    def lap_time(self):
        if not self._lap_time:
            self._generate_per_lap_graphs()
        return self._lap_time

    @property
    def lap_distance(self):
        if not self._lap_distance:
            self._generate_per_lap_graphs()
        return self._lap_distance

    @property
    def time_tuple(self):
        return second2time(self.duration)

    @property
    def date_time(self):
        if self.date_time_local:  #Have a local time stored in DB
            return dateutil.parser.parse(self.date_time_local)
        else:  #No local time in DB
            #datetime with localtime offset (using value from OS)
            return dateutil.parser.parse(self.date_time_utc).astimezone(
                tzlocal())

    @property
    def starttime(self):
        return self.date_time.strftime("%X")

    @property
    def laps(self):
        logging.warning("Deprecated property Activity.laps called")
        ret = []
        for lap in self.Laps:
            d = dict(lap.__dict__)
            d.pop('_sa_instance_state', None)
            ret.append(d)
        return ret

    def __str__(self):
        return '''
tracks (%s)
        tracklist (%s)
        laps (%s)
        us_system (%s)
        distance_data (%s)
        time_data (%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 (%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.uc.us,
        self.distance_data, self.time_data, self.gpx_file, self.gpx,
        self.sport_name, self.sport_id, self.title, self.date, self.duration,
        self.time_tuple, self.beats, self.maxbeats, self.comments,
        self.calories, self.id, 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)

    @property
    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("<<")

    @property
    def time(self):
        logging.warning("Deprecated property Activity.time called")
        return self.duration

    @property
    def sport_name(self):
        logging.warning("Deprecated property Activity.sport_name called")
        return self.sport.name

    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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Pace'), self.uc.unit_pace)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Speed'), self.uc.unit_speed)
        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)
            self._lap_distance.addBars(x=self.uc.distance(dist), y=10)
            self.distance_data['pace_lap'].addBars(x=self.uc.distance(dist),
                                                   y=pacekm2miles(pace))
            self.time_data['pace_lap'].addBars(x=time, y=self.uc.speed(pace))
            self.distance_data['speed_lap'].addBars(x=self.uc.distance(dist),
                                                    y=self.uc.speed(avg_speed))
            self.time_data['speed_lap'].addBars(x=time,
                                                y=self.uc.speed(avg_speed))
        logging.debug("<<")

    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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Elevation'), self.uc.unit_height)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Corrected Elevation'), self.uc.unit_height)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Speed'), self.uc.unit_speed)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Pace'), self.uc.unit_pace)
        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.uc.unit_distance)
        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.profile.getMaxHR()
        title = _("Heart Rate (% of max)")
        xlabel = "%s (%s)" % (_('Distance'), self.uc.unit_distance)
        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.uc.unit_distance)
        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
            self._distance_data['elevation'].addPoints(
                x=self.uc.distance(track['elapsed_distance']),
                y=self.uc.height(track['ele']))
            self._distance_data['cor_elevation'].addPoints(
                x=self.uc.distance(track['elapsed_distance']),
                y=self.uc.height(track['correctedElevation']))
            self._distance_data['speed'].addPoints(
                x=self.uc.distance(track['elapsed_distance']),
                y=self.uc.speed(track['velocity']))
            self._distance_data['pace'].addPoints(x=self.uc.distance(
                track['elapsed_distance']),
                                                  y=self.uc.distance(pace))
            self._distance_data['hr'].addPoints(x=self.uc.distance(
                track['elapsed_distance']),
                                                y=track['hr'])
            self._distance_data['hr_p'].addPoints(x=self.uc.distance(
                track['elapsed_distance']),
                                                  y=hr_p)
            self._distance_data['cadence'].addPoints(x=self.uc.distance(
                track['elapsed_distance']),
                                                     y=track['cadence'])
            self._time_data['elevation'].addPoints(x=track['time_elapsed'],
                                                   y=self.uc.height(
                                                       track['ele']))
            self._time_data['cor_elevation'].addPoints(
                x=track['time_elapsed'],
                y=self.uc.height(track['correctedElevation']))
            self._time_data['speed'].addPoints(x=track['time_elapsed'],
                                               y=self.uc.speed(
                                                   track['velocity']))
            self._time_data['pace'].addPoints(x=track['time_elapsed'],
                                              y=self.uc.distance(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.profile.getZones()
            title = _("Heart Rate zone")
            xlabel = "%s (%s)" % (_('Distance'), self.uc.unit_distance)
            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):
        ''' 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)
        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':
            return self.uc.distance(self.distance)
        elif param == 'average':
            return self.uc.speed(self.average)
        elif param == 'upositive':
            return self.uc.height(self.upositive)
        elif param == 'unegative':
            return self.uc.height(self.unegative)
        elif param == 'maxspeed':
            return self.uc.speed(self.maxspeed)
        elif param == 'maxpace':
            return uc.float2pace(self.uc.pace(self.maxpace))
        elif param == 'pace':
            return uc.float2pace(self.uc.pace(self.pace))
        elif param == 'calories':
            return self.calories
        elif param == 'time':
            if not self.duration:
                return ""
            _hour, _min, _sec = second2time(self.duration)
            if _hour == 0:
                return "%02d:%02d" % (_min, _sec)
            else:
                return "%0d:%02d:%02d" % (_hour, _min, _sec)
        else:
            logging.error(
                "Unable to provide value for unknown parameter (%s) for activity",
                param)
            return None
Beispiel #7
0
class Activity(DeclarativeBase):
    '''
    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
    us_system               - (bool) True: imperial measurement False: metric measurement
    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
    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                      - (int) 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
    '''
    __tablename__ = 'records'
    average = Column(Float)
    beats = Column(Float)
    calories = Column(ForcedInteger)
    comments = Column(UnicodeText)
    date = Column(Date)
    date_time_local = Column(String(length=40))
    date_time_utc = Column(String(length=40))
    distance = Column(Float)
    duration = Column(ForcedInteger)
    gpslog = deferred(Column(String(length=200)))
    id = Column("id_record", Integer, primary_key=True)
    maxbeats = Column(Float)
    maxpace = Column(Float)
    maxspeed = Column(Float)
    pace = Column(Float)
    sport_id = Column("sport", Integer, ForeignKey('sports.id_sports'),
                          index=True, nullable=False)
    title = Column(Unicode(length=200))
    unegative = Column(Float)
    upositive = Column(Float)

    #relation definitions
    sport = relationship("Sport", backref=backref("activities", order_by=date,
                                                  cascade='all, delete-orphan'))
    equipment = relationship("Equipment", secondary=record_to_equipment,
                             backref=backref("activities", order_by=date))
    Laps = relationship('Lap', backref=backref('activity'),
                        order_by='Lap.lap_number',
                        cascade='all, delete-orphan')

    def __init__(self, **kwargs):
        self._initialize()
        super(Activity, self).__init__(**kwargs)

    @reconstructor
    def _initialize(self):
        logging.debug(">>")
        self.environment = Environment()
        self.uc = uc.UC()
        self.profile = Profile()
        self.has_data = True
        self._distance_data = {}
        self._time_data = {}
        self._lap_time = None
        self._lap_distance = None
        self.time_pause = 0
        self.pace_limit = None
        self._gpx = None
        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("<<")

    @property
    def gpx_file(self):
        if self.id:
            filename = "%s/%s.gpx" % (self.environment.gpx_dir, self.id)
            #It is OK to not have a GPX file for an activity - this just limits us to information in the DB
            if os.path.isfile(filename):
                return filename
        logging.debug("No GPX file found for record id: %s", self.id)
        return None

    @property
    def tracks(self):
        if self.gpx:
            return self.gpx.getTrackList()
        else:
            return None

    @property
    def tracklist(self):
        if self.gpx:
            return self.gpx.trkpoints
        else:
            return None

    @property
    def distance_data(self):
        if not self._distance_data:
            self._init_graph_data()
        return self._distance_data

    @property
    def time_data(self):
        if not self._time_data:
            self._init_graph_data()
        return self._time_data

    @property
    def lap_time(self):
        if not self._lap_time:
            self._generate_per_lap_graphs()
        return self._lap_time

    @property
    def lap_distance(self):
        if not self._lap_distance:
            self._generate_per_lap_graphs()
        return self._lap_distance

    @property
    def time_tuple(self):
        return second2time(self.duration)

    @property
    def date_time(self):
        if self.date_time_local: #Have a local time stored in DB
            return dateutil.parser.parse(self.date_time_local)
        else: #No local time in DB
            #datetime with localtime offset (using value from OS)
            return dateutil.parser.parse(self.date_time_utc).astimezone(tzlocal())

    @property
    def starttime(self):
        return self.date_time.strftime("%X")

    @property
    def laps(self):
        warnings.warn("Deprecated property Activity.laps called", DeprecationWarning, stacklevel=2)
        ret = []
        for lap in self.Laps:
            d = dict(lap.__dict__)
            d.pop('_sa_instance_state', None)
            ret.append(d)
        return ret

    def __str__(self):
        return '''
tracks (%s)
        tracklist (%s)
        laps (%s)
        us_system (%s)
        distance_data (%s)
        time_data (%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 (%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.uc.us,
                self.distance_data, self.time_data,
                self.gpx_file, self.gpx, self.sport_name,
                self.sport_id, self.title, self.date, self.duration, self.time_tuple, self.beats,
                self.maxbeats, self.comments, self.calories, self.id, 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)

    @property
    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("<<")

    @property
    def time(self):
        warnings.warn("Deprecated property Activity.time called", DeprecationWarning, stacklevel=2)
        return self.duration

    @property
    def sport_name(self):
        warnings.warn("Deprecated property Activity.sport_name called", DeprecationWarning, stacklevel=2)
        return self.sport.name

    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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Pace'), self.uc.unit_pace)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Speed'), self.uc.unit_speed)
        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']) # 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", pace, 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)
            self._lap_distance.addBars(x=self.uc.distance(dist), y=10)
            self.distance_data['pace_lap'].addBars(x=self.uc.distance(dist), y=pacekm2miles(pace))
            self.time_data['pace_lap'].addBars(x=time, y=self.uc.speed(pace))
            self.distance_data['speed_lap'].addBars(x=self.uc.distance(dist), y=self.uc.speed(avg_speed))
            self.time_data['speed_lap'].addBars(x=time, y=self.uc.speed(avg_speed))
        logging.debug("<<")

    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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Elevation'), self.uc.unit_height)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Corrected Elevation'), self.uc.unit_height)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Speed'), self.uc.unit_speed)
        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.uc.unit_distance)
        ylabel = "%s (%s)" % (_('Pace'), self.uc.unit_pace)
        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.uc.unit_distance)
        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.profile.getMaxHR()
        title = _("Heart Rate (% of max)")
        xlabel = "%s (%s)" % (_('Distance'), self.uc.unit_distance)
        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.uc.unit_distance)
        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", pace, 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
            self._distance_data['elevation'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                       y=self.uc.height(track['ele']))
            self._distance_data['cor_elevation'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                           y=self.uc.height(track['correctedElevation']))
            self._distance_data['speed'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                   y=self.uc.speed(track['velocity']))
            self._distance_data['pace'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                  y=self.uc.distance(pace))
            self._distance_data['hr'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                y=track['hr'])
            self._distance_data['hr_p'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                  y=hr_p)
            self._distance_data['cadence'].addPoints(x=self.uc.distance(track['elapsed_distance']),
                                                     y=track['cadence'])
            self._time_data['elevation'].addPoints(x=track['time_elapsed'],
                                                   y=self.uc.height(track['ele']))
            self._time_data['cor_elevation'].addPoints(x=track['time_elapsed'],
                                                       y=self.uc.height(track['correctedElevation']))
            self._time_data['speed'].addPoints(x=track['time_elapsed'],
                                               y=self.uc.speed(track['velocity']))
            self._time_data['pace'].addPoints(x=track['time_elapsed'],
                                              y=self.uc.distance(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 list(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 list(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.profile.getZones()
            title = _("Heart Rate zone")
            xlabel = "%s (%s)" % (_('Distance'), self.uc.unit_distance)
            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):
        ''' 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)
        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':
            return self.uc.distance(self.distance)
        elif param == 'average':
            return self.uc.speed(self.average)
        elif param == 'upositive':
            return self.uc.height(self.upositive)
        elif param == 'unegative':
            return self.uc.height(self.unegative)
        elif param == 'maxspeed':
            return self.uc.speed(self.maxspeed)
        elif param == 'maxpace':
            return uc.float2pace(self.uc.pace(self.maxpace))
        elif param == 'pace':
            return uc.float2pace(self.uc.pace(self.pace))
        elif param == 'calories':
            return self.calories
        elif param == 'time':
            if not self.duration:
                return ""
            _hour ,_min, _sec = second2time(self.duration)
            if _hour == 0:
                return "%02d:%02d" % (_min, _sec)
            else:
                return "%0d:%02d:%02d" % (_hour, _min, _sec)
        else:
            logging.error("Unable to provide value for unknown parameter (%s) for activity", param)
            return None