Ejemplo n.º 1
0
 def __init__(self, sport_service, data_path=None, parent=None):
     logging.debug('>>')
     self._sport_service = sport_service
     self.parent = parent
     self.pytrainer_main = parent
     self._equipment_service = EquipmentService(self.pytrainer_main.ddbb)
     self.data_path = data_path
     logging.debug('setting date...')
     self.date = Date()
     logging.debug('<<')
Ejemplo n.º 2
0
	def __init__(self, sport_service, data_path = None, parent = None):
		logging.debug('>>')
		self._sport_service = sport_service
		self.parent = parent
		self.pytrainer_main = parent
		self._equipment_service = EquipmentService(self.pytrainer_main.ddbb)
		self.data_path = data_path
		logging.debug('setting date...')
		self.date = Date()
		logging.debug('<<')
Ejemplo n.º 3
0
    def new(self):
        self.gender_options = {0: _("Male"), 1: _("Female")}

        self.ddbb_type = {0: "sqlite", 1: "mysql"}

        #anhadimos las opciones al combobox gender
        for i in self.gender_options:
            self.prf_gender.insert_text(i, self.gender_options[i])

        #hacemos lo propio para el combobox ddbb
        for i in self.ddbb_type:
            self.prf_ddbb.insert_text(i, self.ddbb_type[i])

        #preparamos la lista sports:
        column_names = [
            _("Sport"),
            _("MET"),
            _("Extra Weight"),
            _("Maximum Pace"),
            _("Color")
        ]
        for column_index, column_name in enumerate(column_names):
            if column_index == 4:
                renderer = gtk.CellRendererPixbuf()
            else:
                renderer = gtk.CellRendererText()
            column = gtk.TreeViewColumn(column_name, text=column_index)
            column.pack_start(renderer, expand=False)
            if column_index == 4:
                column.add_attribute(renderer, 'pixbuf', column_index)
            else:
                column.add_attribute(renderer, 'text', column_index)
            column.set_resizable(True)
            self.sportTreeView.append_column(column)

        #initialise equipment tab:
        equipment_service = EquipmentService(self.pytrainer_main.ddbb)
        equipment_ui = EquipmentUi(self.data_path + "/glade",
                                   equipment_service)
        self.equipment_container.add(equipment_ui)
