Exemplo n.º 1
0
class EquipmentUiTest(TestCase):

    def setUp(self):
        initialize_gettext('locale/')
        self.ddbb = DDBB()
        self.ddbb.connect()
        self.ddbb.create_tables()
        self.equipment_service = EquipmentService(self.ddbb)
        self.equipment_ui = EquipmentUi('glade/', self.equipment_service)

    def tearDown(self):
        self.ddbb.disconnect()
        self.ddbb.drop_tables()

    def test_equipment_add(self):
        self.equipment_ui._builder.get_object("entryEquipmentAddDescription").set_text('Test')
        self.equipment_ui._builder.get_object("entryEquipmentAddLifeExpectancy").set_text('500')
        self.equipment_ui._builder.get_object("entryEquipmentAddPriorUsage").set_text('100')
        self.equipment_ui._builder.get_object("checkbuttonEquipmentAddActive").set_active(is_active=True)
        self.equipment_ui._confirm_add_equipment_clicked(None)
        equipment = self.equipment_service.get_equipment_item(1)
        self.assertEqual(equipment.description, 'Test')
        self.assertEqual(equipment.life_expectancy, 500)
        self.assertEqual(equipment.prior_usage, 100)
        self.assertTrue(equipment.active)

    def test_equipment_add_unicode(self):
        self.equipment_ui._builder.get_object("entryEquipmentAddDescription").set_text(u'Test äö')
        self.equipment_ui._builder.get_object("entryEquipmentAddLifeExpectancy").set_text('500')
        self.equipment_ui._builder.get_object("entryEquipmentAddPriorUsage").set_text('100')
        self.equipment_ui._builder.get_object("checkbuttonEquipmentAddActive").set_active(is_active=True)
        self.equipment_ui._confirm_add_equipment_clicked(None)
        equipment = self.equipment_service.get_equipment_item(1)
        self.assertEqual(equipment.description, u'Test äö')
Exemplo n.º 2
0
class EquipmentUiTest(TestCase):
    def setUp(self):
        initialize_gettext('locale/')
        self.ddbb = DDBB()
        self.ddbb.connect()
        self.ddbb.create_tables()
        self.equipment_service = EquipmentService(self.ddbb)
        self.equipment_ui = EquipmentUi('glade/', self.equipment_service)

    def tearDown(self):
        self.ddbb.disconnect()
        self.ddbb.drop_tables()

    def test_equipment_add(self):
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddDescription").set_text('Test')
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddLifeExpectancy").set_text('500')
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddPriorUsage").set_text('100')
        self.equipment_ui._builder.get_object(
            "checkbuttonEquipmentAddActive").set_active(is_active=True)
        self.equipment_ui._confirm_add_equipment_clicked(None)
        equipment = self.equipment_service.get_equipment_item(1)
        self.assertEqual(equipment.description, 'Test')
        self.assertEqual(equipment.life_expectancy, 500)
        self.assertEqual(equipment.prior_usage, 100)
        self.assertTrue(equipment.active)

    def test_equipment_add_unicode(self):
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddDescription").set_text(u'Test äö')
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddLifeExpectancy").set_text('500')
        self.equipment_ui._builder.get_object(
            "entryEquipmentAddPriorUsage").set_text('100')
        self.equipment_ui._builder.get_object(
            "checkbuttonEquipmentAddActive").set_active(is_active=True)
        self.equipment_ui._confirm_add_equipment_clicked(None)
        equipment = self.equipment_service.get_equipment_item(1)
        self.assertEqual(equipment.description, u'Test äö')
Exemplo n.º 3
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,
            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
