class DateRange(object):

    """A date range consisting of a start date and an end date."""

    def __init__(self, start_date, end_date):
        if not isinstance(start_date, datetime.date):
            raise TypeError("Start date must be datetime.date, not {0}.".format(type(start_date).__name__))
        if not isinstance(end_date, datetime.date):
            raise TypeError("End date must be datetime.date, not {0}.".format(type(start_date).__name__))
        if start_date > end_date:
            raise ValueError("End date cannot be before start date.")
        self._start_date = start_date
        self._end_date = end_date

    @property
    def start_date(self):
        return self._start_date

    @property
    def end_date(self):
        return self._end_date

    def __str__(self):
        fmt = "%Y%m%d"
        return self._start_date.strftime(fmt) + "-" + self._end_date.strftime(fmt)

    @classmethod
    def for_week_containing(clazz, date):
        """Get the date range for the week containing the given date.

        The date range will start on the first day of the week and end on the
        last day of the week. The week start and end days are locale dependent.

        Args:
            date (datetime.date): a date within the week to get the date range for.
        Returns:
            (DateRange): the date range for a week.
        """
        day_of_week = (int(date.strftime("%w")) - get_platform().get_first_day_of_week()) % 7
        date_start = date + datetime.timedelta(days = 0 - day_of_week)
        date_end = date + datetime.timedelta(days = 6 - day_of_week)
        return DateRange(date_start, date_end)

    @classmethod
    def for_month_containing(clazz, date):
        """Get the date range for the month containing the given date.

        The date range will start on the first day of the month and end on the
        last day of the month.

        Args:
            date (datetime.date): a date within the month to get the date range for.
        Returns:
            (DateRange): the date range for a month.
        """
        date_start = datetime.date(date.year, date.month, 1)
        next_month = date.month + 1
        next_month_year = date.year
        if (next_month == 13):
            next_month = 1
            next_month_year += 1
        date_end = datetime.date(next_month_year, next_month, 1) - datetime.timedelta(days=1)
        return DateRange(date_start, date_end)

    @classmethod
    def for_year_containing(clazz, date):
        """Get the date range for the year containing the given date.

        The date range will start on the first day of the year and end on the
        last day of the year.

        Args:
            date (datetime.date): a date within the year to get the date range for.
        Returns:
            (DateRange): the date range for a year.
        """
        date_start = datetime.date(date.year, 1, 1)
        date_end = datetime.date(date.year, 12, 31)
        return DateRange(date_start, date_end) def initialize_data(ddbb, conf_dir):
    """Initializes the installation's data."""
    db_url = ddbb.get_connection_url()
    migratable_db = MigratableDb(os.path.dirname(__file__), db_url)
    InstalledData(migratable_db, ddbb, LegacyVersionProvider(conf_dir), UpgradeContext(conf_dir, db_url)).update_to_current() class Environment(Singleton):
    
    """Describes the location of the program's configuration directories and files."""
    
    def __init__(self, conf_dir=None, data_path=None):
        """Initialise an environment.
        
        Arguments:
        conf_dir -- the directory where program configuration should be stored. If None, then the default for the platform is used.
        
        """
        if not hasattr(self, 'conf_dir'):
            if conf_dir:
                self.conf_dir = conf_dir
            else:
                self.conf_dir = get_platform().get_default_conf_dir()

        if not hasattr(self, 'data_path'):
            if data_path:
                self.data_path = data_path
            else:
                self.data_path = get_platform().get_default_data_path()

    @property
    def conf_file(self):
        return os.path.join(self.conf_dir, "conf.xml")

    @property
    def log_file(self):
        return os.path.join(self.conf_dir, "log.out")

    @property
    def temp_dir(self):
        return os.path.join(self.conf_dir, "tmp")

    @property
    def gpx_dir(self):
        return os.path.join(self.conf_dir, "gpx")

    @property
    def extension_dir(self):
        return os.path.join(self.conf_dir, "extensions")

    @property
    def plugin_dir(self):
        return os.path.join(self.conf_dir, "plugins")

    @property
    def glade_dir(self):
        return os.path.join(self.data_path, "glade")

    def clear_temp_dir(self):
        """Remove all files from the tmp directory."""
        if not os.path.isdir(self.temp_dir):
            return
        else:
            files = os.listdir(self.temp_dir)
            for name in files:
                fullname = (os.path.join(self.temp_dir, name))
                if os.path.isfile(fullname):
                    os.remove(os.path.join(self.temp_dir, name))
            
    def create_directories(self):
        """Create all required directories if they do not already exist."""
        self._create_dir(self.conf_dir)
        self._create_dir(self.temp_dir)
        self._create_dir(self.extension_dir)
        self._create_dir(self.plugin_dir)
        self._create_dir(self.gpx_dir)
            
    def _create_dir(self, dir_name):
        if not os.path.isdir(dir_name):
            os.mkdir(dir_name) 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?)'''
        if not value:
            return "0:00"
        #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 = 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 = list_options["rcd_comments"]
        record.date = datetime.datetime.strptime(list_options["rcd_date"],
                                                 "%Y-%m-%d").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 = str(list_options["date_time_local"])
        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: %s', list_options)
        record = self._formatRecordNew(list_options, Activity())
        self.pytrainer_main.ddbb.session.add(record)
        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" % record.id
            #Leave original file in place...
            #shutil.move(gpxOrig, gpxNew)
            #logging.debug('Moving '+gpxOrig+' to '+gpxNew)
            shutil.copy(gpxOrig, gpxNew)
            logging.debug('Copying %s to %s', gpxOrig, 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] or None
            tmp_lap['start_lat'] = lap[5] or None
            tmp_lap['start_lon'] = lap[6] or None
            tmp_lap['end_lat'] = lap[1] or None
            tmp_lap['end_lon'] = lap[2] or None
            tmp_lap['calories'] = lap[3] or None
            tmp_lap['intensity'] = lap[7]
            tmp_lap['avg_hr'] = lap[8] or None
            tmp_lap['max_hr'] = lap[9] or None
            tmp_lap['max_speed'] = lap[10] or None
            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]                                                                                                                             #
            #TODO fix record summaryRecord local and utc time...
        logging.debug('summary: %s', 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: %s', 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"""
        warnings.warn('Deprecated call to getSportMet', DeprecationWarning, stacklevel=2)
        return self._get_sport(sport_name).met

    def getSportWeight(self,sport_name):
        """Deprecated: use sport.weight"""
        warnings.warn('Deprecated call to getSportWeight', DeprecationWarning, stacklevel=2)
        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("--")
        try:
            if sport_id is not None:
                return str(self.pytrainer_main.ddbb.session.query(Activity).
                           filter(Activity.sport_id == sport_id).order_by(Activity.date.desc()).
                           limit(1).one().date)
            else:
                return str(self.pytrainer_main.ddbb.session.query(Activity).order_by(
                    Activity.date.desc()).limit(1).one().date)
        except NoResultFound:
            return None

    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 getRecordListByCondition(self, condition):
        logging.debug('>>')
        if condition is None:
            return self.pytrainer_main.ddbb.session.query(Activity).order_by(Activity.date.desc())
        else:
            return self.pytrainer_main.ddbb.session.query(Activity).filter(condition).order_by(Activity.date.desc())

    def getRecordDayList(self, date, sport=None):
        logging.debug('>>')
        logging.debug('Retrieving data for %s', 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: %s', 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 %s tracks', len(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 %s|%s', 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: %s", gpxFile)
        else:
            logging.info('Retrieving data from %s', 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 WaypointService(object):
    def __init__(self, data_path = None, parent = None):
        logging.debug(">>")
        self.parent = parent
        self.pytrainer_main = parent
        self.data_path = data_path
        logging.debug("<<")

    def removeWaypoint(self,id_waypoint):
        logging.debug(">>")
        logging.debug("Deleting id_waypoint=%s" %id_waypoint)
        waypoint = self.pytrainer_main.ddbb.session.query(Waypoint).filter(Waypoint.id == id_waypoint).one()
        self.pytrainer_main.ddbb.session.delete(waypoint)
        self.pytrainer_main.ddbb.session.commit()
        logging.debug("<<")

    def updateWaypoint(self,id_waypoint,lat,lon,name,desc,sym):
        logging.debug(">>")
        logging.debug("Updating waypoint id: %d with lat %s,lon %s,comment %s,name %s,sym %s" %(id_waypoint,lat,lon,desc,name,sym) )
        waypoint = self.pytrainer_main.ddbb.session.query(Waypoint).filter(Waypoint.id == id_waypoint).one()
        waypoint.lat = lat
        waypoint.lon = lon
        waypoint.name = name
        waypoint.comment = desc
        waypoint.sym = sym
        self.pytrainer_main.ddbb.session.commit()
        logging.debug("<<")

    def addWaypoint(self,lon=None,lat=None,name=None,comment=None,sym=None):
        logging.debug(">>")
        waypoint = Waypoint(lon=lon, lat=lat, name=name, comment=comment, sym=sym)
        logging.debug("Adding waypoint with details lat %s,lon %s,comment %s,name %s,sym %s" % (lat,lon,comment,name,sym)  )
        self.pytrainer_main.ddbb.session.add(waypoint)
        self.pytrainer_main.ddbb.session.commit()
        logging.debug("<<")
        return waypoint.id

    def getwaypointInfo(self,id_waypoint):
        logging.debug(">>")
        retorno = self.pytrainer_main.ddbb.select("waypoints",
                                "lat,lon,ele,comment,time,name,sym",
                                "id_waypoint=%s" %id_waypoint)
        logging.debug("<<")
        return retorno

    def getAllWaypoints(self):
        logging.debug(">>")
        retorno = self.pytrainer_main.ddbb.select("waypoints","id_waypoint,lat,lon,ele,comment,time,name,sym","1=1 order by name")
        logging.debug("<<")
        return retorno

    def actualize_fromgpx(self,gpxfile):
        logging.debug(">>")
        from .lib.gpx import Gpx
        gpx = Gpx(self.data_path,gpxfile)
        tracks = gpx.getTrackRoutes()

        if len(tracks) > 1:
            time = unixtime2date(tracks[0][1])
            self.recordwindow.rcd_date.set_text(time)
            self._actualize_fromgpx(gpx)
        else:
            msg = _("The gpx file seems to be a several days records. Perhaps you will need to edit your gpx file")
            from .gui.warning import Warning
            warning = Warning(self.data_path,self._actualize_fromgpx,[gpx])
            warning.set_text(msg)
            warning.run()
        logging.debug("<<")

    def _actualize_fromgpx(self, gpx):
        logging.debug(">>")
        distance, time = gpx.getMaxValues()
        upositive,unegative = gpx.getUnevenness()
        self.recordwindow.rcd_upositive.set_text(str(upositive))
        self.recordwindow.rcd_unegative.set_text(str(unegative))
        self.recordwindow.set_distance(distance)
        self.recordwindow.set_recordtime(time/60.0/60.0)
        self.recordwindow.on_calcavs_clicked(None)
        logging.debug("<<") class Extension:
	def __init__(self, data_path = None, parent = None):
		self.data_path=data_path
		self.parent = parent
		self.pytrainer_main = parent
	
	def getActiveExtensions(self):
		retorno = []
		for extension in self.getExtensionList():
			if self.getExtensionInfo(extension[0])[2] == "1":
				retorno.append(extension[0])
		return retorno	
	
	def manageExtensions(self):
		ExtensionList = self.getExtensionList()
		windowextension = WindowExtensions(self.data_path, self)
		windowextension.setList(ExtensionList)
		windowextension.run()

	def getExtensionList(self):
		extensiondir = self.data_path+"/extensions"
		extensionList = []
		for extension in os.listdir(extensiondir):
			extensionxmlfile = extensiondir+"/"+extension+"/conf.xml"
			if os.path.isfile(extensionxmlfile):
				extensioninfo = XMLParser(extensionxmlfile)
				name = extensioninfo.getValue("pytrainer-extension","name")
				description = extensioninfo.getValue("pytrainer-extension","description")
				extensionList.append((extensiondir+"/"+extension,name,description))

		return extensionList
	
	def getExtensionInfo(self,pathExtension):
		info = XMLParser(pathExtension+"/conf.xml")
		name = info.getValue("pytrainer-extension","name")
		description = info.getValue("pytrainer-extension","description")
		code = info.getValue("pytrainer-extension","extensioncode")
		extensiondir = self.pytrainer_main.profile.extensiondir
		helpfile = pathExtension+"/"+info.getValue("pytrainer-extension","helpfile")
		type = info.getValue("pytrainer-extension","type")
	
		if not os.path.isfile(extensiondir+"/"+code+"/conf.xml"):
			status = 0
		else:
			info = XMLParser(extensiondir+"/"+code+"/conf.xml")
			status = info.getValue("pytrainer-extension","status")
		#print name,description,status,helpfile,type
		return name,description,status,helpfile,type

	def getExtensionConfParams(self,pathExtension):
		info = XMLParser(pathExtension+"/conf.xml")
		code = info.getValue("pytrainer-extension","extensioncode")
		extensiondir = self.pytrainer_main.profile.extensiondir
		params = {}
		if not os.path.isfile(extensiondir+"/"+code+"/conf.xml"):
			prefs = info.getAllValues("conf-values")
			prefs.append(("status","0"))
			for pref in prefs:
				params[pref[0]] = info.getValue("pytrainer-extension",pref[0])
		else:
			prefs = info.getAllValues("conf-values")
			prefs.append(("status","0"))
			info = XMLParser(extensiondir+"/"+code+"/conf.xml")
			for pref in prefs:
				params[pref[0]] = info.getValue("pytrainer-extension",pref[0])
				#params.append((pref[0],info.getValue("pytrainer-extension",pref[0])))
		return params

	def setExtensionConfParams(self,pathExtension,savedOptions):
		info = XMLParser(pathExtension+"/conf.xml")
		code = info.getValue("pytrainer-extension","extensioncode")
		extensiondir = self.pytrainer_main.profile.extensiondir+"/"+code
		if not os.path.isdir(extensiondir):
			os.mkdir(extensiondir)
		if not os.path.isfile(extensiondir+"/conf.xml"):
			savedOptions.append(("status","0"))
		info = XMLParser(extensiondir+"/conf.xml")
		info.createXMLFile("pytrainer-extension",savedOptions)

	def loadExtension(self,pathExtension):
		info = XMLParser(pathExtension+"/conf.xml")
		txtbutton = info.getValue("pytrainer-extension","extensionbutton")
		name = info.getValue("pytrainer-extension","name")
		type = info.getValue("pytrainer-extension","type")
		#print "Loading Extension %s" %name
		return txtbutton,pathExtension,type
	

	def getCodeConfValue(self,code,value):
		extensiondir = self.pytrainer_main.profile.extensiondir
		info = XMLParser(extensiondir+"/"+code+"/conf.xml")
		return info.getValue("pytrainer-extension",value)
		
	def importClass(self, pathExtension):
		logging.debug('>>')
		info = XMLParser(pathExtension+"/conf.xml")
	  	#import extension
		extension_dir = os.path.realpath(pathExtension) 
		extension_filename = info.getValue("pytrainer-extension","executable")
		extension_classname = info.getValue("pytrainer-extension","extensioncode")
		extension_type = info.getValue("pytrainer-extension","type")
		options = self.getExtensionConfParams(pathExtension)
		logging.debug("Extension Filename: %s", extension_filename )
		logging.debug("Extension Classname: %s", extension_classname)
		logging.debug("Extension Type: %s", extension_type)
		logging.debug("Extension options: %s", options)
		sys.path.insert(0, extension_dir)
		module = __import__(extension_filename)
		extensionMain = getattr(module, extension_classname)
		logging.debug('<<')
		return extensionMain(parent=self, pytrainer_main=self.parent, conf_dir=self.pytrainer_main.profile.confdir, options=options) class Importdata:
	def __init__(self, sport_service, data_path = None, parent = None, config = None):
		self._sport_service = sport_service
		self.data_path=data_path
		self.parent = parent
		self.pytrainer_main = parent
		self.configuration = config
	
	def runImportdata(self):
		windowImportdata = WindowImportdata(self._sport_service, self.data_path, self, self.configuration, self.pytrainer_main)
		windowImportdata.run() class Plugins:
	def __init__(self, data_path = None, parent = None):
		self.data_path=data_path
		self.pytrainer_main = parent
	
	def getActivePlugins(self):
		retorno = []
		for plugin in self.getPluginsList():
			if self.getPluginInfo(plugin[0])[2] == "1":
				retorno.append(plugin[0])
		return retorno	

	def loadPlugin(self,plugin):
		logging.debug('>>')
		info = XMLParser(plugin+"/conf.xml")
		button = info.getValue("pytrainer-plugin","pluginbutton")
		name = info.getValue("pytrainer-plugin","name")
		logging.info('Loading plugin '+name)
		logging.debug('<<')
		return button,plugin

	def importClass(self, pathPlugin):
		logging.debug('>>')
		info = XMLParser(pathPlugin+"/conf.xml")
	  	#import plugin
		plugin_dir = os.path.realpath(pathPlugin) 
		plugin_filename = info.getValue("pytrainer-plugin","executable")
		plugin_classname = info.getValue("pytrainer-plugin","plugincode")
		logging.debug("Plugin Filename: " + plugin_filename )
		logging.debug("Plugin Classname: " + plugin_classname)
		sys.path.insert(0, plugin_dir)
		module = __import__(plugin_filename)
		pluginMain = getattr(module, plugin_classname)
		logging.debug('<<')
		#Only validate files if enabled at startup 
		if self.pytrainer_main.startup_options.validate:
			validate_inputfiles=True
			logging.info("validating plugin input files enabled")
		else:
			validate_inputfiles=False
		return pluginMain(self, validate_inputfiles) 

	def managePlugins(self):
		pluginsList = self.getPluginsList()
		windowplugins = WindowPlugins(self.data_path, self)
		windowplugins.setList(pluginsList)
		windowplugins.run()

	def getPluginsList(self):
		pluginsdir = self.data_path+"/plugins"
		pluginsList = []
		for plugin in os.listdir(pluginsdir):
			pluginxmlfile = pluginsdir+"/"+plugin+"/conf.xml"
			if os.path.isfile(pluginxmlfile):
				plugininfo = XMLParser(pluginxmlfile)
				name = plugininfo.getValue("pytrainer-plugin","name")
				description = plugininfo.getValue("pytrainer-plugin","description")
				pluginsList.append((pluginsdir+"/"+plugin,name,description))
		pluginsList.sort()
		return pluginsList
	
	def getPluginInfo(self,pathPlugin):
		info = XMLParser(pathPlugin+"/conf.xml")
		name = info.getValue("pytrainer-plugin","name")
		description = info.getValue("pytrainer-plugin","description")
		code = info.getValue("pytrainer-plugin","plugincode")
		plugindir = self.pytrainer_main.profile.plugindir
		if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
			status = 0
		else:
			info = XMLParser(plugindir+"/"+code+"/conf.xml")
			status = info.getValue("pytrainer-plugin","status")
		return name,description,status

	def getPluginConfParams(self,pathPlugin):
		info = XMLParser(pathPlugin+"/conf.xml")
		code = info.getValue("pytrainer-plugin","plugincode")
		plugindir = self.pytrainer_main.profile.plugindir
		if not os.path.isfile(plugindir+"/"+code+"/conf.xml"):
			params = info.getAllValues("conf-values")
			params.append(("status","0"))
		else:
			prefs = info.getAllValues("conf-values")
			prefs.append(("status","0"))
			info = XMLParser(plugindir+"/"+code+"/conf.xml")
			params = []
			for pref in prefs:
				params.append((pref[0],info.getValue("pytrainer-plugin",pref[0])))
		return params

	def setPluginConfParams(self,pathPlugin,savedOptions):
		info = XMLParser(pathPlugin+"/conf.xml")
		code = info.getValue("pytrainer-plugin","plugincode")
		plugindir = self.pytrainer_main.profile.plugindir+"/"+code
		if not os.path.isdir(plugindir):
			os.mkdir(plugindir)
		if not os.path.isfile(plugindir+"/conf.xml"):
			if ("status", "1") not in savedOptions:
				savedOptions.append(("status","0"))
		info = XMLParser(plugindir+"/conf.xml")
		info.createXMLFile("pytrainer-plugin",savedOptions)

	def getCodeConfValue(self,code,value):
		plugindir = self.pytrainer_main.profile.plugindir
		info = XMLParser(plugindir+"/"+code+"/conf.xml")
		return info.getValue("pytrainer-plugin",value) class Profile(Singleton):
    def __init__(self):
        logging.debug(">>")
        # The first two variables are singletons themselves, profile_options
        # doesn't change, only xml_tree needs to be protected by has_attr.
        self.environment = Environment()
        self.uc = UC()
        #Profile Options and Defaults
        self.profile_options = {
            "prf_name":"default",
            "prf_gender":"Male",
            "prf_weight":"",
            "prf_height":"",
            "prf_age":"",
            "prf_ddbb":"sqlite",
            "prf_ddbbhost":"",
            "prf_ddbbname":"",
            "prf_ddbbuser":"",
            "prf_ddbbpass":"",
            "version":"0.0",
            "prf_us_system":"False",
            "prf_hrzones_karvonen":"False",
            "prf_maxhr":"",
            "prf_minhr":"",
            "auto_launch_file_selection":"False",
            "import_default_tab":"0",
            "default_viewer":"0",
            "window_size":"800, 640",
            "activitypool_size": "10",
            "prf_startscreen":"current_day",
            }

        if not hasattr(self, 'xml_tree'):
            self.xml_tree = None
            #Parse pytrainer configuration file
            self.refreshConfiguration()

        logging.debug("<<")

    @property
    def data_path(self):
        return self.environment.data_path

    @property
    def tmpdir(self):
        return self.environment.temp_dir

    @property
    def confdir(self):
        return self.environment.conf_dir

    @property
    def config_file(self):
        return self.environment.conf_file

    @property
    def gpxdir(self):
        return self.environment.gpx_dir

    @property
    def extensiondir(self):
        return self.environment.extension_dir

    @property
    def plugindir(self):
        return self.environment.plugin_dir

    @property
    def sqlalchemy_url(self):
        ddbb_type = self.getValue("pytraining","prf_ddbb")
        ddbb_host = self.getValue("pytraining","prf_ddbbhost")
        ddbb = self.getValue("pytraining","prf_ddbbname")
        ddbb_user = self.getValue("pytraining","prf_ddbbuser")
        ddbb_pass = self.getValue("pytraining","prf_ddbbpass")
        if ddbb_type == "sqlite":
            return "sqlite:///%s/pytrainer.ddbb" % self.confdir
        else:
            return "{type}://{user}:{passwd}@{host}/{db}".format(type=ddbb_type,
                                                                 user=ddbb_user,
                                                                 passwd=ddbb_pass,
                                                                 host=ddbb_host,
                                                                 db=ddbb)

    def refreshConfiguration(self):
        logging.debug(">>")
        self.configuration = self._parse_config_file(self.config_file)
        logging.debug("Configuration retrieved: %s", self.configuration)
        self.uc.set_us(self.prf_us_system)
        self._setZones()
        logging.debug("<<")

    def _setZones(self):
        #maxhr = self.getValue("pytraining","prf_maxhr")
        #resthr = self.getValue("pytraining","prf_minhr")
        try:
            maxhr = int(self.getValue("pytraining","prf_maxhr"))
        except Exception as e:
            logging.error("Failed when retrieving Max Heartrate value: %s" %e)
            logging.info("Setting Max Heartrate to default value: 190")
            maxhr = 190
        try:
            resthr = int(self.getValue("pytraining","prf_minhr"))
        except Exception as e:
            logging.error("Failed when retrieving Min Heartrate value: %s" %e)
            logging.info("Setting Min Heartrate to default value: 65")
            resthr = 65
        self.maxhr = maxhr
        self.rethr = resthr

        if self.getValue("pytraining","prf_hrzones_karvonen")=="True":
            #karvonen method
            targethr1 = ((maxhr - resthr) * 0.50) + resthr
            targethr2 = ((maxhr - resthr) * 0.60) + resthr
            targethr3 = ((maxhr - resthr) * 0.70) + resthr
            targethr4 = ((maxhr - resthr) * 0.80) + resthr
            targethr5 = ((maxhr - resthr) * 0.90) + resthr
            targethr6 = maxhr
        else:
            #not karvonen method
            targethr1 = maxhr * 0.50
            targethr2 = maxhr * 0.60
            targethr3 = maxhr * 0.70
            targethr4 = maxhr * 0.80
            targethr5 = maxhr * 0.90
            targethr6 = maxhr

        self.zone1 = (targethr1,targethr2,"#ffff99",_("Moderate activity"))
        self.zone2 = (targethr2,targethr3,"#ffcc00",_("Weight Control"))
        self.zone3 = (targethr3,targethr4,"#ff9900",_("Aerobic"))
        self.zone4 = (targethr4,targethr5,"#ff6600",_("Anaerobic"))
        self.zone5 = (targethr5,targethr6,"#ff0000",_("VO2 MAX"))
        
    def getMaxHR(self):
        return self.maxhr
        
    def getRestHR(self):
        return self.resthr

    def getZones(self):
        return self.zone5,self.zone4,self.zone3,self.zone2,self.zone1

    def _parse_config_file(self, config_file):
        '''
        Parse the xml configuration file and convert to a dict

        returns: dict with option as key
        '''
        if config_file is None:
            logging.error("Configuration file value not set")
            logging.error("Fatal error, exiting")
            exit(-3)
        elif not os.path.isfile(config_file): #File not found
            logging.error("Configuration '%s' file does not exist" % config_file)
            logging.info("No profile found. Creating default one")
            self.setProfile(self.profile_options)
        elif os.stat(config_file)[stat.ST_SIZE] == 0: #File is empty
            logging.error("Configuration '%s' file is empty" % config_file)
            logging.info("Creating default profile")
            self.setProfile(self.profile_options)
        else:
            logging.debug("Attempting to parse content from %s", config_file)
            parser = etree.XMLParser(encoding='UTF8', recover=True)
            try:
                self.xml_tree = etree.parse(config_file, parser=parser)
            except Exception as e:
                logging.error("Error %s while parsing file %s. Exiting", e, config_file)
                exit(-3)
        #Have a populated xml tree, get pytraining node (root) and convert it to a dict
        pytraining_tag = self.xml_tree.getroot()
        config = {}
        config_needs_update = False
        for key, default in self.profile_options.items():
            value = pytraining_tag.get(key)
            #If property is not found, set it to the default
            if value is None:
                config_needs_update = True
                value = default
            config[key] = value
        config_needs_update |= self.fixLocalizedGender(config)
        #Added a property, so update config
        if config_needs_update:
            self.setProfile(config)
        #Set shorthand var for units of measurement
        self.prf_us_system = True if config["prf_us_system"] == "True" else False
        return config

    @staticmethod
    def fixLocalizedGender(config):
        ''' The gender used to be stored as the
            localized string. This fixes it.
        '''
        female_localized = ("Weiblich", "Mujer", "Femme", "Muller", "Feminino", "Kvinna")
        male_localized = (u"Männlich", u"Varón", "Homme", "Home", "Masculino", u"Значение", "Man")
        lgender = config["prf_gender"]
        if lgender in ("Male", "Female"):
            return False
        elif lgender in female_localized:
            config["prf_gender"] = "Female"
        elif lgender in male_localized:
            config["prf_gender"] = "Male"
        else:
            # default to not selected
            config["prf_gender"] = ""
        return True

    def getIntValue(self, tag, variable, default=0):
        ''' Function to return conf value as int
            returns
            -- default if cannot convert to int
            -- None if variable not found
        '''
        result = self.getValue(tag, variable)
        if result is None:
            return None
        try:
            result = int(result)
        except Exception as e:
            logging.debug(str(e))
            result = default
        return result

    def getValue(self, tag, variable):
        if tag != "pytraining":
            logging.critical("ERROR - pytraining is the only profile tag supported")
            return None
        elif variable not in self.configuration:
            return None
        return self.configuration[variable]

    def setValue(self, tag, variable, value, delay_write=False):
        logging.debug(">>")
        if tag != "pytraining":
            logging.error("ERROR: pytraining is the only profile tag supported")
        logging.debug("Setting %s to %s" % (variable, value))
        if self.xml_tree is None:
            #new config file....
            self.xml_tree = etree.parse(BytesIO(b'''<?xml version='1.0' encoding='UTF-8'?><pytraining />'''))
        self.xml_tree.getroot().set(variable, value)
        if not delay_write:
            self.saveProfile()
        logging.debug("<<")

    def setProfile(self,list_options):
        logging.debug(">>")
        for option, value in list_options.items():
            logging.debug("Adding %s|%s", option, value)
            self.setValue("pytraining",option,value,delay_write=True)
        self.uc.set_us(list_options['prf_us_system'])
        logging.debug("<<")

    def saveProfile(self):
        logging.debug("Writting configuration...")
        self.xml_tree.write(self.config_file, xml_declaration=True,
                            encoding='UTF-8') class SportService(object):
    
    """Provides access to stored sports."""
    
    def __init__(self, ddbb):
        self._ddbb = ddbb
        
    def get_sport(self, sport_id):
        """Get the sport with the specified id.

        If no sport with the given id exists then None is returned."""
        if sport_id is None:
            raise ValueError("Sport id cannot be None")
        try:
            return self._ddbb.session.query(Sport).filter(Sport.id == sport_id).one()
        except NoResultFound:
            return None

    def get_sport_by_name(self, name):
        """Get the sport with the specified name.

        If no sport with the given name exists then None is returned."""
        if name is None:
            raise ValueError("Sport name cannot be None")
        try:
            return self._ddbb.session.query(Sport).filter(Sport.name == name).one()
        except NoResultFound:
            return None

    def get_all_sports(self):
        """Get all stored sports."""
        return self._ddbb.session.query(Sport).all()

    def store_sport(self, sport):
        """Store a new or update an existing sport.
        
       The stored object is returned."""
        try:
            self._ddbb.session.add(sport)
            self._ddbb.session.commit()
        except IntegrityError:
            raise SportServiceException("")
        return sport

    def remove_sport(self, sport):
        """Delete a stored sport.
        
        All records associated with the sport will also be deleted."""
        if not sport.id:
            raise SportServiceException("Cannot remove sport which has not been stored: '{0}'.".format(sport.name))
        try:
            self._ddbb.session.delete(sport)
            self._ddbb.session.commit()
        except InvalidRequestError:
             raise SportServiceException("Sport id %s not found" % sport.id)
        logging.debug("Deleted sport: %s", sport.name) class Athlete:
    def __init__(self, data_path = None, parent = None):
        self.parent = parent
        self.pytrainer_main = parent
        self.data_path = data_path
        self.uc = UC()

    def refresh(self):
        logging.debug('>>')
        self.init_from_conf()
        self.data = self.get_athlete_stats()
        self.graphdata = self.get_athlete_data()
        logging.debug('<<')

    def init_from_conf(self):
        logging.debug('>>')
        self.name = self.pytrainer_main.profile.getValue("pytraining","prf_name")
        self.age = self.pytrainer_main.profile.getValue("pytraining","prf_age")
        self.height = self.pytrainer_main.profile.getValue("pytraining","prf_height")
        logging.debug('<<')

    def get_athlete_stats(self):
        logging.debug('>>')
        ret = []
        for stat in self.pytrainer_main.ddbb.session.query(Athletestat).order_by('date'):
            d = dict(stat.__dict__)
            d.pop('_sa_instance_state', None)
            ret.append(d)
        return ret

    def get_athlete_data(self):
        logging.debug('>>')
        graphdata = {}
        graphdata['weight'] = GraphData(title="Weight", xlabel="Date", ylabel="Weight (%s)" % (self.uc.unit_weight))
        graphdata['weight'].set_color('#3300FF', '#3300FF')
        #graphdata['weight'].graphType = 'date'
        graphdata['bodyfat'] = GraphData(title="Body Fat", xlabel="Date", ylabel="Body Fat (%)")
        graphdata['bodyfat'].set_color('#FF6600', '#FF6600')
        #graphdata['bf'].graphType = 'date'
        graphdata['restinghr'] = GraphData(title="Resting Heartrate", xlabel="Date", ylabel="Resting Heartrate (bpm)")
        graphdata['restinghr'].set_color('#660000', '#660000')
        graphdata['restinghr'].show_on_y2 = True
        graphdata['maxhr'] = GraphData(title="Maximum Heartrate", xlabel="Date", ylabel="Maximum Heartrate (bpm)")
        graphdata['maxhr'].set_color('#33CC99', '#33CC99')
        graphdata['maxhr'].show_on_y2 = True
        for row in self.data:
            if not 'date' in row or not row['date']:
                continue
            else:
                date = row['date']
            if 'weight' in row and row['weight']:
                weight = float(row['weight'])
            else:
                weight = ""
            if 'bodyfat' in row and row['bodyfat'] and weight:
                bf = float(row['bodyfat']) / 100 * weight
            else:
                bf = ""
            graphdata['weight'].addPoints(x=date, y=weight)
            graphdata['bodyfat'].addPoints(x=date, y=bf)
            graphdata['restinghr'].addPoints(x=date, y=row['restinghr'])
            graphdata['maxhr'].addPoints(x=date, y=row['maxhr'])
        #Remove empty data
        for item in list(graphdata.keys()):
            if len(graphdata[item]) == 0:
                logging.debug( "No values for %s. Removing...." % item )
                del graphdata[item]
        return graphdata


    def update_athlete_stats(self, id_athletestat, date, weight, bodyfat, restinghr, maxhr):
        logging.debug('>>')
        try:
            date = dateutil.parser.parse(date).date()
            logging.debug("update_athlete_stats called with invalid date")
            logging.debug('!<<')
        except ValueError:
            return
        #Update DB
        data = self.pytrainer_main.ddbb.session.query(Athletestat).filter(Athletestat.id_athletestat == id_athletestat).one()
        data.date = date
        data.weight = weight
        data.bodyfat = bodyfat
        data.restinghr = restinghr
        data.maxhr = maxhr
        self.pytrainer_main.ddbb.session.commit()
        logging.debug('<<')

    def insert_athlete_stats(self, date, weight, bodyfat, restinghr, maxhr):
        logging.debug('>>')
        if not date and not weight and not bodyfat and not restinghr and not maxhr:
            #no data supplied
            logging.debug("insert_athlete_stats called with no data")
            logging.debug('!<<')
            return
        try:
            date = dateutil.parser.parse(date).date()
        except ValueError:
            logging.debug("insert_athlete_stats called with invalid date")
            logging.debug('!<<')
            return
        #Update DB
        data = Athletestat(date=date, weight=weight, bodyfat=bodyfat, restinghr=restinghr, maxhr=maxhr)
        self.pytrainer_main.ddbb.session.add(data)
        self.pytrainer_main.ddbb.session.commit()
        logging.debug('<<')

    def delete_record(self, data):
        logging.debug('>>')
        self.pytrainer_main.ddbb.session.query(Athletestat).filter(Athletestat.id_athletestat == int(data)).delete()
        self.pytrainer_main.ddbb.session.commit()
        logging.debug('<<') class Stats:
    def __init__(self, parent = None):
        logging.debug('>>')
        self.pytrainer_main = parent
        logging.debug('<<')

    def refresh(self):
        self.data = self.get_stats()

    def get_stats(self):
        logging.debug('>>')
        data = {
            'sports' : {},
            'total_duration' : 0,
            'total_distance' : 0,
        }
        
        fields = ['maxspeed', 'beats', 'maxbeats', 'duration', 'distance']
        data['fields'] = fields
        
        for f in fields:
            data[f] = 0

        for r in self.pytrainer_main.ddbb.session.query(Activity):
            if r.sport not in data['sports']:
                data['sports'][r.sport] = {'name': r.sport.name, 'count': 0}
                for f in fields:
                    data['sports'][r.sport][f] = 0
                    data['sports'][r.sport]['total_'+f] = 0
            data['sports'][r.sport]['count'] += 1
            for f in fields:
                field = getattr(r, f)
                if field is not None:
                    data['sports'][r.sport][f] = max(data['sports'][r.sport][f], field)
                    data['sports'][r.sport]['total_'+f] += field
                    data[f] = max(data[f], field)
                else:
                    logging.info('Skipping null values')
                    
            if 'avg_hr' not in data['sports'][r.sport]:
                data['sports'][r.sport]['avg_hr'] = [0, 0]
            if r.beats:
                data['sports'][r.sport]['avg_hr'][0] += 1
                data['sports'][r.sport]['avg_hr'][1] += r.beats
                
            data['total_duration'] += r.duration
            data['total_distance'] += r.distance
            
            if not r.maxspeed and r.duration:
                data['sports'][r.sport]['maxspeed'] = max(data['sports'][r.sport]['maxspeed'], r.distance / r.duration * 3600)
            
            if not 'start_date' in data: data['start_date'] = r.date
            data['start_date'] = min(data['start_date'], r.date)
            if not 'end_date' in data: data['end_date'] = r.date
            data['end_date'] = max(data['end_date'], r.date)
            
        for s in data['sports']:
            if data['sports'][s]['avg_hr'][0]:
                data['sports'][s]['avg_hr'] = int(data['sports'][s]['avg_hr'][1] / data['sports'][s]['avg_hr'][0])
            else:
                data['sports'][s]['avg_hr'] = None
            
        logging.debug('<<')
        return data class Main(SimpleBuilderApp):
    def __init__(self, sport_service, data_path = None, parent = None, version = None, gpxDir = None):
        self._sport_service = sport_service
        self.version = version
        self.parent = parent
        self.pytrainer_main = parent
        self.data_path = data_path
        self.uc = uc.UC()
        SimpleBuilderApp.__init__(self, "pytrainer.ui")

        self.popup = PopupMenu(data_path,self)

        self.block = False
        self.activeSport = None
        self.gpxDir = gpxDir
        self.record_list = None
        self.laps = None

        #Setup graph
        self.grapher = DrawGraph(self, self.pytrainer_main)

        self.y1_limits = None
        self.y1_color = None
        self.y1_linewidth = 1
        # setup Search ListView
        self.listsearch = ListSearch(sport_service, self)

        self.aboutwindow = None
        self.mapviewer = None
        self.mapviewer_fs = None
        self.waypointeditor = None

    def new(self):
        self.menublocking = 0
        self.selected_view="day"
        self.window1.set_title ("pytrainer %s" % self.version)
        try:
            width, height = self.pytrainer_main.profile.getValue("pytraining","window_size").split(',')
            self.window1.resize(int(width), int(height))
        except:
            pass
        self.record_list = []
        #create the columns for the listdayrecord
        columns = [{'name':_("id"), 'visible':False},{'name':_("Start"), }, {'name':_("Sport")},{'name':self.uc.unit_distance}]
        self.create_treeview(self.recordTreeView,columns)
        #create the columns for the listarea
        # different codings for mean see eg http://de.wikipedia.org/wiki/%C3%98#Kodierung
        columns=[   {'name':_("id"), 'visible':False},
                    {'name':_("Title")},
                    {'name':_("Date")},
                    {'name':_("Distance"), 'xalign':1.0, 'format_float':'%.2f', 'quantity': 'distance'},
                    {'name':_("Sport")},
                    {'name':_("Time"), 'xalign':1.0, 'format_duration':True},
                    {'name':_(u"\u2300 HR"), 'xalign':1.0},
                    {'name':_("Max HR"), 'xalign':1.0},
                    {'name':_(u"\u2300 Speed"), 'xalign':1.0, 'format_float':'%.1f', 'quantity': 'speed'},
                    {'name':_(u"\u2300 Pace"), 'xalign':1.0, 'format_pace':True},
                    {'name':_("Calories"), 'xalign':1.0}
                ]
        self.create_treeview(self.allRecordTreeView,columns)
        self.create_menulist(columns)
        #create the columns for the waypoints treeview
        columns=[{'name':_("id"), 'visible':False},{'name':_("Waypoint")}]
        self.create_treeview(self.waypointTreeView,columns)
        #create the columns for the athlete history treeview
        columns=[   {'name':_("id"), 'visible':False},
                    {'name':_("Date")},
                    {'name':_("Weight"), 'xalign':1.0, 'quantity':'weight', 'format_float':'%.1f'},
                    {'name':_("Body Fat %"), 'xalign':1.0, 'quantity':'none', 'format_float':'%.1f'},
                    {'name':_("Resting HR"), 'xalign':1.0},
                    {'name':_("Max HR"), 'xalign':1.0}
                ]
        self.create_treeview(self.athleteTreeView,columns)
        #create the columns for the stats treeview
        columns=[   {'name':_("id"), 'visible':False},
                    {'name':_("Sport")},
                    {'name':_("Records"), 'xalign':1.0},
                    {'name':_("Total duration"), 'xalign':1.0, 'format_duration':True},
                    {'name':_("Total distance"), 'xalign':1.0, 'format_float':'%.1f', 'quantity':'distance'},
                    {'name':_("Avg speed"), 'format_float':'%.2f', 'quantity':'speed', 'xalign':1.0},
                    {'name':_("Max speed"), 'format_float':'%.2f', 'quantity':'speed', 'xalign':1.0},
                    {'name':_("Avg HR"), 'xalign':1.0},
                    {'name':_("Max HR"), 'xalign':1.0},
                    {'name':_("Max duration"), 'xalign':1.0, 'format_duration':True},
                    {'name':_("Max distance"), 'xalign':1.0, 'format_float':'%.1f', 'quantity':'distance'},
                ]
        self.create_treeview(self.statsTreeView,columns)

        #create the columns for the laps treeview
        columns=[
                    {'name':_("Lap")},
                    {'name':_("Trigger"), 'xalign':0, 'pixbuf':True},
                    {'name':_("Distance"), 'xalign':1.0, 'format_float':'%.2f', 'quantity':'distance'},
                    {'name':_("Time"), 'xalign':1.0, 'format_duration':True},
                    {'name':_("Avg speed"), 'format_float':'%.2f', 'quantity':'speed'},
                    {'name':_("Max speed"), 'format_float':'%.2f', 'quantity':'speed'},
                    {'name':_("Avg pace"), 'xalign':1.0, 'quantity':'pace'},
                    {'name':_("Max pace"), 'xalign':1.0, 'quantity':'pace'},
                    {'name':_("Avg HR"), 'xalign':1.0},
                    {'name':_("Max HR"), 'xalign':1.0},
                    {'name':_("Calories"), 'xalign':1.0},
                    {'name':_("Intensity"), 'visible':False},
                    {'name':_("Comments"), 'xalign':0.0},
                ]
        self.create_treeview(self.lapsTreeView,columns)

        #create the columns for the projected times treeview
        columns=[
                    {'name':_("id"), 'visible':False},
                    {'name':_("Race"), 'xalign':1.0},
                    {'name':_("Distance"), 'xalign':1.0, 'format_float':'%.2f', 'quantity':'distance'},
                    {'name':_("Time"), 'xalign':1.0, 'format_duration':True},
                ]
        self.create_treeview(self.analyticsTreeView,columns,sortable=False)

        #create the columns for the rank treeview
        columns=[
                    {'name':_("id"), 'visible':False},
                    {'name':_("Rank"), 'visible':True},
                    {'name':_("Date"), 'xalign':1.0},
                    {'name':_("Distance"), 'xalign':1.0, 'format_float':'%.2f', 'quantity':'distance'},
                    {'name':_("Time"), 'xalign':1.0, 'format_duration':True},
                    {'name':_("Speed"),  'format_float':'%.2f', 'quantity':'speed'},
                    {'name':_("Pace"),  'format_float':'%.2f', 'quantity':'pace'},
                    {'name':_("Color"), 'visible':False},
                ]
        self.create_treeview(self.rankingTreeView,columns,sortable=False)

        self.fileconf = self.pytrainer_main.profile.confdir+"/listviewmenu.xml"
        if not os.path.isfile(self.fileconf):
            self._createXmlListView(self.fileconf)
        self.showAllRecordTreeViewColumns()
        self.allRecordTreeView.set_search_column(1)
        self.notebook.set_current_page(1)

        #Set correct map viewer
        if self.pytrainer_main.profile.getValue("pytraining","default_viewer") == "1":
            self.radiobuttonOSM.set_active(1)
        else:
            self.radiobuttonGMap.set_active(1)
        self.comboMapLineType.set_active(0)

    def _float_or(self, value, default):
        '''Function to parse and return a float, or the default if the parsing fails'''
        try:
            result = float(value)
        except Exception as e:
            #print type(e)
            #print e
            result = default
        return result

    def setup(self):
        logging.debug(">>")
        self.createGraphs()
        self.createMap()
        page = self.notebook.get_current_page()
        self.on_page_change(None,None,page)
        logging.debug("<<")

    def _createXmlListView(self,file):
        menufile = XMLParser(file)
        savedOptions = []
        savedOptions.append(("date","True"))
        savedOptions.append(("distance","True"))
        savedOptions.append(("average","False"))
        savedOptions.append(("title","True"))
        savedOptions.append(("sport","True"))
        savedOptions.append(("id_record","False"))
        savedOptions.append(("time","False"))
        savedOptions.append(("beats","False"))
        savedOptions.append(("maxbeats","False"))
        savedOptions.append(("pace","False"))
        savedOptions.append(("calories","False"))
        menufile.createXMLFile("listviewmenu",savedOptions)

    def removeImportPlugin(self, plugin):
        for widget in self.menuitem1_menu:
            if widget.get_name() == plugin[1]:
                self.menuitem1_menu.remove(widget)

    def removeExtension(self, extension):
        for widget in self.recordbuttons_hbox:
            if widget.get_name() == extension[1]:
                logging.debug("Removing extension: %s ", extension[0])
                self.recordbuttons_hbox.remove(widget)

    def addExtension(self,extension):
        #txtbutton,extensioncode,extensiontype = extension
        button = Gtk.Button(extension[0])
        button.set_name(extension[1])
        button.connect("button_press_event", self.runExtension, extension)
        self.recordbuttons_hbox.pack_start(button,False,False,0)
        self.recordbuttons_hbox.show_all()

    def runExtension(self,widget,widget2,extension):
        #print extension
        txtbutton,extensioncode,extensiontype = extension
        id = None
        if extensiontype=="record":
            selected,iter = self.recordTreeView.get_selection().get_selected()
            id = selected.get_value(iter,0)
        self.parent.runExtension(extension,id)

    def createGraphs(self):
        logging.debug(">>")
        self.drawarearecord = RecordGraph(self.record_graph_vbox, self.window1, self.record_combovalue, self.record_combovalue2, self.btnShowLaps, self.tableConfigY1, pytrainer_main=self.pytrainer_main)
        self.drawareaheartrate = HeartRateGraph([self.heartrate_vbox, self.heartrate_vbox2, self.heartrate_vbox3], self.window1, pytrainer_main=self.pytrainer_main)
        self.day_vbox.hide()
        sports = self._sport_service.get_all_sports()
        self.drawareaweek = WeekGraph(sports, self.weekview, self.window1, self.week_combovalue, self.week_combovalue2, self.pytrainer_main)
        self.drawareamonth = MonthGraph(sports, self.month_vbox, self.window1, self.month_combovalue,self.month_combovalue2, self.pytrainer_main)
        self.drawareayear = YearGraph(sports, self.year_vbox, self.window1, self.year_combovalue,self.year_combovalue2, self.pytrainer_main)
        self.drawareatotal = TotalGraph(sports, self.total_vbox, self.window1, self.total_combovalue,self.total_combovalue2, self.pytrainer_main)
        logging.debug("<<")

    def createMap(self):
        logging.debug(">>")
        if not self.mapviewer and not self.mapviewer_fs and not self.waypointeditor:
            try:
                from pytrainer.extensions.mapviewer import MapViewer
                from pytrainer.extensions.waypointeditor import WaypointEditor
                self.mapviewer = MapViewer(self.data_path, pytrainer_main=self.parent, box=self.map_vbox)
                self.mapviewer_fs = MapViewer(self.data_path, pytrainer_main=self.parent, box=self.map_vbox_old)
                self.waypointeditor = WaypointEditor(self.data_path, self.waypointvbox,
                                                     self.pytrainer_main.waypoint,
                                                     parent=self.pytrainer_main)
            except ImportError:
                logging.error("Webkit not found, map functionality not available")
                for container in self.map_vbox, self.map_vbox_old, self.waypointvbox:
                    message = Gtk.Label(_("Webkit not found, map functionality not available"))
                    message.set_selectable(True)
                    container.foreach(lambda widget:container.remove(widget))
                    container.add(message)
                    container.show_all()
        logging.debug("<<")

    def updateSportList(self,listSport):
        logging.debug(">>")
        liststore =  self.sportlist.get_model()
        firstEntry = _("All Sports")
        liststore.clear() #Delete all items
        #Re-add "All Sports"
        liststore.append([firstEntry])
        #Re-add all sports in listSport
        for sport in listSport:
            liststore.append([sport.name])
        self.sportlist.set_active(0)
        logging.debug("<<")

    def render_duration(self, column, cell, model, iter, notif):
        orig = cell.get_property('text')
        if not ':' in orig:
            h,m,s = second2time(int(orig))
            new = '%d:%02d:%02d' % (h,m,s)
        else:
            new = orig
        if orig[:4] == ' 0:0':
            new = orig[4:]
        elif orig[:3] == ' 0:':
            new = orig[3:]
        if len(new)>5:
            hours = int(new[:-6])
            days = _("d")
            if hours>23:
                new = "%d %s %02d:%s" % (hours / 24, days, hours%24 ,new[-5:])
        cell.set_property('text', new)

    def render_float(self, column, cell, model, iter, data):
        _format, _quantity, _idx = data
        _val = model.get_value(iter, _idx)
        _val = self.uc.sys2usr(_quantity, _val)
        _val_str = _format % float(_val)
        cell.set_property('text', _val_str)

    def render_pace(self, column, cell, model, iter, idx):
        _val = model.get_value(iter, idx)
        _pace_str = uc.float2pace(self.uc.pace(uc.pace_from_db(_val)))
        cell.set_property('text', _pace_str)

    def create_treeview(self,treeview,columns,sortable=True):
        for column_index, column_dict in enumerate(columns):
            if 'pixbuf' in column_dict:
                renderer = Gtk.CellRendererPixbuf()
            else:
                renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(column_dict['name'])
            column.pack_start(renderer, False)
            if 'pixbuf' in column_dict:
                column.add_attribute(renderer, 'pixbuf', column_index)
            else:
                column.add_attribute(renderer, 'text', column_index)
            column.set_sizing(Gtk.TreeViewColumnSizing.AUTOSIZE)
            column.set_resizable(True)
            if 'xalign' in column_dict:
                renderer.set_property('xalign', column_dict['xalign'])
            if 'visible' in column_dict:
                column.set_visible(column_dict['visible'])
            if 'format_float' in column_dict:
                column.set_cell_data_func(renderer, self.render_float, [column_dict['format_float'], column_dict['quantity'], column_index])
            if 'format_duration' in column_dict and column_dict['format_duration']:
                column.set_cell_data_func(renderer, self.render_duration)
            if 'format_pace' in column_dict:
                column.set_cell_data_func(renderer, self.render_pace, column_index)
            if sortable:
                column.set_sort_column_id(column_index)
            treeview.append_column(column)

    def actualize_recordview(self,activity):
        logging.debug(">>")
        if activity.id is None:
            #Blank out fields
            self.record_distance.set_text("")
            self.record_upositive.set_text("")
            self.record_unegative.set_text("")
            self.record_average.set_text("")
            self.record_maxspeed.set_text("")
            self.record_pace.set_text("")
            self.record_maxpace.set_text("")
            self.record_sport.set_text("")
            self.record_date.set_text("")
            self.record_time.set_text("")
            self.record_duration.set_text("")
            #self.record_minute.set_text("")
            #self.record_second.set_text("")
            self.record_calories.set_text("")
            self.record_title.set_text("")
            self.label_record_equipment.set_text("")
            self.frame_laps.hide()
            com_buffer = self.record_comments.get_buffer()
            start,end = com_buffer.get_bounds()
            com_buffer.set_text("")
            #Move to main record page and grey out
            self.recordview.set_current_page(0)
            self.recordview.set_sensitive(0)
            logging.debug("<<")
            return
        #Set the units for the activity results, e.g. km, km/h etc
        self.r_distance_unit.set_text(self.uc.unit_distance)
        self.r_speed_unit.set_text(self.uc.unit_speed)
        self.r_maxspeed_unit.set_text(self.uc.unit_speed)
        self.r_pace_unit.set_text(self.uc.unit_pace)
        self.r_maxpace_unit.set_text(self.uc.unit_pace)
        self.r_ascent_unit.set_text(self.uc.unit_height)
        self.r_descent_unit.set_text(self.uc.unit_height)

        if activity.has_data:
            self.recordview.set_sensitive(1)

            dateTime = activity.date_time
            recordDateTime = dateTime.strftime("%Y-%m-%d %H:%M:%S")
            recordDate = dateTime.strftime("%x")
            recordTime = dateTime.strftime("%X")
            recordDateTimeOffset = dateTime.strftime("%z")

            self.record_distance.set_text(activity.get_value_f('distance', "%0.2f"))
            self.record_upositive.set_text(activity.get_value_f('upositive', "%0.2f"))
            self.record_unegative.set_text(activity.get_value_f('unegative', "%0.2f"))
            self.record_average.set_text(activity.get_value_f('average', "%0.2f"))
            self.record_maxspeed.set_text(activity.get_value_f('maxspeed', "%0.2f"))
            self.record_pace.set_text(activity.get_value_f('pace', "%s"))
            self.record_maxpace.set_text(activity.get_value_f('maxpace', "%s"))

            self.record_sport.set_text(activity.sport_name)
            self.record_date.set_text(recordDate)
            self.record_time.set_text(recordTime)
            self.record_duration.set_text(activity.get_value_f('time', '%s'))
            self.record_calories.set_text(activity.get_value_f('calories', "%0.0f"))
            if activity.title:
                self.record_title.set_text(activity.title)
            else:
                self.record_title.set_text('')
            hrun,mrun,srun = second2time(activity.duration)
            hpause,mpause,spause = second2time(activity.time_pause)
            self.record_runrest.set_text("%02d:%02d:%02d / %02d:%02d:%02d" %(hrun,mrun,srun,hpause,mpause,spause))
            buffer = self.record_comments.get_buffer()
            if activity.comments:
                buffer.set_text(activity.comments)
            else:
                buffer.set_text('')
            if len(activity.equipment) > 0:
                equipment_text = ", ".join([item.description for item in activity.equipment])
                self.label_record_equipment.set_text(equipment_text)
            else:
                self.label_record_equipment.set_markup("<i>None</i>")
            if len(activity.Laps)>1:
                store = Gtk.ListStore(
                    GObject.TYPE_INT,
                    GdkPixbuf.Pixbuf,
                    GObject.TYPE_FLOAT,
                    GObject.TYPE_STRING,
                    GObject.TYPE_FLOAT,
                    GObject.TYPE_FLOAT,
                    GObject.TYPE_STRING,
                    GObject.TYPE_STRING,
                    GObject.TYPE_INT,
                    GObject.TYPE_INT,
                    GObject.TYPE_INT,
                    GObject.TYPE_STRING,
                    GObject.TYPE_STRING,
                    )
                for lap in activity.Laps:
                    t = lap.duration
                    m = lap.distance

                    m = self.uc.speed(m)

                    s = m / float(t) * 3.6
                    max_speed = lap.max_speed * 3.6
                    if s > 0:
                        pace = "%d:%02d" %((3600/s)/60,(3600/s)%60)
                        if max_speed >0:
                            max_pace = "%d:%02d" %((3600/max_speed)/60,(3600/max_speed)%60)
                        else:
                            max_pace = "0:00"
                    else:
                        pace = "0:00"
                        max_pace = "0:00"

                    color = {
                        'active' : '#000000',
                        'rest' : '#808080',
                        'resting' : '#808080',
                    }

                    pic = GdkPixbuf.Pixbuf.new_from_file(self.data_path+"glade/trigger_%s.png" % lap.laptrigger)

                    iter = store.append()
                    store.set(iter,
                        0, lap.lap_number + 1,
                        1, pic,
                        2, m/1000,
                        3, str(int(float(t))),
                        4, s,
                        5, max_speed,
                        6, pace,
                        7, max_pace,
                        8, lap.avg_hr if lap.avg_hr else 0,
                        9, lap.max_hr if lap.max_hr else 0,
                        10, lap.calories,
                        11, color[lap.intensity],
                        12, '' if not lap.comments else (lap.comments if len(lap.comments)<40 else "%s..." % lap.comments[:40]),
                        )
                self.lapsTreeView.set_model(store)
                self.lapsTreeView.set_rules_hint(True)

                # Use grey color for "rest" laps
                for c in self.lapsTreeView.get_columns():
                    for cr in c.get_cells():
                        if type(cr)==Gtk.CellRendererText:
                            cr.set_property('foreground', 'gray')

                def edited_cb(cell, path, new_text, data):
                    liststore, activity = data
                    liststore[path][12] = new_text
                    activity.Laps[int(path)].comments = gtk_str(new_text)
                    self.pytrainer_main.ddbb.session.commit()

                def show_tooltip(widget, x, y, keyboard_mode, tooltip, user_param1):
                     path = self.lapsTreeView.get_path_at_pos(x,y-20)
                     if not path: return False
                     if path[1] != self.lapsTreeView.get_columns()[12]: return False
                     comments = user_param1[1].laps[path[0][0]]['comments']
                     if comments and len(comments)>40:
                         tooltip.set_text(comments)
                         return True
                     return False

                if getattr(self.lapsTreeView, 'tooltip_handler_id', None):
                    self.lapsTreeView.disconnect(self.lapsTreeView.tooltip_handler_id)
                self.lapsTreeView.tooltip_handler_id = self.lapsTreeView.connect('query-tooltip', show_tooltip, (store, activity))
                i = 0
                for cr in self.lapsTreeView.get_columns()[12].get_cells():
                    cr.set_property('editable', True)
                    if getattr(self, 'lapview_handler_id', None):
                        cr.disconnect(self.lapview_handler_id)
                    self.lapview_handler_id = cr.connect('edited', edited_cb, (store, activity))
                    tooltip = Gtk.Tooltip()
                    tooltip.set_text(activity.laps[i]['comments'])
                    # FIXME Use TreePath to set tooltip
                    #self.lapsTreeView.set_tooltip_cell(tooltip, i, self.lapsTreeView.get_columns()[12], cr)
                    self.lapsTreeView.set_tooltip_cell(tooltip, None, self.lapsTreeView.get_columns()[12], cr)
                    i += 1
                self.frame_laps.show()
            else:
                self.frame_laps.hide()

        else:
            self.recordview.set_current_page(0)
            self.recordview.set_sensitive(0)

        logging.debug("<<")

    def actualize_recordgraph(self,activity):
        logging.debug(">>")
        self.record_list = activity.tracks
        self.laps = activity.laps
        if activity.gpx_file is not None:
            if not self.pytrainer_main.startup_options.newgraph:
                logging.debug("Using the original graphing")
                logging.debug("Activity has GPX data")
                #Show drop down boxes
                self.hbox30.show()
                #Hide new graph details
                self.graph_data_hbox.hide()
                self.hboxGraphOptions.hide()
                #Enable graph
                self.record_vbox.set_sensitive(1)
                self.drawarearecord.drawgraph(self.record_list,self.laps)
            else:
                #Still just test code....
                logging.debug("Using the new TEST graphing approach")
                #Hide current drop down boxes
                self.hbox30.hide()
                self.graph_data_hbox.hide()
                #Enable graph
                self.record_vbox.set_sensitive(1)
                #Create a frame showing data available for graphing
                #Remove existing frames
                for child in self.graph_data_hbox.get_children():
                    if isinstance(child, Gtk.Frame):
                        self.graph_data_hbox.remove(child)
                #Build frames and vboxs to hold checkbuttons
                xFrame = Gtk.Frame(label=_("Show on X Axis"))
                y1Frame = Gtk.Frame(label=_("Show on Y1 Axis"))
                y2Frame = Gtk.Frame(label=_("Show on Y2 Axis"))
                limitsFrame = Gtk.Frame(label=_("Axis Limits"))
                xvbox = Gtk.VBox()
                y1box = Gtk.Table()
                y2box = Gtk.Table()
                limitsbox = Gtk.Table()
                #Populate X axis data
                #Create x axis items
                xdistancebutton = Gtk.RadioButton(label=_("Distance"))
                xtimebutton = Gtk.RadioButton(group=xdistancebutton, label=_("Time"))
                xlapsbutton = Gtk.CheckButton(label=_("Laps"))
                y1gridbutton = Gtk.CheckButton(label=_("Left Axis Grid"))
                y2gridbutton = Gtk.CheckButton(label=_("Right Axis Grid"))
                xgridbutton = Gtk.CheckButton(label=_("X Axis Grid"))
                #Set state of buttons
                if activity.x_axis == "distance":
                    xdistancebutton.set_active(True)
                elif activity.x_axis == "time":
                    xtimebutton.set_active(True)
                xlapsbutton.set_active(activity.show_laps)
                y1gridbutton.set_active(activity.y1_grid)
                y2gridbutton.set_active(activity.y2_grid)
                xgridbutton.set_active(activity.x_grid)
                #Connect handlers to buttons
                xdistancebutton.connect("toggled", self.on_xaxischange, "distance", activity)
                xtimebutton.connect("toggled", self.on_xaxischange, "time", activity)
                xlapsbutton.connect("toggled", self.on_xlapschange, activity)
                y1gridbutton.connect("toggled", self.on_gridchange, "y1", activity)
                y2gridbutton.connect("toggled", self.on_gridchange, "y2", activity)
                xgridbutton.connect("toggled", self.on_gridchange, "x", activity)
                #Add buttons to frame
                xvbox.pack_start(xdistancebutton, False, True, 0)
                xvbox.pack_start(xtimebutton, False, True, 0)
                xvbox.pack_start(xlapsbutton, False, True, 0)
                xvbox.pack_start(y1gridbutton, False, True, 0)
                xvbox.pack_start(y2gridbutton, False, True, 0)
                xvbox.pack_start(xgridbutton, False, True, 0)
                xFrame.add(xvbox)

                #Populate axis limits frame
                #TODO Need to change these to editable objects and redraw graphs if changed....
                #Create labels etc
                minlabel = Gtk.Label(label="<small>Min</small>")
                minlabel.set_use_markup(True)
                maxlabel = Gtk.Label(label="<small>Max</small>")
                maxlabel.set_use_markup(True)
                xlimlabel = Gtk.Label(label="X")
                limits = {}
                xminlabel = Gtk.Entry(max_length=10)
                xmaxlabel = Gtk.Entry(max_length=10)
                limits['xminlabel'] = xminlabel
                limits['xmaxlabel'] = xmaxlabel
                xminlabel.set_width_chars(5)
                xminlabel.set_alignment(1.0)
                xmaxlabel.set_width_chars(5)
                xmaxlabel.set_alignment(1.0)
                y1limlabel = Gtk.Label(label="Y1")
                y1minlabel = Gtk.Entry(max_length=10)
                y1maxlabel = Gtk.Entry(max_length=10)
                limits['y1minlabel'] = y1minlabel
                limits['y1maxlabel'] = y1maxlabel
                y1minlabel.set_width_chars(5)
                y1minlabel.set_alignment(1.0)
                y1maxlabel.set_width_chars(5)
                y1maxlabel.set_alignment(1.0)
                y2limlabel = Gtk.Label(label="Y2")
                y2minlabel = Gtk.Entry(max_length=10)
                y2maxlabel = Gtk.Entry(max_length=10)
                limits['y2minlabel'] = y2minlabel
                limits['y2maxlabel'] = y2maxlabel
                y2minlabel.set_width_chars(5)
                y2minlabel.set_alignment(1.0)
                y2maxlabel.set_width_chars(5)
                y2maxlabel.set_alignment(1.0)
                resetbutton = Gtk.Button(_('Reset Limits'))
                resetbutton.connect("clicked", self.on_setlimits, activity, True, None)
                setbutton = Gtk.Button(_('Set Limits'))
                setbutton.connect("clicked", self.on_setlimits, activity, False, limits)
                #Add labels etc to table
                limitsbox.attach(minlabel, 1, 2, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(maxlabel, 2, 3, 0, 1, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(xlimlabel, 0, 1, 1, 2, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(xminlabel, 1, 2, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(xmaxlabel, 2, 3, 1, 2, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(y1limlabel, 0, 1, 2, 3, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(y1minlabel, 1, 2, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(y1maxlabel, 2, 3, 2, 3, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(y2limlabel, 0, 1, 3, 4, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(y2minlabel, 1, 2, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(y2maxlabel, 2, 3, 3, 4, yoptions=Gtk.AttachOptions.SHRINK, xpadding=5)
                limitsbox.attach(setbutton, 0, 3, 4, 5, yoptions=Gtk.AttachOptions.SHRINK)
                limitsbox.attach(resetbutton, 0, 3, 5, 6, yoptions=Gtk.AttachOptions.SHRINK)
                limitsFrame.add(limitsbox)

                row = 0
                if activity.x_axis == "distance":
                    data = activity.distance_data
                elif activity.x_axis == "time":
                    data = activity.time_data
                else:
                    logging.error("x axis is unknown")
                #Populate Y axis data
                for graphdata in sorted(data.keys()):
                    #First Y axis...
                    #Create button
                    y1button = Gtk.CheckButton(label=data[graphdata].title)
                    #Make button active if this data is to be displayed...
                    y1button.set_active(data[graphdata].show_on_y1)
                    #Connect handler for toggle state changes
                    y1button.connect("toggled", self.on_y1change, y1box, graphdata, activity)
                    #Attach button to container
                    y1box.attach(y1button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
                    if data[graphdata].linecolor is not None:
                        #Create a color choser
                        y1color = Gtk.ColorButton()
                        #Set color to current activity color
                        _color = Gdk.color_parse(data[graphdata].linecolor)
                        y1color.set_color(_color)
                        #Connect handler for color state changes
                        y1color.connect("color-set", self.on_y1colorchange, y1box, graphdata, activity)
                        #Attach to container
                        y1box.attach(y1color, 1, 2, row, row+1)
                    else:
                        blanklabel = Gtk.Label(label="")
                        y1box.attach(blanklabel, 1, 2, row, row+1)

                    #Second Y axis
                    y2button = Gtk.CheckButton(label=data[graphdata].title)
                    y2button.set_active(data[graphdata].show_on_y2)
                    y2button.connect("toggled", self.on_y2change, y2box, graphdata, activity)
                    y2box.attach(y2button, 0, 1, row, row+1, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
                    if data[graphdata].y2linecolor is not None:
                        y2color = Gtk.ColorButton()
                        _color = Gdk.color_parse(data[graphdata].y2linecolor)
                        y2color.set_color(_color)
                        y2color.connect("color-set", self.on_y2colorchange, y2box, graphdata, activity)
                        #Attach to container
                        y2box.attach(y2color, 1, 2, row, row+1)
                    else:
                        blanklabel = Gtk.Label(label="")
                        y2box.attach(blanklabel, 1, 2, row, row+1)
                    row += 1

                y1Frame.add(y1box)
                y2Frame.add(y2box)
                self.graph_data_hbox.pack_start(xFrame, expand=False, fill=False, padding=5)
                self.graph_data_hbox.pack_start(y1Frame, expand=False, fill=False, padding=5)
                self.graph_data_hbox.pack_start(y2Frame, expand=False, fill=False, padding=5)
                self.graph_data_hbox.pack_start(limitsFrame, expand=False, fill=True, padding=5)
                #self.graph_data_hbox.show_all()
                self.hboxGraphOptions.show_all()
                act = self.grapher.drawActivityGraph(activity=activity, box=self.record_graph_vbox)
                if act.x_limits_u[0] is not None:
                    xmin, xmax = act.x_limits_u
                else:
                    xmin, xmax = act.x_limits
                if act.y1_limits_u[0] is not None:
                    y1min, y1max = act.y1_limits_u
                else:
                    y1min, y1max = act.y1_limits
                if act.y2_limits_u[0] is not None:
                    y2min, y2max = act.y2_limits_u
                else:
                    y2min, y2max = act.y2_limits
                #print y1min, y1max, y2min, y2max
                if xmin is not None and xmax is not None:
                    xminlabel.set_text(str(xmin))
                    xmaxlabel.set_text(str(xmax))
                if y1min is not None and y1max is not None:
                    y1minlabel.set_text(str(y1min))
                    y1maxlabel.set_text(str(y1max))
                if y2min is not None and y2max is not None:
                    y2minlabel.set_text(str(y2min))
                    y2maxlabel.set_text(str(y2max))


                #Default to showing options
                self.buttonGraphShowOptions.hide()
                self.scrolledwindowGraphOptions.show()
                self.buttonGraphHideOptions.show()
        else:
            logging.debug("Activity has no GPX data")
            #Show drop down boxes
            self.hbox30.show()
            #Hide new graph details
            self.graph_data_hbox.hide()
            self.hboxGraphOptions.hide()
            #Remove graph
            vboxChildren = self.record_graph_vbox.get_children()
            logging.debug('Vbox has %d children %s', len(vboxChildren), vboxChildren)
            # ToDo: check why vertical container is shared
            for child in vboxChildren:
                #Remove all FigureCanvasGTK and NavigationToolbar2GTKAgg to stop double ups of graphs
                if isinstance(child, matplotlib.backends.backend_gtk3agg.FigureCanvasGTK3Agg) or isinstance(child, matplotlib.backends.backend_gtk3.NavigationToolbar2GTK3):
                    logging.debug('Removing child: %s', child)
                    self.record_graph_vbox.remove(child)
            self.record_vbox.set_sensitive(0)
        logging.debug("<<")

    def actualize_heartrategraph(self,activity):
        logging.debug(">>")
        if activity.tracks is not None and len(activity.tracks)>0:
            self.heartrate_vbox_.set_sensitive(1)
            self.drawareaheartrate.drawgraph(activity.tracks)
        else:
            self.heartrate_vbox_.set_sensitive(0)
        logging.debug("<<")

    def actualize_hrview(self,activity):
        logging.debug(">>")
        zones = self.pytrainer_main.profile.getZones()
        record_list = activity.tracks
        is_karvonen_method = self.pytrainer_main.profile.getValue("pytraining","prf_hrzones_karvonen")
        if record_list is not None and len(record_list)>0:
            record_list=record_list[0]
            self.record_zone1.set_text("%s-%s" %(zones[4][0],zones[4][1]))
            self.record_zone2.set_text("%s-%s" %(zones[3][0],zones[3][1]))
            self.record_zone3.set_text("%s-%s" %(zones[2][0],zones[2][1]))
            self.record_zone4.set_text("%s-%s" %(zones[1][0],zones[1][1]))
            self.record_zone5.set_text("%s-%s" %(zones[0][0],zones[0][1]))
            beats = activity.beats
            maxbeats = activity.maxbeats
            self.record_beats.set_text("%0.0f" %beats)
            self.record_maxbeats.set_text("%0.0f" %maxbeats)
            self.record_calories2.set_text("%0.0f" %activity.calories)
            if is_karvonen_method=="True":
                self.record_zonesmethod.set_text(_("Karvonen method"))
            else:
                self.record_zonesmethod.set_text(_("Percentages method"))
        #else:
        #   self.recordview.set_sensitive(0)
        logging.debug("<<")

    def actualize_analytics(self,activity):
        logging.debug(">>")
        record_list = activity.tracks

        def project(d,a):
            # avoid divide by zero if distance is 0
            try:
                return int(a.duration * (d / a.distance)**1.06)
            except ZeroDivisionError:
                return 0

        DISTANCES = {
            .8    : _("800 m"),
            1.5   : _("1500 m"),
            5     : _("5K"),
            7     : _("7K"),
            10    : _("10K"),
            21.1  : _("Half marathon"),
            42.195  : _("Marathon"),
            100   : _("100K"),
        }

        projected_store = Gtk.ListStore(
            GObject.TYPE_STRING,       #id
            GObject.TYPE_STRING,    #name
            GObject.TYPE_STRING,    #distance
            GObject.TYPE_STRING,       #time
            )

        for d in sorted(DISTANCES.keys()):
            v = DISTANCES[d]
            iter = projected_store.append()
            projected_store.set (
                iter,
                0, str(d),
                1, v,
                2, str(d),
                3, str(project(d, activity)),
                )
        self.analyticsTreeView.set_model(projected_store)

        self.analytics_activity = activity
        self.on_change_rank_percentage()

        logging.debug("<<")

    def on_change_rank_percentage(self, widget=None):

        activity = self.analytics_activity
        if widget:
            percentage = widget.get_value() / 100
        else:
            percentage = .05
        records = self.pytrainer_main.ddbb.session.query(Activity).filter(and_(Activity.distance.between(activity.distance * (1-percentage), activity.distance * (1+percentage)), Activity.sport == activity.sport)).all()

        count = 1
        for r in records:
            if r.average > activity.average:
                count += 1

        import numpy
        speeds = [r.average for r in records]
        self.label_ranking_range.set_text("%.2f - %.2f %s" % (self.uc.distance(activity.distance * (1-percentage)), self.uc.distance(activity.distance * (1+percentage)), self.uc.unit_distance))
        self.label_ranking_rank.set_text("%s/%s" % (count, len(records)))
        self.label_ranking_avg.set_text("%.2f %s" % (self.uc.speed(numpy.average(speeds)), self.uc.unit_speed))
        self.label_ranking_speed.set_text("%.2f %s" % (self.uc.speed(activity.average), self.uc.unit_speed))
        self.label_ranking_stddev.set_text("%.4f" % (self.uc.speed(numpy.std(speeds))))
        self.label_ranking_dev.set_text("%+.2fσ" % ((activity.average - numpy.average(speeds)) / numpy.std(speeds)))

        rank_store = Gtk.ListStore(
            GObject.TYPE_INT,       #id
            GObject.TYPE_INT,       #rank
            GObject.TYPE_STRING,    #date
            GObject.TYPE_FLOAT,     #distance
            GObject.TYPE_INT,       #time
            GObject.TYPE_FLOAT,     #speed
            GObject.TYPE_FLOAT,     #pace
            GObject.TYPE_STRING,    #color
            )

        length = len(records)
        rec_set = [0,]
        for r in range(max(count-3, 1) if count>1 else count, min(count+3, length-2) if count < length else count):
            rec_set.append(r)
        if length>1 and count!=length:
            rec_set.append(-1)

        for i in rec_set:
            r = records[i]
            iter = rank_store.append()
            rank = length if i==-1 else i+1
            rank_store.set (
                iter,
                0, i,
                1, rank,
                2, str(r.date),
                3, r.distance,
                4, r.duration,
                5, r.average,
                6, r.pace,
                7, '#3AA142' if rank==count else '#000000',
            )

            for c in self.rankingTreeView.get_columns()[:-1]:
                for cr in c.get_cells():
                    if type(cr)==Gtk.CellRendererText:
                        cr.set_property('foreground', 'gray')

        self.rankingTreeView.set_model(rank_store)

    def actualize_dayview(self, date):
        logging.debug(">>")
        self.d_distance_unit.set_text(self.uc.unit_distance)
        self.d_speed_unit.set_text(self.uc.unit_speed)
        self.d_maxspeed_unit.set_text(self.uc.unit_speed)
        self.d_pace_unit.set_text(self.uc.unit_pace)
        self.d_maxpace_unit.set_text(self.uc.unit_pace)
        if self.activeSport:
            sport = self._sport_service.get_sport_by_name(self.activeSport)
        else:
            sport = None
        activity_list = self.pytrainer_main.activitypool.get_activities_for_day(date, sport=sport)

        tbeats, distance, calories, timeinseconds, beats, maxbeats, maxspeed, average, maxpace, pace, totalascent, totaldescent = self._totals_from_activities(activity_list)
        if timeinseconds:
            self.dayview.set_sensitive(1)
        else:
            self.dayview.set_sensitive(0)
        self.day_distance.set_text("%0.2f" %distance)
        hour,min,sec = second2time(timeinseconds)
        self.day_hour.set_text("%d" %hour)
        self.day_minute.set_text("%02d" %min)
        self.day_second.set_text("%02d" %sec)
        if tbeats:
            self.day_beats.set_text("%0.0f" %tbeats)
        else:
            self.day_beats.set_text("")
        self.day_maxbeats.set_text("%0.0f" %maxbeats)
        if average:
            self.day_average.set_text("%0.2f" %average)
        else:
            self.day_average.set_text("")
        self.day_maxspeed.set_text("%0.2f" %maxspeed)
        self.day_pace.set_text("%s" %pace)
        self.day_maxpace.set_text("%s" %maxpace)
        self.day_ascdesc.set_text("%d/%d" %(int(totalascent),int(totaldescent)))
        self.day_calories.set_text("%0.0f" %calories)
        self.day_topic.set_text(str(date))
        logging.debug("<<")

    def actualize_daygraph(self,record_list):
        logging.debug(">>")
        if len(record_list)>0:
            self.day_vbox.set_sensitive(1)
        else:
            self.day_vbox.set_sensitive(0)
        self.drawareaday.drawgraph(record_list)
        logging.debug("<<")

    def actualize_map(self,activity, full_screen=False):
        logging.debug(">>")
        if self.mapviewer and self.mapviewer_fs:
            #Check which type of map viewer to use
            if self.radiobuttonOSM.get_active():
                #Use OSM to draw map
                logging.debug("Using OSM to draw map....")
                from pytrainer.extensions.osm import Osm
                htmlfile = Osm(data_path=self.data_path, waypoint=self.pytrainer_main.waypoint, pytrainer_main=self.parent).drawMap(activity, self.comboMapLineType.get_active())
            elif self.radiobuttonGMap.get_active():
                #Use Google to draw map
                logging.debug("Using Google to draw map")
                from pytrainer.extensions.googlemaps import Googlemaps
                htmlfile = Googlemaps(data_path=self.data_path, waypoint=self.pytrainer_main.waypoint, pytrainer_main=self.parent).drawMap(activity, self.comboMapLineType.get_active())
            else:
                #Unknown map type...
                logging.error("Unknown map viewer requested")
                htmlfile = self.mapviewer.createErrorHtml()
            logging.debug("Displaying htmlfile: %s" % htmlfile)
            if full_screen:
                logging.debug("Displaying in full screen mode")
                self.mapviewer_fs.display_map(htmlfile=htmlfile)
            else:
                logging.debug("Displaying in embedded mode")
                self.mapviewer.display_map(htmlfile=htmlfile)
        logging.debug("<<")

    def actualize_weekview(self, date_range):
        logging.debug(">>")
        self.week_date.set_text("%s - %s (%d)" % (date_range.start_date.strftime("%a %d %b"), date_range.end_date.strftime("%a %d %b"), int(date_range.end_date.strftime("%V"))) )
        if self.activeSport:
            sport = self._sport_service.get_sport_by_name(self.activeSport)
        else:
            sport = None
        activity_list = self.pytrainer_main.activitypool.get_activities_period(date_range, sport=sport)
        tbeats, distance, calories, timeinseconds, beats, maxbeats, maxspeed, average, maxpace, pace, totalascent, totaldescent = self._totals_from_activities(activity_list)
        if timeinseconds:
            self.weekview.set_sensitive(1)
        else:
            self.weekview.set_sensitive(0)

        self.w_distance_unit.set_text(self.uc.unit_distance)
        self.w_speed_unit.set_text(self.uc.unit_speed)
        self.w_maxspeed_unit.set_text(self.uc.unit_speed)
        self.w_pace_unit.set_text(self.uc.unit_pace)
        self.w_maxpace_unit.set_text(self.uc.unit_pace)
        self.weeka_distance.set_text("%0.2f" %distance)
        hour,min,sec = second2time(timeinseconds)
        self.weeka_hour.set_text("%d" %hour)
        self.weeka_minute.set_text("%02d" %min)
        self.weeka_second.set_text("%02d" %sec)
        self.weeka_maxbeats.set_text("%0.0f" %(maxbeats))
        self.weeka_beats.set_text("%0.0f" %(tbeats))
        self.weeka_average.set_text("%0.2f" %average)
        self.weeka_maxspeed.set_text("%0.2f" %maxspeed)
        self.weeka_pace.set_text(pace)
        self.weeka_maxpace.set_text(maxpace)
        self.weeka_ascdesc.set_text("%d/%d" %(int(totalascent),int(totaldescent)))
        self.weeka_calories.set_text("%0.0f" %calories)
        self.weekview.set_sensitive(1)
        self.drawareaweek.drawgraph(activity_list, date_range.start_date)
        logging.debug("<<")

    def actualize_monthview(self, date_range, nameMonth, daysInMonth):
        logging.debug(">>")
        self.month_date.set_text(nameMonth)
        if self.activeSport:
            sport = self._sport_service.get_sport_by_name(self.activeSport)
        else:
            sport = None
        activity_list = self.pytrainer_main.activitypool.get_activities_period(date_range, sport=sport)
        tbeats, distance, calories, timeinseconds, beats, maxbeats, maxspeed, average, maxpace, pace, totalascent, totaldescent = self._totals_from_activities(activity_list)
        if timeinseconds:
            self.monthview.set_sensitive(1)
        else:
            self.monthview.set_sensitive(0)
        self.m_distance_unit.set_text(self.uc.unit_distance)
        self.m_speed_unit.set_text(self.uc.unit_speed)
        self.m_maxspeed_unit.set_text(self.uc.unit_speed)
        self.m_pace_unit.set_text(self.uc.unit_pace)
        self.m_maxpace_unit.set_text(self.uc.unit_pace)

        self.montha_distance.set_text("%0.2f" %distance)
        hour,min,sec = second2time(timeinseconds)
        self.montha_hour.set_text("%d" %hour)
        self.montha_minute.set_text("%02d" %min)
        self.montha_second.set_text("%02d" %sec)
        self.montha_maxbeats.set_text("%0.0f" %(maxbeats))
        self.montha_beats.set_text("%0.0f" %(tbeats))
        self.montha_average.set_text("%0.2f" %average)
        self.montha_maxspeed.set_text("%0.2f" %maxspeed)
        self.montha_pace.set_text(pace)
        self.montha_maxpace.set_text(maxpace)
        self.montha_ascdesc.set_text("%d/%d" %(int(totalascent),int(totaldescent)))
        self.montha_calories.set_text("%0.0f" %calories)
        self.drawareamonth.drawgraph(activity_list, daysInMonth)
        logging.debug("<<")

    def actualize_yearview(self, date_range, year):
        logging.debug(">>")
        self.year_date.set_text("%d" %int(year))
        if self.activeSport:
            sport = self._sport_service.get_sport_by_name(self.activeSport)
        else:
            sport = None
        activity_list = self.pytrainer_main.activitypool.get_activities_period(date_range, sport=sport)
        tbeats, distance, calories, timeinseconds, beats, maxbeats, maxspeed, average, maxpace, pace, totalascent, totaldescent = self._totals_from_activities(activity_list)
        if timeinseconds:
            self.yearview.set_sensitive(1)
        else:
            self.yearview.set_sensitive(0)
            self.drawareayear.drawgraph([])
        self.y_distance_unit.set_text(self.uc.unit_distance)
        self.y_speed_unit.set_text(self.uc.unit_speed)
        self.y_maxspeed_unit.set_text(self.uc.unit_speed)
        self.y_pace_unit.set_text(self.uc.unit_pace)
        self.y_maxpace_unit.set_text(self.uc.unit_pace)
        self.yeara_distance.set_text("%0.2f" %distance)
        hour,min,sec = second2time(timeinseconds)
        self.yeara_hour.set_text("%d" %hour)
        self.yeara_minute.set_text("%02d" %min)
        self.yeara_second.set_text("%02d" %sec)
        self.yeara_beats.set_text("%0.0f" %tbeats)
        self.yeara_maxbeats.set_text("%0.0f" %(maxbeats))
        self.yeara_average.set_text("%0.2f" %average)
        self.yeara_maxspeed.set_text("%0.2f" %maxspeed)
        self.yeara_pace.set_text(pace)
        self.yeara_maxpace.set_text(maxpace)
        self.yeara_ascdesc.set_text("%d/%d " %(totalascent,totaldescent))
        self.yeara_calories.set_text("%0.0f" %calories)
        self.drawareayear.drawgraph(activity_list)
        logging.debug("<<")

    def actualize_athleteview(self, athlete):
        logging.debug(">>")
        self.labelName.set_text(athlete.name)
        self.labelDOB.set_text(athlete.age)
        self.labelHeight.set_text(athlete.height+" cm")

        #Create history treeview
        history_store = Gtk.ListStore(
            GObject.TYPE_INT,       #id
            GObject.TYPE_STRING,    #date
            GObject.TYPE_FLOAT,     #weight
            GObject.TYPE_FLOAT,     #body fat %
            GObject.TYPE_INT,       #resting HR
            GObject.TYPE_INT        #max HR
            )
        for data in athlete.data:
            iter = history_store.append()
            history_store.set (
                iter,
                0, data['id_athletestat'],
                1, str(data['date']),
                2, data['weight'],
                3, data['bodyfat'],
                4, data['restinghr'],
                5, data['maxhr'],
                )
        self.athleteTreeView.set_model(history_store)
        self.grapher.drawAthleteGraph(athlete=athlete, box=self.boxAthleteGraph)
        logging.debug("<<")

    def actualize_statsview(self, stats, record_list):
        logging.debug(">>")
        self.labelTotalDistance.set_text(str(round(stats.data['total_distance'])) + " km")
        self.labelTotalDuration.set_text(str(round(stats.data['total_duration'] / 3600)) + " hours")
        # skip date format if no stats are saved yet
        try:
            self.labelStartDate.set_text(stats.data['start_date'].strftime('%Y-%m-%d'))
            self.labelEndDate.set_text(stats.data['end_date'].strftime('%Y-%m-%d'))
        except KeyError:
            pass

        data = self.parent.stats.data

        store = Gtk.ListStore(
            GObject.TYPE_INT,
            GObject.TYPE_STRING,
            GObject.TYPE_INT,
            GObject.TYPE_INT,
            GObject.TYPE_FLOAT,
            GObject.TYPE_FLOAT,
            GObject.TYPE_FLOAT,
            GObject.TYPE_INT,
            GObject.TYPE_INT,
            GObject.TYPE_INT,
            GObject.TYPE_FLOAT
            )
        for s in data['sports'].values():
            iter = store.append()

            c = 0
            store.set (iter, c, c)
            c += 1
            store.set (iter, c, s['name'])
            c += 1
            store.set (iter, c, s['count'])
            for f in data['fields'][3:]:
                c += 1
                store.set (iter, c, s['total_'+f])
            c += 1
            if s['total_duration']!=0:    # Avoid division by zero if 0 length sport activity exists in DB
                store.set (iter, c, s['total_distance'] / s['total_duration'] * 3600.)
                for f in data['fields']:
                    c += 1
                    store.set (iter, c, s[f])

        self.statsTreeView.set_model(store)
        self.statsTreeView.set_rules_hint(True)

        store.set_sort_column_id(3, Gtk.SortType.DESCENDING)

        self.drawareatotal.drawgraph(record_list)

        logging.debug("<<")

    def actualize_listview(self,record_list):
        logging.debug(">>")
        store = Gtk.ListStore(
            GObject.TYPE_INT,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_FLOAT,
            GObject.TYPE_STRING,
            GObject.TYPE_STRING,
            GObject.TYPE_INT,
            GObject.TYPE_INT,
            GObject.TYPE_FLOAT,
            GObject.TYPE_FLOAT,
            GObject.TYPE_INT,
            object)
        for i in record_list:
            try:
                hour,min,sec = second2time(i.duration)
            except  (ValueError, TypeError):
                hour,min,sec = (0,0,0)
            _time = "%2d:%02d:%02d" %(hour,min,sec)
            try:
                _id = i.id
            except (ValueError, TypeError) as e:
                logging.debug("Unable to determine id for record: %s. Exception: %s", i, e)
                continue
            _title = i.title
            _date = str(i.date)
            try:
                _distance = float(i.distance)
            except (ValueError, TypeError):
                _distance = 0
            _sport = i.sport.name
            try:
                _average = i.average
            except (ValueError, TypeError):
                _average = 0
            try:
                _pace = i.pace
            except (ValueError, TypeError):
                _pace = None
            try:
                _calories = i.calories
            except (ValueError, TypeError):
                _calories = 0
            try:
                _beats = round(i.beats)
            except (ValueError, TypeError) as e:
                logging.debug("Unable to parse beats for record: %s. Exception: %s", i.id, e)
                _beats = 0.0
            try:
                _maxbeats = round(i.maxbeats)
            except (ValueError, TypeError) as e:
                logging.debug("Unable to parse maxbeats for record: %s. Exception: %s", i.id, e)
                _maxbeats = 0.0

            iter = store.append()
            store.set (
                iter,
                0, _id,
                1, _title,
                2, _date,
                3, _distance,
                4, _sport,
                5, _time,
                6, _beats,
                7, _maxbeats,
                8, _average,
                9, _pace,
                10, _calories
                )
        #self.allRecordTreeView.set_headers_clickable(True)
        self.allRecordTreeView.set_model(store)
        self.allRecordTreeView.set_rules_hint(True)
        logging.debug("<<")

    def actualize_waypointview(self,record_list,default_waypoint,redrawmap = 1):
        logging.debug(">>")
        #redrawmap: indica si tenemos que refrescar tb el mapa. 1 si 0 no
        #waypoint list tiene:
        #id_waypoint,lat,lon,ele,comment,time,name,sym
        #Laas columnas son:
        #column_names=[_("id"),_("Waypoint")]

        store = Gtk.ListStore(
            GObject.TYPE_INT,
            GObject.TYPE_STRING,
            object)
        iterOne = False
        iterDefault = False
        counter = 0
        default_id = 0
        for i in record_list:
            iter = store.append()
            if not iterOne:
                iterOne = iter
            if int(i[0])==default_waypoint:
                iterDefault = iter
                default_id = counter
            store.set (
                iter,
                0, int(i[0]),
                1, str(i[6])
                )
            counter+=1

        self.waypointTreeView.set_model(store)
        if iterDefault:
            self.waypointTreeView.get_selection().select_iter(iterDefault)
        elif iterOne:
            self.waypointTreeView.get_selection().select_iter(iterOne)
        if len(record_list) > 0:
            self.waypoint_latitude.set_text(str(record_list[default_id][1]))
            self.waypoint_longitude.set_text(str(record_list[default_id][2]))
            self.waypoint_name.set_text(str(record_list[default_id][6]))
            self.waypoint_description.set_text(str(record_list[default_id][4]))
            self.set_waypoint_type(str(record_list[default_id][7]))
        if redrawmap == 1 and self.waypointeditor:
            self.waypointeditor.createHtml(default_waypoint)
            self.waypointeditor.drawMap()
        logging.debug("<<")

    def set_waypoint_type(self, type):
        x = 0
        tree_model = self.waypoint_type.get_model()
        if tree_model is not None:
            #iter = tree_model.get_iter_root()
            for item in tree_model:
                #if isinstance(item, Gtk.TreeModelRow):
                if item[0] == type:
                    self.waypoint_type.set_active(x)
                    return
                x += 1
        self.waypoint_type.insert_text(0, type)
        self.waypoint_type.set_active(0)
        return


    def on_waypointTreeView_button_press(self, treeview, event):
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pthinfo = treeview.get_path_at_pos(x, y)
        if pthinfo is not None:
            path, col, cellx, celly = pthinfo
            treeview.grab_focus()
            treeview.set_cursor(path, col, 0)
            if event.button == 1:
                selected,iter = treeview.get_selection().get_selected()
                id_waypoint=selected.get_value(iter,0)
                self.parent.refreshWaypointView(id_waypoint)
        return False

    def on_listareasearch_clicked(self, widget):
        lisOpt = {
            _("Title"):"title",
            _("Date"):"date",
            _("Distance"):"distance",
            _("Sport"):"sport",
            _("Time"):"time",
            _("Beats"):"beats",
            _("Maxbeats"):"maxbeats",
            _("Average"):"average",
            _("Pace"):"pace",
            _("Calories"):"calories"
            }
        self.listsearch.title = gtk_str(self.lsa_searchvalue.get_text())
        self.listsearch.sport = self.lsa_sport.get_active()
        self.listsearch.past = self.lsa_past.get_active()
        self.listsearch.duration = self.lsa_duration.get_active()
        self.listsearch.distance = self.lsa_distance.get_active()
        self.parent.refreshListView(self.listsearch.condition)

    def on_listareareset_clicked(self, widget):
        self.listsearch.reset_lsa()
        self.parent.refreshListView(self.listsearch.condition)

    def create_menulist(self,columns):
        for i, column_dict in enumerate(columns):
            if 'visible' in column_dict and not column_dict['visible']:
                pass
            else:
                item = Gtk.CheckMenuItem(column_dict['name'])
                #self.lsa_searchoption.append_text(name)
                item.connect("button_press_event", self.on_menulistview_activate, i)
                self.menulistviewOptions.append(item)
        self.menulistviewOptions.show_all()

    def on_menulistview_activate(self,widget,widget2,widget_position):
        listMenus = {
            0:"title",
            1:"date",
            2:"distance",
            3:"sport",
            4:"time",
            5:"beats",
            6:"maxbeats",
            7:"average",
            8:"pace",
            9:"calories" }

        items = self.menulistviewOptions.get_children()
        if items[widget_position-1].get_active():
            newValue = "False"
        else:
            newValue = "True"
        menufile = XMLParser(self.fileconf)
        menufile.setValue("listviewmenu",listMenus[widget_position-1],newValue)
        self.showAllRecordTreeViewColumns()

    def showAllRecordTreeViewColumns(self):
        menufile = XMLParser(self.fileconf)
        listMenus = {
            "id_record":0,
            "title":1,
            "date":2,
            "distance":3,
            "sport":4,
            "time":5,
            "beats":6,
            "maxbeats":7,
            "average":8,
            "pace":9,
            "calories":10 }
        columns = self.allRecordTreeView.get_columns()
        menuItems = self.menulistviewOptions.get_children()
        for column in listMenus:
            visible = menufile.getValue("listviewmenu",column)
            if visible == "True":
                visible = True
            else:
                visible = False
            numcolumn = listMenus[column]
            #show the selected columns
            columns[numcolumn].set_visible(visible)
            #select the choice in the menu
            if numcolumn != 0 and self.menublocking != 1:
                menuItems[numcolumn-1].set_active(visible)
        self.menublocking = 1

    def zoom_graph(self, y1limits=None, y1color=None, y1_linewidth=1):
        logging.debug(">>")
        logging.debug("Reseting graph Y axis with ylimits: %s", y1limits)
        self.drawarearecord.drawgraph(self.record_list,self.laps, y1limits=y1limits, y1color=y1color, y1_linewidth=y1_linewidth)
        logging.debug("<<")

    def update_athlete_item(self, idx, date, weight, bf, restingHR, maxHR):
        logging.debug(">>")
        #Prepare vars
        idx = str(idx)
        date = str(date)
        weight = str(weight)
        bf = str(bf)
        restingHR = str(restingHR)
        maxHR = str(maxHR)
        #Set vars
        self.labelAthleteIdx.set_text(idx)
        self.entryAthleteDate.set_text(date)
        self.entryAthleteWeight.set_text(weight)
        self.entryAthleteBF.set_text(bf)
        self.entryAthleteRestingHR.set_text(restingHR)
        self.entryAthleteMaxHR.set_text(maxHR)
        logging.debug("<<")

    ######################
    ## Lista de eventos ##
    ######################

    def on_xaxischange(self, widget, data=None, activity=None):
        '''Handler for record graph axis selection  changes'''
        if widget.get_active():
            activity.x_axis = data
            self.actualize_recordgraph(activity)

    def on_xlapschange(self, widget, activity=None):
        if widget.get_active():
            activity.show_laps = True
        else:
            activity.show_laps = False
        self.actualize_recordgraph(activity)

    def on_gridchange(self, widget, axis=None, activity=None):
        '''Handler for record graph grid selection changes'''
        if axis == 'y1':
            activity.y1_grid = not activity.y1_grid
        elif axis == 'y2':
            activity.y2_grid = not activity.y2_grid
        elif axis == 'x':
            activity.x_grid = not activity.x_grid
        self.actualize_recordgraph(activity)

    def on_y1colorchange(self, widget, box, graphdata, activity):
        '''Hander for changes to y1 color selection'''
        logging.debug("Setting %s to color %s", graphdata, widget.get_color())
        if activity.x_axis == "distance":
            activity.distance_data[graphdata].set_color(str(widget.get_color()))
        elif activity.x_axis == "time":
            activity.time_data[graphdata].set_color(str(widget.get_color()))
        #Replot the activity
        self.actualize_recordgraph(activity)

    def on_y2colorchange(self, widget, box, graphdata, activity):
        '''Hander for changes to y2 color selection'''
        logging.debug("Setting %s to color %s", graphdata, widget.get_color())
        if activity.x_axis == "distance":
            activity.distance_data[graphdata].set_color(None, str(widget.get_color()))
        elif activity.x_axis == "time":
            activity.time_data[graphdata].set_color(None, str(widget.get_color()))
        #Replot the activity
        self.actualize_recordgraph(activity)

    def on_y1change(self, widget, box, graphdata, activity):
        '''Hander for changes to y1 selection'''
        logging.debug("Y1 selection toggled: %s", graphdata)
        #Loop through all options at set data correctly
        for child in box.get_children():
            if activity.x_axis == "distance":
                for item in activity.distance_data:
                    if activity.distance_data[item].title == child.get_label():
                        logging.debug("Setting %s to %s", item, child.get_active())
                        activity.distance_data[item].show_on_y1 = child.get_active()
            elif activity.x_axis == "time":
                for item in activity.time_data:
                    if activity.time_data[item].title == child.get_label():
                        logging.debug("Setting %s to %s", item, child.get_active())
                        activity.time_data[item].show_on_y1 = child.get_active()
        #Replot the activity
        self.actualize_recordgraph(activity)

    def on_y2change(self, widget, box, graphdata, activity):
        '''Hander for changes to y2 selection'''
        logging.debug("Y2 selection toggled: %s", graphdata)
        #Loop through all options at set data correctly
        for child in box.get_children():
            if activity.x_axis == "distance":
                for item in activity.distance_data:
                    if activity.distance_data[item].title == child.get_label():
                        logging.debug("Setting %s to %s", item, child.get_active())
                        activity.distance_data[item].show_on_y2 = child.get_active()
            elif activity.x_axis == "time":
                for item in activity.time_data:
                    if activity.time_data[item].title == child.get_label():
                        logging.debug("Setting %s to %s", item, child.get_active())
                        activity.time_data[item].show_on_y2 = child.get_active()
        #Replot the activity
        self.actualize_recordgraph(activity)

    def on_setlimits(self, widget, activity, reset, data):
        '''Handler for setting graph limits buttons'''
        if data is None:
            logging.debug("Resetting graph limits...")
            activity.x_limits_u = (None, None)
            activity.y1_limits_u = (None, None)
            activity.y2_limits_u = (None, None)
            #Replot the activity
            self.actualize_recordgraph(activity)
        else:
            #Setting to limits in boxes
            logging.debug("Setting graph limits...")
            #Determine contents of boxes...
            xmin = self._float_or(gtk_str(data['xminlabel'].get_text()), activity.x_limits[0])
            xmax = self._float_or(gtk_str(data['xmaxlabel'].get_text()), activity.x_limits[1])
            y1min = self._float_or(gtk_str(data['y1minlabel'].get_text()), activity.y1_limits[0])
            y1max = self._float_or(gtk_str(data['y1maxlabel'].get_text()), activity.y1_limits[1])
            y2min = self._float_or(gtk_str(data['y2minlabel'].get_text()), activity.y2_limits[0])
            y2max = self._float_or(gtk_str(data['y2maxlabel'].get_text()), activity.y2_limits[1])
            logging.debug("Setting graph limits x: (%s,%s), y1: (%s,%s), y2: (%s,%s)",
                          xmin, xmax, y1min, y1max, y2min, y2max)
            activity.x_limits_u = (xmin, xmax)
            activity.y1_limits_u = (y1min, y1max)
            activity.y2_limits_u = (y2min, y2max)
            #Replot the activity
            self.actualize_recordgraph(activity)

    def on_window1_configure_event(self, widget, event):
        #print widget #window widget
        #print event # resize event
        self.size = self.window1.get_size()

    def on_buttonShowOptions_clicked(self, widget):
        position_set = self.hpaned1.get_property('position-set')
        if position_set:
            #Currently not showing options - show them
            self.hpaned1.set_property('position-set', False)
            self.buttonShowOptions.set_tooltip_text(_('Hide graph display options') )
        else:
            #Hide options
            self.hpaned1.set_position(0)
            self.buttonShowOptions.set_tooltip_text(_('Show graph display options') )
        logging.debug('Position set: %s', self.hpaned1.get_property('position-set'))

    def on_buttonGraphHideOptions_clicked(self, widget):
        logging.debug('on_buttonGraphHideOptions_clicked')
        self.buttonGraphHideOptions.hide()
        self.scrolledwindowGraphOptions.hide()
        #for child in self.graph_data_hbox.get_children():
        #    if isinstance(child, Gtk.Frame):
        #        child.hide()
        self.buttonGraphShowOptions.show()


    def on_buttonGraphShowOptions_clicked(self, widget):
        logging.debug('on_buttonGraphShowOptions_clicked')
        self.buttonGraphShowOptions.hide()
        #for child in self.graph_data_hbox.get_children():
        #    if isinstance(child, Gtk.Frame):
        #        child.show()
        self.scrolledwindowGraphOptions.show()
        self.buttonGraphHideOptions.show()

    def on_buttonRedrawMap_clicked(self, widget):
        logging.debug('on_buttonRedrawMap_clicked')
        self.parent.refreshMapView()

    def on_radiobuttonMap_toggled(self, widget):
        #Ignore the deselected toggle event
        if widget.get_active() == False:
            return
        logging.debug('on_radiobuttonMap_toggled %s activated', widget.get_name())
        self.parent.refreshMapView()

    def on_comboMapLineType_changed(self, widget):
        logging.debug('on_comboMapLineType_changed %s = %s', widget.get_name(), widget.get_active())
        self.parent.refreshMapView()

    def on_hpaned1_move_handle(self, widget):
        logging.debug("Handler %s", widget)

    def on_spinbuttonY1_value_changed(self, widget):
        y1min = self.spinbuttonY1Min.get_value()
        y1max = self.spinbuttonY1Max.get_value()
        #Check to see if the min and max have the same...
        if y1min == y1max:
            if widget.get_name() == "spinbuttonY1Min": #User was changing the min spinbutton, so move max up
                y1max += 1
            else:   #Move min down
                y1min -= 1
        self.y1_limits=(y1min, y1max)
        self.zoom_graph(y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)

    def on_buttonResetGraph_clicked(self, widget):
        #self.zoom_graph()
        #Reset stored values
        self.y1_limits = None
        self.y1_color = None
        self.y1_linewidth = 1
        self.zoom_graph()

    def on_colorbuttonY1LineColor_color_set(self, widget):
        y1color = widget.get_color()
        cs = y1color.to_string()
        self.y1_color = cs[0:3] + cs[5:7] + cs[9:11]
        self.drawarearecord.drawgraph(self.record_list,self.laps, y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)

    def on_spinbuttonY1LineWeight_value_changed(self, widget):
        self.y1_linewidth = self.spinbuttonY1LineWeight.get_value_as_int()
        self.drawarearecord.drawgraph(self.record_list,self.laps, y1limits=self.y1_limits, y1color=self.y1_color, y1_linewidth=self.y1_linewidth)

    def on_edit_clicked(self,widget):
        selected,iter = self.recordTreeView.get_selection().get_selected()
        id_record = selected.get_value(iter,0)
        self.parent.editRecord(id_record, self.selected_view)

    def on_remove_clicked(self,widget):
        selected,iter = self.recordTreeView.get_selection().get_selected()
        id_record = selected.get_value(iter,0)
        self.parent.removeRecord(id_record)

    def on_export_csv_activate(self,widget):
        self.parent.exportCsv()

    def on_newrecord_clicked(self,widget):
        if self.selected_view  == 'athlete':
            #print 'New athlete'
            self.on_athleteTreeView_edit( None, None)
        else:
            self.parent.newRecord(view=self.selected_view)

    def on_edituser_activate(self,widget):
        self.parent.editProfile()

    def on_calendar_doubleclick(self,widget):
        self.parent.newRecord()

    def on_sportlist_changed(self,widget):
        logging.debug("--")
        if gtk_str(self.sportlist.get_active_text()) != self.activeSport:
            self.activeSport = gtk_str(self.sportlist.get_active_text())
            self.parent.refreshListRecords()
            self.parent.refreshGraphView(self.selected_view)
        else:
            logging.debug("on_sportlist_changed called with no change")

    def on_page_change(self,widget,gpointer,page):
        logging.debug("--")
        if page == 0:
            self.selected_view="record"
        elif page == 1:
            self.selected_view="day"
        elif page == 2:
            self.selected_view="week"
        elif page == 3:
            self.selected_view="month"
        elif page == 4:
            self.selected_view="year"
        elif page == 5:
            self.selected_view="athlete"
        elif page == 6:
            self.selected_view="stats"
        else:
            self.selected_view="record"
        self.parent.refreshGraphView(self.selected_view)

    def on_recordpage_change(self,widget,gpointer,page):
        if page == 0:
            selected_view="info"
        elif page == 1:
            selected_view="graphs"
        elif page == 2:
            selected_view="map"
        elif page == 3:
            selected_view="heartrate"
        elif page == 4:
            selected_view="analytics"
        self.parent.refreshRecordGraphView(selected_view)

    def on_showmap_clicked(self,widget):
        self.infoarea.hide()
        self.maparea.show()
        self.parent.refreshMapView(full_screen=True)

    def on_hidemap_clicked(self,widget):
        self.maparea.hide()
        self.infoarea.show()

    def on_btnShowLaps_toggled(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_day_combovalue_changed(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_week_combovalue_changed(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_month_combovalue_changed(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_year_combovalue_changed(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_total_combovalue_changed(self,widget):
        logging.debug("--")
        self.parent.refreshGraphView(self.selected_view)

    def on_calendar_selected(self, widget):
        logging.debug(">>")
        logging.debug("Block (%s) | Selected view: %s", self.block, self.selected_view)
        if self.block:
            self.block = False
        else:
            if self.selected_view == "record":
                self.recordview.set_current_page(0)
                self.parent.refreshRecordGraphView("info")
            self.parent.refreshListRecords()
            self.parent.refreshGraphView(self.selected_view)
        logging.debug("<<")

    def on_calendar_changemonth(self,widget):
        logging.debug("--")
        self.block = True
        self.notebook.set_current_page(3)
        self.selected_view="month"
        self.parent.refreshListRecords()
        self.parent.refreshGraphView(self.selected_view)

    def on_calendar_next_year(self,widget):
        logging.debug("--")
        self.block = True
        self.notebook.set_current_page(4)
        self.selected_view="year"
        self.parent.refreshListRecords()
        self.parent.refreshGraphView(self.selected_view)

    def on_classicview_activate(self,widget):
        self.waypointarea.hide()
        self.listarea.hide()
        #self.athletearea.hide()
        self.selected_view = "record"
        self.classicarea.show()

    def on_listview_activate(self,widget):
        self.waypointarea.hide()
        self.classicarea.hide()
        #self.athletearea.hide()
        self.selected_view = "listview"
        #self.parent.refreshListView()
        self.parent.refreshListView(self.listsearch.condition)
        self.listarea.show()

    def on_athleteview_activate(self,widget=None):
        #self.waypointarea.hide()
        #self.classicarea.hide()
        #self.listarea.hide()
        self.parent.refreshAthleteView()
        #self.athletearea.show()

    def on_statsview_activate(self,widget=None):
        self.parent.refreshStatsView()

    def on_waypointsview_activate(self,widget):
        self.listarea.hide()
        self.classicarea.hide()
        #self.athletearea.hide()
        self.parent.refreshWaypointView()
        self.waypointarea.show()

    def on_menu_importdata_activate(self,widget):
        self.parent.importData()

    def on_extensions_activate(self,widget):
        self.parent.editExtensions()

    def on_gpsplugins_activate(self,widget):
        self.parent.editGpsPlugins()
    #hasta aqui revisado

    def on_recordTreeView_button_press_event(self, treeview, event):
        ''' Handler for clicks on recordTreeview list (all records for a day)
            event.button = mouse button pressed (i.e. 1 = left, 3 = right)
        '''
        logging.debug(">>")
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pthinfo = treeview.get_path_at_pos(x, y)
        if pthinfo is not None:
            path, col, cellx, celly = pthinfo
            treeview.grab_focus()
            treeview.set_cursor(path, col, 0)
            if event.button == 3:
                selected,iter = treeview.get_selection().get_selected()
                #Por si hay un registro (malo) sin fecha, pa poder borrarlo
                try:
                    date = self.parent.date.getDate()
                except:
                    date = None
                self.popup.show(selected.get_value(iter,0), event.button, time, str(date))
            elif event.button == 1:
                self.notebook.set_current_page(0)
                self.parent.refreshGraphView("record")
        logging.debug("<<")
        return False

    def on_allRecordTreeView_button_press(self, treeview, event):
        ''' Handler for clicks on listview list
            event.button = mouse button pressed (i.e. 1 = left, 3 = right)
        '''
        logging.debug(">>")
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pthinfo = treeview.get_path_at_pos(x, y)
        if pthinfo is not None:
            path, col, cellx, celly = pthinfo
            treeview.grab_focus()
            treeview.set_cursor(path, col, 0)
            if event.button == 3:
                selected,iter = treeview.get_selection().get_selected()
                #Por si hay un registro (malo) sin fecha, pa poder borrarlo
                try:
                    date = self.parent.date.getDate()
                except:
                    pass
                self.popup.show(selected.get_value(iter,0), event.button, time, selected.get_value(iter,2))
            elif event.button == 1:
                self.notebook.set_current_page(0)
                self.parent.refreshGraphView("record")
        logging.debug("<<")
        return False

    def actualize_recordTreeView(self, date):
        logging.debug(">>")
        iterOne = False
        store = Gtk.TreeStore(
            GObject.TYPE_INT,           #record_id
            GObject.TYPE_STRING,        #Time
            GObject.TYPE_STRING,        #Sport
            GObject.TYPE_STRING,        #Distance
            object)
        if self.activeSport:
            sport = self._sport_service.get_sport_by_name(self.activeSport)
        else:
            sport = None
        for activity in self.pytrainer_main.activitypool.get_activities_for_day(date, sport=sport):
            iter = store.append(None)
            if not iterOne:
                iterOne = iter
            localTime = activity.date_time.strftime("%H:%M")
            dist = self.uc.distance(activity.distance)
            distance = "%0.2f" % (float(dist) )
            store.set (
                iter,
                0, activity.id,
                1, localTime,
                2, activity.sport.name,
                3, str(distance)  #Needs to be US pref aware....
                )
            for lap in activity.Laps:
                lapNumber = "%s %02d" % (_("lap"), lap.lap_number + 1)
                dist = self.uc.distance(lap.distance)
                distance = "%0.2f" % (float(dist) / 1000.0)
                timeHours = int(lap.duration / 3600)
                timeMin = int((lap.duration / 3600.0 - timeHours) * 60)
                timeSec = lap.duration - (timeHours * 3600) - (timeMin * 60)
                if timeHours > 0:
                    duration = "%d%s%02d%s%02d%s" % (timeHours, _("h"), timeMin, _("m"), timeSec, _("s"))
                else:
                    duration = "%2d%s%02d%s" % (timeMin, _("m"), timeSec, _("s"))

                child_iter = store.append(iter)
                store.set (
                    child_iter,
                    0, activity.id,
                    1, lapNumber,
                    2, duration,
                    3, distance
                    )
                store.set_sort_column_id(1, Gtk.SortType.ASCENDING)
        self.recordTreeView.set_model(store)
        if iterOne:
            self.recordTreeView.get_selection().select_iter(iterOne)
        logging.debug("<<")

    def parseFloat(self,string):
        try:
            return float(string)
        except:
            return float(0)

    def actualize_calendar(self,record_list):
        logging.debug(">>")
        self.calendar.clear_marks()
        #Mark each day that has activity
        for i in record_list:
            self.calendar.mark_day(i)
        #Turn on displaying of week numbers
        display_options = self.calendar.get_display_options()
        self.calendar.set_display_options(display_options|Gtk.CalendarDisplayOptions.SHOW_WEEK_NUMBERS)
        logging.debug("<<")

    def on_about_activate(self,widget):
        if self.aboutwindow is None:
            self.aboutwindow = About(self.data_path, self.version)
            self.aboutwindow.run()
        else:
            self.aboutwindow.present()

    def getSportSelected(self):
        sport = self.sportlist.get_active()
        if (sport > 0):
            return gtk_str(self.sportlist.get_active_text())
        else:
            return None

    def quit(self, *args):
        window_size = "%d, %d" % self.size

        self.pytrainer_main.profile.setValue("pytraining","window_size", window_size)
        self.parent.quit()
        #sys.exit("Exit!")
        #self.parent.webservice.stop()
        #self.gtk_main_quit()

    def on_yearview_clicked(self,widget):
        self.notebook.set_current_page(2)
        self.selected_view="year"
        self.actualize_yearview()

    def on_recordTree_clicked(self,widget,num,num2):
        selected,iter = widget.get_selection().get_selected()
        self.parent.editRecord(selected.get_value(iter,0), self.selected_view)

    ### athleteview events ###
    def on_athleteTreeView_button_press_event(self, treeview, event):
        x = int(event.x)
        y = int(event.y)
        time = event.time
        pthinfo = treeview.get_path_at_pos(x, y)
        if pthinfo is not None:
            path, col, cellx, celly = pthinfo
            treeview.grab_focus()
            treeview.set_cursor(path, col, 0)
            selected,iter = treeview.get_selection().get_selected()
            if event.button == 3:
                #Right mouse button...
                idx = selected.get_value(iter,0)
                date = selected.get_value(iter,1)
                weight = selected.get_value(iter,2)
                bf = selected.get_value(iter,3)
                restingHR = selected.get_value(iter,4)
                maxHR = selected.get_value(iter,5)
                #print "show popup etc (clicked on idx %s, date %s)" % (idx, date)
                #Show popup menu...
                popup = Gtk.Menu()
                #Edit Entry Item
                menuitem = Gtk.MenuItem(label=_("Edit Entry"))
                menuitem.connect("activate", self.on_athleteTreeView_edit, {'id':idx, 'date':date, 'weight':weight, 'bf':bf, 'restingHR':restingHR, 'maxHR':maxHR})
                popup.attach(menuitem, 0, 1, 0, 1)
                #New Entry Item
                menuitem = Gtk.MenuItem(label=_("New Entry"))
                menuitem.connect("activate", self.on_athleteTreeView_edit, None)
                popup.attach(menuitem, 0, 1, 1, 2)
                #Separator
                menuitem = Gtk.SeparatorMenuItem()
                popup.attach(menuitem, 0, 1, 2, 3)
                #Delete Entry Item
                menuitem = Gtk.MenuItem(label=_("Delete Entry"))
                menuitem.connect("activate", self.on_athleteTreeView_delete, idx)
                popup.attach(menuitem, 0, 1, 3, 4)
                popup.show_all()
                popup.popup_at_pointer(None)
            else:
                #Left mouse - so display this row
                pass
                '''
                idx = selected.get_value(iter,0)
                date = selected.get_value(iter,1)
                weight = selected.get_value(iter,2)
                bf = selected.get_value(iter,3)
                restingHR = selected.get_value(iter,4)
                maxHR = selected.get_value(iter,5)
                self.update_athlete_item(idx, date, weight, bf, restingHR, maxHR)'''

    def on_athleteTreeView_edit(self, widget, data):
        logging.debug('>>')
        if data is None:
            #New entry...
            logging.debug('New athlete entry')
            title = _('Create Athlete Entry')
            data = {'id': None, 'date': Date().getDate().strftime("%Y-%m-%d"),
                    'weight': None, 'bf': None, 'restingHR': None, 'maxHR': None}
        else:
            logging.debug('Edit existing athlete entry: %s', data)
            title = _('Edit Athlete Entry')
        dialog = Gtk.Dialog(title=title, parent=self.pytrainer_main.windowmain.window1, flags= Gtk.DialogFlags.DESTROY_WITH_PARENT,
                     buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.REJECT,
                      Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT))
        dialog.set_modal(False)
        #Get Content area of dialog
        vbox = dialog.get_content_area()

        #Build data display
        table = Gtk.Table(1,2)
        self.entryList = []
        #Add date
        label = Gtk.Label(label=_("<b>Date</b>"))
        label.set_use_markup(True)
        entry = Gtk.Entry()
        entry.set_text(data['date'])
        self.entryList.append(entry)
        #Date calander widget
        cal = Gtk.Image()
        cal.set_from_stock(Gtk.STOCK_INDEX, Gtk.IconSize.BUTTON)
        calbut = Gtk.Button()
        calbut.add(cal)
        calbut.connect("clicked", self.on_athletecalendar_clicked)
        table.attach(label,0,1,0,1)
        table.attach(entry,1,2,0,1)
        #table.attach(calbut,2,3,0,1) #TODO

        #Add weight
        label = Gtk.Label(label=_("<b>Weight</b>"))
        label.set_use_markup(True)
        entry = Gtk.Entry()
        if data['weight']:
            entry.set_text(str(round(data['weight'], 2)))
        self.entryList.append(entry)
        table.attach(label,0,1,1,2)
        table.attach(entry,1,2,1,2)
        #Add Body fat
        label = Gtk.Label(label=_("<b>Body Fat</b>"))
        label.set_use_markup(True)
        entry = Gtk.Entry()
        if data['bf']:
            entry.set_text(str(round(data['bf'], 2)))
        self.entryList.append(entry)
        table.attach(label,0,1,2,3)
        table.attach(entry,1,2,2,3)
        #Add Resting HR
        label = Gtk.Label(label=_("<b>Resting Heart Rate</b>"))
        label.set_use_markup(True)
        entry = Gtk.Entry()
        if data['restingHR']:
            entry.set_text(str(data['restingHR']))
        self.entryList.append(entry)
        table.attach(label,0,1,3,4)
        table.attach(entry,1,2,3,4)
        #Add Max HR
        label = Gtk.Label(label=_("<b>Max Heart Rate</b>"))
        label.set_use_markup(True)
        entry = Gtk.Entry()
        if data['maxHR']:
            entry.set_text(str(data['maxHR']))
        self.entryList.append(entry)
        table.attach(label,0,1,4,5)
        table.attach(entry,1,2,4,5)

        vbox.add(table)
        vbox.show_all()
        response = dialog.run()
        #dialog.destroy()
        if response == Gtk.ResponseType.ACCEPT:
            #print "on_athleteTreeView_edit save called", data
            data['date'] = gtk_str(self.entryList[0].get_text())
            data['weight'] = gtk_str(self.entryList[1].get_text())
            data['bf'] = gtk_str(self.entryList[2].get_text())
            data['restingHR'] = gtk_str(self.entryList[3].get_text())
            data['maxHR'] = gtk_str(self.entryList[4].get_text())
            self.on_athleteSave(data)
            logging.debug('Athlete data saved: %s', data)
        dialog.destroy()
        logging.debug('<<')


    def on_athleteTreeView_delete(self, widget, data):
        '''User has opted to delete entry'''
        logging.debug(">>")
        msg = _("Delete this database entry?")
        md = Gtk.MessageDialog(self.pytrainer_main.windowmain.window1, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.QUESTION, Gtk.ButtonsType.OK_CANCEL, msg)
        md.set_title(_("Are you sure?"))
        response = md.run()
        md.destroy()
        if response == Gtk.ResponseType.OK:
            logging.debug("User confirmed deletion of athlete entry with id: %s", data)
            self.pytrainer_main.athlete.delete_record(data)
            self.parent.refreshAthleteView()
        else:
            logging.debug("User canceled athlete record deletion for id %s", data)
        logging.debug("<<")

    def on_athleteSave(self, data):
        #Get data in fields
        id_athletestat = data['id']
        date = data['date']
        #Check if valid date supplied
        try:
            _date = dateutil.parser.parse(date).date()
        except (ValueError) as e:
            logging.error("Invalid date %s", e)
            return
        weight = data['weight'] or None
        bodyfat = data['bf'] or None
        restinghr = data['restingHR'] or None
        maxhr = data['maxHR'] or None
        #TODO - are any other fields required?

        #Check if an entry has been edited or is a new one
        if id_athletestat is None or id_athletestat == "":
            #New entry
            logging.debug('Creating new entry with values: date %s, weight %s, bodyfat %s, restinghr %s, maxhr %s', date, weight, bodyfat, restinghr, maxhr)
            self.parent.athlete.insert_athlete_stats(date, weight, bodyfat, restinghr, maxhr)
        else:
            #Edited existing entry
            logging.debug('Updating id_athletestat:%s with values: date %s, weight %s, bodyfat %s, restinghr %s, maxhr %s', id_athletestat, date, weight, bodyfat, restinghr, maxhr)
            self.parent.athlete.update_athlete_stats(id_athletestat, date, weight, bodyfat, restinghr, maxhr)
        self.parent.refreshAthleteView()

    def on_athletecalendar_clicked(self,widget):
        logging.debug(">>")
        calendardialog = WindowCalendar(self.data_path,self)
        calendardialog.run()
        logging.debug("<<")

    def setDate(self,date):
        logging.debug(date)
        #self.entryAthleteDate.set_text(date)

    ######## waypoints events ##########
    def on_savewaypoint_clicked(self,widget):
        selected,iter = self.waypointTreeView.get_selection().get_selected()
        id_waypoint = selected.get_value(iter,0)
        lat = gtk_str(self.waypoint_latitude.get_text())
        lon = gtk_str(self.waypoint_longitude.get_text())
        name = gtk_str(self.waypoint_name.get_text())
        desc = gtk_str(self.waypoint_description.get_text())
        sym = gtk_str(self.waypoint_type.get_active_text())
        self.parent.updateWaypoint(id_waypoint,lat,lon,name,desc,sym)

    def on_removewaypoint_clicked(self,widget):
        selected,iter = self.waypointTreeView.get_selection().get_selected()
        id_waypoint = selected.get_value(iter,0)
        self.parent.removeWaypoint(id_waypoint)

    def on_hrpiebutton_clicked(self,widget):
        self.heartrate_vbox2.show()
        self.heartrate_vbox.hide()
        self.heartrate_vbox3.hide()

    def on_hrplotbutton_clicked(self,widget):
        self.heartrate_vbox.show()
        self.heartrate_vbox2.hide()
        self.heartrate_vbox3.hide()

    def on_hrhistobutton_clicked(self,widget):
        self.heartrate_vbox3.show()
        self.heartrate_vbox.hide()
        self.heartrate_vbox2.hide()

    def _totals_from_activities(self, activity_list):
        tbeats = 0
        distance = 0
        calories = 0
        timeinseconds = 0
        beats = 0
        maxbeats = 0
        maxspeed = 0
        average = 0
        maxpace = "0:00"
        pace = "0:00"
        totalascent = 0
        totaldescent = 0
        for activity in activity_list:
            distance += activity.distance
            if activity.calories:
                calories += activity.calories
            timeinseconds += activity.duration
            if activity.upositive:
                totalascent += activity.upositive
            if activity.unegative:
                totaldescent += activity.unegative
            if activity.beats:
                tbeats += activity.beats*(activity.duration/60/60)
            if activity.maxspeed and activity.maxspeed > maxspeed:
                maxspeed = activity.maxspeed
            if activity.maxbeats and activity.maxbeats > maxbeats:
                maxbeats = activity.maxbeats

        distance = self.uc.distance(distance)
        maxspeed = self.uc.speed(maxspeed)

        if tbeats > 0 and timeinseconds > 0:
            tbeats = tbeats/(timeinseconds/60/60)
        if distance > 0 and timeinseconds > 0:
            average = distance/(float(timeinseconds)/60/60)
        if maxspeed > 0:
            maxpace = "%d:%02d" %((3600/maxspeed)/60,(3600/maxspeed)%60)
        if average > 0:
            pace = "%d:%02d" %((3600/average)/60,(3600/average)%60)
        return tbeats, distance, calories, timeinseconds, beats, maxbeats, maxspeed, average, maxpace, pace, totalascent, totaldescent class Warning(object):
    def __init__(self, data_path = None, okmethod = None, okparams = None, cancelmethod = None, cancelparams = None):
        warnings.warn("Deprecated Warning class called", DeprecationWarning, stacklevel=2)
        self.okmethod = okmethod
        self.cancelmethod = cancelmethod
        self.okparams = okparams
        self.cancelparams = cancelparams
        self.text = ""
        self.title = "Warning"

    def set_title(self, title):
        self.title = title

    def set_text(self, msg):
        self.text = msg

    def on_accept_clicked(self):
        if self.okparams != None:
            num = len(self.okparams)
            if num==0:
                self.okmethod()
            if num==1:
                self.okmethod(self.okparams[0])
            if num==2:
                self.okmethod(self.okparams[0],self.okparams[1])

    def on_cancel_clicked(self):
        if self.cancelparams != None:
            num = len(self.cancelparams)
            if num==0:
                self.cancelmethod()
            if num==1:
                self.cancelmethod(self.cancelparams[0])
            if num==2:
                self.cancelmethod(self.cancelparams[0], self.cancelparams[1])

    def run(self):
        if self.okmethod:
            response = warning_dialog(text=self.text, title=self.title, cancel=True)
        else:
            response = warning_dialog(text=self.text, title=self.title, cancel=False)
        if response == Gtk.ResponseType.OK:
            self.on_accept_clicked()
        elif response == Gtk.ResponseType.CANCEL:
            self.on_cancel_clicked() class Date:
    def __init__(self, calendar=None):
        self.calendar = calendar

    def getDate(self):
        #hack for the gtk calendar widget
        if self.calendar is not None:
            year,month,day = self.calendar.get_date()
            # Selected day might be larger than current month's number of days.
            # Iterate backwards until we find valid date.
            while day >= 1:
                try:
                    # months from gtk calendar are numbered from 0 to 11
                    return datetime.date(year, month + 1, day)
                except ValueError :
                    day -= 1
            raise ValueError("Invalid date supplied: "
                             "day is before 1st of month.")
        else:
            return datetime.date.today()

    def setDate(self,newdate):
        year,month,day = newdate.split("-")
        self.calendar.select_month(int(month)-1,int(year))
        self.calendar.select_day(int(day)) def getNameMonth(date):
    day, daysInMonth = calendar.monthrange(date.year, date.month)
    monthName = locale_str(calendar.month_name[date.month])
    return monthName, daysInMonth class ActivityService(object):
    '''
    Class maintains a pool of activities
            size is set at initialisation
    '''
    def __init__(self, pytrainer_main=None, size=1):
        logging.debug(">>")
        #It is an error to try to initialise with no reference to pytrainer_main
        if pytrainer_main is None:
            logging.critical("Error - must initialise with a reference to the main pytrainer class")
            return
        self.pytrainer_main = pytrainer_main
        self.max_size = size
        self.pool = {}
        self.pool_queue = []
        logging.debug("Initialising ActivityPool to size: %d", size)
        logging.debug("<<")

    def clear_pool(self):
        logging.debug(">>")
        logging.debug("Clearing ActivityPool")
        self.pool = {}
        self.pool_queue = []
        logging.debug("<<")

    def remove_activity_from_cache(self, id):
        sid = str(id)
        if sid in list(self.pool.keys()):
            logging.debug("Found activity in pool")
            self.pool_queue.remove(sid)
            del self.pool[sid]

    def get_activity(self, id):
        if id is None:
            warnings.warn("Deprecated call to get_activity with None id", DeprecationWarning, stacklevel=2)
            return Activity()
        sid = str(id)
        if sid in list(self.pool.keys()):
            logging.debug("Found activity in pool")
            #Have accessed this activity, place at end of queue
            self.pool_queue.remove(sid)
            self.pool_queue.append(sid)
        else:
            logging.debug("Activity NOT found in pool")
            self.pool[sid] = self.pytrainer_main.ddbb.session.query(Activity).options(
                joinedload('sport'), joinedload('equipment'), joinedload('Laps')
            ).filter(Activity.id == id).one()
            self.pool_queue.append(sid)
        if len(self.pool_queue) > self.max_size:
            sid_to_remove = self.pool_queue.pop(0)
            logging.debug("Removing activity: %s", sid_to_remove)
            del self.pool[sid_to_remove]
        logging.debug("ActivityPool queue length: %d", len(self.pool_queue))
        logging.debug("ActivityPool queue: %s", self.pool_queue)
        return self.pool[sid]

    def remove_activity_from_db(self, activity):
        """Delete a stored Activity."""

        if not activity.id:
            raise ActivityServiceException("Cannot remove activity which has not been stored: '{0}'.".format(activity.name))
        try:
            self.remove_activity_from_cache(activity.id)
            self.pytrainer_main.ddbb.session.delete(activity)
            self.pytrainer_main.ddbb.session.commit()
            if activity.gpx_file and os.path.isfile(activity.gpx_file):
                os.remove(activity.gpx_file)
        except InvalidRequestError:
             raise ActivityServiceException("Activity id %s not found" % activity.id)
        logging.debug("Deleted activity: %s", activity.title)

    def get_activities_for_day(self, date, sport=None):
        """Iterates the activities for a specific date, optionally restricted by Sport)"""
        if not sport:
            activities = self.pytrainer_main.ddbb.session.query(Activity).filter(Activity.date == date).options(joinedload('Laps'))
        else:
            activities = self.pytrainer_main.ddbb.session.query(Activity).filter(and_(Activity.date == date, Activity.sport == sport)).options(joinedload('Laps'))
        for activity in activities:
            sid = str(activity.id)
            if sid in self.pool:
                yield self.pool[sid]
            else:
                self.pool[sid] = activity
                self.pool_queue.append(sid)
                yield activity

    def get_activities_period(self, date_range, sport=None):
        """Iterate over activities for a specific time period, optionally restricted by Sport.
Does not add them to the cache."""
        if not sport:
            return self.pytrainer_main.ddbb.session.query(Activity).filter(Activity.date.between(date_range.start_date, date_range.end_date))
        else:
            return self.pytrainer_main.ddbb.session.query(Activity).filter(and_(Activity.date.between(date_range.start_date, date_range.end_date), Activity.sport == sport))

    def get_all_activities(self):
        """Iterates over all activities ordered by date"""
        return self.pytrainer_main.ddbb.session.query(Activity).order_by('date') class DDBB(Singleton):
    url = None
    engine = None
    sessionmaker = sessionmaker()
    session = None

    def __init__(self, url=None):
        """Initialize database connection, defaulting to SQLite in-memory
if no url is provided"""
        if not self.url and not url:
            # Starting from scratch without specifying a url
            if 'PYTRAINER_ALCHEMYURL' in os.environ:
                url = os.environ['PYTRAINER_ALCHEMYURL']
            else:
                url = "sqlite://"

        # Mysql special case
        if not self.url and url.startswith('mysql'):
            self.url = '%s%s' % (url, '?charset=utf8')

        if url and self.url and not self.url != url:
            # The url has changed, destroy the engine
            self.engine.dispose()
            self.engine = None
            self.session = None
            self.url = url

        if not self.url:
            self.url = url

        if not self.engine:
            self.engine = create_engine(self.url, logging_name='db')
            self.sessionmaker.configure(bind=self.engine)
        logging.info("DDBB created with url %s", self.url)

    def get_connection_url(self):
        return self.url

    def connect(self):
        self.session = self.sessionmaker()

    def disconnect(self):
        self.session.close()

    def select(self,table,cells,condition=None, mod=None):
        warnings.warn("Deprecated call to ddbb.select", DeprecationWarning, stacklevel=2)
        sql = "select %s from %s" %(cells,table)
        if condition is not None:
            sql = "%s where %s" % (sql, condition)
        if mod is not None:
            sql = "%s %s" % (sql, mod)
        return list(self.session.execute(sql))

    def create_tables(self, add_default=True):
        """Initialise the database schema from an empty database."""
        logging.info("Creating database tables")
        from pytrainer.core.sport import Sport
        from pytrainer.core.equipment import Equipment
        from pytrainer.waypoint import Waypoint
        from pytrainer.core.activity import Lap
        from pytrainer.athlete import Athletestat
        DeclarativeBase.metadata.create_all(self.engine)
        if add_default:
            for item in [Sport(name=u"Mountain Bike", weight=0.0, color=color_from_hex_string("0000ff")),
                         Sport(name=u"Bike", weight=0.0, color=color_from_hex_string("00ff00")),
                         Sport(name=u"Run", weight=0.0, color=color_from_hex_string("ffff00"))]:
                self.session.add(item)
            self.session.commit()

    def drop_tables(self):
        """Drop the database schema"""
        DeclarativeBase.metadata.drop_all(self.engine)
                
    def create_backup(self):
        """Create a backup of the current database."""
        try:
            import urlparse
            from urllib import url2pathname
        except ImportError:
            import urllib.parse as urlparse
            from urllib.request import url2pathname
        scheme, netloc, path, params, query, fragment = urlparse.urlparse(self.url)
        if scheme == 'sqlite':
            import datetime
            import gzip
            logging.info("Creating compressed copy of current DB")
            logging.debug('Database path: %s', self.url)
            path = url2pathname(path)
            backup_path = '%s_%s.gz' % (path, datetime.datetime.now().strftime('%Y%m%d_%H%M'))
            with open(path, 'rb') as orig_file:
                with gzip.open(backup_path, 'wb') as backup_file:
                    backup_file.write(orig_file.read())
                    logging.info('Database backup successfully created') class UC(Singleton):
    """
    When instantiated first time us is assigned to False.
      us = False; user system is metric
      us = True ; user system is imperial
    """

    """ Units of physical quantities [metric, imperial] """
    uc_units = {'distance' : [_('km'),_('mi')] , 'speed' : [_('km/h'), _('mph')],
                'pace' : [_('min/km'),_('min/mi')], 'height' : [_('m'), _('ft')],
                'weight': [_('kg'), _('lb')]}

    """ Conversion factors from metric to imperial, units as in uc_units """
    uc_factors = {'distance' : 0.621371192, 'speed': 0.621371192, 'pace':1.609344,
                  'height': 3.2808399, 'weight': 2.204624, 'none': 1}

    def __init__(self):
        if not hasattr(self, 'us'):
            self.us = False

    def __str__(self):
        if self.us:
            return 'imperial'
        else:
            return 'metric'

    def set_us(self, us):
        if type(us) == bool:
            self.us = us

    def get_unit(self, quantity):
        if self.us:
            return self.uc_units[quantity][1]
        else:
            return self.uc_units[quantity][0]

    unit_distance = property(lambda self: self.get_unit('distance') )
    unit_speed = property( lambda self: self.get_unit('speed') )
    unit_pace = property( lambda self: self.get_unit('pace') )
    unit_height = property( lambda self: self.get_unit('height') )
    unit_weight = property( lambda self: self.get_unit('weight') )

    def sys2usr(self, quantity, value):
        """ Gives value of physical quantity (metric) in users system"""
        try:
            _val = float(value)
        except (ValueError, TypeError):
            return None
        if self.us:
            return _val * self.uc_factors[quantity]
        else:
            return _val

    def usr2sys(self, quantity, value):
        """ Takes value (users system) and convert to metric (sys)"""
        try:
            _val = float(value)
        except (ValueError, TypeError):
            return None
        if self.us:
            return _val / self.uc_factors[quantity]
        else:
            return _val

    def usr2sys_str(self, quantity, val_str):
        """ Similar to usr2sys but I/O is string representing a float.
            Necessary until we have proper input validation in windowrecord.
            Escpecially pace fix here should be eliminated asap.
        """
        if not self.us:
            return val_str

        if quantity == 'pace':
            _pace_dec = pace2float(val_str)
            _pace_uc = self.usr2sys('pace', _pace_dec)
            return float2pace(_pace_uc)
        else:
            try:
                _val = float(val_str)
            except (ValueError, TypeError):
                return ""
            return str( self.usr2sys(quantity, _val))

    """ Aliases for sys2usr """
    def distance(self, value):
        return self.sys2usr('distance', value)
    def speed(self, value):
        return self.sys2usr('speed', value)
    def pace(self, value):
        return self.sys2usr('pace', value)
    def height(self, value):
        return self.sys2usr('height', value)
    def weight(self, value):
        return self.sys2usr('weight', value)
import sys
import os
import logging
import logging.handlers
import traceback
import warnings
import gi
from optparse import OptionParser
from pytrainer.lib.date import DateRange
from .upgrade.data import initialize_data
from .environment import Environment
from .record import Record
from .waypoint import WaypointService
from .extension import Extension
from .importdata import Importdata
from .plugins import Plugins
from .profile import Profile
from pytrainer.core.sport import SportService
from .athlete import Athlete
from .stats import Stats
from .gui.windowmain import Main
from .gui.warning import Warning
from .lib.date import Date, getNameMonth
from pytrainer.core.activity import ActivityService
from .lib.ddbb import DDBB
from .lib.uc import UC
        from .save import Save
        from .gui.windowprofile import WindowProfile
# -*- coding: utf-8 -*-

#Copyright (C) Fiz Vazquez vud1@sindominio.net
# Modified by dgranda

#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License
#as published by the Free Software Foundation; either version 2
#of the License, or (at your option) any later version.

#This program is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.

#You should have received a copy of the GNU General Public License
#along with this program; if not, write to the Free Software
#Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


gi.require_version('Gtk', '3.0')



class pyTrainer:
    def __init__(self,filename = None, data_path = None):
        # Based on Django's approach -> http://code.djangoproject.com/svn/django/trunk/django/__init__.py
        self.version = __import__('pytrainer').get_version()
        #Process command line options
        self.startup_options = self.get_options()
        #Setup logging
        self.environment = Environment(self.startup_options.conf_dir, data_path)
        self.environment.create_directories()
        self.environment.clear_temp_dir()
        self.set_logging(self.startup_options.log_level, self.startup_options.log_type)
        logging.debug('>>')
        logging.info("pytrainer version %s" % (self.version))
        self.data_path = data_path

        # Checking profile
        logging.debug('Checking configuration and profile...')
        self.profile = Profile()
        # Write the default config to disk
        self.profile.saveProfile()
        self.uc = UC()
        self.profilewindow = None
        self.ddbb = DDBB(self.profile.sqlalchemy_url)
        logging.debug('connecting to DDBB')
        self.ddbb.connect()

        logging.info('Checking if some upgrade action is needed...')

        initialize_data(self.ddbb, self.environment.conf_dir)