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, self.format_date(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 format_date(self, date): return date.strftime("%Y-%m-%d") 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" ' % self.format_date(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' % (self.format_date(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_range, sport=None): # TODO This is essentially the same as getrecordPeriodSport (except date ranges) - need to look at merging the two date_ini = self.format_date(date_range.start_date) date_end = self.format_date(date_range.end_date) 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(">>") logging.debug("Retrieving data for " + str(date)) # Why is looking for all days of the same month? if not id_sport: records = self.pytrainer_main.ddbb.select( "records", "date", "date LIKE '" + str(date.year) + "-" + date.strftime("%m") + "-%'" ) else: records = self.pytrainer_main.ddbb.select( "records", "date", 'date LIKE "%d-%0.2d-%%" and sport="%s"' % (date.year, date.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
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
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, self.format_date(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) sports = self._sport_service.get_all_sports() self.recordwindow = WindowRecord(self._equipment_service, self.data_path, sports, self, None, windowTitle=_("Edit Entry"), equipment=activity.equipment) self.recordwindow.setValuesFromActivity(activity) logging.debug('launching window') self.recordwindow.run() self.pytrainer_main.refreshMainSportList() 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, record): """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('>>') if (list_options["rcd_beats"] == ""): list_options["rcd_beats"] = 0 #retrieving sport (adding sport if it doesn't exist yet) sport = self._get_sport(list_options["rcd_sport"]) if not sport: sport = Sport(name=list_options["rcd_sport"]) record.title = unicode(list_options["rcd_title"]) record.beats = self.parseFloatRecord(list_options["rcd_beats"]) record.pace = self.pace_to_float(list_options["rcd_pace"]) record.maxbeats = self.parseFloatRecord(list_options["rcd_maxbeats"]) record.distance = self.parseFloatRecord(list_options["rcd_distance"]) record.average = self.parseFloatRecord(list_options["rcd_average"]) record.calories = self.parseFloatRecord(list_options["rcd_calories"]) record.comments = unicode(list_options["rcd_comments"]) record.date = list_options["date_time_local"].date() record.unegative = self.parseFloatRecord(list_options["rcd_unegative"]) record.upositive = self.parseFloatRecord(list_options["rcd_upositive"]) record.maxspeed = self.parseFloatRecord(list_options["rcd_maxvel"]) record.maxpace = self.pace_to_float(list_options["rcd_maxpace"]) record.date_time_utc = list_options["date_time_utc"] record.duration = time2second(list_options["rcd_time"]) record.date_time_local = list_options["date_time_local"].strftime( "%Y-%m-%d %H:%M:%S%z") record.sport = sport return record 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)) record = self._formatRecordNew(list_options, Activity()) self.pytrainer_main.ddbb.session.add(record) id_record = record.id gpxOrig = list_options["rcd_gpxfile"] # Load laps from gpx if not provided by the caller if laps is None and os.path.isfile(gpxOrig): gpx = Gpx(self.data_path, gpxOrig) laps = self.lapsFromGPX(gpx) #Create entry(s) for activity in laps table if laps is not None: for lap in laps: new_lap = Lap(**lap) record.Laps.append(new_lap) if equipment: record.equipment = self.pytrainer_main.ddbb.session.query( Equipment).filter(Equipment.id.in_(equipment)).all() self.pytrainer_main.ddbb.session.commit() 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) logging.debug('<<') return record.id 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 hrFromLaps(self, laps): logging.debug('>>') lap_avg_hr = 0 lap_max_hr = 0 total_duration = 0 ponderate_hr = 0 for lap in laps: if (lap['max_hr'] is None): lap['max_hr'] = 0 if (lap['avg_hr'] is None): lap['avg_hr'] = 0 if int(lap['max_hr']) > lap_max_hr: lap_max_hr = int(lap['max_hr']) total_duration = total_duration + float(lap['elapsed_time']) ponderate_hr = ponderate_hr + float(lap['elapsed_time']) * int( lap['avg_hr']) logging.debug( "Lap number: %s | Duration: %s | Average hr: %s | Maximum hr: %s" % (lap['lap_number'], lap['elapsed_time'], lap['avg_hr'], lap['max_hr'])) lap_avg_hr = int(round(ponderate_hr / total_duration)) # ceil?, floor?, round? logging.debug('<<') return lap_avg_hr, lap_max_hr 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) # Heartrate data can't be retrieved if no trackpoints present, calculating from lap info lap_avg_hr, lap_max_hr = self.hrFromLaps(laps) logging.debug("HR data from laps. Average: %s | Maximum hr: %s" % (lap_avg_hr, lap_max_hr)) if int(summaryRecord['rcd_beats']) > 0: logging.debug("Average heartbeat - Summary: %s | Laps: %s" % (summaryRecord['rcd_beats'], lap_avg_hr)) else: logging.debug( "No average heartbeat found, setting value (%s) from laps", lap_avg_hr) summaryRecord['rcd_beats'] = lap_avg_hr if int(summaryRecord['rcd_maxbeats']) > 0: logging.debug("Max heartbeat - Summary: %s | Laps: %s" % (summaryRecord['rcd_maxbeats'], lap_max_hr)) else: logging.debug( "No max heartbeat found, setting value (%s) from laps", lap_max_hr) summaryRecord['rcd_maxbeats'] = lap_max_hr 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('>>') logging.debug('list_options: ' + str(list_options)) # No need to remove from the pool, sqlalchemy keeps things in sync record = self.pytrainer_main.activitypool.get_activity(id_record) gpxfile = self.pytrainer_main.profile.gpxdir + "/%d.gpx" % int( record.id) 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') self._formatRecordNew(list_options, record) if equipment: record.equipment = self.pytrainer_main.ddbb.session.query( Equipment).filter(Equipment.id.in_(equipment)).all() self.pytrainer_main.ddbb.session.commit() 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 format_date(self, date): return date.strftime("%Y-%m-%d") 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 getLastRecordDateString(self, sport_id=None): """ Retrieve date (string format) of last record stored in DB. It may select per sport """ logging.debug("--") #select date from records order by date desc limit 1; query_cond = None if sport_id is not None: query_cond = "sport = %s" % sport_id result = self.pytrainer_main.ddbb.select("records", "date", query_cond, "order by date desc limit 1") # It returns a list of tuples, so we take first element of list (tuple) and then select first element return result[0][0] def getAllrecord(self): """ Retrieve all record data (no lap nor equipment) stored in database. Initially intended for csv export arguments: returns: list of data sorted by date desc""" logging.debug('--') return self.pytrainer_main.ddbb.select( "records,sports", "date_time_local,title,sports.name,distance,duration,average,maxspeed,pace,maxpace,beats,maxbeats,calories,upositive,unegative,comments", "sports.id_sports = records.sport", "order by date_time_local asc") def getAllRecordList(self): logging.debug('--') return self.pytrainer_main.ddbb.select( "records,sports", "date,distance,average,title,sports.name,id_record,duration,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,duration,beats,calories", "sports.id_sports = records.sport and %s order by date desc" % condition) def getRecordDayList(self, date, sport=None): logging.debug('>>') logging.debug('Retrieving data for ' + str(date)) date_range = DateRange.for_month_containing(date) for activity in self.pytrainer_main.activitypool.get_activities_period( date_range, sport=sport): yield activity.date.day 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