Ejemplo n.º 4
0
class Record:
    def __init__(self, sport_service, data_path=None, parent=None):
        logging.debug('>>')
        self._sport_service = sport_service
        self.parent = parent
        self.pytrainer_main = parent
        self._equipment_service = EquipmentService(self.pytrainer_main.ddbb)
        self.data_path = data_path
        logging.debug('setting date...')
        self.date = Date()
        logging.debug('<<')

    def newRecord(self,
                  date,
                  title=None,
                  distance=None,
                  time=None,
                  upositive=None,
                  unegative=None,
                  bpm=None,
                  calories=None,
                  comment=None):
        logging.debug('>>')
        sports = self._sport_service.get_all_sports()
        self.recordwindow = WindowRecord(self._equipment_service,
                                         self.data_path, sports, self, date,
                                         title, distance, time, upositive,
                                         unegative, bpm, calories, comment)
        self.recordwindow.run()
        logging.debug('<<')

    def newMultiRecord(self, activities):
        logging.debug('>>')
        sports = self._sport_service.get_all_sports()
        self.recordwindow = WindowRecord(
            self._equipment_service,
            self.data_path,
            sports,
            parent=self,
            windowTitle=_("Modify details before importing"))
        self.recordwindow.populateMultiWindow(activities)
        self.recordwindow.run()
        return self.recordwindow.getActivityData()
        logging.debug('<<')

    def editRecord(self, id_record):
        logging.debug('>>')
        activity = self.pytrainer_main.activitypool.get_activity(id_record)
        record_equipment = self.get_record_equipment(id_record)
        sports = self._sport_service.get_all_sports()
        self.recordwindow = WindowRecord(self._equipment_service,
                                         self.data_path,
                                         sports,
                                         self,
                                         None,
                                         windowTitle=_("Edit Entry"),
                                         equipment=record_equipment)
        self.recordwindow.setValuesFromActivity(activity)
        logging.debug('launching window')
        self.recordwindow.run()
        logging.debug('<<')

    def removeRecord(self, id_record):
        logging.debug('>>')
        record = self.pytrainer_main.ddbb.delete(
            "records", "id_record=\"%s\"" % id_record)
        laps = self.pytrainer_main.ddbb.delete("laps",
                                               "record=\"%s\"" % id_record)
        logging.debug('removed record ' + str(id_record) +
                      ' (and associated laps) from DB')
        gpxfile = self.pytrainer_main.profile.gpxdir + "/%d.gpx" % int(
            id_record)
        if os.path.isfile(gpxfile):
            os.remove(gpxfile)
            logging.debug('removed gpxfile ' + gpxfile)
        logging.debug('<<')

    def pace_to_float(self, value):
        '''Take a mm:ss or mm.ss and return float'''
        try:
            value = float(value)
        except:
            if ":" in value:  # 'mm:ss' found
                mins, sec = value.split(":")
                value = float(mins + "." + "%02d" % round(int(sec) * 5 / 3))
            elif "," in value:
                value = float(value.replace(',', '.'))
            else:
                logging.error("Wrong value provided: %s" % value)
                value = None
        return value

    def pace_from_float(self, value, fromDB=False):
        '''Helper to generate mm:ss from float representation mm.ss (or mm,ss?)'''
        #Check that value supplied is a float
        try:
            _value = "%0.2f" % float(value)
        except ValueError:
            _value = str(value)
        if fromDB:  # paces in DB are stored in mixed format -> 4:30 as 4.3 (NOT as 4.5 aka 'decimal')
            pace = _value
        else:
            mins, sec_dec = _value.split(".")
            pace = mins + ":" + "%02d" % round(int(sec_dec) * 3 / 5)
        return pace

    def _formatRecordNew(self, list_options):
        """20.07.2008 - dgranda
		New records handle date_time_utc field which is transparent when updating, so logic method has been splitted
		args: list with keys and values without valid format
		returns: keys and values matching DB schema"""
        logging.debug('>>')
        time = self.date.time2second(list_options["rcd_time"])
        average = self.parseFloatRecord(list_options["rcd_average"])
        keys = "date,sport,distance,time,beats,comments,average,calories,title,upositive,unegative,maxspeed,maxpace,pace,maxbeats,date_time_utc,date_time_local, duration"
        if (list_options["rcd_beats"] == ""):
            list_options["rcd_beats"] = 0

        #retrieving sport id (adding sport if it doesn't exist yet)
        sport_id = self.getSportId(list_options["rcd_sport"], add=True)

        values = (
            list_options["rcd_date"],
            sport_id,
            self.parseFloatRecord(list_options["rcd_distance"]),
            time,
            self.parseFloatRecord(list_options["rcd_beats"]),
            list_options["rcd_comments"],
            average,
            self.parseFloatRecord(list_options["rcd_calories"]),
            list_options["rcd_title"],
            self.parseFloatRecord(list_options["rcd_upositive"]),
            self.parseFloatRecord(list_options["rcd_unegative"]),
            self.parseFloatRecord(list_options["rcd_maxvel"]),
            self.pace_to_float(list_options["rcd_maxpace"]),
            self.pace_to_float(list_options["rcd_pace"]),
            self.parseFloatRecord(list_options["rcd_maxbeats"]),
            list_options["date_time_utc"],
            list_options["date_time_local"],
            time,
        )
        logging.debug('<<')
        return keys, values

    def insertRecord(self, list_options, laps=None, equipment=None):
        logging.debug('>>')
        #Create entry for activity in records table
        if list_options is None:
            logging.info('No data provided, abort adding entry')
            return None
        logging.debug('list_options: ' + str(list_options))
        cells, values = self._formatRecordNew(list_options)
        self.pytrainer_main.ddbb.insert("records", cells, values)
        logging.debug('DB updated: ' + str(cells) + ' | ' + str(values))
        id_record = self.pytrainer_main.ddbb.lastRecord("records")
        #Create entry(s) for activity in laps table
        if laps is not None:
            for lap in laps:
                lap['record'] = id_record  #Add reference to entry in record table
                lap_keys = ", ".join(map(str, lap.keys()))
                lap_values = lap.values()
                self.insertLaps(lap_keys, lap.values())
        if equipment is not None:
            for equipment_id in equipment:
                self._insert_record_equipment(id_record, equipment_id)
        gpxOrig = list_options["rcd_gpxfile"]
        if os.path.isfile(gpxOrig):
            gpxDest = self.pytrainer_main.profile.gpxdir
            gpxNew = gpxDest + "/%d.gpx" % id_record
            #Leave original file in place...
            #shutil.move(gpxOrig, gpxNew)
            #logging.debug('Moving '+gpxOrig+' to '+gpxNew)
            shutil.copy(gpxOrig, gpxNew)
            logging.debug('Copying ' + gpxOrig + ' to ' + gpxNew)
        #self.parent.refreshListRecords()
        logging.debug('<<')
        return self.pytrainer_main.ddbb.lastRecord("records")

    def insertNewRecord(self, gpxOrig,
                        entry):  #TODO consolidate with insertRecord
        """29.03.2008 - dgranda
		Moves GPX file to store destination and updates database
		args: path to source GPX file"""
        logging.debug('--')
        (list_options, gpx_laps) = self.summaryFromGPX(gpxOrig, entry)
        if list_options is None:
            return None
        return self.insertRecord(list_options, laps=gpx_laps)

    def lapsFromGPX(self, gpx):
        logging.debug('>>')
        laps = []
        gpxLaps = gpx.getLaps()
        for lap in gpxLaps:
            lap_number = gpxLaps.index(lap)
            tmp_lap = {}
            tmp_lap['record'] = ""
            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]
            tmp_lap['intensity'] = lap[7]
            tmp_lap['avg_hr'] = lap[8]
            tmp_lap['max_hr'] = lap[9]
            tmp_lap['max_speed'] = lap[10]
            tmp_lap['laptrigger'] = lap[11]
            tmp_lap['comments'] = ""
            laps.append(tmp_lap)
        logging.debug('<<')
        return laps

    def summaryFromGPX(self, gpxOrig, entry):
        """29.03.2008 - dgranda
		Retrieves info which will be stored in DB from GPX file
		args: path to source GPX file
		returns: list with fields and values, list of laps
		"""
        logging.debug('>>')
        gpx = Gpx(self.data_path, gpxOrig)
        distance, time, maxspeed, maxheartrate = gpx.getMaxValues()
        #if time == 0: #invalid record
        #	print "Invalid record"
        #	return (None, None)
        upositive, unegative = gpx.getUnevenness()
        if time > 0:
            speed = distance * 3600 / time
            time_hhmmss = [time // 3600, (time / 60) % 60, time % 60]
        else:
            speed = 0
            time_hhmmss = [0, 0, 0]
        summaryRecord = {}
        summaryRecord['rcd_gpxfile'] = gpxOrig
        summaryRecord['rcd_sport'] = entry[0]
        summaryRecord['rcd_date'] = gpx.getDate()
        summaryRecord['rcd_calories'] = gpx.getCalories()
        summaryRecord['rcd_comments'] = ''
        summaryRecord['rcd_title'] = ''
        summaryRecord[
            'rcd_time'] = time_hhmmss  #ToDo: makes no sense to work with arrays
        summaryRecord['rcd_distance'] = "%0.2f" % distance
        if speed == 0:
            summaryRecord['rcd_pace'] = "0"
        else:
            summaryRecord['rcd_pace'] = "%d.%02d" % ((3600 / speed) / 60,
                                                     (3600 / speed) % 60)
        if maxspeed == 0:
            summaryRecord['rcd_maxpace'] = "0"
        else:
            summaryRecord['rcd_maxpace'] = "%d.%02d" % ((3600 / maxspeed) / 60,
                                                        (3600 / maxspeed) % 60)
        summaryRecord['rcd_average'] = speed
        summaryRecord['rcd_maxvel'] = maxspeed
        summaryRecord['rcd_beats'] = gpx.getHeartRateAverage()
        summaryRecord['rcd_maxbeats'] = maxheartrate
        summaryRecord['rcd_upositive'] = upositive
        summaryRecord['rcd_unegative'] = unegative
        if entry[
                1] == "":  # coming from new track dialog (file opening)												#TODO This if-else needs checking
            summaryRecord['date_time_utc'], summaryRecord[
                'date_time_local'] = gpx.getStartTimeFromGPX(gpxOrig)  #
        else:  # coming from GPS device																				#
            summaryRecord['date_time_utc'] = entry[1]  #
            summaryRecord['date_time_local'] = entry[1]  #
            print "#TODO fix record summaryRecord local and utc time..."  #
        logging.debug('summary: ' + str(summaryRecord))
        laps = self.lapsFromGPX(gpx)
        logging.debug('<<')
        return summaryRecord, laps

    def updateRecord(
        self,
        list_options,
        id_record,
        equipment=None
    ):  # ToDo: update only fields that can change if GPX file is present
        logging.debug('>>')
        #Remove activity from pool so data is updated
        self.pytrainer_main.activitypool.remove_activity(id_record)
        gpxfile = self.pytrainer_main.profile.gpxdir + "/%d.gpx" % int(
            id_record)
        gpxOrig = list_options["rcd_gpxfile"]
        if os.path.isfile(gpxOrig):
            if gpxfile != gpxOrig:
                shutil.copy2(gpxOrig, gpxfile)
        else:
            if (list_options["rcd_gpxfile"] == ""):
                logging.debug('Activity not based in GPX file')  # ein?
        logging.debug('Updating bbdd')
        cells, values = self._formatRecordNew(list_options)
        self.pytrainer_main.ddbb.update("records", cells, values,
                                        " id_record=%d" % int(id_record))
        if equipment is not None:
            self._update_record_equipment(id_record, equipment)
        self.pytrainer_main.refreshListView()
        logging.debug('<<')

    def parseFloatRecord(self, string):
        logging.debug('--')
        if string != "":
            try:
                return float(string.replace(",", ","))
            except:
                return float(string)
        else:
            return 0

    def getrecordInfo(self, id_record):
        logging.debug('--')
        if id_record is None or id_record == "":
            return []
        return self.pytrainer_main.ddbb.select(
            "records,sports",
            "sports.name,date,distance,time,beats,comments,average,calories,id_record,title,upositive,unegative,maxspeed,maxpace,pace,maxbeats,date_time_utc,date_time_local",
            "id_record=\"%s\" and records.sport=sports.id_sports" % id_record)

    def getrecordList(self, date, id_sport=None):
        logging.debug('--')
        if not id_sport:
            # outer join on sport id to workaround bug where sport reference is null on records from GPX import
            return self.pytrainer_main.ddbb.select(
                "records left outer join sports on records.sport=sports.id_sports",
                "sports.name,date,distance,time,beats,comments,average,calories,id_record,maxspeed,maxbeats,date_time_utc,date_time_local,upositive,unegative",
                "date=\"%s\" " % date)
        else:
            return self.pytrainer_main.ddbb.select(
                "records,sports",
                "sports.name,date,distance,time,beats,comments,average,calories,id_record,maxspeed,maxbeats,date_time_utc,date_time_local,upositive,unegative",
                "date=\"%s\" and sports.id_sports=\"%s\" and records.sport=sports.id_sports"
                % (date, id_sport))

    def getLaps(self, id_record):
        logging.debug('--')
        laps = self.pytrainer_main.ddbb.select(
            "laps",
            "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, laptrigger, comments",
            "record=\"%s\"" % id_record)
        if laps is None or laps == []:  #No laps stored - update DB
            logging.debug("No laps in DB for record %d" % id_record)
            #print ("No laps in DB for record %d" % id_record)
            gpx_dest = self.pytrainer_main.profile.gpxdir
            gpxfile = gpx_dest + "/%d.gpx" % id_record
            gpx = Gpx(self.data_path, gpxfile)
            laps = self.lapsFromGPX(gpx)
            if laps is not None:
                for lap in laps:
                    lap['record'] = id_record  #Add reference to entry in record table
                    lap_keys = ", ".join(map(str, lap.keys()))
                    lap_values = lap.values()
                    self.insertLaps(lap_keys, lap.values())
            #Try to get lap info again #TODO? refactor
            laps = self.pytrainer_main.ddbb.select(
                "laps",
                "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, laptrigger, comments",
                "record=\"%s\"" % id_record)
        return laps

    def insertLaps(self, cells, values):
        logging.debug('--')
        logging.debug("Adding lap information: " + ", ".join(map(str, values)))
        self.pytrainer_main.ddbb.insert("laps", cells, values)

    def _insert_record_equipment(self, record_id, equipment_id):
        self.pytrainer_main.ddbb.insert("record_equipment",
                                        "record_id, equipment_id",
                                        [record_id, equipment_id])

    def _update_record_equipment(self, record_id, equipment_ids):
        self.pytrainer_main.ddbb.delete("record_equipment",
                                        "record_id={0}".format(record_id))
        for id in equipment_ids:
            self._insert_record_equipment(record_id, id)

    def get_record_equipment(self, record_id):
        record_equipment = []
        results = self.pytrainer_main.ddbb.select(
            "record_equipment", "equipment_id",
            "record_id={0}".format(record_id))
        for row in results:
            id = row[0]
            equipment_item = self._equipment_service.get_equipment_item(id)
            record_equipment.append(equipment_item)
        return record_equipment

    def getrecordPeriod(self, date_ini, date_end, sport=None):
        #TODO This is essentially the same as getrecordPeriodSport (except date ranges) - need to look at merging the two
        tables = "records,sports"
        if not sport:
            condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports" % (
                date_ini, date_end)
        else:
            condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" % (
                date_ini, date_end, sport)

        return self.pytrainer_main.ddbb.select(
            tables,
            "date,distance,time,beats,comments,average,calories,maxspeed,maxbeats, sports.name,upositive,unegative",
            condition)

    def getrecordPeriodSport(self, date_ini, date_end, sport):
        if not sport:
            tables = "records"
            condition = "date>\"%s\" and date<\"%s\"" % (date_ini, date_end)
        else:
            tables = "records,sports"
            condition = "date>\"%s\" and date<\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" % (
                date_ini, date_end, sport)

        return self.pytrainer_main.ddbb.select(
            tables,
            "date,distance,time,beats,comments,average,calories,maxspeed,maxbeats,upositive,unegative",
            condition)

    def _get_sport(self, sport_name):
        return self._sport_service.get_sport_by_name(sport_name)

    def getSportMet(self, sport_name):
        """Deprecated: use sport.met"""
        logging.debug('--')
        return self._get_sport(sport_name).met

    def getSportWeight(self, sport_name):
        """Deprecated: use sport.weight"""
        logging.debug('--')
        return self._get_sport(sport_name).weight

    def getSportId(self, sport_name, add=None):
        """Deprecated: use sport_service.get_sport_by_name()
		
		Get the id of a sport by name, optionally adding a new sport if
		none exists with the given name.
		arguments:
			sport_name: sport's name to get id for
			add: whether the sport should be added if not found
		returns: id for sport with given name or None"""
        if sport_name is None:
            return None
        sport = self._get_sport(sport_name)
        if sport is None:
            logging.debug("No sport with name: '%s'", str(sport_name))
            if add is not None:
                logging.debug("Adding sport '%s'", str(sport_name))
                new_sport = Sport()
                new_sport.name = unicode(sport_name)
                sport = self._sport_service.store_sport(new_sport)
        return None if sport is None else sport.id

    def getAllrecord(self):
        logging.debug('--')
        return self.pytrainer_main.ddbb.select(
            "records", "date,distance,time,beats,comments,average,calories")

    def getAllRecordList(self):
        logging.debug('--')
        return self.pytrainer_main.ddbb.select(
            "records,sports",
            "date,distance,average,title,sports.name,id_record,time,beats,calories",
            "sports.id_sports = records.sport order by date desc")

    def getRecordListByCondition(self, condition):
        logging.debug('--')
        if condition is None:
            return self.getAllRecordList()
        else:
            logging.debug("condition: %s" % condition)
            return self.pytrainer_main.ddbb.select(
                "records,sports",
                "date,distance,average,title,sports.name,id_record,time,beats,calories",
                "sports.id_sports = records.sport and %s order by date desc" %
                condition)

    def getRecordDayList(self, date, id_sport=None):
        logging.debug('>>')
        year, month, day = date.split("-")
        logging.debug('Retrieving data for ' + year + '.' + month + '.' + day)
        # Why is looking for all days of the same month?
        if not id_sport:
            records = self.pytrainer_main.ddbb.select(
                "records", "date", "date LIKE '" + year + "-" + month + "-%'")
        else:
            records = self.pytrainer_main.ddbb.select(
                "records", "date", "date LIKE \"%s-%s-%%\" and sport=\"%s\"" %
                (year, month, id_sport))
        logging.debug('Found ' + str(len(records)) + ' entries')
        day_list = []
        for i in records:
            record = str(i[0]).split("-")
            logging.debug('date:' + str(i[0]))
            day_list.append(record[2])
        logging.debug('<<')
        return day_list

    def actualize_fromgpx(
        self, gpxfile
    ):  #TODO remove? - should never have multiple tracks per GPX file
        logging.debug('>>')
        logging.debug('loading file: ' + gpxfile)
        gpx = Gpx(self.data_path, gpxfile)
        tracks = gpx.getTrackRoutes()

        if len(tracks) == 1:
            logging.debug('Just 1 track')
            self._actualize_fromgpx(gpx)
        elif len(tracks) > 1:
            logging.debug('Found ' + str(len(tracks)) + ' tracks')
            self._select_trkfromgpx(gpxfile, tracks)
        else:
            msg = _("pytrainer can't import data from your gpx file")
            from gui.warning import Warning
            warning = Warning(self.data_path)
            warning.set_text(msg)
            warning.run()
        logging.debug('<<')

    def _actualize_fromgpx(self, gpx):
        logging.debug('>>')
        distance, time, maxspeed, maxheartrate = gpx.getMaxValues()
        upositive, unegative = gpx.getUnevenness()
        heartrate = gpx.getHeartRateAverage()
        date = gpx.getDate()
        calories = gpx.getCalories()
        start_time = gpx.getStart_time()

        self.recordwindow.rcd_date.set_text(date)
        self.recordwindow.rcd_starttime.set_text(start_time)
        self.recordwindow.rcd_upositive.set_text(str(upositive))
        self.recordwindow.rcd_unegative.set_text(str(unegative))
        self.recordwindow.rcd_beats.set_text(str(heartrate))
        self.recordwindow.rcd_calories.set_text(str(calories))
        self.recordwindow.set_distance(distance)
        self.recordwindow.set_maxspeed(maxspeed)
        self.recordwindow.set_maxhr(maxheartrate)
        self.recordwindow.set_recordtime(time / 60.0 / 60.0)
        self.recordwindow.on_calcavs_clicked(None)
        self.recordwindow.on_calccalories_clicked(None)
        self.recordwindow.rcd_maxpace.set_text(
            "%d.%02d" % ((3600 / maxspeed) / 60, (3600 / maxspeed) % 60))
        logging.debug('<<')

    def __actualize_fromgpx(self, gpxfile, name=None):
        logging.debug('>>')
        gpx = Gpx(self.data_path, gpxfile, name)
        self._actualize_fromgpx(gpx)
        logging.debug('<<')

    def _select_trkfromgpx(
        self, gpxfile, tracks
    ):  #TODO remove? - should never have multiple tracks per GPX file
        logging.debug('>>')
        logging.debug('Track dialog ' + self.data_path + '|' + gpxfile)
        selectrckdialog = DialogSelectTrack(self.data_path, tracks,
                                            self.__actualize_fromgpx, gpxfile)
        logging.debug('Launching window...')
        selectrckdialog.run()
        logging.debug('<<')

    def importFromGPX(self, gpxFile, sport):
        """
		Add a record from a valid pytrainer type GPX file
		"""
        logging.debug('>>')
        entry_id = None
        if not os.path.isfile(gpxFile):
            logging.error("Invalid file: " + gpxFile)
        else:
            logging.info('Retrieving data from ' + gpxFile)
            if not sport:
                sport = "import"
            entry = [sport, ""]
            entry_id = self.insertNewRecord(gpxFile, entry)
            if entry_id is None:
                logging.error("Entry not created for file %s" % gpxFile)
            else:
                logging.info("Entry %d has been added" % entry_id)
        logging.debug('<<')
        return entry_id