Exemplo n.º 4
0
class EquipmentServiceTest(unittest.TestCase):
    
    def setUp(self):
        self.mock_ddbb = DDBB()
        self.mock_ddbb.connect()
        self.mock_ddbb.create_tables()
        self.equipment_service = EquipmentService(self.mock_ddbb)
        self.equipment_table = DeclarativeBase.metadata.tables['equipment']
        
    def tearDown(self):
        self.mock_ddbb.disconnect()
        self.mock_ddbb.drop_tables()
    
    def test_get_equipment_item(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"Test Description",
                                           "prior_usage": 200, "active": True})
        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.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"Test Description",
                                           "prior_usage": 200, "active": True})
        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):
        item = self.equipment_service.get_equipment_item(1)
        self.assertEquals(None, item)
        
    def test_get_all_equipment(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes 1.",
                                           "description": u"Test item 1",
                                           "prior_usage": 200, "active": True})
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 600,
                                                "notes": u"Test notes 2.",
                                           "description": u"Test item 2",
                                           "prior_usage": 300, "active": False})
        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):
        items = self.equipment_service.get_all_equipment()
        self.assertEquals([], items)
        
    def test_get_active_equipment(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes 1.",
                                           "description": u"Test item 1",
                                           "prior_usage": 200, "active": True})
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 600,
                                                "notes": u"Test notes 2.",
                                           "description": u"Test item 2",
                                           "prior_usage": 300, "active": True})
        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):
        items = self.equipment_service.get_active_equipment()
        self.assertEquals([], items)
        
    def test_store_equipment(self):
        equipment = Equipment()
        equipment.description = u"test description"
        stored_equipment = self.equipment_service.store_equipment(equipment)
        self.assertEquals(1, stored_equipment.id)
        
    def test_store_equipment_duplicate_description(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"test item",
                                           "prior_usage": 200, "active": True})
        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):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"old description",
                                           "prior_usage": 200, "active": True})
        equipment = self.equipment_service.get_equipment_item(1)
        equipment.description = u"new description"
        self.equipment_service.store_equipment(equipment)
        equipment = self.equipment_service.get_equipment_item(1)
        self.assertEquals("new description", equipment.description)
        
    def test_update_equipment_duplicate_description(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"test item",
                                           "prior_usage": 200, "active": True})
        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.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"test item",
                                           "prior_usage": 0, "active": True})
        self.mock_ddbb.session.execute("insert into records (distance,sport) values (250,1)")
        self.mock_ddbb.session.execute("insert into record_equipment (record_id,equipment_id) values (1,1)")
        equipment = self.equipment_service.get_equipment_item(1)
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(250, usage)
        
    def test_get_equipment_usage_none(self):
        self.mock_ddbb.session.execute(self.equipment_table.insert(),
                                           {"life_expectancy": 500,
                                                "notes": u"Test notes.",
                                           "description": u"test item",
                                           "prior_usage": 0, "active": True})
        equipment = self.equipment_service.get_equipment_item(1)
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(0, usage)

    def test_get_equipment_prior_usage(self):
        equipment = Equipment()
        equipment.id = 1
        equipment.prior_usage = 250
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(250, usage)

    def test_get_equipment_prior_usage(self):
        equipment = Equipment()
        equipment.id = 1
        equipment.prior_usage = 250
        usage = self.equipment_service.get_equipment_usage(equipment)
        self.assertEquals(250, usage)
Exemplo n.º 5
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,
                                         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_number, lap in enumerate(gpxLaps):
            tmp_lap = {}
            tmp_lap['record'] = ""
            tmp_lap['lap_number'] = lap_number
            tmp_lap['elapsed_time'] = lap[0]
            tmp_lap['end_lat'] = lap[1]
            tmp_lap['end_lon'] = lap[2]
            tmp_lap['calories'] = lap[3]
            tmp_lap['distance'] = lap[4]
            tmp_lap['start_lat'] = lap[5]
            tmp_lap['start_lon'] = lap[6]
            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('>>')
        total_duration = 0
        ponderate_hr = 0

        max_hr = max([int(lap.get('max_hr', 0) or 0) for lap in laps])

        avg_hr_time_pairs = [(lap.get('avg_hr', None),
                              float(lap.get('elapsed_time', 0) or 0))
                             for lap in laps]

        for lap_avg_hr, elapsed_time in avg_hr_time_pairs:
            if lap_avg_hr is not None:
                total_duration += elapsed_time
                ponderate_hr += elapsed_time * int(lap_avg_hr)

        avg_hr = int(round(ponderate_hr /
                           total_duration))  # ceil?, floor?, round?

        logging.debug('<<')
        return avg_hr, 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('>>')
        #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):
        """
		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,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
Exemplo 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)
Exemplo n.º 7
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)