Ejemplo n.º 5
0
 def setUp(self):
     self.mock_ddbb = mock.Mock(spec=Sql)
     self.equipment_service = EquipmentService(self.mock_ddbb)
Ejemplo n.º 6
0
class EquipmentServiceTest(unittest.TestCase):
    
    def setUp(self):
        self.mock_ddbb = mock.Mock(spec=Sql)
        self.equipment_service = EquipmentService(self.mock_ddbb)
        
    def tearDown(self):
        pass
    
    def test_get_equipment_item(self):
        self.mock_ddbb.select.return_value = [(1, u"Test Description", True, 500, 200, u"Test notes.")]
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals(1, item.id)
        self.assertEquals("Test Description", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes.", item.notes)
    
    def test_get_equipment_item_non_unicode(self):
        self.mock_ddbb.select.return_value = [(1, "Test Description", True, 500, 200, "Test notes.")]
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals("Test Description", item.description)
        self.assertEquals("Test notes.", item.notes)
    
    def test_get_equipment_item_non_existant(self):
        self.mock_ddbb.select.return_value = []
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals(None, item)
        
    def test_get_all_equipment(self):
        self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."),
                                              (2, u"Test item 2", False, 600, 300, u"Test notes 2.")]
        items = self.equipment_service.get_all_equipment()
        item = items[0]
        self.assertEquals(1, item.id)
        self.assertEquals("Test item 1", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes 1.", item.notes)
        item = items[1]
        self.assertEquals(2, item.id)
        self.assertEquals("Test item 2", item.description)
        self.assertFalse(item.active)
        self.assertEquals(600, item.life_expectancy)
        self.assertEquals(300, item.prior_usage)
        self.assertEquals("Test notes 2.", item.notes)
        
    def test_get_all_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        items = self.equipment_service.get_all_equipment()
        self.assertEquals([], items)
        
    def test_get_active_equipment(self):
        self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."),
                                              (2, u"Test item 2", True, 600, 300, u"Test notes 2.")]
        items = self.equipment_service.get_active_equipment()
        item = items[0]
        self.assertEquals(1, item.id)
        self.assertEquals("Test item 1", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes 1.", item.notes)
        item = items[1]
        self.assertEquals(2, item.id)
        self.assertEquals("Test item 2", item.description)
        self.assertTrue(item.active)
        self.assertEquals(600, item.life_expectancy)
        self.assertEquals(300, item.prior_usage)
        self.assertEquals("Test notes 2.", item.notes)
        
    def test_get_active_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        items = self.equipment_service.get_active_equipment()
        self.assertEquals([], items)
        
    def test_store_equipment(self):
        equipment = Equipment()
        equipment.description = u"test description"
        equipment_ids = []
        def mock_select(table, columns, where):
            if columns == "id":
                return equipment_ids
            else:
                return [(2, u"", 1, 0, 0,u"")]
        def update_equipment_ids(*args):
            equipment_ids.append([1])
        self.mock_ddbb.select = mock.Mock(wraps=mock_select)
        self.mock_ddbb.insert.side_effect = update_equipment_ids
        stored_equipment = self.equipment_service.store_equipment(equipment)
        self.mock_ddbb.insert.assert_called_with("equipment", 
                                                 "description,active,life_expectancy,prior_usage,notes", 
                                                 ["test description", 1, 0, 0,"" ])
        self.assertEquals(2, stored_equipment.id)
        
    def test_store_equipment_duplicate_description(self):
        self.mock_ddbb.select.return_value = [(1,)]
        equipment = Equipment()
        equipment.description = u"test item"
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail("Should not be able to store new item with non-unique description.")
        except(EquipmentServiceException):
            pass
        
    def test_update_equipment(self):
        equipment = Equipment()
        equipment.id = 1
        equipment.description = u"new description"
        self.mock_ddbb.select.return_value =  [(1, u"old description", 1, 0, 0,u"")]
        self.equipment_service.store_equipment(equipment)
        self.mock_ddbb.update.assert_called_with("equipment", 
                                                 "description,active,life_expectancy,prior_usage,notes", 
                                                 ["new description", 1, 0, 0,"" ], 
                                                 "id = 1")
        
    def test_update_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        equipment = Equipment()
        equipment.id = 1
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail("Should not be able to update an item which did not previously exist.")
        except(EquipmentServiceException):
            pass
        
    def test_update_equipment_duplicate_description(self):
        self.mock_ddbb.select.return_value = [(1, u"test item", True, 500, 200, u"Test notes.")]
        equipment = Equipment()
        equipment.id = 2
        equipment.description = u"test item"
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail("Should not be able to change item description to non-unique value.")
        except(EquipmentServiceException):
            pass
        
    def test_get_equipment_usage(self):
        self.mock_ddbb.select.return_value = [(250,)]
        equipment = Equipment()
        equipment.id = 1
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(250, usage)
        
    def test_get_equipment_usage_none(self):
        self.mock_ddbb.select.return_value = [(None,)]
        equipment = Equipment()
        equipment.id = 1
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(0, usage)
Ejemplo n.º 7
0
 def setUp(self):
     self.mock_ddbb = mock.Mock(spec=Sql)
     self.equipment_service = EquipmentService(self.mock_ddbb)
Ejemplo n.º 8
0
class EquipmentServiceTest(unittest.TestCase):
    def setUp(self):
        self.mock_ddbb = mock.Mock(spec=Sql)
        self.equipment_service = EquipmentService(self.mock_ddbb)

    def tearDown(self):
        pass

    def test_get_equipment_item(self):
        self.mock_ddbb.select.return_value = [(1, u"Test Description", True,
                                               500, 200, u"Test notes.")]
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals(1, item.id)
        self.assertEquals("Test Description", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes.", item.notes)

    def test_get_equipment_item_non_unicode(self):
        self.mock_ddbb.select.return_value = [(1, "Test Description", True,
                                               500, 200, "Test notes.")]
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals("Test Description", item.description)
        self.assertEquals("Test notes.", item.notes)

    def test_get_equipment_item_non_existant(self):
        self.mock_ddbb.select.return_value = []
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals(None, item)

    def test_get_all_equipment(self):
        self.mock_ddbb.select.return_value = [
            (1, u"Test item 1", True, 500, 200, u"Test notes 1."),
            (2, u"Test item 2", False, 600, 300, u"Test notes 2.")
        ]
        items = self.equipment_service.get_all_equipment()
        item = items[0]
        self.assertEquals(1, item.id)
        self.assertEquals("Test item 1", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes 1.", item.notes)
        item = items[1]
        self.assertEquals(2, item.id)
        self.assertEquals("Test item 2", item.description)
        self.assertFalse(item.active)
        self.assertEquals(600, item.life_expectancy)
        self.assertEquals(300, item.prior_usage)
        self.assertEquals("Test notes 2.", item.notes)

    def test_get_all_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        items = self.equipment_service.get_all_equipment()
        self.assertEquals([], items)

    def test_get_active_equipment(self):
        self.mock_ddbb.select.return_value = [
            (1, u"Test item 1", True, 500, 200, u"Test notes 1."),
            (2, u"Test item 2", True, 600, 300, u"Test notes 2.")
        ]
        items = self.equipment_service.get_active_equipment()
        item = items[0]
        self.assertEquals(1, item.id)
        self.assertEquals("Test item 1", item.description)
        self.assertTrue(item.active)
        self.assertEquals(500, item.life_expectancy)
        self.assertEquals(200, item.prior_usage)
        self.assertEquals("Test notes 1.", item.notes)
        item = items[1]
        self.assertEquals(2, item.id)
        self.assertEquals("Test item 2", item.description)
        self.assertTrue(item.active)
        self.assertEquals(600, item.life_expectancy)
        self.assertEquals(300, item.prior_usage)
        self.assertEquals("Test notes 2.", item.notes)

    def test_get_active_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        items = self.equipment_service.get_active_equipment()
        self.assertEquals([], items)

    def test_store_equipment(self):
        equipment = Equipment()
        equipment.description = u"test description"
        equipment_ids = []

        def mock_select(table, columns, where):
            if columns == "id":
                return equipment_ids
            else:
                return [(2, u"", 1, 0, 0, u"")]

        def update_equipment_ids(*args):
            equipment_ids.append([1])

        self.mock_ddbb.select = mock.Mock(wraps=mock_select)
        self.mock_ddbb.insert.side_effect = update_equipment_ids
        stored_equipment = self.equipment_service.store_equipment(equipment)
        self.mock_ddbb.insert.assert_called_with(
            "equipment",
            "description,active,life_expectancy,prior_usage,notes",
            ["test description", 1, 0, 0, ""])
        self.assertEquals(2, stored_equipment.id)

    def test_store_equipment_duplicate_description(self):
        self.mock_ddbb.select.return_value = [(1, )]
        equipment = Equipment()
        equipment.description = u"test item"
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail(
                "Should not be able to store new item with non-unique description."
            )
        except (EquipmentServiceException):
            pass

    def test_update_equipment(self):
        equipment = Equipment()
        equipment.id = 1
        equipment.description = u"new description"
        self.mock_ddbb.select.return_value = [(1, u"old description", 1, 0, 0,
                                               u"")]
        self.equipment_service.store_equipment(equipment)
        self.mock_ddbb.update.assert_called_with(
            "equipment",
            "description,active,life_expectancy,prior_usage,notes",
            ["new description", 1, 0, 0, ""], "id = 1")

    def test_update_equipment_non_existant(self):
        self.mock_ddbb.select.return_value = []
        equipment = Equipment()
        equipment.id = 1
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail(
                "Should not be able to update an item which did not previously exist."
            )
        except (EquipmentServiceException):
            pass

    def test_update_equipment_duplicate_description(self):
        self.mock_ddbb.select.return_value = [(1, u"test item", True, 500, 200,
                                               u"Test notes.")]
        equipment = Equipment()
        equipment.id = 2
        equipment.description = u"test item"
        try:
            self.equipment_service.store_equipment(equipment)
            self.fail(
                "Should not be able to change item description to non-unique value."
            )
        except (EquipmentServiceException):
            pass

    def test_get_equipment_usage(self):
        self.mock_ddbb.select.return_value = [(250, )]
        equipment = Equipment()
        equipment.id = 1
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(250, usage)

    def test_get_equipment_usage_none(self):
        self.mock_ddbb.select.return_value = [(None, )]
        equipment = Equipment()
        equipment.id = 1
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(0, usage)
Ejemplo n.º 9
0
class Record:
	def __init__(self, sport_service, data_path = None, parent = None):
		logging.debug('>>')
		self._sport_service = sport_service
		self.parent = parent
		self.pytrainer_main = parent
		self._equipment_service = EquipmentService(self.pytrainer_main.ddbb)
		self.data_path = data_path
		logging.debug('setting date...')
		self.date = Date()
		logging.debug('<<')

	def newRecord(self, date, title=None, distance=None, time=None, upositive=None, unegative=None, bpm=None, calories=None, comment=None):
		logging.debug('>>')
		sports = self._sport_service.get_all_sports()
		self.recordwindow = WindowRecord(self._equipment_service, self.data_path, sports, self, date, title, distance, time, upositive, unegative, bpm, calories, comment)
		self.recordwindow.run()
		logging.debug('<<')

	def newMultiRecord(self, activities):
		logging.debug('>>')
		sports = self._sport_service.get_all_sports()
		self.recordwindow = WindowRecord(self._equipment_service, self.data_path, sports, parent=self, windowTitle=_("Modify details before importing"))
		self.recordwindow.populateMultiWindow(activities)
		self.recordwindow.run()
		return self.recordwindow.getActivityData()
		logging.debug('<<')

	def editRecord(self,id_record):
		logging.debug('>>')
		activity = self.pytrainer_main.activitypool.get_activity(id_record)
		record_equipment = self.get_record_equipment(id_record)
		sports = self._sport_service.get_all_sports()
		self.recordwindow = WindowRecord(self._equipment_service, self.data_path, sports, self, None, windowTitle=_("Edit Entry"), equipment=record_equipment)
		self.recordwindow.setValuesFromActivity(activity)
		logging.debug('launching window')
		self.recordwindow.run()
		logging.debug('<<')

	def removeRecord(self,id_record):
		logging.debug('>>')
		record = self.pytrainer_main.ddbb.delete("records", "id_record=\"%s\"" %id_record)
		laps = self.pytrainer_main.ddbb.delete("laps", "record=\"%s\"" %id_record)
		logging.debug('removed record '+str(id_record)+' (and associated laps) from DB')
		gpxfile = self.pytrainer_main.profile.gpxdir+"/%d.gpx"%int(id_record)
		if os.path.isfile(gpxfile):
			os.remove(gpxfile)
			logging.debug('removed gpxfile '+gpxfile)
		logging.debug('<<')

	def pace_to_float(self, value):
		'''Take a mm:ss or mm.ss and return float'''
		try:
			value = float(value)			
		except:
			if ":" in value: # 'mm:ss' found
				mins, sec = value.split(":")
				value = float(mins + "." + "%02d" %round(int(sec)*5/3))
			elif "," in value:
				value = float(value.replace(',','.'))
			else:
				logging.error("Wrong value provided: %s" %value)
				value = None
		return value

	def pace_from_float(self, value, fromDB=False):
		'''Helper to generate mm:ss from float representation mm.ss (or mm,ss?)'''
		#Check that value supplied is a float
		try:
			_value = "%0.2f" % float(value)
		except ValueError:
			_value = str(value)
		if fromDB:  # paces in DB are stored in mixed format -> 4:30 as 4.3 (NOT as 4.5 aka 'decimal')
			pace = _value
		else:
			mins, sec_dec = _value.split(".")
			pace = mins + ":" + "%02d" %round(int(sec_dec)*3/5)
		return pace

	def _formatRecordNew (self, list_options):
		"""20.07.2008 - dgranda
		New records handle date_time_utc field which is transparent when updating, so logic method has been splitted
		args: list with keys and values without valid format
		returns: keys and values matching DB schema"""
		logging.debug('>>')
		time = self.date.time2second(list_options["rcd_time"])
		average = self.parseFloatRecord(list_options["rcd_average"])
		keys= "date,sport,distance,time,beats,comments,average,calories,title,upositive,unegative,maxspeed,maxpace,pace,maxbeats,date_time_utc,date_time_local, duration"
		if (list_options["rcd_beats"] == ""):
			list_options["rcd_beats"] = 0

		#retrieving sport id (adding sport if it doesn't exist yet)
		sport_id = self.getSportId(list_options["rcd_sport"],add=True)

		values= (
			list_options["rcd_date"],
			sport_id,
			self.parseFloatRecord(list_options["rcd_distance"]),
			time,
			self.parseFloatRecord(list_options["rcd_beats"]),
			list_options["rcd_comments"],
			average,
			self.parseFloatRecord(list_options["rcd_calories"]),
			list_options["rcd_title"],
			self.parseFloatRecord(list_options["rcd_upositive"]),
			self.parseFloatRecord(list_options["rcd_unegative"]),
			self.parseFloatRecord(list_options["rcd_maxvel"]),
			self.pace_to_float(list_options["rcd_maxpace"]),
			self.pace_to_float(list_options["rcd_pace"]),
			self.parseFloatRecord(list_options["rcd_maxbeats"]),
			list_options["date_time_utc"],
			list_options["date_time_local"],
			time,
			)
		logging.debug('<<')
		return keys,values

	def insertRecord(self, list_options, laps=None, equipment=None):
		logging.debug('>>')
		#Create entry for activity in records table
		if list_options is None:
			logging.info('No data provided, abort adding entry')
			return None
		logging.debug('list_options: '+str(list_options))
		cells,values = self._formatRecordNew(list_options)
		self.pytrainer_main.ddbb.insert("records",cells,values)
		logging.debug('DB updated: '+str(cells)+' | '+str(values))
		id_record = self.pytrainer_main.ddbb.lastRecord("records")
		#Create entry(s) for activity in laps table
		if laps is not None:
			for lap in laps:
				lap['record'] = id_record #Add reference to entry in record table
				lap_keys = ", ".join(map(str, lap.keys()))
				lap_values = lap.values()
				self.insertLaps(lap_keys,lap.values())
		if equipment is not None:
			for equipment_id in equipment:
				self._insert_record_equipment(id_record, equipment_id)
		gpxOrig = list_options["rcd_gpxfile"]
		if os.path.isfile(gpxOrig):
			gpxDest = self.pytrainer_main.profile.gpxdir
			gpxNew = gpxDest+"/%d.gpx"%id_record
			#Leave original file in place...
			#shutil.move(gpxOrig, gpxNew)
			#logging.debug('Moving '+gpxOrig+' to '+gpxNew)
			shutil.copy(gpxOrig, gpxNew)
			logging.debug('Copying '+gpxOrig+' to '+gpxNew)
		#self.parent.refreshListRecords()
		logging.debug('<<')
		return self.pytrainer_main.ddbb.lastRecord("records")

	def insertNewRecord(self, gpxOrig, entry): #TODO consolidate with insertRecord
		"""29.03.2008 - dgranda
		Moves GPX file to store destination and updates database
		args: path to source GPX file"""
		logging.debug('--')
		(list_options, gpx_laps) = self.summaryFromGPX(gpxOrig, entry)
		if list_options is None:
			return None
		return self.insertRecord(list_options, laps=gpx_laps)

	def lapsFromGPX(self, gpx):
		logging.debug('>>')
		laps = []
		gpxLaps = gpx.getLaps()
		for lap in gpxLaps:
			lap_number = gpxLaps.index(lap)
			tmp_lap = {}
			tmp_lap['record'] = ""
			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]
			tmp_lap['intensity'] = lap[7]
			tmp_lap['avg_hr'] = lap[8]
			tmp_lap['max_hr'] = lap[9]
			tmp_lap['max_speed'] = lap[10]
			tmp_lap['laptrigger'] = lap[11]
			tmp_lap['comments'] = ""
			laps.append(tmp_lap)
		logging.debug('<<')
		return laps

	def summaryFromGPX(self, gpxOrig, entry):
		"""29.03.2008 - dgranda
		Retrieves info which will be stored in DB from GPX file
		args: path to source GPX file
		returns: list with fields and values, list of laps
		"""
		logging.debug('>>')
		gpx = Gpx(self.data_path,gpxOrig)
		distance, time, maxspeed, maxheartrate = gpx.getMaxValues()
		#if time == 0: #invalid record
		#	print "Invalid record"
		#	return (None, None)
		upositive,unegative = gpx.getUnevenness()
		if time > 0:
			speed = distance*3600/time
			time_hhmmss = [time//3600,(time/60)%60,time%60]
		else:
			speed = 0
			time_hhmmss = [0,0,0]
		summaryRecord = {}
		summaryRecord['rcd_gpxfile'] = gpxOrig
		summaryRecord['rcd_sport'] = entry[0]
		summaryRecord['rcd_date'] = gpx.getDate()
		summaryRecord['rcd_calories'] = gpx.getCalories()
		summaryRecord['rcd_comments'] = ''
		summaryRecord['rcd_title'] = ''
		summaryRecord['rcd_time'] = time_hhmmss #ToDo: makes no sense to work with arrays
		summaryRecord['rcd_distance'] = "%0.2f" %distance
		if speed == 0:
			summaryRecord['rcd_pace'] = "0"
		else:
			summaryRecord['rcd_pace'] = "%d.%02d" %((3600/speed)/60,(3600/speed)%60)
		if maxspeed == 0:
			summaryRecord['rcd_maxpace'] = "0"
		else:
			summaryRecord['rcd_maxpace'] = "%d.%02d" %((3600/maxspeed)/60,(3600/maxspeed)%60)
		summaryRecord['rcd_average'] = speed
		summaryRecord['rcd_maxvel'] = maxspeed
		summaryRecord['rcd_beats'] = gpx.getHeartRateAverage()
		summaryRecord['rcd_maxbeats'] = maxheartrate
		summaryRecord['rcd_upositive'] = upositive
		summaryRecord['rcd_unegative'] = unegative
		if entry[1]=="": # coming from new track dialog (file opening)												#TODO This if-else needs checking
			summaryRecord['date_time_utc'], summaryRecord['date_time_local'] = gpx.getStartTimeFromGPX(gpxOrig)		#
		else: # coming from GPS device																				#
			summaryRecord['date_time_utc'] = entry[1]																#
			summaryRecord['date_time_local'] = entry[1]																#
			print "#TODO fix record summaryRecord local and utc time..."											#
		logging.debug('summary: '+str(summaryRecord))
		laps = self.lapsFromGPX(gpx)
		logging.debug('<<')
		return summaryRecord, laps

	def updateRecord(self, list_options, id_record, equipment=None): # ToDo: update only fields that can change if GPX file is present
		logging.debug('>>')
		#Remove activity from pool so data is updated
		self.pytrainer_main.activitypool.remove_activity(id_record)
		gpxfile = self.pytrainer_main.profile.gpxdir+"/%d.gpx"%int(id_record)
		gpxOrig = list_options["rcd_gpxfile"]
		if os.path.isfile(gpxOrig):
			if gpxfile != gpxOrig:
				shutil.copy2(gpxOrig, gpxfile)
		else:
			if (list_options["rcd_gpxfile"]==""):
				logging.debug('Activity not based in GPX file') # ein?
		logging.debug('Updating bbdd')
		cells,values = self._formatRecordNew(list_options)
		self.pytrainer_main.ddbb.update("records",cells,values," id_record=%d" %int(id_record))
		if equipment is not None:
			self._update_record_equipment(id_record, equipment)
		self.pytrainer_main.refreshListView()
		logging.debug('<<')

	def parseFloatRecord(self,string):
		logging.debug('--')
		if string != "":
			try:
				return float(string.replace(",",","))
			except:
				return float(string)
		else:
			return 0

	def getrecordInfo(self,id_record):
		logging.debug('--')
		if id_record is None or id_record == "":
			return []
		return self.pytrainer_main.ddbb.select("records,sports",
					"sports.name,date,distance,time,beats,comments,average,calories,id_record,title,upositive,unegative,maxspeed,maxpace,pace,maxbeats,date_time_utc,date_time_local",
					"id_record=\"%s\" and records.sport=sports.id_sports" %id_record)

	def getrecordList(self,date, id_sport=None):
		logging.debug('--')
		if not id_sport:
			# outer join on sport id to workaround bug where sport reference is null on records from GPX import
			return self.pytrainer_main.ddbb.select("records left outer join sports on records.sport=sports.id_sports",
					"sports.name,date,distance,time,beats,comments,average,calories,id_record,maxspeed,maxbeats,date_time_utc,date_time_local,upositive,unegative",
					"date=\"%s\" " %date)
		else:
			return self.pytrainer_main.ddbb.select("records,sports",
					"sports.name,date,distance,time,beats,comments,average,calories,id_record,maxspeed,maxbeats,date_time_utc,date_time_local,upositive,unegative",
					"date=\"%s\" and sports.id_sports=\"%s\" and records.sport=sports.id_sports" %(date,id_sport))

	def getLaps(self, id_record):
		logging.debug('--')
		laps = self.pytrainer_main.ddbb.select("laps",
					"id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, laptrigger, comments",
					"record=\"%s\"" % id_record)
		if laps is None or laps == []:  #No laps stored - update DB
			logging.debug("No laps in DB for record %d" % id_record)
			#print ("No laps in DB for record %d" % id_record)
			gpx_dest = self.pytrainer_main.profile.gpxdir
			gpxfile = gpx_dest+"/%d.gpx"%id_record
			gpx = Gpx(self.data_path,gpxfile)
			laps = self.lapsFromGPX(gpx)
			if laps is not None:
				for lap in laps:
					lap['record'] = id_record #Add reference to entry in record table
					lap_keys = ", ".join(map(str, lap.keys()))
					lap_values = lap.values()
					self.insertLaps(lap_keys,lap.values())
			#Try to get lap info again #TODO? refactor
			laps = self.pytrainer_main.ddbb.select("laps",
					"id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, laptrigger, comments",
					"record=\"%s\"" % id_record)
		return laps

	def insertLaps(self, cells, values):
		logging.debug('--')
		logging.debug("Adding lap information: " + ", ".join(map(str, values)))
		self.pytrainer_main.ddbb.insert("laps",cells,values)
		
	def _insert_record_equipment(self, record_id, equipment_id):
		self.pytrainer_main.ddbb.insert("record_equipment", "record_id, equipment_id", [record_id, equipment_id])
		
	def _update_record_equipment(self, record_id, equipment_ids):
		self.pytrainer_main.ddbb.delete("record_equipment", "record_id={0}".format(record_id))
		for id in equipment_ids:
			self._insert_record_equipment(record_id, id)
			
	def get_record_equipment(self, record_id):
		record_equipment = []
		results = self.pytrainer_main.ddbb.select("record_equipment", "equipment_id", "record_id={0}".format(record_id))
		for row in results:
			id = row[0]
			equipment_item = self._equipment_service.get_equipment_item(id)
			record_equipment.append(equipment_item)
		return record_equipment

	def getrecordPeriod(self,date_ini, date_end, sport=None):
		#TODO This is essentially the same as getrecordPeriodSport (except date ranges) - need to look at merging the two
		tables = "records,sports"
		if not sport:
			condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports" %(date_ini,date_end)
		else:
			condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" %(date_ini,date_end, sport)

		return self.pytrainer_main.ddbb.select(tables,"date,distance,time,beats,comments,average,calories,maxspeed,maxbeats, sports.name,upositive,unegative", condition)

	def getrecordPeriodSport(self,date_ini, date_end,sport):
		if not sport:
			tables = "records"
			condition = "date>\"%s\" and date<\"%s\"" %(date_ini,date_end)
		else :
			tables = "records,sports"
			condition = "date>\"%s\" and date<\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" %(date_ini,date_end,sport)

		return self.pytrainer_main.ddbb.select(tables,
					"date,distance,time,beats,comments,average,calories,maxspeed,maxbeats,upositive,unegative",
					condition)
		
	def _get_sport(self, sport_name):
		return self._sport_service.get_sport_by_name(sport_name)

	def getSportMet(self,sport_name):
		"""Deprecated: use sport.met"""
		logging.debug('--')
		return self._get_sport(sport_name).met

	def getSportWeight(self,sport_name):
		"""Deprecated: use sport.weight"""
		logging.debug('--')
		return self._get_sport(sport_name).weight

	def getSportId(self, sport_name, add=None):
		"""Deprecated: use sport_service.get_sport_by_name()
		
		Get the id of a sport by name, optionally adding a new sport if
		none exists with the given name.
		arguments:
			sport_name: sport's name to get id for
			add: whether the sport should be added if not found
		returns: id for sport with given name or None"""
		if sport_name is None:
			return None
		sport = self._get_sport(sport_name)
		if sport is None:
			logging.debug("No sport with name: '%s'", str(sport_name))
			if add is not None:
				logging.debug("Adding sport '%s'", str(sport_name))
				new_sport = Sport()
				new_sport.name = unicode(sport_name)
				sport = self._sport_service.store_sport(new_sport)
		return None if sport is None else sport.id

	def getAllrecord(self):
		logging.debug('--')
		return self.pytrainer_main.ddbb.select("records", "date,distance,time,beats,comments,average,calories")

	def getAllRecordList(self):
		logging.debug('--')
		return self.pytrainer_main.ddbb.select("records,sports",
			"date,distance,average,title,sports.name,id_record,time,beats,calories",
			"sports.id_sports = records.sport order by date desc")

	def getRecordListByCondition(self,condition):
		logging.debug('--')
		if condition is None:
			return self.getAllRecordList()
		else:
			logging.debug("condition: %s" % condition)
			return self.pytrainer_main.ddbb.select("records,sports",
				"date,distance,average,title,sports.name,id_record,time,beats,calories",
				"sports.id_sports = records.sport and %s order by date desc" %condition)

	def getRecordDayList(self,date, id_sport=None):
		logging.debug('>>')
		year,month,day = date.split("-")
		logging.debug('Retrieving data for '+year+'.'+month+'.'+day)
		# Why is looking for all days of the same month?
		if not id_sport:
			records = self.pytrainer_main.ddbb.select("records","date","date LIKE '"+year+"-"+month+"-%'")
		else:
			records = self.pytrainer_main.ddbb.select("records","date","date LIKE \"%s-%s-%%\" and sport=\"%s\"" %(year,month,id_sport))
		logging.debug('Found '+str(len(records))+' entries')
		day_list = []
		for i in records:
			record = str(i[0]).split("-")
			logging.debug('date:'+str(i[0]))
			day_list.append(record[2])
		logging.debug('<<')
		return day_list

	def actualize_fromgpx(self,gpxfile): #TODO remove? - should never have multiple tracks per GPX file
		logging.debug('>>')
		logging.debug('loading file: '+gpxfile)
		gpx = Gpx(self.data_path,gpxfile)
		tracks = gpx.getTrackRoutes()

		if len(tracks) == 1:
			logging.debug('Just 1 track')
			self._actualize_fromgpx(gpx)
		elif len(tracks) > 1:
			logging.debug('Found '+str(len(tracks))+' tracks')
			self._select_trkfromgpx(gpxfile,tracks)
		else:
			msg = _("pytrainer can't import data from your gpx file")
			from gui.warning import Warning
			warning = Warning(self.data_path)
			warning.set_text(msg)
			warning.run()
		logging.debug('<<')

	def _actualize_fromgpx(self, gpx):
		logging.debug('>>')
		distance, time, maxspeed, maxheartrate = gpx.getMaxValues()
		upositive,unegative = gpx.getUnevenness()
		heartrate = gpx.getHeartRateAverage()
		date = gpx.getDate()
		calories = gpx.getCalories()
		start_time = gpx.getStart_time()

		self.recordwindow.rcd_date.set_text(date)
		self.recordwindow.rcd_starttime.set_text(start_time)
		self.recordwindow.rcd_upositive.set_text(str(upositive))
		self.recordwindow.rcd_unegative.set_text(str(unegative))
		self.recordwindow.rcd_beats.set_text(str(heartrate))
		self.recordwindow.rcd_calories.set_text(str(calories))
		self.recordwindow.set_distance(distance)
		self.recordwindow.set_maxspeed(maxspeed)
		self.recordwindow.set_maxhr(maxheartrate)
		self.recordwindow.set_recordtime(time/60.0/60.0)
		self.recordwindow.on_calcavs_clicked(None)
		self.recordwindow.on_calccalories_clicked(None)
		self.recordwindow.rcd_maxpace.set_text("%d.%02d" %((3600/maxspeed)/60,(3600/maxspeed)%60))
		logging.debug('<<')

	def __actualize_fromgpx(self, gpxfile, name=None):
		logging.debug('>>')
		gpx = Gpx(self.data_path,gpxfile,name)
		self._actualize_fromgpx(gpx)
		logging.debug('<<')

	def _select_trkfromgpx(self,gpxfile,tracks):  #TODO remove? - should never have multiple tracks per GPX file
		logging.debug('>>')
		logging.debug('Track dialog '+ self.data_path +'|'+ gpxfile)
		selectrckdialog = DialogSelectTrack(self.data_path, tracks,self.__actualize_fromgpx, gpxfile)
		logging.debug('Launching window...')
		selectrckdialog.run()
		logging.debug('<<')

	def importFromGPX(self, gpxFile, sport):
		"""
		Add a record from a valid pytrainer type GPX file
		"""
		logging.debug('>>')
		entry_id = None
		if not os.path.isfile(gpxFile):
			logging.error("Invalid file: " +gpxFile)
		else:
			logging.info('Retrieving data from '+gpxFile)
			if not sport:
				sport = "import"
			entry = [sport,""]
			entry_id = self.insertNewRecord(gpxFile, entry)
			if entry_id is None:
				logging.error("Entry not created for file %s" % gpxFile)
			else:
				logging.info("Entry %d has been added" % entry_id)
		logging.debug('<<')
		return entry_id