From: <sig...@us...> - 2011-04-26 23:33:17
|
Revision: 780 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=780&view=rev Author: siggipals Date: 2011-04-26 23:33:11 +0000 (Tue, 26 Apr 2011) Log Message: ----------- #111 - Added capability for specifying graph display color for sports Modified Paths: -------------- pytrainer/trunk/glade/profile.glade pytrainer/trunk/pytrainer/gui/drawArea.py pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/gui/windowprofile.py pytrainer/trunk/pytrainer/lib/ddbb.py pytrainer/trunk/pytrainer/main.py pytrainer/trunk/pytrainer/monthgraph.py pytrainer/trunk/pytrainer/profile.py pytrainer/trunk/pytrainer/timegraph.py pytrainer/trunk/pytrainer/totalgraph.py pytrainer/trunk/pytrainer/weekgraph.py pytrainer/trunk/pytrainer/yeargraph.py Modified: pytrainer/trunk/glade/profile.glade =================================================================== --- pytrainer/trunk/glade/profile.glade 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/glade/profile.glade 2011-04-26 23:33:11 UTC (rev 780) @@ -591,6 +591,7 @@ <widget class="GtkTreeView" id="sportTreeView"> <property name="visible">True</property> <property name="can_focus">True</property> + <signal name="row_activated" handler="on_sporttreeview_row_activated"/> </widget> </child> </widget> @@ -808,8 +809,8 @@ </widget> <packing> <property name="right_attach">3</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> @@ -841,6 +842,42 @@ <property name="y_options"></property> </packing> </child> + <child> + <widget class="GtkHBox" id="hboxcolor"> + <property name="visible">True</property> + <child> + <widget class="GtkImage" id="newcolor"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + </widget> + <packing> + </packing> + </child> + <child> + <widget class="GtkButton" id="buttoncolor"> + <property name="label">gtk-edit</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_editcolor_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> <child> <placeholder/> </child> @@ -1188,8 +1225,8 @@ </widget> <packing> <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> <property name="x_options">GTK_FILL</property> <property name="y_options"></property> </packing> @@ -1198,7 +1235,7 @@ <widget class="GtkLabel" id="label-9"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes">Maxiumum Pace</property> + <property name="label" translatable="yes">Maximum Pace</property> </widget> <packing> <property name="top_attach">3</property> @@ -1221,6 +1258,55 @@ <property name="y_options"></property> </packing> </child> + <child> + <widget class="GtkLabel" id="label-10"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Color</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hboxcolor"> + <property name="visible">True</property> + <child> + <widget class="GtkImage" id="editcolor"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + </widget> + <packing> + </packing> + </child> + <child> + <widget class="GtkButton" id="buttoncolor"> + <property name="label">gtk-edit</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_editcolor_clicked"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="y_options"></property> + </packing> + </child> </widget> <packing> <property name="position">0</property> Modified: pytrainer/trunk/pytrainer/gui/drawArea.py =================================================================== --- pytrainer/trunk/pytrainer/gui/drawArea.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/gui/drawArea.py 2011-04-26 23:33:11 UTC (rev 780) @@ -171,7 +171,7 @@ else: return '%1.1f' % x - def drawStackedBars(self,xvalues,yvalues,ylabel,title, valuesAreTime=False): + def drawStackedBars(self,xvalues,yvalues,ylabel,title, valuesAreTime=False, colors={}): '''function to draw stacked bars xvalues needs to be a list of lists of strings, e.g. [0]["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"] yvalues needs to be a list of dicts e.g. [0]{'Kayak': {'Tue': 10.08, 'Fri': 17.579999999999998, 'Thu': 15.66, 'Sat': 30.619999999999997}, {'Run': {'Mon': 9.65, 'Sun': 15.59}} @@ -218,7 +218,9 @@ for ind in inds: ybottoms[ind] += yheights[ind] yheights[ind] = 0 #Zero heights - color = self.getColor(keys.index(key)) + color = "#"+colors.get(key, '') + if len(color)<2: + color = self.getColor(keys.index(key)) for xvalue in xvalues[0]: index = xvalues[0].index(xvalue) if xvalue in yvalues[0][key]: @@ -255,7 +257,9 @@ for ind in inds: ybottoms[ind] += yheights[ind] yheights[ind] = 0.0 #Zero heights - color = self.getColor(keys.index(key)) + color = "#"+colors.get(key, '') + if len(color)<2: + color = self.getColor(keys.index(key)) for xvalue in xvalues[0]: index = xvalues[0].index(xvalue) if xvalue in yvalues[1][key]: Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-04-26 23:33:11 UTC (rev 780) @@ -246,10 +246,10 @@ self.drawareaheartrate = HeartRateGraph(self.heartrate_vbox, self.window1, self.heartrate_vbox2, pytrainer_main=self.pytrainer_main) #self.drawareaday = DayGraph(self.day_vbox, self.day_combovalue) self.day_vbox.hide() - self.drawareaweek = WeekGraph(self.weekview, self.window1, self.week_combovalue, self.week_combovalue2) - self.drawareamonth = MonthGraph(self.month_vbox, self.window1, self.month_combovalue,self.month_combovalue2) - self.drawareayear = YearGraph(self.year_vbox, self.window1, self.year_combovalue,self.year_combovalue2) - self.drawareatotal = TotalGraph(self.total_vbox, self.window1, self.total_combovalue,self.total_combovalue2) + self.drawareaweek = WeekGraph(self.weekview, self.window1, self.week_combovalue, self.week_combovalue2, self.pytrainer_main) + self.drawareamonth = MonthGraph(self.month_vbox, self.window1, self.month_combovalue,self.month_combovalue2, self.pytrainer_main) + self.drawareayear = YearGraph(self.year_vbox, self.window1, self.year_combovalue,self.year_combovalue2, self.pytrainer_main) + self.drawareatotal = TotalGraph(self.total_vbox, self.window1, self.total_combovalue,self.total_combovalue2, self.pytrainer_main) def createMap(self,MapViewer,waypoint): self.waypoint = waypoint @@ -298,9 +298,9 @@ def create_treeview(self,treeview,columns): for column_index, column_dict in enumerate(columns): if 'pixbuf' in column_dict: - renderer = gtk.CellRendererPixbuf() + renderer = gtk.CellRendererPixbuf() else: - renderer = gtk.CellRendererText() + renderer = gtk.CellRendererText() column = gtk.TreeViewColumn(column_dict['name']) column.pack_start(renderer, expand=False) if 'pixbuf' in column_dict: @@ -433,10 +433,10 @@ # Use grey color for "rest" laps for c in self.lapsTreeView.get_columns()[:-1]: - for cr in c.get_cell_renderers(): - if type(cr)==gtk.CellRendererText: - c.add_attribute(cr, 'foreground', 11) - + for cr in c.get_cell_renderers(): + if type(cr)==gtk.CellRendererText: + c.add_attribute(cr, 'foreground', 11) + self.frame_laps.show() else: self.frame_laps.hide() Modified: pytrainer/trunk/pytrainer/gui/windowprofile.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowprofile.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/gui/windowprofile.py 2011-04-26 23:33:11 UTC (rev 780) @@ -35,6 +35,7 @@ self.data_path = data_path SimpleGladeApp.__init__(self, data_path+glade_path, root, domain) self.conf_options = parent.profile_options + self.stored_color = "000000" def new(self): self.gender_options = { @@ -56,9 +57,18 @@ self.prf_ddbb.insert_text(i,self.ddbb_type[i]) #preparamos la lista sports: - column_names=[_("Sport"),_("MET"),_("Extra Weight"), _("Maximum Pace")] + column_names=[_("Sport"),_("MET"),_("Extra Weight"), _("Maximum Pace"), _("Color")] for column_index, column_name in enumerate(column_names): - column = gtk.TreeViewColumn(column_name, gtk.CellRendererText(), text=column_index) + if column_index==4: + renderer = gtk.CellRendererPixbuf() + else: + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn(column_name, text=column_index) + column.pack_start(renderer, expand=False) + if column_index==4: + column.add_attribute(renderer, 'pixbuf', column_index) + else: + column.add_attribute(renderer, 'text', column_index) column.set_resizable(True) self.sportTreeView.append_column(column) @@ -153,6 +163,7 @@ gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, + gtk.gdk.Pixbuf, object) for i in sport_list: try: @@ -171,6 +182,8 @@ #print type(e), e max_pace = "" + colorPixBuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 25, 15) + colorPixBuf.fill(0 if i[5] in (None,'') else long(i[5]+'00',16)) iter = store.append() store.set ( @@ -179,6 +192,7 @@ 1, met, 2, weight, 3, max_pace, + 4, colorPixBuf, ) self.sportTreeView.set_model(store) self.sportTreeView.set_cursor(0) @@ -328,7 +342,7 @@ md.run() md.destroy() return - self.parent.addNewSport(sport,met,weight,maxpace) + self.parent.addNewSport(sport,met,weight,maxpace,self.stored_color) self.parent.actualize_mainsportlist() self.on_switch_page(None,None,2) self.hidesportsteps() @@ -358,7 +372,7 @@ if iter: self.buttonbox.set_sensitive(0) sport = selected.get_value(iter,0) - name,met,weight,maxpace = self.parent.getSportInfo(sport) + name,met,weight,maxpace,color = self.parent.getSportInfo(sport) self.editsportentry.set_text(sport) self.sportnameedit.set_text(sport) if weight is not None: @@ -373,8 +387,39 @@ self.editmaxpace.set_text(str(maxpace)) else: self.editmaxpace.set_text("") + if color == None: + color = "0000" + + colorPixBuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 250, 20) + colorPixBuf.fill(long("0x%s00" % color, 16)) + self.editcolor.set_from_pixbuf(colorPixBuf) + self.hidesportsteps() self.editsport.show() + + def on_editcolor_clicked(self, widget): + selected,iter = self.sportTreeView.get_selection().get_selected() + if iter: + sport = selected.get_value(iter,0) + name,met,weight,maxpace,color = self.parent.getSportInfo(sport) + if color==None: + color = "000000" + colorseldlg = gtk.ColorSelectionDialog("test") + colorseldlg.colorsel.set_has_palette(True) + colorseldlg.colorsel.set_current_color(gtk.gdk.color_parse("#"+color)) + colorseldlg.run() + col = colorseldlg.colorsel.get_current_color() + self.stored_color = ("%02x" % col.red)[:2] + ("%02x" % col.green)[:2] + ("%02x" % col.blue)[:2] + + colorPixBuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 250, 20) + colorPixBuf.fill(long("0x%s00" % self.stored_color, 16)) + self.newcolor.set_from_pixbuf(colorPixBuf) + self.editcolor.set_from_pixbuf(colorPixBuf) + + colorseldlg.hide() + + def on_sporttreeview_row_activated(self, widget, path, column): + self.on_editsport_clicked(None) def on_editsport_accept_clicked(self,widget): oldnamesport = self.sportnameedit.get_text() @@ -382,7 +427,7 @@ newmetsport = self.editmetentry.get_text() newweightsport = self.editweightentry.get_text() newmaxpace = self.editmaxpace.get_text() - self.parent.updateSport(oldnamesport,newnamesport,newmetsport,newweightsport, newmaxpace) + self.parent.updateSport(oldnamesport,newnamesport,newmetsport,newweightsport, newmaxpace, self.stored_color) self.parent.actualize_mainsportlist() self.on_switch_page(None,None,2) self.hidesportsteps() Modified: pytrainer/trunk/pytrainer/lib/ddbb.py =================================================================== --- pytrainer/trunk/pytrainer/lib/ddbb.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/lib/ddbb.py 2011-04-26 23:33:11 UTC (rev 780) @@ -54,6 +54,7 @@ "weight":"float", "met":"float", "max_pace":"integer", + "color":"char(6)", }, "waypoints":{ "id_waypoint":"integer primary key autoincrement", "lat":"float", Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/main.py 2011-04-26 23:33:11 UTC (rev 780) @@ -55,7 +55,7 @@ def __init__(self,filename = None, data_path = None): #Version constants self.version ="1.8.0-svn#774" - self.DB_version = 7 + self.DB_version = 8 #Process command line options self.startup_options = self.get_options() self.environment = Environment(platform.get_platform(), self.startup_options.conf_dir) Modified: pytrainer/trunk/pytrainer/monthgraph.py =================================================================== --- pytrainer/trunk/pytrainer/monthgraph.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/monthgraph.py 2011-04-26 23:33:11 UTC (rev 780) @@ -30,8 +30,8 @@ (_("day"),_("Calories"), _("Daily Calories"),"b"), ] - def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None): - TimeGraph.__init__(self, vbox=vbox, window=window) + def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, main = None): + TimeGraph.__init__(self, vbox=vbox, window=window, main=main) self.combovalue = combovalue self.combovalue2 = combovalue2 self.KEY_FORMAT = "%d" Modified: pytrainer/trunk/pytrainer/profile.py =================================================================== --- pytrainer/trunk/pytrainer/profile.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/profile.py 2011-04-26 23:33:11 UTC (rev 780) @@ -225,11 +225,11 @@ #connection = self.pytrainer_main.ddbb.connect() #if (connection == 1): logging.debug("retrieving sports info") - return self.pytrainer_main.ddbb.select("sports","name,met,weight,id_sports,max_pace",None) + return self.pytrainer_main.ddbb.select("sports","name,met,weight,id_sports,max_pace,color",None) #else: # return connection - def addNewSport(self,sport,met,weight,maxpace): + def addNewSport(self,sport,met,weight,maxpace,color): """31.08.2008 - dgranda It adds a new sport. arguments: @@ -238,9 +238,9 @@ weight: returns: id_sports from new sport""" logging.debug(">>") - logging.debug("Adding new sport: "+sport+"|"+weight+"|"+met+"|"+maxpace) - sport = [sport,met,weight,maxpace] - self.pytrainer_main.ddbb.insert("sports","name,met,weight,max_pace",sport) + logging.debug("Adding new sport: "+sport+"|"+weight+"|"+met+"|"+maxpace+"|"+str(color)) + sport = [sport,met,weight,maxpace,color] + self.pytrainer_main.ddbb.insert("sports","name,met,weight,max_pace,color",sport) sport_id = self.pytrainer_main.ddbb.select("sports","id_sports","name=\"%s\"" %(sport)) logging.debug("<<") return sport_id @@ -254,13 +254,13 @@ self.pytrainer_main.ddbb.delete("sports","id_sports=\"%d\""%id_sport) logging.debug("<<") - def updateSport(self,oldnamesport,newnamesport,newmetsport,newweightsport,newmaxpace=None): + def updateSport(self,oldnamesport,newnamesport,newmetsport,newweightsport,newmaxpace=None,newcolor=None): logging.debug("--") - self.pytrainer_main.ddbb.update("sports","name,met,weight,max_pace",[newnamesport,newmetsport,newweightsport, newmaxpace],"name=\"%s\""%oldnamesport) + self.pytrainer_main.ddbb.update("sports","name,met,weight,max_pace,color",[newnamesport,newmetsport,newweightsport, newmaxpace, newcolor],"name=\"%s\""%oldnamesport) def getSportInfo(self,namesport): logging.debug("--") - return self.pytrainer_main.ddbb.select("sports","name,met,weight,max_pace","name=\"%s\""%namesport)[0] + return self.pytrainer_main.ddbb.select("sports","name,met,weight,max_pace,color","name=\"%s\""%namesport)[0] def build_ddbb(self): logging.debug("--") Modified: pytrainer/trunk/pytrainer/timegraph.py =================================================================== --- pytrainer/trunk/pytrainer/timegraph.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/timegraph.py 2011-04-26 23:33:11 UTC (rev 780) @@ -20,9 +20,10 @@ from gui.drawArea import DrawArea class TimeGraph(object): - def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None): + def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, main = None): self.drawarea = DrawArea(vbox, window) self.SPORT_FIELD = 9 + self.sportlist = dict([(s[0],s[5]) for s in main.profile.getSportList()]) def getFloatValue(self, value): try: @@ -49,6 +50,7 @@ def get_values(self, values, value_selected, key_format, sportfield=9): valueDict = {} #Stores the totals valueCount = {} #Counts the totals to allow for averaging if needed + sportColors = {} for record in values: day = unicode(datetime.datetime.strptime(record[0], "%Y-%m-%d").strftime(key_format)) # Gives year for this record @@ -118,8 +120,7 @@ xlab.append(xvalues) valsAreTime.append(valuesAreTime) #Draw chart - - self.drawarea.drawStackedBars(xlab,yval,ylab,tit,valsAreTime) + self.drawarea.drawStackedBars(xlab,yval,ylab,tit,valsAreTime, colors = self.sportlist) def get_value_params(self,value): return self.value_params[value] Modified: pytrainer/trunk/pytrainer/totalgraph.py =================================================================== --- pytrainer/trunk/pytrainer/totalgraph.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/totalgraph.py 2011-04-26 23:33:11 UTC (rev 780) @@ -28,8 +28,8 @@ (_("year"),_("Calories"), _("Annual Calories"),"b"), ] - def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None): - TimeGraph.__init__(self, vbox=vbox, window=window) + def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, main = None): + TimeGraph.__init__(self, vbox=vbox, window=window, main=main) self.combovalue = combovalue self.combovalue2 = combovalue2 self.KEY_FORMAT = "%Y" Modified: pytrainer/trunk/pytrainer/weekgraph.py =================================================================== --- pytrainer/trunk/pytrainer/weekgraph.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/weekgraph.py 2011-04-26 23:33:11 UTC (rev 780) @@ -32,8 +32,8 @@ (None, _("Calories"), _("Daily Calories"), None), ] - def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None): - TimeGraph.__init__(self, vbox=vbox, window=window) + def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, main = None): + TimeGraph.__init__(self, vbox=vbox, window=window, main=main) self.combovalue = combovalue self.combovalue2 = combovalue2 self.KEY_FORMAT = "%a" Modified: pytrainer/trunk/pytrainer/yeargraph.py =================================================================== --- pytrainer/trunk/pytrainer/yeargraph.py 2011-04-26 23:16:34 UTC (rev 779) +++ pytrainer/trunk/pytrainer/yeargraph.py 2011-04-26 23:33:11 UTC (rev 780) @@ -30,8 +30,8 @@ (_("month"),_("Calories"), _("Monthly Calories"),"b"), ] - def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None): - TimeGraph.__init__(self, vbox=vbox, window=window) + def __init__(self, vbox = None, window = None, combovalue = None, combovalue2 = None, main = None): + TimeGraph.__init__(self, vbox=vbox, window=window, main=main) self.combovalue = combovalue self.combovalue2 = combovalue2 self.KEY_FORMAT = "%m" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-04-28 10:33:59
|
Revision: 781 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=781&view=rev Author: dgranda Date: 2011-04-28 10:33:43 +0000 (Thu, 28 Apr 2011) Log Message: ----------- #114 Added patch from Friso for ascend and descent figures. Includes time spent moving and paused in record view Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/main.py pytrainer/trunk/pytrainer/record.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-04-26 23:33:11 UTC (rev 780) +++ pytrainer/trunk/glade/pytrainer.glade 2011-04-28 10:33:43 UTC (rev 781) @@ -1,4 +1,4 @@ -<?xml version="1.0"?> +<?xml version="1.0" encoding="UTF-8"?> <glade-interface> <!-- interface-requires gtk+ 2.6 --> <!-- interface-naming-policy toplevel-contextual --> @@ -928,13 +928,44 @@ <placeholder/> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label42"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Run / Rest:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="record_runrest"> + <property name="visible">True</property> + <property name="xalign">1</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label50"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">h:m:s</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">5</property> + <property name="bottom_attach">6</property> + </packing> </child> </widget> <packing> @@ -952,49 +983,49 @@ <property name="position">1</property> </packing> </child> - <child> - <widget class="GtkFrame" id="frame_laps"> - <property name="visible">True</property> - <property name="label_xalign">0</property> - <child> - <widget class="GtkAlignment" id="alignment12l"> - <property name="visible">True</property> - <property name="left_padding">12</property> - <property name="right_padding">12</property> - <child> - <widget class="GtkScrolledWindow" id="scrolledwindow2l"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="height_request">300</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <widget class="GtkTreeView" id="lapsTreeView"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </widget> - </child> - </widget> - </child> - </widget> - </child> - <child> - <widget class="GtkLabel" id="label14"> - <property name="visible">True</property> - <property name="label" translatable="yes"><b>Laps</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">5</property> - <property name="position">2</property> - </packing> - </child> <child> + <widget class="GtkFrame" id="frame_laps"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkAlignment" id="alignment12l"> + <property name="visible">True</property> + <property name="left_padding">12</property> + <property name="right_padding">12</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2l"> + <property name="height_request">300</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">automatic</property> + <property name="vscrollbar_policy">automatic</property> + <child> + <widget class="GtkTreeView" id="lapsTreeView"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label14"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Laps</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">5</property> + <property name="position">2</property> + </packing> + </child> + <child> <widget class="GtkHBox" id="recordbuttons_hbox"> <property name="visible">True</property> <child> @@ -1038,7 +1069,6 @@ </child> </widget> <packing> - <property name="expand">True</property> <property name="fill">False</property> <property name="position">3</property> </packing> @@ -1358,7 +1388,7 @@ <widget class="GtkSpinButton" id="spinbuttonY1Min"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> <signal name="value_changed" handler="on_spinbuttonY1_value_changed"/> @@ -1376,7 +1406,7 @@ <widget class="GtkSpinButton" id="spinbuttonY1Max"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> <signal name="value_changed" handler="on_spinbuttonY1_value_changed"/> @@ -1439,7 +1469,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 1 10 1 1 0</property> <signal name="value_changed" handler="on_spinbuttonY1LineWeight_value_changed"/> </widget> @@ -1481,7 +1511,7 @@ <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1573,7 +1603,7 @@ <widget class="GtkSpinButton" id="spinbuttonY2Min"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -1590,7 +1620,7 @@ <widget class="GtkSpinButton" id="spinbuttonY2Max"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">1 -500 1000 1 10 0</property> </widget> @@ -1660,7 +1690,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1692,7 +1722,7 @@ <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1769,7 +1799,7 @@ <widget class="GtkSpinButton" id="spinbuttonXMin"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -1786,7 +1816,7 @@ <widget class="GtkSpinButton" id="spinbuttonXMax"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -3199,13 +3229,44 @@ </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label31"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Total Asc/Desc:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="day_ascdesc"> + <property name="visible">True</property> + <property name="xalign">1</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label32"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">m</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> </widget> </child> @@ -3811,13 +3872,44 @@ </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label33"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Total Asc/Desc:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="weeka_ascdesc"> + <property name="visible">True</property> + <property name="xalign">1</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label34"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">m</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> </widget> </child> @@ -4434,13 +4526,44 @@ </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label36"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Total Asc/Desc:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="montha_ascdesc"> + <property name="visible">True</property> + <property name="xalign">1</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label49"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">m</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> </widget> </child> @@ -5056,13 +5179,44 @@ </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label35"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Total Asc/Desc:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="yeara_ascdesc"> + <property name="visible">True</property> + <property name="xalign">1</property> + </widget> + <packing> + <property name="left_attach">4</property> + <property name="right_attach">5</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> <child> - <placeholder/> + <widget class="GtkLabel" id="label41"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label" translatable="yes">m</property> + </widget> + <packing> + <property name="left_attach">5</property> + <property name="right_attach">6</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + </packing> </child> </widget> </child> @@ -5387,7 +5541,7 @@ </widget> </child> <child> - <widget class="GtkLabel" id="label14"> + <widget class="GtkLabel" id="label15"> <property name="visible">True</property> <property name="label" translatable="yes"><b>History</b></property> <property name="use_markup">True</property> @@ -5443,21 +5597,19 @@ <property name="can_focus">True</property> <signal name="button_press_event" handler="on_sportstatsTreeView_button_press"/> </widget> - <packing> - </packing> </child> </widget> </child> - <child> - <widget class="GtkLabel" id="label27"> - <property name="visible">True</property> - <property name="label" translatable="yes"><b>Sports</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="type">label_item</property> - </packing> - </child> + <child> + <widget class="GtkLabel" id="label27"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Sports</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> </widget> <packing> <property name="expand">False</property> @@ -5473,219 +5625,238 @@ <widget class="GtkAlignment" id="alignment101"> <property name="visible">True</property> <property name="left_padding">12</property> - <child> - <widget class="GtkHBox" id="total_vbox"> - <property name="height_request">650</property> - <property name="visible">True</property> - <property name="border_width">5</property> - <property name="spacing">8</property> - <child> - <widget class="GtkVBox" id="vbox123"> - <property name="visible">True</property> - <child> - <widget class="GtkComboBoxEntry" id="total_combovalue"> - <property name="visible">True</property> - <property name="items" translatable="yes">Distance + <child> + <widget class="GtkHBox" id="total_vbox"> + <property name="height_request">650</property> + <property name="visible">True</property> + <property name="border_width">5</property> + <property name="spacing">8</property> + <child> + <widget class="GtkVBox" id="vbox123"> + <property name="visible">True</property> + <child> + <widget class="GtkComboBoxEntry" id="total_combovalue"> + <property name="visible">True</property> + <property name="items" translatable="yes">Distance Time Average Heart Rate Average Speed Calories</property> - <signal name="changed" handler="on_total_combovalue_changed"/> - </widget> - <packing> - <property name="expand">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label-37"> - <property name="visible">True</property> - <property name="label" translatable="yes">Versus</property> - </widget> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <widget class="GtkComboBoxEntry" id="total_combovalue2"> - <property name="visible">True</property> - <property name="items" translatable="yes">None + <signal name="changed" handler="on_total_combovalue_changed"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label-39"> + <property name="visible">True</property> + <property name="label" translatable="yes">Versus</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkComboBoxEntry" id="total_combovalue2"> + <property name="visible">True</property> + <property name="items" translatable="yes">None Distance Time Average Heart Rate Average Speed Calories</property> - <signal name="changed" handler="on_total_combovalue_changed"/> - </widget> - <packing> - <property name="expand">False</property> - <property name="position">2</property> - </packing> - </child> - <child> - <widget class="GtkTable" id="table3"> - <property name="visible">True</property> - <property name="n_rows">4</property> - <property name="n_columns">2</property> - <child> - <widget class="GtkLabel" id="label15"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="yalign">0</property> - <property name="label" translatable="yes">Total distance:</property> - </widget> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label19"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="yalign">0</property> - <property name="label" translatable="yes">Total duration:</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelTotalDistance"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="xpad">5</property> - </widget> - <packing> - <property name="top_attach">0</property> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelTotalDuration"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="xpad">5</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label21a"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="yalign">0</property> - <property name="label" translatable="yes">Start date:</property> - </widget> - <packing> - <property name="top_attach">3</property> - <property name="left_attach">0</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label22a"> - <property name="visible">True</property> - <property name="xalign">1</property> - <property name="yalign">0</property> - <property name="label" translatable="yes">End date:</property> - </widget> - <packing> - <property name="left_attach">0</property> - <property name="top_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelStartDate"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="xpad">5</property> - </widget> - <packing> - <property name="top_attach">3</property> - <property name="left_attach">1</property> - <property name="right_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="labelEndDate"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="xpad">5</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">4</property> - <property name="top_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="x_padding">10</property> - <property name="y_padding">10</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - </widget> - </child> - - - </widget> - <packing> - <property name="expand">False</property> - <property name="position">0</property> - </packing> - </child> - </widget> - <packing> - </packing> - </child> + <signal name="changed" handler="on_total_combovalue_changed"/> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table3"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <child> + <widget class="GtkLabel" id="label19"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="yalign">0</property> + <property name="label" translatable="yes">Total distance:</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_SHRINK</property> + <property name="x_padding">10</property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label28"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="yalign">0</property> + <property name="label" translatable="yes">Total duration:</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_SHRINK</property> + <property name="x_padding">10</property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="labelTotalDistance"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">5</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_SHRINK</property> + <property name="x_padding">10</property> + <property name="y_padding">10</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="labelTotalDuration"> + <property name="visible">True</property> + ... [truncated message content] |
From: <sig...@us...> - 2011-05-01 01:43:55
|
Revision: 782 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=782&view=rev Author: siggipals Date: 2011-05-01 01:43:48 +0000 (Sun, 01 May 2011) Log Message: ----------- #116 - Added new page to record view, containing projected times and ranking of activity compared to similar-distance same-sport activities, based on average speed/pace. Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/main.py Added Paths: ----------- pytrainer/trunk/glade/glasses.png Added: pytrainer/trunk/glade/glasses.png =================================================================== (Binary files differ) Property changes on: pytrainer/trunk/glade/glasses.png ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-04-28 10:33:43 UTC (rev 781) +++ pytrainer/trunk/glade/pytrainer.glade 2011-05-01 01:43:48 UTC (rev 782) @@ -2742,6 +2742,313 @@ <property name="type">tab</property> </packing> </child> + <child> + <widget class="GtkFrame" id="frame_analytics"> + <property name="visible">True</property> + <property name="border_width">6</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> + <child> + <widget class="GtkVBox" id="analytics_vbox_"> + <property name="visible">True</property> + <child> + <widget class="GtkHBox" id="analytics_hbox_"> + <property name="visible">True</property> + <child> + <widget class="GtkFrame" id="frame_projected"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkAlignment" id="alignment_analytics"> + <property name="visible">True</property> + <property name="left_padding">12</property> + <property name="can_focus">False</property> + <child> + <widget class="GtkTreeView" id="analyticsTreeView"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers-clickable">False</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="labelprojected"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Projected times</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + <property name="padding">10</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">10</property> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkFrame" id="frame_ranking"> + <property name="visible">True</property> + <property name="label_xalign">0</property> + <child> + <widget class="GtkAlignment" id="alignment_ranking"> + <property name="visible">True</property> + <property name="left_padding">12</property> + <child> + <widget class="GtkVBox" id="vbox_ranking"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label11125"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><i>Rank relative to same-sport activities for distances ± x%:</i></property> + <property name="use_markup">True</property> + </widget> + </child> + <child> + <widget class="GtkHScale" id="slider_ranking"> + <property name="visible">True</property> + <property name="sensitive">True</property> + <property name="adjustment">5 0 20 1 0 0</property> + <signal name="value-changed" handler="on_change_rank_percentage"/> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table_ranking"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="column_spacing">3</property> + <property name="row_spacing">5</property> + <child> + <widget class="GtkLabel" id="label11125"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Range:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">0</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label11125"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Rank:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label11127"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Speed: </b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="wssdadadsada"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Avg speed:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label-2"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Standard deviation:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label-3"> + <property name="width_request">90</property> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Deviation:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_range"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_rank"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_speed"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_avg"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_stddev"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_dev"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="label_ranking"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Ranking</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">10</property> + <property name="position">3</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label14anal"> + <property name="visible">True</property> + <property name="label" translatable="yes"><b>Analytics:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="type">label_item</property> + </packing> + </child> + </widget> + <packing> + <property name="position">4</property> + </packing> + </child> + <child> + <widget class="GtkImage" id="image28pre"> + <property name="visible">True</property> + <property name="pixbuf">glasses.png</property> + </widget> + <packing> + <property name="position">4</property> + <property name="tab_fill">False</property> + <property name="type">tab</property> + </packing> + </child> </widget> </child> <child> Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-04-28 10:33:43 UTC (rev 781) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-01 01:43:48 UTC (rev 782) @@ -161,6 +161,15 @@ ] 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) + self.fileconf = self.pytrainer_main.profile.confdir+"/listviewmenu.xml" if not os.path.isfile(self.fileconf): self._createXmlListView(self.fileconf) @@ -290,12 +299,14 @@ def render_float(self, column, cell, model, iter, data): _format, _quantity = data - _val = model.get_value(iter, column.get_sort_column_id()) + col = column.get_sort_column_id() + if col == -1: col = 0 + _val = model.get_value(iter, col) _val = self.uc.sys2usr(_quantity, _val) _val_str = _format % float(_val) cell.set_property('text', _val_str) - def create_treeview(self,treeview,columns): + def create_treeview(self,treeview,columns,sortable=True): for column_index, column_dict in enumerate(columns): if 'pixbuf' in column_dict: renderer = gtk.CellRendererPixbuf() @@ -317,7 +328,8 @@ column.set_cell_data_func(renderer, self.render_float, [column_dict['format_float'], column_dict['quantity']]) if 'format_duration' in column_dict and column_dict['format_duration']: column.set_cell_data_func(renderer, self.render_duration) - column.set_sort_column_id(column_index) + if sortable: + column.set_sort_column_id(column_index) treeview.append_column(column) def actualize_recordview(self,activity): @@ -441,7 +453,7 @@ self.frame_laps.show() else: self.frame_laps.hide() - runTime += float(activity.laps[0]['elapsed_time']) + runTime = activity.time else: self.recordview.set_current_page(0) @@ -719,7 +731,74 @@ #else: # self.recordview.set_sensitive(0) logging.debug("<<") + + def actualize_analytics(self,activity): + logging.debug(">>") + record_list = activity.tracks + + def project(d,a): + return int(a.time * (d / a.distance)**1.06) + + 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 + ) + ds = DISTANCES.keys() + ds = sorted(ds) + for d in ds: + 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.select_dict("records", ["distance","time"], "distance > %f AND distance < %f AND sport=%d" % (activity.distance * (1-percentage), activity.distance * (1+percentage), activity.sport_id)) + + count = 1 + for r in records: + if r['distance']/int(r['time']) > activity.distance/activity.time: + count += 1 + + import numpy + speeds = [r['distance']/int(r['time'])*3600 for r in records] + self.label_ranking_range.set_text("%.2f - %.2f" % (activity.distance * (1-percentage), activity.distance * (1+percentage))) + self.label_ranking_rank.set_text("%s/%s" % (count, len(records))) + self.label_ranking_avg.set_text("%.4f" % numpy.average(speeds)) + self.label_ranking_speed.set_text("%.4f" % (activity.distance/activity.time*3600)) + self.label_ranking_stddev.set_text("%.4f" % numpy.std(speeds)) + self.label_ranking_dev.set_text("%+f" % ((activity.distance/activity.time*3600 - numpy.average(speeds)) / numpy.std(speeds))) + def actualize_dayview(self,record_list=None, activity_list=None): logging.debug(">>") if self.pytrainer_main.profile.getValue("pytraining","prf_us_system") == "True": @@ -1667,6 +1746,8 @@ 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): Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-04-28 10:33:43 UTC (rev 781) +++ pytrainer/trunk/pytrainer/main.py 2011-05-01 01:43:48 UTC (rev 782) @@ -238,6 +238,8 @@ self.refreshRecordGraphView("map") elif self.windowmain.recordview.get_current_page()==3: self.refreshRecordGraphView("heartrate") + elif self.windowmain.recordview.get_current_page()==4: + self.refreshRecordGraphView("analytics") elif view=="day": logging.debug('day view') sport = self.windowmain.activeSport @@ -313,6 +315,8 @@ if view=="heartrate": self.windowmain.actualize_heartrategraph(activity) self.windowmain.actualize_hrview(activity) + if view=="analytics": + self.windowmain.actualize_analytics(activity) logging.debug('<<') def refreshMapView(self, full_screen=False): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sig...@us...> - 2011-05-05 01:28:57
|
Revision: 785 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=785&view=rev Author: siggipals Date: 2011-05-05 01:28:51 +0000 (Thu, 05 May 2011) Log Message: ----------- #116 - Add rank list to new analytics page in record view. Fixed a date selection bug. Fixed integer formatting for running/resting times. Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/lib/date.py pytrainer/trunk/pytrainer/record.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-05-03 22:35:33 UTC (rev 784) +++ pytrainer/trunk/glade/pytrainer.glade 2011-05-05 01:28:51 UTC (rev 785) @@ -2800,7 +2800,7 @@ <property name="left_padding">12</property> <child> <widget class="GtkVBox" id="vbox_ranking"> - <property name="visible">True</property> + <property name="visible">True</property> <child> <widget class="GtkLabel" id="label11125"> <property name="visible">True</property> @@ -2809,190 +2809,213 @@ <property name="use_markup">True</property> </widget> </child> - <child> - <widget class="GtkHScale" id="slider_ranking"> - <property name="visible">True</property> - <property name="sensitive">True</property> - <property name="adjustment">5 0 20 1 0 0</property> - <signal name="value-changed" handler="on_change_rank_percentage"/> - </widget> - <packing> - <property name="expand">False</property> - </packing> - </child> - <child> - <widget class="GtkTable" id="table_ranking"> - <property name="visible">True</property> - <property name="border_width">10</property> - <property name="n_rows">5</property> - <property name="n_columns">2</property> - <property name="column_spacing">3</property> - <property name="row_spacing">5</property> - <child> - <widget class="GtkLabel" id="label11125"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Range:</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">0</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label11125"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Rank:</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">1</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label11127"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Speed: </b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="wssdadadsada"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Avg speed:</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label-2"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Standard deviation:</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label-3"> - <property name="width_request">90</property> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Deviation:</b></property> - <property name="use_markup">True</property> - </widget> - <packing> - <property name="top_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_range"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">0</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_rank"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">1</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_speed"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_avg"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_stddev"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <widget class="GtkLabel" id="label_ranking_dev"> - <property name="visible">True</property> - <property name="xalign">0</property> - <property name="xpad">5</property> - <property name="label"></property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="top_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - </widget> - </child> + <child> + <widget class="GtkHBox" id="hbox_ranking"> + <property name="visible">True</property> + <child> + <widget class="GtkVBox" id="alignment_ranking2"> + <property name="visible">True</property> + <child> + <widget class="GtkHScale" id="slider_ranking"> + <property name="visible">True</property> + <property name="sensitive">True</property> + <property name="adjustment">5 0 20 1 0 0</property> + <signal name="value-changed" handler="on_change_rank_percentage"/> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table_ranking"> + <property name="visible">True</property> + <property name="border_width">10</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="column_spacing">3</property> + <property name="row_spacing">5</property> + <child> + <widget class="GtkLabel" id="label11125"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Range:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">0</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label11125"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Rank:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label11127"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Speed: </b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="wssdadadsada"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Avg speed:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label-2"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Standard deviation:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label-3"> + <property name="width_request">90</property> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><b>Deviation:</b></property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_range"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_rank"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_speed"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_avg"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_stddev"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label_ranking_dev"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">5</property> + <property name="label"></property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">5</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">10</property> + </packing> + </child> + <child> + <widget class="GtkTreeView" id="rankingTreeView"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="headers-clickable">False</property> + </widget> + <packing> + <property name="padding">10</property> + </packing> + </child> + </widget> + </child> </widget> </child> </widget> Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-03 22:35:33 UTC (rev 784) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-05 01:28:51 UTC (rev 785) @@ -170,6 +170,18 @@ ] 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':_("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) @@ -298,10 +310,8 @@ cell.set_property('text', new) def render_float(self, column, cell, model, iter, data): - _format, _quantity = data - col = column.get_sort_column_id() - if col == -1: col = 0 - _val = model.get_value(iter, col) + _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) @@ -325,11 +335,11 @@ 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.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 sortable: - column.set_sort_column_id(column_index) + column.set_sort_column_id(column_index) treeview.append_column(column) def actualize_recordview(self,activity): @@ -461,7 +471,7 @@ pauseTime = activity.time - runTime hrun,mrun,srun = self.pytrainer_main.date.second2time(runTime) hpause,mpause,spause = self.pytrainer_main.date.second2time(pauseTime) - self.record_runrest.set_text("%d:%d:%d / %d:%d:%d" %(hrun,mrun,srun,hpause,mpause,spause) ) + self.record_runrest.set_text("%02d:%02d:%02d / %02d:%02d:%02d" %(hrun,mrun,srun,hpause,mpause,spause) ) logging.debug("<<") def actualize_recordgraph(self,activity): @@ -783,22 +793,62 @@ percentage = widget.get_value() / 100 else: percentage = .05 - records = self.pytrainer_main.ddbb.select_dict("records", ["distance","time"], "distance > %f AND distance < %f AND sport=%d" % (activity.distance * (1-percentage), activity.distance * (1+percentage), activity.sport_id)) + records = self.pytrainer_main.ddbb.select_dict("records", ["distance","time","id_record","date","average"], "distance > %f AND distance < %f AND sport=%d order by average desc" % (activity.distance * (1-percentage), activity.distance * (1+percentage), activity.sport_id)) count = 1 for r in records: - if r['distance']/int(r['time']) > activity.distance/activity.time: + if r['average'] > activity.average: count += 1 import numpy - speeds = [r['distance']/int(r['time'])*3600 for r in records] + speeds = [r['average'] for r in records] self.label_ranking_range.set_text("%.2f - %.2f" % (activity.distance * (1-percentage), activity.distance * (1+percentage))) self.label_ranking_rank.set_text("%s/%s" % (count, len(records))) - self.label_ranking_avg.set_text("%.4f" % numpy.average(speeds)) - self.label_ranking_speed.set_text("%.4f" % (activity.distance/activity.time*3600)) + self.label_ranking_avg.set_text("%.2f" % numpy.average(speeds)) + self.label_ranking_speed.set_text("%.2f" % activity.average) self.label_ranking_stddev.set_text("%.4f" % numpy.std(speeds)) - self.label_ranking_dev.set_text("%+f" % ((activity.distance/activity.time*3600 - numpy.average(speeds)) / numpy.std(speeds))) + self.label_ranking_dev.set_text("%+f" % ((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_STRING, #distance + gobject.TYPE_STRING, #time + gobject.TYPE_STRING, #speed + gobject.TYPE_STRING, #color + ) + + length = len(records) + rec_set = [0,] + for r in xrange(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, r['date'], + 3, r['distance'], + 4, str(r['time']), + 5, r['average'], + 6, '#3AA142' if rank==count else '#000000', + ) + + # Use grey color for "rest" laps + for c in self.rankingTreeView.get_columns()[:-1]: + for cr in c.get_cell_renderers(): + if type(cr)==gtk.CellRendererText: + c.add_attribute(cr, 'foreground', 6) + + self.rankingTreeView.set_model(rank_store) + def actualize_dayview(self,record_list=None, activity_list=None): logging.debug(">>") if self.pytrainer_main.profile.getValue("pytraining","prf_us_system") == "True": Modified: pytrainer/trunk/pytrainer/lib/date.py =================================================================== --- pytrainer/trunk/pytrainer/lib/date.py 2011-05-03 22:35:33 UTC (rev 784) +++ pytrainer/trunk/pytrainer/lib/date.py 2011-05-05 01:28:51 UTC (rev 785) @@ -121,7 +121,7 @@ dayOfWeek += 7 #print "Adjusted today is %s day of the week:" % dayOfWeek date_ini = weekDate + datetime.timedelta(days=0-dayOfWeek) - date_end = weekDate + datetime.timedelta(days=6-dayOfWeek) + date_end = weekDate + datetime.timedelta(days=7-dayOfWeek) #print "weekrange", date_ini.strftime("%A"), date_end.strftime("%A") #print "dates", date_ini.strftime("%Y-%m-%d"), date_end.strftime("%Y-%m-%d") return date_ini.strftime("%Y-%m-%d"), date_end.strftime("%Y-%m-%d") Modified: pytrainer/trunk/pytrainer/record.py =================================================================== --- pytrainer/trunk/pytrainer/record.py 2011-05-03 22:35:33 UTC (rev 784) +++ pytrainer/trunk/pytrainer/record.py 2011-05-05 01:28:51 UTC (rev 785) @@ -398,9 +398,9 @@ #TODO This is essentially the same as getrecordPeriodSport (except date ranges) - need to look at merging the two tables = "records,sports" if not sport: - condition = "date>=\"%s\" and date<=\"%s\" and records.sport=sports.id_sports" %(date_ini,date_end) + condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports" %(date_ini,date_end) else: - condition = "date>=\"%s\" and date<=\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" %(date_ini,date_end, sport) + condition = "date>=\"%s\" and date<\"%s\" and records.sport=sports.id_sports and sports.id_sports=\"%s\"" %(date_ini,date_end, sport) return self.pytrainer_main.ddbb.select(tables,"date,distance,time,beats,comments,average,calories,maxspeed,maxbeats, sports.name,upositive,unegative", condition) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sig...@us...> - 2011-05-07 23:01:19
|
Revision: 788 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=788&view=rev Author: siggipals Date: 2011-05-07 23:01:12 +0000 (Sat, 07 May 2011) Log Message: ----------- #122 - Added comments for laps Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/lib/activity.py pytrainer/trunk/pytrainer/lib/ddbb.py pytrainer/trunk/pytrainer/main.py pytrainer/trunk/pytrainer/record.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/glade/pytrainer.glade 2011-05-07 23:01:12 UTC (rev 788) @@ -1003,6 +1003,7 @@ <widget class="GtkTreeView" id="lapsTreeView"> <property name="visible">True</property> <property name="can_focus">True</property> + <property name="has_tooltip">True</property> </widget> </child> </widget> Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-07 23:01:12 UTC (rev 788) @@ -158,6 +158,7 @@ {'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) @@ -427,6 +428,7 @@ gobject.TYPE_INT, gobject.TYPE_INT, gobject.TYPE_STRING, + gobject.TYPE_STRING, ) for lap in activity.laps: t = lap['elapsed_time'] @@ -450,16 +452,56 @@ pic = gtk.gdk.pixbuf_new_from_file(self.data_path+"glade/trigger_%s.png" % lap['trigger']) 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']]) + 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()[:-1]: + for c in self.lapsTreeView.get_columns(): for cr in c.get_cell_renderers(): if type(cr)==gtk.CellRendererText: c.add_attribute(cr, 'foreground', 11) - + + def edited_cb(cell, path, new_text, (liststore, activity)): + liststore[path][12] = new_text + activity.laps[int(path)]['comments'] = new_text + self.pytrainer_main.ddbb.update("laps", "comments", [new_text,], "record=%s and lap_number=%s" % (activity.id, path)) + + 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 = activity.laps[path[0][0]]['comments'] + if comments and len(comments)>40: + tooltip.set_text(comments) + return True + return False + + self.lapsTreeView.connect('query-tooltip', show_tooltip, (store, activity)) + i = 0 + for cr in self.lapsTreeView.get_columns()[12].get_cell_renderers(): + 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']) + self.lapsTreeView.set_tooltip_cell(tooltip, i, self.lapsTreeView.get_columns()[12], cr) + i += 1 self.frame_laps.show() else: self.frame_laps.hide() Modified: pytrainer/trunk/pytrainer/lib/activity.py =================================================================== --- pytrainer/trunk/pytrainer/lib/activity.py 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/pytrainer/lib/activity.py 2011-05-07 23:01:12 UTC (rev 788) @@ -285,7 +285,7 @@ raise Exception( "Error - multiple results from DB for id: %s" % self.id ) #Get lap information laps = self.pytrainer_main.ddbb.select_dict("laps", - ("id_lap", "record", "elapsed_time", "distance", "start_lat", "start_lon", "end_lat", "end_lon", "calories", "lap_number", "intensity", "avg_hr", "max_hr", "max_speed", "trigger"), + ("id_lap", "record", "elapsed_time", "distance", "start_lat", "start_lon", "end_lat", "end_lon", "calories", "lap_number", "intensity", "avg_hr", "max_hr", "max_speed", "trigger", "comments"), "record=\"%s\"" % self.id) if laps is None or laps == [] or len(laps) < 1: #No laps found logging.debug("No laps in DB for record %d" % self.id) Modified: pytrainer/trunk/pytrainer/lib/ddbb.py =================================================================== --- pytrainer/trunk/pytrainer/lib/ddbb.py 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/pytrainer/lib/ddbb.py 2011-05-07 23:01:12 UTC (rev 788) @@ -80,6 +80,7 @@ "max_speed": "float", "avg_hr": "int", "max_hr": "int", + "comments":"text", }, "athletestats": { "id_athletestat": "integer primary key autoincrement", Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/pytrainer/main.py 2011-05-07 23:01:12 UTC (rev 788) @@ -54,8 +54,8 @@ class pyTrainer: def __init__(self,filename = None, data_path = None): #Version constants - self.version ="1.8.0-svn#786" - self.DB_version = 8 + self.version ="1.8.0-svn#787" + self.DB_version = 9 #Process command line options self.startup_options = self.get_options() self.environment = Environment(platform.get_platform(), self.startup_options.conf_dir) Modified: pytrainer/trunk/pytrainer/record.py =================================================================== --- pytrainer/trunk/pytrainer/record.py 2011-05-06 16:37:47 UTC (rev 787) +++ pytrainer/trunk/pytrainer/record.py 2011-05-07 23:01:12 UTC (rev 788) @@ -242,6 +242,7 @@ tmp_lap['max_hr'] = lap[9] tmp_lap['max_speed'] = lap[10] tmp_lap['trigger'] = lap[11] + tmp_lap['comments'] = "" laps.append(tmp_lap) logging.debug('<<') return laps @@ -351,7 +352,7 @@ def getLaps(self, id_record): logging.debug('--') laps = self.pytrainer_main.ddbb.select("laps", - "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, trigger", + "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, trigger, comments", "record=\"%s\"" % id_record) if laps is None or laps == []: #No laps stored - update DB logging.debug("No laps in DB for record %d" % id_record) @@ -368,7 +369,7 @@ self.insertLaps(lap_keys,lap.values()) #Try to get lap info again #TODO? refactor laps = self.pytrainer_main.ddbb.select("laps", - "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, trigger", + "id_lap, record, elapsed_time, distance, start_lat, start_lon, end_lat, end_lon, calories, lap_number, intensity, max_speed, avg_hr, max_hr, trigger, comments", "record=\"%s\"" % id_record) return laps This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ilo...@us...> - 2011-05-08 17:26:05
|
Revision: 791 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=791&view=rev Author: ilovesquigly Date: 2011-05-08 17:25:59 +0000 (Sun, 08 May 2011) Log Message: ----------- New option in open street maps to select the area to be anonymized (removed) from GPX file before uploading Modified Paths: -------------- pytrainer/trunk/extensions/openstreetmap/conf.xml pytrainer/trunk/extensions/openstreetmap/openstreetmap.py pytrainer/trunk/pytrainer/main.py Modified: pytrainer/trunk/extensions/openstreetmap/conf.xml =================================================================== --- pytrainer/trunk/extensions/openstreetmap/conf.xml 2011-05-08 15:41:16 UTC (rev 790) +++ pytrainer/trunk/extensions/openstreetmap/conf.xml 2011-05-08 17:25:59 UTC (rev 791) @@ -10,4 +10,5 @@ > <conf-values variable="username" value=""/> <conf-values variable="password" value=""/> +<conf-values variable="privPolygon" value=""/> </pytrainer-extension> Modified: pytrainer/trunk/extensions/openstreetmap/openstreetmap.py =================================================================== --- pytrainer/trunk/extensions/openstreetmap/openstreetmap.py 2011-05-08 15:41:16 UTC (rev 790) +++ pytrainer/trunk/extensions/openstreetmap/openstreetmap.py 2011-05-08 17:25:59 UTC (rev 791) @@ -11,7 +11,11 @@ import httplib, httplib2 import urllib2 import mimetools, mimetypes +from json import dumps, loads # for deserializing JSON data form javascript +from pytrainer.extensions.mapviewer import MapViewer +from pytrainer.extensions.osm import Osm + class openstreetmap: def __init__(self, parent = None, pytrainer_main = None, conf_dir = None, options = None): self.parent = parent @@ -21,22 +25,21 @@ self.description = " " self.tags = "" self.visibility = "private" + self.mozTitle="" # Keeps embedded mozilla document title while displaying map for private area selection + self.privBounds=[] # Bounds of areas to be anonymized def run(self, id, activity=None): #TODO Convert to use activity... logging.debug(">>") - uri = "http://api.openstreetmap.org/api/0.6/gpx/create" #URI for uploading traces to OSM - if 'username' not in self.options or self.options['username'] == "" or 'password' not in self.options or self.options['password'] == "": - logging.error("Must have username and password configured") - msg = _("Must have username and password configured") - md = gtk.MessageDialog(self.pytrainer_main.windowmain.window1, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) - md.set_title(_("Openstreetmap Extension Error")) - md.run() - md.destroy() - return - username = self.options['username'] - password = self.options['password'] - gpx_file = "%s/gpx/%s.gpx" % (self.conf_dir, id) - if os.path.isfile(gpx_file): + try: + uri = "http://api.openstreetmap.org/api/0.6/gpx/create" #URI for uploading traces to OSM + if 'username' not in self.options or self.options['username'] == "" or 'password' not in self.options or self.options['password'] == "": + logging.error("Must have username and password configured") + raise Exception("Must have username and password configured") + username = self.options['username'] + password = self.options['password'] + gpx_file = "%s/gpx/%s.gpx" % (self.conf_dir, id) + if not os.path.isfile(gpx_file): + raise Exception(str(gps_file) + ' File not found') #GPX file is ok and found, so open it logging.debug("GPX file: %s found, size: %d" % (gpx_file, os.path.getsize(gpx_file))) f = open(gpx_file, 'r') @@ -57,7 +60,7 @@ logging.debug("User abort") return if self.makeanon: - logging.debug("User requested anonymising of GPX data") + logging.debug("User requested anonymizing of GPX data") f.close() #Close standard gpxfile gpx_file = self.make_gpx_private(gpx_file) f = open(gpx_file, 'r') #Open anonymous gpxfile in readonly mode @@ -100,15 +103,20 @@ md.set_modal(False) md.run() md.destroy() + except Exception as e: + msg = _("Error while uploading file to OSM: " + str(e)) + md = gtk.MessageDialog(self.pytrainer_main.windowmain.window1, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) + md.set_title(_("Openstreetmap Extension Error")) + md.run() + md.destroy() + return + finally: + logging.debug("<<") - else: - logging.error("GPX file: %s NOT found!!!" % (gpx_file)) - logging.debug("<<") - def display_options_window(self): self.prefwindow = gtk.Dialog(title=_("Please add any additional information for this upload"), parent=None, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)) self.prefwindow.set_modal(False) - table = gtk.Table(1,2) + table = gtk.Table(1,3) self.entryList = [] #Add description label = gtk.Label("<b>Description</b>") @@ -143,7 +151,12 @@ checkbutton = gtk.CheckButton() table.attach(checkbutton,1,2,3,4) self.entryList.append(checkbutton) - #Buld dialog and show + #Add anon area selection button + button = gtk.Button("Area selection") + button.connect("clicked",self.areaSelect) + table.attach(button,1,2,4,5) + self.entryList.append(button) + #Build dialog and show self.prefwindow.vbox.pack_start(table) self.prefwindow.show_all() self.prefwindow.connect("response", self.on_options_ok_clicked) @@ -163,6 +176,80 @@ self.makeanon = self.entryList[3].get_active() logging.debug("Description: %s, tags: %s, visibility: %s, makeanon: %s" % ( self.description, self.tags, self.visibility, self.makeanon) ) + def areaSelect(self,dc): + """ + Open a window with OSM map so user could select his private/anonymized area - + all GPX dots in this area will be removed before uploading to OSM + """ + try: + wTree = gtk.glade.XML(self.pytrainer_main.data_path+"extensions/openstreetmap/OSM_AnonSelection.glade") + self.privAreaWindow = wTree.get_widget("OSM_AnonSelection") + dic = { + "on_buttonOk_clicked" : self.privArea_Ok, + "on_buttonCancel_clicked" : self.privArea_Cancel + } + wTree.signal_autoconnect( dic ) + mapviewer = MapViewer(self.pytrainer_main.data_path, pytrainer_main=self.pytrainer_main, box=wTree.get_widget("mapBox")) + json=None + if self.options.has_key('privPolygon'): + json=self.options['privPolygon'] + htmlfile = Osm(data_path=self.pytrainer_main.data_path, waypoint=json, pytrainer_main=self.pytrainer_main).selectArea() + mapviewer.display_map(htmlfile=htmlfile) + mapviewer.moz.connect('title', self.parseTitle) + self.privAreaWindow.show() + + except Exception as e: + msg = "Could not init map selection screen, Error: " + str(e) + md = gtk.MessageDialog(self.pytrainer_main.windowmain.window1, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) + md.set_title(_("Error")) + md.run() + md.destroy() + return + + def parseTitle(self, moz): + "Event fired when document title was changed -> meaning polygon was changed" + if moz.get_title() != "": + self.mozTitle=str(moz.get_title()) + + def privArea_Cancel(self,button): + "Event fired when cancel button was pressed" + self.privAreaWindow.destroy() + + def privArea_Ok(self,button): + "Event fired when ok button was pressed" + logging.debug(">> privArea_Ok") + # save new private area polygon if changed + if self.mozTitle=="": + return + # try parsing JSON + try: + # verify json is correct by deserializing and serializing it + polygonString=dumps(loads(self.mozTitle)) + # try saving + extensionDir = self.pytrainer_main.data_path + "/extensions" + "/openstreetmap" + if not os.path.isdir(extensionDir): + loggin.error(str(e)) + print ("Could not find extension path: " + str(extensionDir)) + raise Exception("Could not find extension path: " + str(extensionDir)) + # save new options + self.options['privPolygon'] = polygonString + # convert dictionary to a lists set + savedOptions = [] + for key in self.options: + savedOptions.append((key,self.options[key])) + # write new xml config file + self.parent.setExtensionConfParams(extensionDir, savedOptions) + except Exception as e: + logging.error(str(e)) + print "Error while saving extension configuration: " + str(e) + msg = _(str(e)) + md = gtk.MessageDialog(self.pytrainer_main.windowmain.window1, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE, msg) + md.set_title(_("Error while saving extension configuration")) + md.run() + md.destroy() + finally: + self.privAreaWindow.destroy() + def multipart_encode(self, fields, files, boundary = None, buffer = None): ''' Multipart encode data for posting @@ -176,7 +263,6 @@ buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"' % key buffer += '\r\n\r\n' + value + '\r\n' - print files for (key, fd) in files: file_size = os.fstat(fd.fileno())[stat.ST_SIZE] filename = os.path.basename(fd.name) @@ -202,15 +288,19 @@ filen = os.path.basename(gpx_file) tmpdir = self.pytrainer_main.profile.tmpdir anon_gpx_file = "%s/%s" % (tmpdir, filen) - - # Filtered home area, example Berlin - # corners NorthEast and SouthWest - #TODO This needs to be a config item.... - NE_LAT = 52.518 - NE_LON = 13.408 - SW_LAT = 52.4 - SW_LON = 13.3 + # get saved private area polygon + pP=loads(self.options['privPolygon']) + pP=pP['geometry']['coordinates'][0] + # converts polygon's 2D matrix into a vector of just the lats or lons + vector = lambda lonLat: [pP[i][lonLat] for i in range(len(pP))] # 0:lon, 1:lat + # try reading private area's bounds, stored as [lon,lat] + NE_LAT = max([pP[i][1] for i in range(len(pP))]) + NE_LON = max([pP[i][0] for i in range(len(pP))]) + SW_LAT = min([pP[i][1] for i in range(len(pP))]) + SW_LON = min([pP[i][0] for i in range(len(pP))]) + logging.info("Anonymizing Area: NE:%f,%f -> SW: %f,%f" % (NE_LON, NE_LAT, SW_LON, SW_LAT)) + # Config parameters, not used yet FILTER_BOX = True ERASE_TIME = True Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-05-08 15:41:16 UTC (rev 790) +++ pytrainer/trunk/pytrainer/main.py 2011-05-08 17:25:59 UTC (rev 791) @@ -54,7 +54,7 @@ class pyTrainer: def __init__(self,filename = None, data_path = None): #Version constants - self.version ="1.8.0-svn#787" + self.version ="1.8.0-svn#791" self.DB_version = 9 #Process command line options self.startup_options = self.get_options() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <sig...@us...> - 2011-05-10 00:05:30
|
Revision: 797 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=797&view=rev Author: siggipals Date: 2011-05-10 00:05:19 +0000 (Tue, 10 May 2011) Log Message: ----------- Double-click in list view opens record edit - Take 2 Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-05-10 00:01:31 UTC (rev 796) +++ pytrainer/trunk/glade/pytrainer.glade 2011-05-10 00:05:19 UTC (rev 797) @@ -6383,6 +6383,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <signal name="button_press_event" handler="on_allRecordTreeView_button_press"/> + <signal name="row_activated" handler="on_recordTree_clicked"/> </widget> </child> </widget> Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-10 00:01:31 UTC (rev 796) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-05-10 00:05:19 UTC (rev 797) @@ -2110,7 +2110,7 @@ self.actualize_yearview() def on_recordTree_clicked(self,widget,num,num2): - selected,iter = self.recordTreeView.get_selection().get_selected() + selected,iter = widget.get_selection().get_selected() self.parent.editRecord(selected.get_value(iter,0), self.selected_view) ### athleteview events ### This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-05-20 10:00:52
|
Revision: 805 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=805&view=rev Author: dgranda Date: 2011-05-20 10:00:46 +0000 (Fri, 20 May 2011) Log Message: ----------- Changes in GUI so main window can be as smaller as possible to better fit in lower screen resolutions Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/main.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-05-20 09:38:57 UTC (rev 804) +++ pytrainer/trunk/glade/pytrainer.glade 2011-05-20 10:00:46 UTC (rev 805) @@ -342,7 +342,7 @@ <child> <widget class="GtkTable" id="table13"> <property name="visible">True</property> - <property name="n_rows">11</property> + <property name="n_rows">9</property> <property name="n_columns">6</property> <property name="column_spacing">3</property> <property name="row_spacing">6</property> @@ -787,6 +787,7 @@ <child> <widget class="GtkTextView" id="record_comments"> <property name="width_request">440</property> + <property name="height_request">25</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="editable">False</property> @@ -994,7 +995,6 @@ <property name="right_padding">12</property> <child> <widget class="GtkScrolledWindow" id="scrolledwindow2l"> - <property name="height_request">300</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="hscrollbar_policy">automatic</property> @@ -1229,7 +1229,6 @@ <property name="visible">True</property> <child> <widget class="GtkScrolledWindow" id="scrolledwindowGraphOptions"> - <property name="height_request">100</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="hscrollbar_policy">automatic</property> @@ -1389,7 +1388,7 @@ <widget class="GtkSpinButton" id="spinbuttonY1Min"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> <signal name="value_changed" handler="on_spinbuttonY1_value_changed"/> @@ -1407,7 +1406,7 @@ <widget class="GtkSpinButton" id="spinbuttonY1Max"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> <signal name="value_changed" handler="on_spinbuttonY1_value_changed"/> @@ -1470,7 +1469,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 1 10 1 1 0</property> <signal name="value_changed" handler="on_spinbuttonY1LineWeight_value_changed"/> </widget> @@ -1512,7 +1511,7 @@ <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1604,7 +1603,7 @@ <widget class="GtkSpinButton" id="spinbuttonY2Min"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -1621,7 +1620,7 @@ <widget class="GtkSpinButton" id="spinbuttonY2Max"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">1 -500 1000 1 10 0</property> </widget> @@ -1691,7 +1690,7 @@ <property name="visible">True</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1723,7 +1722,7 @@ <property name="sensitive">False</property> <property name="can_focus">True</property> <property name="max_length">2</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="adjustment">1 0 10 1 1 0</property> </widget> <packing> @@ -1800,7 +1799,7 @@ <widget class="GtkSpinButton" id="spinbuttonXMin"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -1817,7 +1816,7 @@ <widget class="GtkSpinButton" id="spinbuttonXMax"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> <property name="width_chars">4</property> <property name="adjustment">0 -500 1000 1 10 0</property> </widget> @@ -5821,7 +5820,7 @@ <property name="left_padding">12</property> <child> <widget class="GtkHBox" id="boxAthleteGraph"> - <property name="height_request">150</property> + <property name="height_request">30</property> <property name="visible">True</property> <child> <placeholder/> @@ -5958,7 +5957,6 @@ <property name="left_padding">12</property> <child> <widget class="GtkHBox" id="total_vbox"> - <property name="height_request">650</property> <property name="visible">True</property> <property name="border_width">5</property> <property name="spacing">8</property> @@ -6190,7 +6188,6 @@ </child> </widget> <packing> - <property name="expand">False</property> <property name="padding">6</property> <property name="position">3</property> </packing> @@ -6239,7 +6236,7 @@ <property name="visible">True</property> <child> <widget class="GtkHBox" id="hbox36"> - <property name="height_request">43</property> + <property name="height_request">40</property> <property name="visible">True</property> <property name="border_width">8</property> <property name="spacing">8</property> @@ -6259,7 +6256,7 @@ <widget class="GtkEntry" id="lsa_searchvalue"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="expand">False</property> @@ -6562,7 +6559,7 @@ <widget class="GtkEntry" id="waypoint_longitude"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="left_attach">3</property> @@ -6577,7 +6574,7 @@ <widget class="GtkEntry" id="waypoint_description"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="left_attach">1</property> @@ -6592,7 +6589,7 @@ <widget class="GtkEntry" id="waypoint_latitude"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="left_attach">3</property> @@ -6605,7 +6602,7 @@ <widget class="GtkEntry" id="waypoint_name"> <property name="visible">True</property> <property name="can_focus">True</property> - <property name="invisible_char">●</property> + <property name="invisible_char">●</property> </widget> <packing> <property name="left_attach">1</property> Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-05-20 09:38:57 UTC (rev 804) +++ pytrainer/trunk/pytrainer/main.py 2011-05-20 10:00:46 UTC (rev 805) @@ -54,7 +54,7 @@ class pyTrainer: def __init__(self,filename = None, data_path = None): #Version constants - self.version ="1.8.0-svn#791" + self.version ="1.8.0-svn#805" self.DB_version = 9 #Process command line options self.startup_options = self.get_options() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-06-29 11:57:32
|
Revision: 809 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=809&view=rev Author: dgranda Date: 2011-06-29 11:57:25 +0000 (Wed, 29 Jun 2011) Log Message: ----------- Ticket #131 - Moving from GtkMozEmbed to WebKit. Thx to cperrier Modified Paths: -------------- pytrainer/trunk/INSTALL pytrainer/trunk/bin/pytrainer pytrainer/trunk/pytrainer/extensions/googlemaps.py pytrainer/trunk/pytrainer/extensions/mapviewer.py pytrainer/trunk/pytrainer/extensions/osm.py pytrainer/trunk/pytrainer/extensions/waypointeditor.py pytrainer/trunk/pytrainer/main.py Modified: pytrainer/trunk/INSTALL =================================================================== --- pytrainer/trunk/INSTALL 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/INSTALL 2011-06-29 11:57:25 UTC (rev 809) @@ -7,23 +7,22 @@ Here you are dependencies for PyTrainer. Of course you must have a working environment with proper shell configuration and typical GNU tools to uncompress (gunzip) and untar (tar) files. -1.1.- Packages (Fedora 12 working versions as of 2010.03.03 for 1.7.2 dev) +1.1.- Packages (Fedora 14 working versions as of 2011.06.28 for 1.8.1) -python-2.6.2 -python-devel-2.6.2 (to run installation from tarball) -python-matplotlib-0.98.5.2 +python-2.7.8 +python-devel-2.7.8 (to run installation from tarball) +python-matplotlib-1.0.1 python-dateutil-1.4.1 -gnome-python2-gtkmozembed-2.25.3 -pygtk2-libglade-2.16.0 -pygtk2-2.16.0 +pywebkitgtk-1.1.6-5 (see ticket #131) +pygtk2-libglade-2.17.0 +pygtk2-2.17.0 SOAPpy-0.11.6 -python-lxml-2.2.3 -libxml2-python-2.7.6 -libxml2-2.7.6 +python-lxml-2.2.8 +libxml2-python-2.7.7 +libxml2-2.7.7 libxslt-python-1.1.26 libxslt-1.1.26 -sqlite-3.6.20 (sqlite is preferred as db than mysql) -xulrunner-1.9.1.8 +sqlite-3.6.23 (sqlite is preferred as db than mysql, MySQL-python has been reported to be installed if so) - Only needed if correspondent plugin is enabled: gpsbabel-1.3.5 ("Garmin via GPSBabel 1.3.5" aka garmin_hr) Modified: pytrainer/trunk/bin/pytrainer =================================================================== --- pytrainer/trunk/bin/pytrainer 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/bin/pytrainer 2011-06-29 11:57:25 UTC (rev 809) @@ -95,25 +95,6 @@ sys.path.insert(0, site_path) from pytrainer.main import pyTrainer -#Determine xulrunner location -xulrunners = glob.glob("/usr/li*/xulrunner*/xulrunner") -if len(xulrunners) == 0: - #Didnt find any - fall back to old approach - xul_env = commands.getstatusoutput("find /usr/li* -name xulrunner -exec dirname {} \; 2>/dev/null")[1] -elif len(xulrunners) == 1: - #Found just one location - use that - xul_env = os.path.dirname(xulrunners[0]) -else: - #Found more than one - need to choose one... - max_version = None - for item in xulrunners: - xul_dir = os.path.dirname(item) - max_version = _max(max_version, xul_dir) - xul_env = max_version - -print "Using xulrunner dir: %s" % xul_env -os.environ['MOZILLA_FIVE_HOME'] = xul_env - def main(): pytrainer = pyTrainer(None, data_path) Modified: pytrainer/trunk/pytrainer/extensions/googlemaps.py =================================================================== --- pytrainer/trunk/pytrainer/extensions/googlemaps.py 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/pytrainer/extensions/googlemaps.py 2011-06-29 11:57:25 UTC (rev 809) @@ -65,9 +65,7 @@ def drawMap(self,activity, linetype): '''Draw google map create html file using Google API version3 - render using embedded Mozilla - - info at http://www.pygtk.org/pygtkmozembed/class-gtkmozembed.html + render using embedded Webkit ''' logging.debug(">>") points = [] Modified: pytrainer/trunk/pytrainer/extensions/mapviewer.py =================================================================== --- pytrainer/trunk/pytrainer/extensions/mapviewer.py 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/pytrainer/extensions/mapviewer.py 2011-06-29 11:57:25 UTC (rev 809) @@ -16,7 +16,7 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -import gtkmozembed +import webkit import logging import os @@ -31,14 +31,13 @@ logging.debug("Display box (%s) is None" % ( str(box))) return self.box = box - gtkmozembed.set_profile_path("/tmp", "foobar") # http://faq.pygtk.org/index.py?req=show&file=faq19.018.htp #TODO FIX??? - self.moz = gtkmozembed.MozEmbed() + self.wkview = webkit.WebView() self.pack_box() logging.debug("<<") def pack_box(self): logging.debug(">>") - self.box.pack_start(self.moz, True, True) + self.box.pack_start(self.wkview, True, True) self.box.show_all() logging.debug("<<") @@ -46,7 +45,7 @@ logging.debug(">>") if htmlfile is None: htmlfile = self.createErrorHtml() - self.moz.load_url("file://%s" % (htmlfile)) + self.wkview.load_uri("file://%s" % (htmlfile)) #self.box.show_all() logging.debug("<<") Modified: pytrainer/trunk/pytrainer/extensions/osm.py =================================================================== --- pytrainer/trunk/pytrainer/extensions/osm.py 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/pytrainer/extensions/osm.py 2011-06-29 11:57:25 UTC (rev 809) @@ -3,7 +3,6 @@ # TODO: Add Google satellite images layers ? # TODO: Create map class/interface that osm/googlemaps will inherit from ? -import gtkmozembed # embedded mozilla import os import re import logging @@ -78,9 +77,7 @@ def drawMap(self, activity, linetype): '''Draw OSM map create HTML file using Open Layers and Open Street Map - render using embedded Mozilla - - info at http://www.pygtk.org/pygtkmozembed/class-gtkmozembed.html + render using embedded Webkit ''' logging.debug(">>") points = [] Modified: pytrainer/trunk/pytrainer/extensions/waypointeditor.py =================================================================== --- pytrainer/trunk/pytrainer/extensions/waypointeditor.py 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/pytrainer/extensions/waypointeditor.py 2011-06-29 11:57:25 UTC (rev 809) @@ -16,7 +16,7 @@ #along with this program; if not, write to the Free Software #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -import gtkmozembed +import webkit import os import re @@ -33,9 +33,9 @@ logging.debug(">>") self.data_path = data_path self.extension = Extension() - self.moz = gtkmozembed.MozEmbed() - self.moz.connect('title', self.handle_title_changed) - vbox.pack_start(self.moz, True, True) + self.wkview = webkit.WebView() + self.wkview.connect('notify::title', self.handle_title_changed) + vbox.pack_start(self.wkview, True, True) vbox.show_all() self.htmlfile = "" self.waypoint=waypoint @@ -43,7 +43,9 @@ logging.debug("<<") def handle_title_changed(self, *args): - title = self.moz.get_title() + title = self.wkview.get_title() + if title == None: + return logging.debug("Received title: "+ title) m = re.match("call:([a-zA-Z]*)[(](.*)[)]", title) if m: @@ -86,7 +88,7 @@ tmpdir = self.pytrainer_main.profile.tmpdir htmlfile = tmpdir+"/waypointeditor.html" logging.debug("HTML file: "+str(htmlfile)) - self.moz.load_url("file://"+htmlfile) + self.wkview.load_uri("file://"+htmlfile) logging.debug("<<") def createHtml(self,default_waypoint=None): Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-06-23 00:30:19 UTC (rev 808) +++ pytrainer/trunk/pytrainer/main.py 2011-06-29 11:57:25 UTC (rev 809) @@ -54,7 +54,7 @@ class pyTrainer: def __init__(self,filename = None, data_path = None): #Version constants - self.version ="1.8.0-svn#805" + self.version ="1.8.0-svn#809" self.DB_version = 9 #Process command line options self.startup_options = self.get_options() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-07-18 09:37:32
|
Revision: 814 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=814&view=rev Author: dgranda Date: 2011-07-18 09:37:21 +0000 (Mon, 18 Jul 2011) Log Message: ----------- Changing pytrainer version, still hardcoded\! Modified Paths: -------------- pytrainer/trunk/pytrainer/main.py pytrainer/trunk/setup.py Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-07-18 01:24:13 UTC (rev 813) +++ pytrainer/trunk/pytrainer/main.py 2011-07-18 09:37:21 UTC (rev 814) @@ -54,7 +54,7 @@ class pyTrainer: def __init__(self,filename = None, data_path = None): #Version constants - self.version ="1.8.0-svn#809" + self.version ="1.9.0-dev" self.DB_version = 9 #Process command line options self.startup_options = self.get_options() Modified: pytrainer/trunk/setup.py =================================================================== --- pytrainer/trunk/setup.py 2011-07-18 01:24:13 UTC (rev 813) +++ pytrainer/trunk/setup.py 2011-07-18 09:37:21 UTC (rev 814) @@ -15,7 +15,7 @@ return "share/pytrainer/extensions/%s"%extension_name, glob("extensions/%s/*"%extension_name) setup( name="pytrainer", - version="1.8.0", + version="1.9.0", description="The free sport tracking center", long_description="Pytrainer is a tool to log all your sport excursion coming from GPS devices (with a focus on ForeRunner 205, 305 and 405) or GPX (http://www.topografix.com) files. Pytrainer supports GPS track files and displays it in graphs, maps... ", author="Fiz Vazquez, John Blance, David Garcia Granda, Arnd Zapletal, Nathan Jones", This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-07-18 10:29:40
|
Revision: 815 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=815&view=rev Author: ncjones Date: 2011-07-18 10:29:34 +0000 (Mon, 18 Jul 2011) Log Message: ----------- Add empty sqlalchemy-migrate repository. ticket:132 Modified Paths: -------------- pytrainer/trunk/INSTALL Added Paths: ----------- pytrainer/trunk/pytrainer/migrate/ pytrainer/trunk/pytrainer/migrate/README pytrainer/trunk/pytrainer/migrate/__init__.py pytrainer/trunk/pytrainer/migrate/migrate.cfg pytrainer/trunk/pytrainer/migrate/versions/ pytrainer/trunk/pytrainer/migrate/versions/__init__.py Modified: pytrainer/trunk/INSTALL =================================================================== --- pytrainer/trunk/INSTALL 2011-07-18 09:37:21 UTC (rev 814) +++ pytrainer/trunk/INSTALL 2011-07-18 10:29:34 UTC (rev 815) @@ -23,6 +23,7 @@ libxslt-python-1.1.26 libxslt-1.1.26 sqlite-3.6.23 (sqlite is preferred as db than mysql, MySQL-python has been reported to be installed if so) +sqlalchemy-migrate-0.6.1 - Only needed if correspondent plugin is enabled: gpsbabel-1.3.5 ("Garmin via GPSBabel 1.3.5" aka garmin_hr) Added: pytrainer/trunk/pytrainer/migrate/README =================================================================== --- pytrainer/trunk/pytrainer/migrate/README (rev 0) +++ pytrainer/trunk/pytrainer/migrate/README 2011-07-18 10:29:34 UTC (rev 815) @@ -0,0 +1,4 @@ +This is a database migration repository. + +More information at +http://code.google.com/p/sqlalchemy-migrate/ Added: pytrainer/trunk/pytrainer/migrate/__init__.py =================================================================== --- pytrainer/trunk/pytrainer/migrate/__init__.py (rev 0) +++ pytrainer/trunk/pytrainer/migrate/__init__.py 2011-07-18 10:29:34 UTC (rev 815) @@ -0,0 +1 @@ +# template repository default module Added: pytrainer/trunk/pytrainer/migrate/migrate.cfg =================================================================== --- pytrainer/trunk/pytrainer/migrate/migrate.cfg (rev 0) +++ pytrainer/trunk/pytrainer/migrate/migrate.cfg 2011-07-18 10:29:34 UTC (rev 815) @@ -0,0 +1,20 @@ +[db_settings] +# Used to identify which repository this database is versioned under. +# You can use the name of your project. +repository_id=pytrainer + +# The name of the database table used to track the schema version. +# This name shouldn't already be used by your project. +# If this is changed once a database is under version control, you'll need to +# change the table name in each database too. +version_table=migrate_version + +# When committing a change script, Migrate will attempt to generate the +# sql for all supported databases; normally, if one of them fails - probably +# because you don't have that database installed - it is ignored and the +# commit continues, perhaps ending successfully. +# Databases in this list MUST compile successfully during a commit, or the +# entire commit will fail. List the databases your application will actually +# be using to ensure your updates to that database work properly. +# This must be a list; example: ['postgres','sqlite'] +required_dbs=[] Added: pytrainer/trunk/pytrainer/migrate/versions/__init__.py =================================================================== --- pytrainer/trunk/pytrainer/migrate/versions/__init__.py (rev 0) +++ pytrainer/trunk/pytrainer/migrate/versions/__init__.py 2011-07-18 10:29:34 UTC (rev 815) @@ -0,0 +1 @@ +# template repository default versions module This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-07-29 09:59:17
|
Revision: 821 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=821&view=rev Author: ncjones Date: 2011-07-29 09:59:10 +0000 (Fri, 29 Jul 2011) Log Message: ----------- Create data version checking module. Added Paths: ----------- pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/pytrainer/upgrade/migratedb.py pytrainer/trunk/test/pytrainer/upgrade/ pytrainer/trunk/test/pytrainer/upgrade/data_test.py Added: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-07-29 09:59:10 UTC (rev 821) @@ -0,0 +1,135 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import pytrainer +from pytrainer.upgrade.context import UpgradeContext +from pytrainer.upgrade.migratedb import MigratableDb + +MIGRATE_REPOSITORY_PATH = "pytrainer/upgrade" + +class InstalledData(object): + + """Encapsulates an installation's existing data and provides means to + check version state and upgrade.""" + + def __init__(self, migratable_db, profile): + self._migratable_db = migratable_db + self._profile = profile + + def update_to_current(self): + """Check the current state of the installation's data and update them + if necessary so they are compatible with the latest version. + + The update steps depend on the state of the installation's data. The + possible states and the update steps from those states are: + + 1. Current (data is up to date): + - do nothing + 2. Fresh (new installation): + - initialise empty db + - initialise db version metadata + 3. Stale: (data requires upgrading): + - run upgrade scripts + 4. Legacy (data requires upgrading but is missing version metadata): + - initialise db version metadata + - run upgrade scripts + + """ + data_state = self.get_state() + data_state.update_to_current(self) + + def get_state(self): + if self.is_versioned(): + if self.get_version() == self.get_available_version(): + return DataState.CURRENT + else: + return DataState.STALE + else: + if self.get_version() == -1: + return DataState.FRESH + else: + return DataState.LEGACY + + def get_version(self): + """Get the version number of an installation's data. + + If the data is empty then -1 is returned.""" + if self.is_versioned(): + return self._migratable_db.get_version() + else: + # Calculate data version in older version that does not use the + # current data versioning scheme. Versions earlier than 1.7 are not + # supported for upgrades. In versions 1.7 and 1.8 the database + # version was stored as a property in the profile config file. + legacy_version = self._profile.getValue("pytraining", "DB_version") + if legacy_version is not None: + legacy_version = int(legacy_version) + if legacy_version <= 6: + return legacy_version + elif legacy_version == 7: + return 9 + elif legacy_version == 8: + return 10 + elif legacy_version == 9: + return 10 + return -1 + + def get_available_version(self): + return self._migratable_db.get_upgrade_version() + + def is_versioned(self): + """ Check if the version metadata has been initiaized.""" + return self._migratable_db.is_versioned() + + def initialize_version(self, initial_version): + """Initialize the version metadata.""" + self._migratable_db.version(initial_version) + + def upgrade(self): + self._migratable_db.upgrade() + +class DataState(object): + + """The state of an installation's data. + + The state knows how to update the data to the "current" state.""" + + def __init__(self, name, update_function): + self.name = name + self._update_function = update_function + + def update_to_current(self, installed_data): + """Update the installed data so it is compatible with the current + version.""" + self._update_function(installed_data) + +def _update_fresh(data): + # TODO create empty DB + data.initialize_version(data.get_available_version()) + +def _update_stale(data): + data.upgrade() + +def _update_legacy(data): + data.initialize_version(data.get_version()) + data.upgrade() + +DataState.CURRENT = DataState("CURRENT", lambda data: None) +DataState.FRESH = DataState("FRESH", _update_fresh) +DataState.STALE = DataState("STALE", _update_stale) +DataState.LEGACY = DataState("LEGACY", _update_legacy) Added: pytrainer/trunk/pytrainer/upgrade/migratedb.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/migratedb.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/migratedb.py 2011-07-29 09:59:10 UTC (rev 821) @@ -0,0 +1,55 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +from migrate.versioning.api import db_version, upgrade, version, version_control +from migrate.versioning.exceptions import DatabaseNotControlledError + +class MigratableDb(object): + """Object bridge for sqlalchemy-migrate API functions.""" + + def __init__(self, repository_path, db_url): + self._repository_path = repository_path + self._db_url = db_url + + def is_versioned(self): + """ Check if the DB has been initiaized with version metadata.""" + try: + self.get_version() + except DatabaseNotControlledError: + return False + return True + + def get_version(self): + """Get the current version of the versioned DB. + + Raises DatabaseNotControlledError if the DB is not initialized.""" + return db_version(self._db_url, self._repository_path) + + def get_upgrade_version(self): + """Get the latest version available in upgrade repository.""" + return version(self._repository_path) + + def version(self, initial_version): + """Initialize the database with migrate metadata. + + Raises DatabaseAlreadyControlledError if the DB is already initialized.""" + version_control(self._db_url, self._repository_path, initial_version) + + def upgrade(self): + """Run all available upgrade scripts for the repository.""" + upgrade(self._db_url, self._repository_path) Added: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py (rev 0) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-07-29 09:59:10 UTC (rev 821) @@ -0,0 +1,66 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import unittest +from mock import Mock +from pytrainer.upgrade.data import InstalledData, DataState +from migrate.versioning.exceptions import DatabaseNotControlledError + +class InstalledDataTest(unittest.TestCase): + + def setUp(self): + self._mock_migratable_db = Mock() + self._mock_ddbb = Mock() + self._mock_profile = Mock() + self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_profile) + + def test_get_version_should_return_migrate_version_when_available(self): + self._mock_migratable_db.get_version.return_value = 1 + self.assertEquals(1, self._installed_data.get_version()) + + def test_get_version_should_return_legacy_version_when_available(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_profile.getValue.return_value = 2 + self.assertEquals(2, self._installed_data.get_version()) + + def test_get_version_should_return_negative_one_when_no_existing_version(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_profile.getValue.return_value = None + self.assertEquals(-1, self._installed_data.get_version()) + + def test_get_state_should_return_current_when_data_version_equals_repository_version(self): + self._mock_migratable_db.get_version.return_value = 1 + self._mock_migratable_db.get_upgrade_version.return_value = 1 + self.assertEquals(DataState.CURRENT, self._installed_data.get_state()) + + def test_get_state_should_return_fresh_when_data_version_unavailable(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_profile.getValue.return_value = None + self.assertEquals(DataState.FRESH, self._installed_data.get_state()) + + + def test_get_state_should_return_stale_when_data_version_less_than_repository_version(self): + self._mock_migratable_db.get_version.return_value = 1 + self._mock_migratable_db.get_upgrade_version.return_value = 2 + self.assertEquals(DataState.STALE, self._installed_data.get_state()) + + + def test_get_state_should_return_legacy_when_data_version_from_profile(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_profile.getValue.return_value = 1 + self.assertEquals(DataState.LEGACY, self._installed_data.get_state()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-07-29 10:01:23
|
Revision: 824 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=824&view=rev Author: ncjones Date: 2011-07-29 10:01:17 +0000 (Fri, 29 Jul 2011) Log Message: ----------- Remove DB_version property from configuration file. Modified Paths: -------------- pytrainer/trunk/pytrainer/main.py pytrainer/trunk/pytrainer/profile.py pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Added Paths: ----------- pytrainer/trunk/pytrainer/upgrade/context.py pytrainer/trunk/pytrainer/upgrade/versions/011_remove_db_version.py Modified: pytrainer/trunk/pytrainer/main.py =================================================================== --- pytrainer/trunk/pytrainer/main.py 2011-07-29 10:00:27 UTC (rev 823) +++ pytrainer/trunk/pytrainer/main.py 2011-07-29 10:01:17 UTC (rev 824) @@ -56,7 +56,6 @@ def __init__(self,filename = None, data_path = None): #Version constants self.version ="1.9.0-dev" - self.DB_version = 9 #Process command line options self.startup_options = self.get_options() self.environment = Environment(platform.get_platform(), self.startup_options.conf_dir) @@ -64,7 +63,7 @@ #Setup logging self.set_logging(self.startup_options.log_level, self.startup_options.log_type) logging.debug('>>') - logging.debug("PyTrainer version %s, DB version %s" % (self.version, self.DB_version)) + logging.debug("PyTrainer version %s" % (self.version)) self.data_path = data_path self.date = Date() self.ddbb = None @@ -77,7 +76,7 @@ logging.debug('connecting to DDBB') self.ddbb.connect() - initialize_data(self.ddbb, self.profile) + initialize_data(self.ddbb, self.profile, self.environment.conf_dir) if self.startup_options.check: logging.debug("Checking DB as per user's request") self.sanityCheck() Modified: pytrainer/trunk/pytrainer/profile.py =================================================================== --- pytrainer/trunk/pytrainer/profile.py 2011-07-29 10:00:27 UTC (rev 823) +++ pytrainer/trunk/pytrainer/profile.py 2011-07-29 10:01:17 UTC (rev 824) @@ -56,7 +56,6 @@ "prf_ddbbuser":"", "prf_ddbbpass":"", "version":"0.0", - "DB_version":"0", "prf_us_system":"False", "prf_hrzones_karvonen":"False", "prf_maxhr":"", Added: pytrainer/trunk/pytrainer/upgrade/context.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/context.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/context.py 2011-07-29 10:01:17 UTC (rev 824) @@ -0,0 +1,31 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +class UpgradeContext(object): + + """Context used by upgrade scripts. + + Provides access to the application base dir.""" + + def __init__(self, conf_dir): + self.conf_dir = conf_dir + +# sqlalchemy-migrate does not provide any means to inject the context object in +# to upgrade scripts so instead we provide access via this global which must be +# initialised before upgrading. +UPGRADE_CONTEXT = None Modified: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py 2011-07-29 10:00:27 UTC (rev 823) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-07-29 10:01:17 UTC (rev 824) @@ -22,21 +22,22 @@ MIGRATE_REPOSITORY_PATH = "pytrainer/upgrade" -def initialize_data(ddbb, profile): +def initialize_data(ddbb, profile, conf_dir): """Initializes the installation's data.""" db_url = ddbb.get_connection_url() migratable_db = MigratableDb(MIGRATE_REPOSITORY_PATH, db_url) - InstalledData(migratable_db, ddbb, profile).update_to_current() + InstalledData(migratable_db, ddbb, profile, conf_dir).update_to_current() class InstalledData(object): """Encapsulates an installation's existing data and provides means to check version state and upgrade.""" - def __init__(self, migratable_db, ddbb, profile): + def __init__(self, migratable_db, ddbb, profile, conf_dir): self._migratable_db = migratable_db self._ddbb = ddbb self._profile = profile + self._conf_dir = conf_dir def update_to_current(self): """Check the current state of the installation's data and update them @@ -112,6 +113,7 @@ self._ddbb.create_tables() def upgrade(self): + pytrainer.upgrade.context.UPGRADE_CONTEXT = UpgradeContext(self._conf_dir) self._migratable_db.upgrade() class DataState(object): Added: pytrainer/trunk/pytrainer/upgrade/versions/011_remove_db_version.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/versions/011_remove_db_version.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/versions/011_remove_db_version.py 2011-07-29 10:01:17 UTC (rev 824) @@ -0,0 +1,10 @@ +from lxml import etree +from pytrainer.upgrade.context import UPGRADE_CONTEXT + +def upgrade(migrate_engine): + config_file = UPGRADE_CONTEXT.conf_dir + "/conf.xml" + parser = etree.XMLParser(encoding="UTF8", recover=True) + xml_tree = etree.parse(config_file, parser=parser) + config_element = xml_tree.getroot() + del config_element.attrib["DB_version"] + xml_tree.write(config_file, xml_declaration=True, encoding="UTF-8") Modified: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-07-29 10:00:27 UTC (rev 823) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-07-29 10:01:17 UTC (rev 824) @@ -27,7 +27,7 @@ self._mock_migratable_db = Mock() self._mock_ddbb = Mock() self._mock_profile = Mock() - self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_profile) + self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_profile, "") def test_get_version_should_return_migrate_version_when_available(self): self._mock_migratable_db.get_version.return_value = 1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-02 12:52:25
|
Revision: 829 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=829&view=rev Author: ncjones Date: 2011-08-02 12:52:19 +0000 (Tue, 02 Aug 2011) Log Message: ----------- Fix version checking for pytrainer built with distutils. ticket:132 Modified Paths: -------------- pytrainer/trunk/pytrainer/upgrade/migratedb.py pytrainer/trunk/setup.py Modified: pytrainer/trunk/pytrainer/upgrade/migratedb.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/migratedb.py 2011-07-29 22:50:26 UTC (rev 828) +++ pytrainer/trunk/pytrainer/upgrade/migratedb.py 2011-08-02 12:52:19 UTC (rev 829) @@ -18,12 +18,22 @@ from migrate.versioning.api import db_version, upgrade, version, version_control from migrate.versioning.exceptions import DatabaseNotControlledError +import logging +import os +import sys class MigratableDb(object): """Object bridge for sqlalchemy-migrate API functions.""" def __init__(self, repository_path, db_url): - self._repository_path = repository_path + """Create a migratable DB. + + Arguments: + repository_path -- The path to the migrate repository, relative to the + pypath. + db_url -- the connection URL string for the DB. + """ + self._repository_path = _get_resource_absolute_path(repository_path) self._db_url = db_url def is_versioned(self): @@ -53,3 +63,12 @@ def upgrade(self): """Run all available upgrade scripts for the repository.""" upgrade(self._db_url, self._repository_path) + +def _get_resource_absolute_path(resource_name): + """Get the absolute path to a resource on the python system path.""" + for path in sys.path: + candidate = os.path.join(path, resource_name) + if os.path.exists(candidate): + logging.debug("Found resource: %s", candidate) + return candidate + raise ValueError("Resource '{0}' could not be found".format(resource_name)) Modified: pytrainer/trunk/setup.py =================================================================== --- pytrainer/trunk/setup.py 2011-07-29 22:50:26 UTC (rev 828) +++ pytrainer/trunk/setup.py 2011-08-02 12:52:19 UTC (rev 829) @@ -28,7 +28,9 @@ 'pytrainer.lib', 'pytrainer.upgrade' ], - + package_data={ + 'pytrainer.upgrade': ['migrate.cfg', 'versions/*.sql', 'versions/*.py'] + }, data_files=[ ('share/pytrainer/glade/',glob("glade/*.glade")), ('share/pytrainer/glade/',glob("glade/*.png")), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-04 13:03:50
|
Revision: 830 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=830&view=rev Author: ncjones Date: 2011-08-04 13:03:44 +0000 (Thu, 04 Aug 2011) Log Message: ----------- Fix data module unit tests. Modified Paths: -------------- pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Modified: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-02 12:52:19 UTC (rev 829) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:03:44 UTC (rev 830) @@ -28,17 +28,17 @@ """Initializes the installation's data.""" db_url = ddbb.get_connection_url() migratable_db = MigratableDb(MIGRATE_REPOSITORY_PATH, db_url) - InstalledData(migratable_db, ddbb, conf_dir).update_to_current() + InstalledData(migratable_db, ddbb, LegacyVersionProvider(conf_dir)).update_to_current() class InstalledData(object): """Encapsulates an installation's existing data and provides means to check version state and upgrade.""" - def __init__(self, migratable_db, ddbb, conf_dir): + def __init__(self, migratable_db, ddbb, legacy_version_provider): self._migratable_db = migratable_db self._ddbb = ddbb - self._conf_dir = conf_dir + self._legacy_version_provider = legacy_version_provider def update_to_current(self): """Check the current state of the installation's data and update them @@ -84,7 +84,7 @@ else: # Calculate data version in older version that does not use the # current data versioning scheme. - legacy_version = self._get_legacy_version() + legacy_version = self._legacy_version_provider.get_legacy_version() if legacy_version is not None: legacy_version = int(legacy_version) if legacy_version <= 6: @@ -97,16 +97,6 @@ return 10 return -1 - def _get_legacy_version(self): - # In versions 1.7 and 1.8 the database version was stored as a property - # in the config file. Versions earlier than 1.7 are not supported for - # upgrades. - config_file = self._conf_dir + "/conf.xml" - parser = etree.XMLParser(encoding="UTF8", recover=True) - xml_tree = etree.parse(config_file, parser=parser) - config_element = xml_tree.getroot() - return config_element.get("DB_version") - def get_available_version(self): return self._migratable_db.get_upgrade_version() @@ -161,3 +151,21 @@ DataState.FRESH = DataState("FRESH", _update_fresh) DataState.STALE = DataState("STALE", _update_stale) DataState.LEGACY = DataState("LEGACY", _update_legacy) + +class LegacyVersionProvider(object): + + """Provides the DB version number for data not using the current data + versioning scheme.""" + + def __init__(self, conf_dir): + self._conf_dir = conf_dir + + def get_legacy_version(self): + # In versions 1.7 and 1.8 the database version was stored as a property + # in the config file. Versions earlier than 1.7 are not supported for + # upgrades. + config_file = self._conf_dir + "/conf.xml" + parser = etree.XMLParser(encoding="UTF8", recover=True) + xml_tree = etree.parse(config_file, parser=parser) + config_element = xml_tree.getroot() + return config_element.get("DB_version") Modified: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-02 12:52:19 UTC (rev 829) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:03:44 UTC (rev 830) @@ -26,8 +26,8 @@ def setUp(self): self._mock_migratable_db = Mock() self._mock_ddbb = Mock() - self._mock_profile = Mock() - self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_profile, "") + self._mock_version_provider = Mock() + self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_version_provider) def test_get_version_should_return_migrate_version_when_available(self): self._mock_migratable_db.get_version.return_value = 1 @@ -35,12 +35,12 @@ def test_get_version_should_return_legacy_version_when_available(self): self._mock_migratable_db.is_versioned.return_value = False - self._mock_profile.getValue.return_value = 2 + self._mock_version_provider.get_legacy_version.return_value = 2 self.assertEquals(2, self._installed_data.get_version()) def test_get_version_should_return_negative_one_when_no_existing_version(self): self._mock_migratable_db.is_versioned.return_value = False - self._mock_profile.getValue.return_value = None + self._mock_version_provider.get_legacy_version.return_value = None self.assertEquals(-1, self._installed_data.get_version()) def test_get_state_should_return_current_when_data_version_equals_repository_version(self): @@ -50,7 +50,7 @@ def test_get_state_should_return_fresh_when_data_version_unavailable(self): self._mock_migratable_db.is_versioned.return_value = False - self._mock_profile.getValue.return_value = None + self._mock_version_provider.get_legacy_version.return_value = None self.assertEquals(DataState.FRESH, self._installed_data.get_state()) @@ -60,7 +60,7 @@ self.assertEquals(DataState.STALE, self._installed_data.get_state()) - def test_get_state_should_return_legacy_when_data_version_from_profile(self): + def test_get_state_should_return_legacy_when_data_version_is_legacy(self): self._mock_migratable_db.is_versioned.return_value = False - self._mock_profile.getValue.return_value = 1 + self._mock_version_provider.get_legacy_version.return_value = 1 self.assertEquals(DataState.LEGACY, self._installed_data.get_state()) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-04 13:04:33
|
Revision: 831 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=831&view=rev Author: ncjones Date: 2011-08-04 13:04:27 +0000 (Thu, 04 Aug 2011) Log Message: ----------- Check for invalid database state. Modified Paths: -------------- pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Modified: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:03:44 UTC (rev 830) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:04:27 UTC (rev 831) @@ -64,13 +64,17 @@ data_state.update_to_current(self) def get_state(self): + version = self.get_version() + available_version= self.get_available_version() if self.is_versioned(): - if self.get_version() == self.get_available_version(): + if version == available_version: return DataState.CURRENT + elif version > available_version: + raise ValueError("Current version ({0}) is greater than available version ({1}).".format(version, available_version)) else: return DataState.STALE else: - if self.get_version() == -1: + if version == -1: return DataState.FRESH else: return DataState.LEGACY Modified: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:03:44 UTC (rev 830) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:04:27 UTC (rev 831) @@ -19,7 +19,6 @@ import unittest from mock import Mock from pytrainer.upgrade.data import InstalledData, DataState -from migrate.versioning.exceptions import DatabaseNotControlledError class InstalledDataTest(unittest.TestCase): @@ -53,14 +52,23 @@ self._mock_version_provider.get_legacy_version.return_value = None self.assertEquals(DataState.FRESH, self._installed_data.get_state()) - def test_get_state_should_return_stale_when_data_version_less_than_repository_version(self): self._mock_migratable_db.get_version.return_value = 1 self._mock_migratable_db.get_upgrade_version.return_value = 2 self.assertEquals(DataState.STALE, self._installed_data.get_state()) - def test_get_state_should_return_legacy_when_data_version_is_legacy(self): self._mock_migratable_db.is_versioned.return_value = False self._mock_version_provider.get_legacy_version.return_value = 1 self.assertEquals(DataState.LEGACY, self._installed_data.get_state()) + + def test_get_state_should_raise_error_when_version_too_large(self): + self._mock_migratable_db.is_versioned.return_value = True + self._mock_migratable_db.get_version.return_value = 2 + self._mock_migratable_db.get_upgrade_version.return_value = 1 + try: + self._installed_data.get_state() + except ValueError: + pass + else: + self.fail() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-04 13:05:40
|
Revision: 833 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=833&view=rev Author: ncjones Date: 2011-08-04 13:05:34 +0000 (Thu, 04 Aug 2011) Log Message: ----------- Return None instead of -1 to indicate no version. Modified Paths: -------------- pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Modified: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:05:04 UTC (rev 832) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:05:34 UTC (rev 833) @@ -74,7 +74,7 @@ else: return DataState.STALE else: - if version == -1: + if version == None: return DataState.FRESH else: return DataState.LEGACY @@ -82,7 +82,7 @@ def get_version(self): """Get the version number of an installation's data. - If the data is empty then -1 is returned.""" + If the data is empty then None is returned.""" if self.is_versioned(): return self._migratable_db.get_version() else: @@ -99,7 +99,7 @@ return 10 elif legacy_version == 9: return 10 - return -1 + return None def get_available_version(self): return self._migratable_db.get_upgrade_version() Modified: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:05:04 UTC (rev 832) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:05:34 UTC (rev 833) @@ -37,10 +37,10 @@ self._mock_version_provider.get_legacy_version.return_value = 2 self.assertEquals(2, self._installed_data.get_version()) - def test_get_version_should_return_negative_one_when_no_existing_version(self): + def test_get_version_should_return_none_when_no_existing_version(self): self._mock_migratable_db.is_versioned.return_value = False self._mock_version_provider.get_legacy_version.return_value = None - self.assertEquals(-1, self._installed_data.get_version()) + self.assertEquals(None, self._installed_data.get_version()) def test_get_state_should_return_current_when_data_version_equals_repository_version(self): self._mock_migratable_db.get_version.return_value = 1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-06 11:14:08
|
Revision: 834 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=834&view=rev Author: ncjones Date: 2011-08-06 11:14:02 +0000 (Sat, 06 Aug 2011) Log Message: ----------- Fix initialisation of upgrade context. Modified Paths: -------------- pytrainer/trunk/pytrainer/upgrade/data.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Modified: pytrainer/trunk/pytrainer/upgrade/data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-04 13:05:34 UTC (rev 833) +++ pytrainer/trunk/pytrainer/upgrade/data.py 2011-08-06 11:14:02 UTC (rev 834) @@ -28,17 +28,18 @@ """Initializes the installation's data.""" db_url = ddbb.get_connection_url() migratable_db = MigratableDb(MIGRATE_REPOSITORY_PATH, db_url) - InstalledData(migratable_db, ddbb, LegacyVersionProvider(conf_dir)).update_to_current() + InstalledData(migratable_db, ddbb, LegacyVersionProvider(conf_dir), UpgradeContext(conf_dir)).update_to_current() class InstalledData(object): """Encapsulates an installation's existing data and provides means to check version state and upgrade.""" - def __init__(self, migratable_db, ddbb, legacy_version_provider): + def __init__(self, migratable_db, ddbb, legacy_version_provider, upgrade_context): self._migratable_db = migratable_db self._ddbb = ddbb self._legacy_version_provider = legacy_version_provider + self._upgrade_context = upgrade_context def update_to_current(self): """Check the current state of the installation's data and update them @@ -119,7 +120,7 @@ def upgrade(self): logging.info("Upgrading data from version '%s' to version '%s'.", self.get_version(), self.get_available_version()) - pytrainer.upgrade.context.UPGRADE_CONTEXT = UpgradeContext(self._conf_dir) + pytrainer.upgrade.context.UPGRADE_CONTEXT = self._upgrade_context self._migratable_db.upgrade() class DataState(object): Modified: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-04 13:05:34 UTC (rev 833) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-06 11:14:02 UTC (rev 834) @@ -26,7 +26,8 @@ self._mock_migratable_db = Mock() self._mock_ddbb = Mock() self._mock_version_provider = Mock() - self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_version_provider) + self._mock_upgrade_context = Mock() + self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_version_provider, self._mock_upgrade_context) def test_get_version_should_return_migrate_version_when_available(self): self._mock_migratable_db.get_version.return_value = 1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <nc...@us...> - 2011-08-08 12:39:19
|
Revision: 837 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=837&view=rev Author: ncjones Date: 2011-08-08 12:39:11 +0000 (Mon, 08 Aug 2011) Log Message: ----------- Move test modules to source package hierachy. ticket:139 Added Paths: ----------- pytrainer/trunk/pytrainer/test/ pytrainer/trunk/pytrainer/test/environment_test.py pytrainer/trunk/pytrainer/test/equipment_test.py pytrainer/trunk/pytrainer/test/gui/ pytrainer/trunk/pytrainer/test/gui/equipment_test.py pytrainer/trunk/pytrainer/test/upgrade/ pytrainer/trunk/pytrainer/test/upgrade/data_test.py Removed Paths: ------------- pytrainer/trunk/test/pytrainer/environment_test.py pytrainer/trunk/test/pytrainer/equipment_test.py pytrainer/trunk/test/pytrainer/gui/equipment_test.py pytrainer/trunk/test/pytrainer/upgrade/data_test.py Copied: pytrainer/trunk/pytrainer/test/environment_test.py (from rev 836, pytrainer/trunk/test/pytrainer/environment_test.py) =================================================================== --- pytrainer/trunk/pytrainer/test/environment_test.py (rev 0) +++ pytrainer/trunk/pytrainer/test/environment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -0,0 +1,75 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import unittest + +from mock import Mock + +import pytrainer.platform +from pytrainer.environment import Environment + +TEST_DIR_NAME = "/test/.pytrainer_test" + +PLATFORM = pytrainer.platform.get_platform() + +class Test(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_get_conf_dir(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME, environment.conf_dir) + + def test_get_conf_dir_default(self): + test_platform = Mock(spec=pytrainer.platform.get_platform()) + test_platform.get_default_conf_dir.return_value = "/test/.pytrainer_test" + environment = Environment(test_platform, None) + self.assertEquals("/test/.pytrainer_test", environment.conf_dir) + + def test_get_conf_file(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/conf.xml", environment.conf_file) + + def test_get_log_file(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/log.out", environment.log_file) + + def test_get_temp_dir(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/tmp", environment.temp_dir) + + def test_get_gpx_dir(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/gpx", environment.gpx_dir) + + def test_get_extension_dir(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/extensions", environment.extension_dir) + + def test_get_plugin_dir(self): + environment = Environment(PLATFORM, TEST_DIR_NAME) + self.assertEquals(TEST_DIR_NAME + "/plugins", environment.plugin_dir) + + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() \ No newline at end of file Copied: pytrainer/trunk/pytrainer/test/equipment_test.py (from rev 836, pytrainer/trunk/test/pytrainer/equipment_test.py) =================================================================== --- pytrainer/trunk/pytrainer/test/equipment_test.py (rev 0) +++ pytrainer/trunk/pytrainer/test/equipment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -0,0 +1,348 @@ +# -*- coding: utf-8 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import unittest +import mock +from pytrainer.equipment import Equipment, EquipmentService,\ + EquipmentServiceException +from pytrainer.lib.sqliteUtils import Sql + +class EquipmentTest(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + def test_id_defaults_to_none(self): + equipment = Equipment() + self.assertEqual(None, equipment.id) + + def test_id_set_to_integer(self): + equipment = Equipment() + equipment.id = 2 + self.assertEquals(2, equipment.id) + + def test_id_set_to_numeric_string(self): + equipment = Equipment() + equipment.id = "3" + self.assertEquals(3, equipment.id) + + def test_id_set_to_non_numeric_string(self): + equipment = Equipment() + try: + equipment.id = "test" + except(ValueError): + pass + else: + self.fail("Should not be able to set equipment id to non numeric value.") + + def test_description_defaults_to_empty_string(self): + equipment = Equipment() + self.assertEquals(u"", equipment.description) + + def test_description_set_to_string(self): + equipment = Equipment() + try: + equipment.description = "100$ Shoes" + except(TypeError): + pass + else: + self.fail("Should not be able to set description to non unicode string value.") + + def test_description_set_to_unicode_string(self): + equipment = Equipment() + equipment.description = u"Zapatos de €100" + self.assertEquals(u"Zapatos de €100", equipment.description) + + def test_description_set_to_non_string(self): + equipment = Equipment() + try: + equipment.description = 42 + except(TypeError): + pass + else: + self.fail("Should not be able to set description to non string value.") + + def test_active_defaults_to_true(self): + equipment = Equipment() + self.assertTrue(equipment.active) + + def test_active_set_to_boolean(self): + equipment = Equipment() + equipment.active = False + self.assertFalse(equipment.active) + + def test_active_set_to_non_boolean(self): + equipment = Equipment() + try: + equipment.active = "test" + except(TypeError): + pass + else: + self.fail("Should not be able to set active to non-boolean value.") + + def test_life_expectancy_defaults_to_zero(self): + equipment = Equipment() + self.assertEqual(0, equipment.life_expectancy) + + def test_life_expectancy_set_to_integer(self): + equipment = Equipment() + equipment.life_expectancy = 2 + self.assertEquals(2, equipment.life_expectancy) + + def test_life_expectancy_set_to_numeric_string(self): + equipment = Equipment() + equipment.life_expectancy = "3" + self.assertEquals(3, equipment.life_expectancy) + + def test_life_expectancy_set_to_non_numeric_string(self): + equipment = Equipment() + try: + equipment.life_expectancy = "test" + except(ValueError): + pass + else: + self.fail("Should not be able to set life expectancy to non numeric value.") + + def test_prior_usage_defaults_to_zero(self): + equipment = Equipment() + self.assertEqual(0, equipment.prior_usage) + + def test_prior_usage_set_to_integer(self): + equipment = Equipment() + equipment.prior_usage = 2 + self.assertEquals(2, equipment.prior_usage) + + def test_prior_usage_set_to_numeric_string(self): + equipment = Equipment() + equipment.prior_usage = "3" + self.assertEquals(3, equipment.prior_usage) + + def test_prior_usage_set_to_non_numeric_string(self): + equipment = Equipment() + try: + equipment.prior_usage = "test" + except(ValueError): + pass + else: + self.fail("Should not be able to set life expectancy to non numeric value.") + + def test_notes_defaults_to_empty_string(self): + equipment = Equipment() + self.assertEquals(u"", equipment.notes) + + def test_notes_set_to_string(self): + equipment = Equipment() + try: + equipment.notes = "100$ Shoes" + except(TypeError): + pass + else: + self.fail("Should not be able to set notes to non-unicode string value.") + + def test_notes_set_to_unicode_string(self): + equipment = Equipment() + equipment.notes = u"Zapatos de €100." + self.assertEquals(u"Zapatos de €100.", equipment.notes) + + def test_notes_set_to_non_string(self): + equipment = Equipment() + try: + equipment.notes = 42 + except(TypeError): + pass + else: + self.fail("Should not be able to set notes to non-string value.") + + def test_equals_new_instances(self): + equipment1 = Equipment() + equipment2 = Equipment() + self.assertNotEqual(equipment1, equipment2, "") + + def test_equals_instances_with_same_id(self): + equipment1 = Equipment() + equipment1.id = 1 + equipment2 = Equipment() + equipment2.id = 1 + self.assertEqual(equipment1, equipment2, "Equipment instances with same id should be equal.") + + def test_equals_instances_with_different_ids(self): + equipment1 = Equipment() + equipment1.id = 1 + equipment2 = Equipment() + equipment2.id = 2 + self.assertNotEqual(equipment1, equipment2, "Equipment instances with different ids should not be equal.") + +class EquipmentServiceTest(unittest.TestCase): + + def setUp(self): + self.mock_ddbb = mock.Mock(spec=Sql) + self.equipment_service = EquipmentService(self.mock_ddbb) + + def tearDown(self): + pass + + def test_get_equipment_item(self): + self.mock_ddbb.select.return_value = [(1, u"Test Description", True, 500, 200, u"Test notes.")] + item = self.equipment_service.get_equipment_item(1) + self.assertEquals(1, item.id) + self.assertEquals("Test Description", item.description) + self.assertTrue(item.active) + self.assertEquals(500, item.life_expectancy) + self.assertEquals(200, item.prior_usage) + self.assertEquals("Test notes.", item.notes) + + def test_get_equipment_item_non_unicode(self): + self.mock_ddbb.select.return_value = [(1, "Test Description", True, 500, 200, "Test notes.")] + item = self.equipment_service.get_equipment_item(1) + self.assertEquals("Test Description", item.description) + self.assertEquals("Test notes.", item.notes) + + def test_get_equipment_item_non_existant(self): + self.mock_ddbb.select.return_value = [] + item = self.equipment_service.get_equipment_item(1) + self.assertEquals(None, item) + + def test_get_all_equipment(self): + self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."), + (2, u"Test item 2", False, 600, 300, u"Test notes 2.")] + items = self.equipment_service.get_all_equipment() + item = items[0] + self.assertEquals(1, item.id) + self.assertEquals("Test item 1", item.description) + self.assertTrue(item.active) + self.assertEquals(500, item.life_expectancy) + self.assertEquals(200, item.prior_usage) + self.assertEquals("Test notes 1.", item.notes) + item = items[1] + self.assertEquals(2, item.id) + self.assertEquals("Test item 2", item.description) + self.assertFalse(item.active) + self.assertEquals(600, item.life_expectancy) + self.assertEquals(300, item.prior_usage) + self.assertEquals("Test notes 2.", item.notes) + + def test_get_all_equipment_non_existant(self): + self.mock_ddbb.select.return_value = [] + items = self.equipment_service.get_all_equipment() + self.assertEquals([], items) + + def test_get_active_equipment(self): + self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."), + (2, u"Test item 2", True, 600, 300, u"Test notes 2.")] + items = self.equipment_service.get_active_equipment() + item = items[0] + self.assertEquals(1, item.id) + self.assertEquals("Test item 1", item.description) + self.assertTrue(item.active) + self.assertEquals(500, item.life_expectancy) + self.assertEquals(200, item.prior_usage) + self.assertEquals("Test notes 1.", item.notes) + item = items[1] + self.assertEquals(2, item.id) + self.assertEquals("Test item 2", item.description) + self.assertTrue(item.active) + self.assertEquals(600, item.life_expectancy) + self.assertEquals(300, item.prior_usage) + self.assertEquals("Test notes 2.", item.notes) + + def test_get_active_equipment_non_existant(self): + self.mock_ddbb.select.return_value = [] + items = self.equipment_service.get_active_equipment() + self.assertEquals([], items) + + def test_store_equipment(self): + equipment = Equipment() + equipment.description = u"test description" + equipment_ids = [] + def mock_select(table, columns, where): + if columns == "id": + return equipment_ids + else: + return [(2, u"", 1, 0, 0,u"")] + def update_equipment_ids(*args): + equipment_ids.append([1]) + self.mock_ddbb.select = mock.Mock(wraps=mock_select) + self.mock_ddbb.insert.side_effect = update_equipment_ids + stored_equipment = self.equipment_service.store_equipment(equipment) + self.mock_ddbb.insert.assert_called_with("equipment", + "description,active,life_expectancy,prior_usage,notes", + ["test description", 1, 0, 0,"" ]) + self.assertEquals(2, stored_equipment.id) + + def test_store_equipment_duplicate_description(self): + self.mock_ddbb.select.return_value = [(1,)] + equipment = Equipment() + equipment.description = u"test item" + try: + self.equipment_service.store_equipment(equipment) + self.fail("Should not be able to store new item with non-unique description.") + except(EquipmentServiceException): + pass + + def test_update_equipment(self): + equipment = Equipment() + equipment.id = 1 + equipment.description = u"new description" + self.mock_ddbb.select.return_value = [(1, u"old description", 1, 0, 0,u"")] + self.equipment_service.store_equipment(equipment) + self.mock_ddbb.update.assert_called_with("equipment", + "description,active,life_expectancy,prior_usage,notes", + ["new description", 1, 0, 0,"" ], + "id = 1") + + def test_update_equipment_non_existant(self): + self.mock_ddbb.select.return_value = [] + equipment = Equipment() + equipment.id = 1 + try: + self.equipment_service.store_equipment(equipment) + self.fail("Should not be able to update an item which did not previously exist.") + except(EquipmentServiceException): + pass + + def test_update_equipment_duplicate_description(self): + self.mock_ddbb.select.return_value = [(1, u"test item", True, 500, 200, u"Test notes.")] + equipment = Equipment() + equipment.id = 2 + equipment.description = u"test item" + try: + self.equipment_service.store_equipment(equipment) + self.fail("Should not be able to change item description to non-unique value.") + except(EquipmentServiceException): + pass + + def test_get_equipment_usage(self): + self.mock_ddbb.select.return_value = [(250,)] + equipment = Equipment() + equipment.id = 1 + usage = self.equipment_service.get_equipment_usage(equipment) + self.assertEquals(250, usage) + + def test_get_equipment_usage_none(self): + self.mock_ddbb.select.return_value = [(None,)] + equipment = Equipment() + equipment.id = 1 + usage = self.equipment_service.get_equipment_usage(equipment) + self.assertEquals(0, usage) + +if __name__ == "__main__": + #import sys;sys.argv = ['', 'Test.testName'] + unittest.main() Copied: pytrainer/trunk/pytrainer/test/gui/equipment_test.py (from rev 836, pytrainer/trunk/test/pytrainer/gui/equipment_test.py) =================================================================== --- pytrainer/trunk/pytrainer/test/gui/equipment_test.py (rev 0) +++ pytrainer/trunk/pytrainer/test/gui/equipment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +from unittest import TestCase +from mock import Mock +from pytrainer.equipment import Equipment, EquipmentService +from pytrainer.gui.equipment import EquipmentStore + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +class EquipmentStoreTest(TestCase): + + def setUp(self): + self.mock_equipment_service = Mock(spec=EquipmentService) + self.mock_equipment_service.get_equipment_usage.return_value = 0 + + def tearDown(self): + pass + + def test_get_item_id(self): + equipment = Equipment() + equipment.id = 1 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals(1, equipment_store.get_value(iter, 0)) + + def test_get_item_description(self): + equipment = Equipment() + equipment.id = 1 + equipment.description = u"item description" + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("item description", equipment_store.get_value(iter, 1)) + + def test_get_item_usage_percent(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 100 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals(50, equipment_store.get_value(iter, 2)) + + def test_get_item_usage_percent_prior_usage(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + equipment.prior_usage = 50 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 100 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals(75, equipment_store.get_value(iter, 2)) + + def test_get_item_usage_percent_zero_usage(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 0 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals(0, equipment_store.get_value(iter, 2)) + + def test_get_item_usage_percent_usage_exceeds_life_expectancy(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 300 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals(100, equipment_store.get_value(iter, 2), "Progress bar cannot exceed 100%.") + + + def test_get_item_usage_text(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 100 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("100 / 200", equipment_store.get_value(iter, 3)) + + def test_get_item_usage_text_rounded(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 100.5 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("101 / 200", equipment_store.get_value(iter, 3)) + + def test_get_item_usage_text_prior_usage(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + equipment.prior_usage = 50 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 100 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("150 / 200", equipment_store.get_value(iter, 3)) + + def test_get_item_usage_text_zero_usage(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 0 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("0 / 200", equipment_store.get_value(iter, 3)) + + def test_get_item_usage_text_usage_exceeds_life_expectancy(self): + equipment = Equipment() + equipment.id = 1 + equipment.life_expectancy = 200 + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + self.mock_equipment_service.get_equipment_usage.return_value = 300 + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertEquals("300 / 200", equipment_store.get_value(iter, 3)) + + def test_get_item_active(self): + equipment = Equipment() + equipment.id = 1 + equipment.active = False + self.mock_equipment_service.get_all_equipment.return_value = [equipment] + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + self.assertFalse(equipment_store.get_value(iter, 4)) + + def test_multiple_equipment_items(self): + equipment1 = Equipment() + equipment1.id = 1 + equipment2 = Equipment() + equipment2.id = 2 + self.mock_equipment_service.get_all_equipment.return_value = [equipment1, equipment2] + equipment_store = EquipmentStore(self.mock_equipment_service) + iter = equipment_store.get_iter_first() + iter = equipment_store.iter_next(iter) + self.assertEquals(2, equipment_store.get_value(iter, 0)) + + Copied: pytrainer/trunk/pytrainer/test/upgrade/data_test.py (from rev 836, pytrainer/trunk/test/pytrainer/upgrade/data_test.py) =================================================================== --- pytrainer/trunk/pytrainer/test/upgrade/data_test.py (rev 0) +++ pytrainer/trunk/pytrainer/test/upgrade/data_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -0,0 +1,106 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import unittest +from mock import Mock +from pytrainer.upgrade.data import InstalledData, DataState +import pytrainer.upgrade.context + +class InstalledDataTest(unittest.TestCase): + + def setUp(self): + self._mock_migratable_db = Mock() + self._mock_ddbb = Mock() + self._mock_version_provider = Mock() + self._mock_upgrade_context = Mock() + self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_version_provider, self._mock_upgrade_context) + + def test_get_version_should_return_migrate_version_when_available(self): + self._mock_migratable_db.get_version.return_value = 1 + self.assertEquals(1, self._installed_data.get_version()) + + def test_get_version_should_return_legacy_version_when_available(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_version_provider.get_legacy_version.return_value = 2 + self.assertEquals(2, self._installed_data.get_version()) + + def test_get_version_should_return_none_when_no_existing_version(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_version_provider.get_legacy_version.return_value = None + self.assertEquals(None, self._installed_data.get_version()) + + def test_get_state_should_return_current_when_data_version_equals_repository_version(self): + self._mock_migratable_db.get_version.return_value = 1 + self._mock_migratable_db.get_upgrade_version.return_value = 1 + self.assertEquals(DataState.CURRENT, self._installed_data.get_state()) + + def test_get_state_should_return_fresh_when_data_version_unavailable(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_version_provider.get_legacy_version.return_value = None + self.assertEquals(DataState.FRESH, self._installed_data.get_state()) + + def test_get_state_should_return_stale_when_data_version_less_than_repository_version(self): + self._mock_migratable_db.get_version.return_value = 1 + self._mock_migratable_db.get_upgrade_version.return_value = 2 + self.assertEquals(DataState.STALE, self._installed_data.get_state()) + + def test_get_state_should_return_legacy_when_data_version_is_legacy(self): + self._mock_migratable_db.is_versioned.return_value = False + self._mock_version_provider.get_legacy_version.return_value = 1 + self.assertEquals(DataState.LEGACY, self._installed_data.get_state()) + + def test_get_state_should_raise_error_when_version_too_large(self): + self._mock_migratable_db.is_versioned.return_value = True + self._mock_migratable_db.get_version.return_value = 2 + self._mock_migratable_db.get_upgrade_version.return_value = 1 + try: + self._installed_data.get_state() + except ValueError: + pass + else: + self.fail() + + def test_get_available_version_returns_migratable_db_upgrade_version(self): + self._mock_migratable_db.get_upgrade_version.return_value = 1 + self.assertEquals(1, self._installed_data.get_available_version()) + + def test_is_versioned_returns_migratable_db_is_versioned(self): + self._mock_migratable_db.is_versioned.return_value = True + self.assertEquals(True, self._installed_data.is_versioned()) + + def test_initialize_version_versions_migratable_db(self): + self._installed_data.initialize_version(1) + self._mock_migratable_db.version.assert_called_with(1) + + def test_initialize_creates_db_tables(self): + self._installed_data.initialize() + self.assertTrue(self._mock_ddbb.create_tables.called) + + def test_upgrade_upgrades_migratable_db(self): + self._installed_data.upgrade() + self.assertTrue(self._mock_migratable_db.upgrade.called) + + def test_upgrade_initializes_global_upgrade_context(self): + self._installed_data.upgrade() + self.assertEqual(self._mock_upgrade_context, pytrainer.upgrade.context.UPGRADE_CONTEXT) + + def test_update_to_current_delegates_to_data_state(self): + mock_state = Mock() + self._installed_data.get_state = Mock(return_value=mock_state) + self._installed_data.update_to_current() + self.assertTrue(mock_state.update_to_current.called) Deleted: pytrainer/trunk/test/pytrainer/environment_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/environment_test.py 2011-08-06 13:13:35 UTC (rev 836) +++ pytrainer/trunk/test/pytrainer/environment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -1,75 +0,0 @@ -# -*- coding: iso-8859-1 -*- - -#Copyright (C) Nathan Jones nc...@us... - -#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. - -import unittest - -from mock import Mock - -import pytrainer.platform -from pytrainer.environment import Environment - -TEST_DIR_NAME = "/test/.pytrainer_test" - -PLATFORM = pytrainer.platform.get_platform() - -class Test(unittest.TestCase): - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_get_conf_dir(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME, environment.conf_dir) - - def test_get_conf_dir_default(self): - test_platform = Mock(spec=pytrainer.platform.get_platform()) - test_platform.get_default_conf_dir.return_value = "/test/.pytrainer_test" - environment = Environment(test_platform, None) - self.assertEquals("/test/.pytrainer_test", environment.conf_dir) - - def test_get_conf_file(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/conf.xml", environment.conf_file) - - def test_get_log_file(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/log.out", environment.log_file) - - def test_get_temp_dir(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/tmp", environment.temp_dir) - - def test_get_gpx_dir(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/gpx", environment.gpx_dir) - - def test_get_extension_dir(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/extensions", environment.extension_dir) - - def test_get_plugin_dir(self): - environment = Environment(PLATFORM, TEST_DIR_NAME) - self.assertEquals(TEST_DIR_NAME + "/plugins", environment.plugin_dir) - - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() \ No newline at end of file Deleted: pytrainer/trunk/test/pytrainer/equipment_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/equipment_test.py 2011-08-06 13:13:35 UTC (rev 836) +++ pytrainer/trunk/test/pytrainer/equipment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -1,348 +0,0 @@ -# -*- coding: utf-8 -*- - -#Copyright (C) Nathan Jones nc...@us... - -#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. - -import unittest -import mock -from pytrainer.equipment import Equipment, EquipmentService,\ - EquipmentServiceException -from pytrainer.lib.sqliteUtils import Sql - -class EquipmentTest(unittest.TestCase): - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_id_defaults_to_none(self): - equipment = Equipment() - self.assertEqual(None, equipment.id) - - def test_id_set_to_integer(self): - equipment = Equipment() - equipment.id = 2 - self.assertEquals(2, equipment.id) - - def test_id_set_to_numeric_string(self): - equipment = Equipment() - equipment.id = "3" - self.assertEquals(3, equipment.id) - - def test_id_set_to_non_numeric_string(self): - equipment = Equipment() - try: - equipment.id = "test" - except(ValueError): - pass - else: - self.fail("Should not be able to set equipment id to non numeric value.") - - def test_description_defaults_to_empty_string(self): - equipment = Equipment() - self.assertEquals(u"", equipment.description) - - def test_description_set_to_string(self): - equipment = Equipment() - try: - equipment.description = "100$ Shoes" - except(TypeError): - pass - else: - self.fail("Should not be able to set description to non unicode string value.") - - def test_description_set_to_unicode_string(self): - equipment = Equipment() - equipment.description = u"Zapatos de €100" - self.assertEquals(u"Zapatos de €100", equipment.description) - - def test_description_set_to_non_string(self): - equipment = Equipment() - try: - equipment.description = 42 - except(TypeError): - pass - else: - self.fail("Should not be able to set description to non string value.") - - def test_active_defaults_to_true(self): - equipment = Equipment() - self.assertTrue(equipment.active) - - def test_active_set_to_boolean(self): - equipment = Equipment() - equipment.active = False - self.assertFalse(equipment.active) - - def test_active_set_to_non_boolean(self): - equipment = Equipment() - try: - equipment.active = "test" - except(TypeError): - pass - else: - self.fail("Should not be able to set active to non-boolean value.") - - def test_life_expectancy_defaults_to_zero(self): - equipment = Equipment() - self.assertEqual(0, equipment.life_expectancy) - - def test_life_expectancy_set_to_integer(self): - equipment = Equipment() - equipment.life_expectancy = 2 - self.assertEquals(2, equipment.life_expectancy) - - def test_life_expectancy_set_to_numeric_string(self): - equipment = Equipment() - equipment.life_expectancy = "3" - self.assertEquals(3, equipment.life_expectancy) - - def test_life_expectancy_set_to_non_numeric_string(self): - equipment = Equipment() - try: - equipment.life_expectancy = "test" - except(ValueError): - pass - else: - self.fail("Should not be able to set life expectancy to non numeric value.") - - def test_prior_usage_defaults_to_zero(self): - equipment = Equipment() - self.assertEqual(0, equipment.prior_usage) - - def test_prior_usage_set_to_integer(self): - equipment = Equipment() - equipment.prior_usage = 2 - self.assertEquals(2, equipment.prior_usage) - - def test_prior_usage_set_to_numeric_string(self): - equipment = Equipment() - equipment.prior_usage = "3" - self.assertEquals(3, equipment.prior_usage) - - def test_prior_usage_set_to_non_numeric_string(self): - equipment = Equipment() - try: - equipment.prior_usage = "test" - except(ValueError): - pass - else: - self.fail("Should not be able to set life expectancy to non numeric value.") - - def test_notes_defaults_to_empty_string(self): - equipment = Equipment() - self.assertEquals(u"", equipment.notes) - - def test_notes_set_to_string(self): - equipment = Equipment() - try: - equipment.notes = "100$ Shoes" - except(TypeError): - pass - else: - self.fail("Should not be able to set notes to non-unicode string value.") - - def test_notes_set_to_unicode_string(self): - equipment = Equipment() - equipment.notes = u"Zapatos de €100." - self.assertEquals(u"Zapatos de €100.", equipment.notes) - - def test_notes_set_to_non_string(self): - equipment = Equipment() - try: - equipment.notes = 42 - except(TypeError): - pass - else: - self.fail("Should not be able to set notes to non-string value.") - - def test_equals_new_instances(self): - equipment1 = Equipment() - equipment2 = Equipment() - self.assertNotEqual(equipment1, equipment2, "") - - def test_equals_instances_with_same_id(self): - equipment1 = Equipment() - equipment1.id = 1 - equipment2 = Equipment() - equipment2.id = 1 - self.assertEqual(equipment1, equipment2, "Equipment instances with same id should be equal.") - - def test_equals_instances_with_different_ids(self): - equipment1 = Equipment() - equipment1.id = 1 - equipment2 = Equipment() - equipment2.id = 2 - self.assertNotEqual(equipment1, equipment2, "Equipment instances with different ids should not be equal.") - -class EquipmentServiceTest(unittest.TestCase): - - def setUp(self): - self.mock_ddbb = mock.Mock(spec=Sql) - self.equipment_service = EquipmentService(self.mock_ddbb) - - def tearDown(self): - pass - - def test_get_equipment_item(self): - self.mock_ddbb.select.return_value = [(1, u"Test Description", True, 500, 200, u"Test notes.")] - item = self.equipment_service.get_equipment_item(1) - self.assertEquals(1, item.id) - self.assertEquals("Test Description", item.description) - self.assertTrue(item.active) - self.assertEquals(500, item.life_expectancy) - self.assertEquals(200, item.prior_usage) - self.assertEquals("Test notes.", item.notes) - - def test_get_equipment_item_non_unicode(self): - self.mock_ddbb.select.return_value = [(1, "Test Description", True, 500, 200, "Test notes.")] - item = self.equipment_service.get_equipment_item(1) - self.assertEquals("Test Description", item.description) - self.assertEquals("Test notes.", item.notes) - - def test_get_equipment_item_non_existant(self): - self.mock_ddbb.select.return_value = [] - item = self.equipment_service.get_equipment_item(1) - self.assertEquals(None, item) - - def test_get_all_equipment(self): - self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."), - (2, u"Test item 2", False, 600, 300, u"Test notes 2.")] - items = self.equipment_service.get_all_equipment() - item = items[0] - self.assertEquals(1, item.id) - self.assertEquals("Test item 1", item.description) - self.assertTrue(item.active) - self.assertEquals(500, item.life_expectancy) - self.assertEquals(200, item.prior_usage) - self.assertEquals("Test notes 1.", item.notes) - item = items[1] - self.assertEquals(2, item.id) - self.assertEquals("Test item 2", item.description) - self.assertFalse(item.active) - self.assertEquals(600, item.life_expectancy) - self.assertEquals(300, item.prior_usage) - self.assertEquals("Test notes 2.", item.notes) - - def test_get_all_equipment_non_existant(self): - self.mock_ddbb.select.return_value = [] - items = self.equipment_service.get_all_equipment() - self.assertEquals([], items) - - def test_get_active_equipment(self): - self.mock_ddbb.select.return_value = [(1, u"Test item 1", True, 500, 200, u"Test notes 1."), - (2, u"Test item 2", True, 600, 300, u"Test notes 2.")] - items = self.equipment_service.get_active_equipment() - item = items[0] - self.assertEquals(1, item.id) - self.assertEquals("Test item 1", item.description) - self.assertTrue(item.active) - self.assertEquals(500, item.life_expectancy) - self.assertEquals(200, item.prior_usage) - self.assertEquals("Test notes 1.", item.notes) - item = items[1] - self.assertEquals(2, item.id) - self.assertEquals("Test item 2", item.description) - self.assertTrue(item.active) - self.assertEquals(600, item.life_expectancy) - self.assertEquals(300, item.prior_usage) - self.assertEquals("Test notes 2.", item.notes) - - def test_get_active_equipment_non_existant(self): - self.mock_ddbb.select.return_value = [] - items = self.equipment_service.get_active_equipment() - self.assertEquals([], items) - - def test_store_equipment(self): - equipment = Equipment() - equipment.description = u"test description" - equipment_ids = [] - def mock_select(table, columns, where): - if columns == "id": - return equipment_ids - else: - return [(2, u"", 1, 0, 0,u"")] - def update_equipment_ids(*args): - equipment_ids.append([1]) - self.mock_ddbb.select = mock.Mock(wraps=mock_select) - self.mock_ddbb.insert.side_effect = update_equipment_ids - stored_equipment = self.equipment_service.store_equipment(equipment) - self.mock_ddbb.insert.assert_called_with("equipment", - "description,active,life_expectancy,prior_usage,notes", - ["test description", 1, 0, 0,"" ]) - self.assertEquals(2, stored_equipment.id) - - def test_store_equipment_duplicate_description(self): - self.mock_ddbb.select.return_value = [(1,)] - equipment = Equipment() - equipment.description = u"test item" - try: - self.equipment_service.store_equipment(equipment) - self.fail("Should not be able to store new item with non-unique description.") - except(EquipmentServiceException): - pass - - def test_update_equipment(self): - equipment = Equipment() - equipment.id = 1 - equipment.description = u"new description" - self.mock_ddbb.select.return_value = [(1, u"old description", 1, 0, 0,u"")] - self.equipment_service.store_equipment(equipment) - self.mock_ddbb.update.assert_called_with("equipment", - "description,active,life_expectancy,prior_usage,notes", - ["new description", 1, 0, 0,"" ], - "id = 1") - - def test_update_equipment_non_existant(self): - self.mock_ddbb.select.return_value = [] - equipment = Equipment() - equipment.id = 1 - try: - self.equipment_service.store_equipment(equipment) - self.fail("Should not be able to update an item which did not previously exist.") - except(EquipmentServiceException): - pass - - def test_update_equipment_duplicate_description(self): - self.mock_ddbb.select.return_value = [(1, u"test item", True, 500, 200, u"Test notes.")] - equipment = Equipment() - equipment.id = 2 - equipment.description = u"test item" - try: - self.equipment_service.store_equipment(equipment) - self.fail("Should not be able to change item description to non-unique value.") - except(EquipmentServiceException): - pass - - def test_get_equipment_usage(self): - self.mock_ddbb.select.return_value = [(250,)] - equipment = Equipment() - equipment.id = 1 - usage = self.equipment_service.get_equipment_usage(equipment) - self.assertEquals(250, usage) - - def test_get_equipment_usage_none(self): - self.mock_ddbb.select.return_value = [(None,)] - equipment = Equipment() - equipment.id = 1 - usage = self.equipment_service.get_equipment_usage(equipment) - self.assertEquals(0, usage) - -if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] - unittest.main() Deleted: pytrainer/trunk/test/pytrainer/gui/equipment_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/gui/equipment_test.py 2011-08-06 13:13:35 UTC (rev 836) +++ pytrainer/trunk/test/pytrainer/gui/equipment_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -1,162 +0,0 @@ -# -*- coding: utf-8 -*- -from unittest import TestCase -from mock import Mock -from pytrainer.equipment import Equipment, EquipmentService -from pytrainer.gui.equipment import EquipmentStore - -#Copyright (C) Nathan Jones nc...@us... - -#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. - -class EquipmentStoreTest(TestCase): - - def setUp(self): - self.mock_equipment_service = Mock(spec=EquipmentService) - self.mock_equipment_service.get_equipment_usage.return_value = 0 - - def tearDown(self): - pass - - def test_get_item_id(self): - equipment = Equipment() - equipment.id = 1 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals(1, equipment_store.get_value(iter, 0)) - - def test_get_item_description(self): - equipment = Equipment() - equipment.id = 1 - equipment.description = u"item description" - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("item description", equipment_store.get_value(iter, 1)) - - def test_get_item_usage_percent(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 100 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals(50, equipment_store.get_value(iter, 2)) - - def test_get_item_usage_percent_prior_usage(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - equipment.prior_usage = 50 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 100 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals(75, equipment_store.get_value(iter, 2)) - - def test_get_item_usage_percent_zero_usage(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 0 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals(0, equipment_store.get_value(iter, 2)) - - def test_get_item_usage_percent_usage_exceeds_life_expectancy(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 300 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals(100, equipment_store.get_value(iter, 2), "Progress bar cannot exceed 100%.") - - - def test_get_item_usage_text(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 100 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("100 / 200", equipment_store.get_value(iter, 3)) - - def test_get_item_usage_text_rounded(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 100.5 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("101 / 200", equipment_store.get_value(iter, 3)) - - def test_get_item_usage_text_prior_usage(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - equipment.prior_usage = 50 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 100 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("150 / 200", equipment_store.get_value(iter, 3)) - - def test_get_item_usage_text_zero_usage(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 0 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("0 / 200", equipment_store.get_value(iter, 3)) - - def test_get_item_usage_text_usage_exceeds_life_expectancy(self): - equipment = Equipment() - equipment.id = 1 - equipment.life_expectancy = 200 - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - self.mock_equipment_service.get_equipment_usage.return_value = 300 - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertEquals("300 / 200", equipment_store.get_value(iter, 3)) - - def test_get_item_active(self): - equipment = Equipment() - equipment.id = 1 - equipment.active = False - self.mock_equipment_service.get_all_equipment.return_value = [equipment] - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - self.assertFalse(equipment_store.get_value(iter, 4)) - - def test_multiple_equipment_items(self): - equipment1 = Equipment() - equipment1.id = 1 - equipment2 = Equipment() - equipment2.id = 2 - self.mock_equipment_service.get_all_equipment.return_value = [equipment1, equipment2] - equipment_store = EquipmentStore(self.mock_equipment_service) - iter = equipment_store.get_iter_first() - iter = equipment_store.iter_next(iter) - self.assertEquals(2, equipment_store.get_value(iter, 0)) - - Deleted: pytrainer/trunk/test/pytrainer/upgrade/data_test.py =================================================================== --- pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-06 13:13:35 UTC (rev 836) +++ pytrainer/trunk/test/pytrainer/upgrade/data_test.py 2011-08-08 12:39:11 UTC (rev 837) @@ -1,106 +0,0 @@ -# -*- coding: iso-8859-1 -*- - -#Copyright (C) Nathan Jones nc...@us... - -#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. - -import unittest -from mock import Mock -from pytrainer.upgrade.data import InstalledData, DataState -import pytrainer.upgrade.context - -class InstalledDataTest(unittest.TestCase): - - def setUp(self): - self._mock_migratable_db = Mock() - self._mock_ddbb = Mock() - self._mock_version_provider = Mock() - self._mock_upgrade_context = Mock() - self._installed_data = InstalledData(self._mock_migratable_db, self._mock_ddbb, self._mock_version_provider, self._mock_upgrade_context) - - def test_get_version_should_return_migrate_version_when_available(self): - self._mock_migratable_db.get_version.return_value = 1 - self.assertEquals(1, self._installed_data.get_version()) - - def test_get_version_should_return_legacy_version_when_available(self): - self._mock_migratable_db.is_versioned.return_value = False - self._mock_version_provider.get_legacy_version.return_value = 2 - self.assertEquals(2, self._installed_data.get_version()) - - def test_get_version_should_return_none_when_no_existing_version(self): - self._mock_migratable_db.is_versioned.return_value = False - self._mock_version_provider.get_legacy_version.return_value = None - self.assertEquals(None, self._installed_data.get_version()) - - def test_get_state_should_return_current_when_data_version_equals_repository_version(self): - self._mock_migratable_db.get_version.return_value = 1 - self._mock_migratable_db.get_upgrade_version.return_value = 1 - self.assertEquals(DataState.CURRENT, self._installed_data.get_state()) - - def test_get_state_should_return_fresh_when_data_version_unavailable(self): - self._mock_migratable_db.is_versioned.return_value = False - self._mock_version_provider.get_legacy_version.return_value = None - self.assertEquals(DataState.FRESH, self._installed_data.get_state()) - -... [truncated message content] |
From: <nc...@us...> - 2011-08-13 08:29:48
|
Revision: 844 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=844&view=rev Author: ncjones Date: 2011-08-13 08:29:41 +0000 (Sat, 13 Aug 2011) Log Message: ----------- Ensure sport data is valid. ticket:138 Modified Paths: -------------- pytrainer/trunk/INSTALL pytrainer/trunk/pytrainer/lib/ddbb.py Added Paths: ----------- pytrainer/trunk/pytrainer/test/upgrade/versions/ pytrainer/trunk/pytrainer/test/upgrade/versions/__init__.py pytrainer/trunk/pytrainer/test/upgrade/versions/version012_test.py pytrainer/trunk/pytrainer/upgrade/versions/012_clean_sport_data.py pytrainer/trunk/pytrainer/upgrade/versions/version012.py Modified: pytrainer/trunk/INSTALL =================================================================== --- pytrainer/trunk/INSTALL 2011-08-13 08:28:05 UTC (rev 843) +++ pytrainer/trunk/INSTALL 2011-08-13 08:29:41 UTC (rev 844) @@ -23,6 +23,7 @@ libxslt-python-1.1.26 libxslt-1.1.26 sqlite-3.6.23 (sqlite is preferred as db than mysql, MySQL-python has been reported to be installed if so) +sqlalchemy-0.6.4 sqlalchemy-migrate-0.6.1 - Only needed if correspondent plugin is enabled: Modified: pytrainer/trunk/pytrainer/lib/ddbb.py =================================================================== --- pytrainer/trunk/pytrainer/lib/ddbb.py 2011-08-13 08:28:05 UTC (rev 843) +++ pytrainer/trunk/pytrainer/lib/ddbb.py 2011-08-13 08:29:41 UTC (rev 844) @@ -104,7 +104,11 @@ "equipment_id": "int", } } -tablesDefaultData = { "sports": [({ "name":"Mountain Bike" } ), ( {"name": "Bike"}), ({"name": "Run"}) ]} +tablesDefaultData = { "sports": [ + ({ "name": u"Mountain Bike", "weight": 0.0, "color": "0000ff" } ), + ({ "name": u"Bike", "weight": 0.0, "color": "00ff00"}), + ({ "name": u"Run", "weight": 0.0, "color": "ffff00"}) +]} class DDBB: @@ -219,6 +223,10 @@ #Text so is OK?? #TODO check length against spec? return value + elif cell_type.startswith('char'): + #Text so is OK?? + #TODO check length against spec? + return value elif cell_type.startswith('date'): try: result = dateutil.parser.parse(value).date() Added: pytrainer/trunk/pytrainer/test/upgrade/versions/__init__.py =================================================================== Added: pytrainer/trunk/pytrainer/test/upgrade/versions/version012_test.py =================================================================== --- pytrainer/trunk/pytrainer/test/upgrade/versions/version012_test.py (rev 0) +++ pytrainer/trunk/pytrainer/test/upgrade/versions/version012_test.py 2011-08-13 08:29:41 UTC (rev 844) @@ -0,0 +1,81 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +import unittest +import sqlalchemy +import pytrainer.upgrade.versions.version012 as version012 + +class UpgradeTest(unittest.TestCase): + + def setUp(self): + self._engine = sqlalchemy.create_engine("sqlite:///:memory:") + self._metadata = sqlalchemy.MetaData() + self._metadata.bind = self._engine + self._sports_table = sqlalchemy.Table("sports", self._metadata, + sqlalchemy.Column("id_sports", sqlalchemy.Integer, primary_key=True), + sqlalchemy.Column("name", sqlalchemy.String(100)), + sqlalchemy.Column("weight", sqlalchemy.Float), + sqlalchemy.Column("color", sqlalchemy.String(6)), + sqlalchemy.Column("met", sqlalchemy.Float), + sqlalchemy.Column("max_pace", sqlalchemy.Integer) + ) + self._metadata.drop_all() + self._metadata.create_all() + + def upgradeAndAssert(self, original, expected): + self._engine.execute(sqlalchemy.text("insert into sports (id_sports, name, weight, color, met, max_pace)" + " values (:id, :name, :weight, :color, :met, :max_pace)"), + id= 1, + name= "Test Sport", + weight= original["weight"], + color= original["color"], + met= original["met"], + max_pace= original["max_pace"]) + version012.upgrade(self._engine) + result = self._engine.execute(self._sports_table.select(self._sports_table.c.id_sports==1)) + (_, _, weight, color, met, max_pace) = result.fetchone() + result.close() + self.assertEqual(expected["weight"], weight) + self.assertEqual(expected["color"], color) + self.assertEqual(expected["met"], met) + self.assertEqual(expected["max_pace"], max_pace) + + def testUpgradeFromNullValues(self): + original = { "weight": None, "color": None, "met": None, "max_pace": None } + expected = { "weight": 0.0, "color": "0000ff", "met": None, "max_pace": None } + self.upgradeAndAssert(original, expected) + + def testUpgradeFromZeroValues(self): + original = { "weight": 0, "color": 0, "met": 0, "max_pace": 0 } + expected = { "weight": 0.0, "color": "0", "met": 0, "max_pace": None } + self.upgradeAndAssert(original, expected) + + def testUpgradeFromEmptyValues(self): + original = { "weight": "", "color": "", "met": "", "max_pace": "" } + expected = { "weight": 0.0, "color": "0000ff", "met": None, "max_pace": None } + self.upgradeAndAssert(original, expected) + + def testUpgradeFromNegativeValues(self): + original = { "weight": -1, "color": -1, "met": -1, "max_pace": -1 } + expected = { "weight": 0.0, "color": "-1", "met": None, "max_pace": None } + self.upgradeAndAssert(original, expected) + + def testUpgradeFromValidValues(self): + original = { "weight": 3.4, "color": "abc123", "met": 45.6, "max_pace": 123 } + expected = { "weight": 3.4, "color": "abc123", "met": 45.6, "max_pace": 123 } + self.upgradeAndAssert(original, expected) Added: pytrainer/trunk/pytrainer/upgrade/versions/012_clean_sport_data.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/versions/012_clean_sport_data.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/versions/012_clean_sport_data.py 2011-08-13 08:29:41 UTC (rev 844) @@ -0,0 +1,4 @@ +import pytrainer.upgrade.versions.version012 as version12 + +def upgrade(migrate_engine): + version12.upgrade(migrate_engine) \ No newline at end of file Added: pytrainer/trunk/pytrainer/upgrade/versions/version012.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/versions/version012.py (rev 0) +++ pytrainer/trunk/pytrainer/upgrade/versions/version012.py 2011-08-13 08:29:41 UTC (rev 844) @@ -0,0 +1,91 @@ +# -*- coding: iso-8859-1 -*- + +#Copyright (C) Nathan Jones nc...@us... + +#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. + +from sqlalchemy.sql.expression import text +import logging + +class _SportNormalizer(object): + + """A row from the sport DB table that can be "normalized" to have invalid + values replaced with valid defaults.""" + + def __init__(self, id, weight, color, met, max_pace): + self._id = id + self._weight = weight + self._color = color + self._met = met + self._max_pace = max_pace + + def normalize(self, migrate_engine): + self._normalize_weight(migrate_engine) + self._normalize_color(migrate_engine) + self._normalize_met(migrate_engine) + self._normalize_max_pace(migrate_engine) + + def _normalize_weight(self, migrate_engine): + valid = True + try: + weight = float(self._weight) + if weight < 0: + valid = False + except: + valid = False + if not valid: + logging.debug("Sport with id '%s' has invalid weight: '%s'. Replacing with default.", self._id, self._weight) + migrate_engine.execute(text("update sports set weight=:weight where id_sports=:id"), id=self._id, weight=0.0) + + def _normalize_color(self, migrate_engine): + try: + # colors that do not have exactly 6 hexadecimal digits should also + # be invalid, but we do not expect such invalid values to exist. + int(self._color, 16) + except: + logging.debug("Sport with id '%s' has invalid color: '%s'. Replacing with default.", self._id, self._color) + migrate_engine.execute(text("update sports set color=:color where id_sports=:id"), id=self._id, color="0000ff") + + def _normalize_met(self, migrate_engine): + valid = True + if self._met is not None: + try: + met = float(self._met) + if met < 0: + valid = False + except: + valid = False + if not valid: + logging.debug("Sport with id '%s' has invalid MET: '%s'. Replacing with default.", self._id, self._met) + migrate_engine.execute(text("update sports set met=:met where id_sports=:id"), id=self._id, met=None) + + def _normalize_max_pace(self, migrate_engine): + valid = True + if self._max_pace is not None: + try: + max_pace = int(self._max_pace) + if max_pace <= 0: + valid = False + except: + valid = False + if not valid: + logging.debug("Sport with id '%s' has invalid max pace: '%s'. Replacing with default.", self._id, self._max_pace) + migrate_engine.execute(text("update sports set max_pace=:max_pace where id_sports=:id"), id=self._id, max_pace=None) + +def upgrade(migrate_engine): + sport_rows = migrate_engine.execute("select id_sports, weight, color, met, max_pace from sports") + for (id, weight, color, met, max_pace) in sport_rows: + _SportNormalizer(id, weight, color, met, max_pace).normalize(migrate_engine) + sport_rows.close() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-08-27 23:24:30
|
Revision: 862 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=862&view=rev Author: dgranda Date: 2011-08-27 23:24:24 +0000 (Sat, 27 Aug 2011) Log Message: ----------- Pause time value is now available to activity and printed in register tab. Renaming legend from 'Run' to 'Active' to make it multisport - Ticket:103 Modified Paths: -------------- pytrainer/trunk/glade/pytrainer.glade pytrainer/trunk/pytrainer/gui/windowmain.py pytrainer/trunk/pytrainer/lib/activity.py pytrainer/trunk/pytrainer/lib/gpx.py Modified: pytrainer/trunk/glade/pytrainer.glade =================================================================== --- pytrainer/trunk/glade/pytrainer.glade 2011-08-27 22:28:36 UTC (rev 861) +++ pytrainer/trunk/glade/pytrainer.glade 2011-08-27 23:24:24 UTC (rev 862) @@ -932,7 +932,7 @@ <widget class="GtkLabel" id="label42"> <property name="visible">True</property> <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Run / Rest:</b></property> + <property name="label" translatable="yes"><b>Active / Rest:</b></property> <property name="use_markup">True</property> </widget> <packing> Modified: pytrainer/trunk/pytrainer/gui/windowmain.py =================================================================== --- pytrainer/trunk/pytrainer/gui/windowmain.py 2011-08-27 22:28:36 UTC (rev 861) +++ pytrainer/trunk/pytrainer/gui/windowmain.py 2011-08-27 23:24:24 UTC (rev 862) @@ -415,6 +415,9 @@ self.record_duration.set_text(activity.get_value_f('time', '%s')) self.record_calories.set_text(activity.get_value_f('calories', "%0.0f")) self.record_title.set_text(activity.title) + hrun,mrun,srun = self.pytrainer_main.date.second2time(activity.time) + hpause,mpause,spause = self.pytrainer_main.date.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() start,end = buffer.get_bounds() buffer.set_text(activity.comments) @@ -423,8 +426,7 @@ equipment_text = ", ".join(map(lambda(item): item.description, equipment)) self.label_record_equipment.set_text(equipment_text) else: - self.label_record_equipment.set_markup("<i>None</i>") - runTime = 0.0; + self.label_record_equipment.set_markup("<i>None</i>") if len(activity.laps)>1: store = gtk.ListStore( gobject.TYPE_INT, @@ -442,8 +444,7 @@ gobject.TYPE_STRING, ) for lap in activity.laps: - t = lap['elapsed_time'] - runTime += float(t) + t = lap['elapsed_time'] m = lap['distance'] if self.pytrainer_main.profile.getValue("pytraining","prf_us_system") == "True": @@ -518,15 +519,11 @@ self.frame_laps.show() else: self.frame_laps.hide() - runTime = float(activity.laps[0]['elapsed_time']) else: self.recordview.set_current_page(0) self.recordview.set_sensitive(0) - pauseTime = activity.time - runTime - hrun,mrun,srun = self.pytrainer_main.date.second2time(runTime) - hpause,mpause,spause = self.pytrainer_main.date.second2time(pauseTime) - self.record_runrest.set_text("%02d:%02d:%02d / %02d:%02d:%02d" %(hrun,mrun,srun,hpause,mpause,spause) ) + logging.debug("<<") def actualize_recordgraph(self,activity): Modified: pytrainer/trunk/pytrainer/lib/activity.py =================================================================== --- pytrainer/trunk/pytrainer/lib/activity.py 2011-08-27 22:28:36 UTC (rev 861) +++ pytrainer/trunk/pytrainer/lib/activity.py 2011-08-27 23:24:24 UTC (rev 862) @@ -99,6 +99,7 @@ self.has_data = False self.distance_data = {} self.time_data = {} + self.time_pause = 0 self.pace_limit = None self.starttime = None self.gpx_distance = None @@ -216,7 +217,11 @@ self.tracks = self.gpx.getTrackList() #TODO fix - this should removed and replaced with self.tracklist functionality self.tracklist = self.gpx.trkpoints self.gpx_distance = self.gpx.total_dist - logging.debug("GPX Distance: %s" % self.gpx_distance) + logging.info("GPX Distance: %s | distance (trkpts): %s | duration: %s | duration (trkpts): %s" % (self.gpx_distance, self.gpx.total_dist_trkpts, self.gpx.total_time, self.gpx.total_time_trkpts)) + time_diff = self.gpx.total_time_trkpts - self.gpx.total_time + if time_diff > 10: + self.time_pause = time_diff + logging.debug("Identified non active time: %s s" % self.time_pause) logging.debug("<<") def _init_from_db(self): Modified: pytrainer/trunk/pytrainer/lib/gpx.py =================================================================== --- pytrainer/trunk/pytrainer/lib/gpx.py 2011-08-27 22:28:36 UTC (rev 861) +++ pytrainer/trunk/pytrainer/lib/gpx.py 2011-08-27 23:24:24 UTC (rev 862) @@ -66,6 +66,7 @@ self.trkpoints = [] self.vel_array = [] self.total_dist = 0 + self.total_dist_trkpts = 0 self.total_time = 0 self.total_time_trkpts = 0 self.upositive = 0 @@ -348,7 +349,7 @@ #Accumulate distances if dist is not None: dist_elapsed += dist #TODO fix - total_dist += dist + self.total_dist_trkpts += dist #Calculate speed... vel = self._calculate_speed(dist, time_elapsed, smoothing_factor=3) @@ -368,10 +369,10 @@ waiting_points = [] dist_elapsed = 0 else: - retorno.append((total_dist,ele, self.total_time,vel,lat,lon,hr,cadence,corEle)) + retorno.append((self.total_dist_trkpts,ele, self.total_time,vel,lat,lon,hr,cadence,corEle)) dist_elapsed = 0 else: # time_ is None - waiting_points.append((total_dist, dist_elapsed, ele, self.total_time, lat, lon, hr, cadence, corEle)) + waiting_points.append((self.total_dist_trkpts, dist_elapsed, ele, self.total_time, lat, lon, hr, cadence, corEle)) #Add to dict of values to trkpoint list self.trkpoints.append({ 'id': i, @@ -385,7 +386,7 @@ 'ele':ele, 'ele_change': rel_alt, 'distance_from_previous': dist, - 'elapsed_distance': total_dist, + 'elapsed_distance': self.total_dist_trkpts, 'velocity':vel, 'correctedElevation':corEle, @@ -399,12 +400,15 @@ self.hr_average = total_hr/len_validhrpoints # In case there is no other way to calculate distance, we rely on trackpoints (number of trackpoints is configurable!) if self.total_dist is None or self.total_dist == 0: - self.total_dist = total_dist + self.total_dist = self.total_dist_trkpts + else: + dist_diff = 1000*(self.total_dist_trkpts - self.total_dist) + logging.info("Distance difference between laps and trkpts calculation: %f m" % dist_diff) if self.total_time is None or self.total_time == 0: self.total_time = self.total_time_trkpts else: time_diff = self.total_time_trkpts - self.total_time - logging.info("Duration difference between laps and trkpts calculation: %d seconds" % time_diff) + logging.info("Duration difference between laps and trkpts calculation: %d s" % time_diff) logging.debug("<<") return retorno This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-08-29 16:25:40
|
Revision: 872 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=872&view=rev Author: dgranda Date: 2011-08-29 16:25:34 +0000 (Mon, 29 Aug 2011) Log Message: ----------- First approach of (source) manpage - ticket:125 Added Paths: ----------- pytrainer/trunk/man/ pytrainer/trunk/man/manpage.src Added: pytrainer/trunk/man/manpage.src =================================================================== --- pytrainer/trunk/man/manpage.src (rev 0) +++ pytrainer/trunk/man/manpage.src 2011-08-29 16:25:34 UTC (rev 872) @@ -0,0 +1,52 @@ +.TH pytrainer 7 2011-08-29 +.SH NAME +pytrainer - manual page for pytrainer 1.9.0 + +.SH SYNOPSIS +pytrainer [options] + +.SH DESCRIPTION +pytrainer is a tool to log all sport excursions coming from GPS devices (with a focus on ForeRunnner 205, 305 and 405) or GPX (http://www.topografix.com) files. +.br +Pytrainer supports GPS track files and displays it in graphs, maps... + +.SH OPTIONS +.IP "-h, --help" +show this help message and exit +.IP "-d, --debug" +enable logging at debug level +.IP "-i, --info" +enable logging at info level +.IP "-w, --warn" +enable logging at warning level +.IP --valid +enable validation of files imported by plugins (details at info or debug logging level) - note plugin must support validation +.IP --check +triggers database (only sqlite based) and configuration file sanity checks, adding fields if necessary. Backup of database is done before any change. Details at info or debug logging level +.br +.IP --oldgraph +Turn off new graphing approach +.IP --newgraph +Deprecated option: turn on new graphing approach +.IP --confdir=CONF_DIR +Specify the directory where application configuration will be stored. +.IP --logtype=TYPE +Specify where logging should be output to. TYPE is one of 'file' (default), or 'console'. + +.SH EXAMPLES +.IP pytrainer +starts pytrainer with log level set to ERROR +.IP "pytrainer -d" +starts pytrainer with log level set to DEBUG (troubleshooting) + +.SH AUTHORS +This manual page was originally written by Braulio Valdivielso <brl...@gm...> and polished by Christian Perrier <bu...@de...>. +.P +For more information please check 'Credits' in 'About' menu. +.P +Thanks to everyone who contributes and supports pytrainer. + +.SH SEE ALSO +Project web site: http://sourceforge.net/apps/trac/pytrainer/ +.br +Distribution list: pyt...@li... This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-10-11 07:43:19
|
Revision: 885 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=885&view=rev Author: dgranda Date: 2011-10-11 07:43:06 +0000 (Tue, 11 Oct 2011) Log Message: ----------- Added galician localization Modified Paths: -------------- pytrainer/trunk/setup.py Added Paths: ----------- pytrainer/trunk/locale/gl/ pytrainer/trunk/locale/gl/LC_MESSAGES/ pytrainer/trunk/locale/gl/LC_MESSAGES/Makefile pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer.mo pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer_gl.po Added: pytrainer/trunk/locale/gl/LC_MESSAGES/Makefile =================================================================== --- pytrainer/trunk/locale/gl/LC_MESSAGES/Makefile (rev 0) +++ pytrainer/trunk/locale/gl/LC_MESSAGES/Makefile 2011-10-11 07:43:06 UTC (rev 885) @@ -0,0 +1,8 @@ +default: + msgfmt pytrainer_gl.po -o ./pytrainer.mo + +merge: + msgmerge pytrainer_gl.po pytrainer_gl.po_new > pytrainer_gl.po.tmp + mv pytrainer_gl.po.tmp pytrainer_gl.po + rm pytrainer_gl.po_new + Property changes on: pytrainer/trunk/locale/gl/LC_MESSAGES/Makefile ___________________________________________________________________ Added: svn:executable + * Added: pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer.mo =================================================================== (Binary files differ) Property changes on: pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer.mo ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer_gl.po =================================================================== --- pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer_gl.po (rev 0) +++ pytrainer/trunk/locale/gl/LC_MESSAGES/pytrainer_gl.po 2011-10-11 07:43:06 UTC (rev 885) @@ -0,0 +1,2078 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: Pytrainer 1.8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-10-08 19:47+0200\n" +"PO-Revision-Date: 2011-10-10 16:56+0100\n" +"Last-Translator: Kaptan <kh...@gm...>\n" +"Language-Team: http://trasno.net/ <pro...@tr...>\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Galician\n" +"X-Poedit-Country: SPAIN\n" + +#: glade/calendar.glade:7 +msgid "Calendar" +msgstr "Calendario" + +#: glade/equipment.glade:109 +msgid "<b>Equipment List</b>" +msgstr "<b>Lista de equipamento</b>" + +#: glade/equipment.glade:149 +#: glade/equipment.glade:406 +msgid "Description" +msgstr "Descrición" + +#: glade/equipment.glade:174 +#: glade/equipment.glade:431 +msgid "Life Expectancy" +msgstr "Vida útil esperada" + +#: glade/equipment.glade:211 +#: glade/equipment.glade:467 +msgid "Prior Usage" +msgstr "Uso Anterior" + +#: glade/equipment.glade:243 +#: glade/equipment.glade:499 +msgid "Active" +msgstr "Activo" + +#: glade/equipment.glade:277 +#: glade/equipment.glade:533 +#: pytrainer/gui/windowimportdata.py:352 +msgid "Notes" +msgstr "Notas" + +#: glade/equipment.glade:366 +msgid "<b>Add New Equipment</b>" +msgstr "<b>Engadir Novo Equipamento</b>" + +#: glade/equipment.glade:622 +msgid "<b>Edit Equipment</b>" +msgstr "<b>Editar Equipamento</b>" + +#: glade/equipment.glade:651 +msgid "Really delete the equipment item?" +msgstr "Realmente desexa borrar o equipamento?" + +#: glade/equipment.glade:704 +msgid "<b>Delete Equipment</b>" +msgstr "<b>Borrar Equipamento</b>" + +#: glade/extensions.glade:9 +#: glade/pytrainer.glade:163 +msgid "Extensions" +msgstr "Extensións" + +#: glade/extensions.glade:54 +msgid "<b>Extension Details</b>" +msgstr "<b>Detalles das extensións</b>" + +#: glade/extensions.glade:67 +msgid "name-entry" +msgstr "nome-entrada" + +#: glade/extensions.glade:82 +#: glade/plugins.glade:144 +#: glade/profile.glade:727 +#: glade/pytrainer.glade:5161 +msgid "Name:" +msgstr "Nome:" + +#: glade/extensions.glade:96 +#: glade/plugins.glade:115 +msgid "Status:" +msgstr "Estado:" + +#: glade/extensions.glade:110 +#: glade/plugins.glade:86 +#: glade/pytrainer.glade:5690 +msgid "Description:" +msgstr "Descrición:" + +#: glade/extensions.glade:124 +#: glade/plugins.glade:100 +msgid "status-entry" +msgstr "estado-entrada" + +#: glade/extensions.glade:139 +msgid "description-entry" +msgstr "descrición-entrada" + +#: glade/importdata.glade:7 +#: glade/importdata.glade:325 +msgid "Import" +msgstr "Importar" + +#: glade/importdata.glade:36 +msgid "" +"Test 1\n" +"Test 2" +msgstr "" +"Proba 1\n" +"Proba 2" + +#: glade/importdata.glade:46 +msgid "<b>Select your GPS device</b>" +msgstr "<b>Seleccione o seu dispositivo GPS</b>" + +#: glade/importdata.glade:64 +msgid "Import from GPS Device is not yet implemented" +msgstr "Importar dende dispositivo GPS aínda non está dispoñible" + +#: glade/importdata.glade:92 +msgid "<b>Tools</b>" +msgstr "<b>Ferramentas</b>" + +#: glade/importdata.glade:111 +msgid "Rescan" +msgstr "Examinar de novo" + +#: glade/importdata.glade:115 +msgid "Rescan system for available tools" +msgstr "Examinando de novo ferramentas dispoñibles no sistema" + +#: glade/importdata.glade:168 +msgid "<b>Import from GPS Device</b>" +msgstr "<b>Importar dende un dispositivo GPS</b>" + +#: glade/importdata.glade:221 +msgid "Remove selected files and the associated activities" +msgstr "Borrar ficheiros seleccionados e as actividades asociadas" + +#: glade/importdata.glade:239 +msgid "Add files to import activities from" +msgstr "Engadir ficheiros dende os que importar actividades" + +#: glade/importdata.glade:264 +msgid "<b>Add file(s) to import activities from</b>" +msgstr "<b>Engadir ficheiro(s) dende o(os) que importar actividades </b>" + +#: glade/importdata.glade:307 +msgid "<b>Select activities to import</b>" +msgstr "<b>Seleccionar actividades para importar</b>" + +#: glade/importdata.glade:330 +msgid "Import selected activities" +msgstr "Importar actividades seleccionadas" + +#: glade/importdata.glade:347 +msgid "Close Import dialog" +msgstr "Pechar diálogo de Importación" + +#: glade/importdata.glade:387 +msgid "<b>Import from File</b>" +msgstr "<b>Importar dendde Ficheiro</b>" + +#: glade/importdata.glade:464 +msgid "<b>Plugins</b>" +msgstr "<b>Engadidos</b>" + +#: glade/importdata.glade:491 +msgid "Import from GPS Device" +msgstr "Importar dende dispositivo GPS" + +#: glade/importdata.glade:510 +msgid "Import from File" +msgstr "Importar dende Ficheiro" + +#: glade/importdata.glade:525 +msgid "Launch 'File Select' on start" +msgstr "Lanzar 'Ficheiro Seleccionado' ó inicio" + +#: glade/importdata.glade:530 +msgid "Automatically start the file selection dialog" +msgstr "Lanzar diálogo de selección de ficheiros automaticamente" + +#: glade/importdata.glade:546 +#: glade/plugins.glade:9 +msgid "Plugins" +msgstr "Engadidos" + +#: glade/importdata.glade:572 +msgid "<b>Default to Tab</b>" +msgstr "<b>Lingüeta por defecto</b>" + +#: glade/importdata.glade:595 +msgid "Reset" +msgstr "Restablecer" + +#: glade/importdata.glade:611 +msgid "Save" +msgstr "Gardar" + +#: glade/importdata.glade:670 +msgid "<b>Options</b>" +msgstr "<b>Opcións</b>" + +#: glade/importdata.glade:702 +msgid "<b>Select file to import from</b>" +msgstr "<b>Seleccionar ficheiro para importar dende</b>" + +#: glade/importdata.glade:731 +msgid "Tab" +msgstr "Lingüeta" + +#: glade/importdata.glade:746 +msgid "Comma" +msgstr "Coma" + +#: glade/importdata.glade:763 +msgid "Other" +msgstr "Outro" + +#: glade/importdata.glade:799 +msgid "Read File" +msgstr "Ler Ficheiro" + +#: glade/importdata.glade:820 +msgid "<b>File delimiter</b>" +msgstr "<b>Delimitador no ficheiro</b>" + +#: glade/importdata.glade:864 +#: pytrainer/gui/dialogselecttrack.py:40 +#: pytrainer/gui/windowmain.py:109 +#: pytrainer/gui/windowmain.py:124 +#: pytrainer/gui/windowmain.py:1141 +msgid "Date" +msgstr "Data" + +#: glade/importdata.glade:877 +#: glade/importdata.glade:918 +#: glade/importdata.glade:933 +#: glade/importdata.glade:1128 +#: glade/importdata.glade:1143 +#: glade/importdata.glade:1158 +#: glade/importdata.glade:1173 +#: glade/importdata.glade:1188 +#: glade/importdata.glade:1203 +#: glade/importdata.glade:1218 +#: glade/importdata.glade:1233 +#: glade/importdata.glade:1248 +#: glade/importdata.glade:1263 +#: glade/importdata.glade:1278 +#: glade/importdata.glade:1293 +msgid "Exclude" +msgstr "Descartar" + +#: glade/importdata.glade:892 +#: pytrainer/daygraph.py:52 +#: pytrainer/daygraph.py:54 +#: pytrainer/daygraph.py:56 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/monthgraph.py:70 +#: pytrainer/recordgraph.py:134 +#: pytrainer/recordgraph.py:136 +#: pytrainer/recordgraph.py:138 +#: pytrainer/recordgraph.py:140 +#: pytrainer/recordgraph.py:142 +#: pytrainer/recordgraph.py:144 +#: pytrainer/recordgraph.py:146 +#: pytrainer/weekgraph.py:115 +#: pytrainer/yeargraph.py:70 +msgid "Distance (km)" +msgstr "Distancia (km)" + +#: glade/importdata.glade:905 +msgid "Duration (sec)" +msgstr "Duración (seg)" + +#: glade/importdata.glade:947 +msgid "<b>PyTrainer Field</b>" +msgstr "<b>Campo en PyTrainer</b>" + +#: glade/importdata.glade:958 +msgid "<b>File Column</b>" +msgstr "<b>Columna no Ficheiro</b>" + +#: glade/importdata.glade:972 +#: pytrainer/gui/windowmain.py:108 +#: pytrainer/gui/windowmain.py:1140 +msgid "Title" +msgstr "Título" + +#: glade/importdata.glade:985 +msgid "Descent (m)" +msgstr "Descendido (m)" + +#: glade/importdata.glade:998 +msgid "Accent (m)" +msgstr "Ascendido (m)" + +#: glade/importdata.glade:1011 +msgid "Calories (kcal)" +msgstr "Calorías (kcal)" + +#: glade/importdata.glade:1024 +msgid "Max Speed (km/h)" +msgstr "Velocidade Máx (km/h)" + +#: glade/importdata.glade:1037 +#: pytrainer/monthgraph.py:76 +#: pytrainer/weekgraph.py:121 +#: pytrainer/yeargraph.py:76 +msgid "Average Speed (km/h)" +msgstr "Velocidade Media (km/h)" + +#: glade/importdata.glade:1050 +#: glade/pytrainer.glade:277 +#: pytrainer/gui/windowimportdata.py:352 +#: pytrainer/gui/windowmain.py:103 +#: pytrainer/gui/windowmain.py:111 +#: pytrainer/gui/windowmain.py:1143 +#: pytrainer/gui/windowprofile.py:59 +#: pytrainer/gui/windowrecord.py:210 +msgid "Sport" +msgstr "Deporte" + +#: glade/importdata.glade:1063 +msgid "Average Heartrate (bpm)" +msgstr "Pulsacións medias (ppm)" + +#: glade/importdata.glade:1076 +msgid "Max Heartrate (bpm)" +msgstr "Pulsacións Máximas (ppm)" + +#: glade/importdata.glade:1089 +#: pytrainer/recordgraph.py:138 +msgid "Pace (min/km)" +msgstr "Ritmo (min/km)" + +#: glade/importdata.glade:1102 +msgid "Max Pace (min/km)" +msgstr "Ritmo Máx (min/km)" + +#: glade/importdata.glade:1115 +#: glade/newrecord.glade:1094 +msgid "Comments" +msgstr "Comentarios" + +#: glade/importdata.glade:1309 +msgid "Force sport to:" +msgstr "Forzar deporte a:" + +#: glade/importdata.glade:1323 +#: glade/newrecord.glade:404 +msgid " " +msgstr " " + +#: glade/importdata.glade:1394 +msgid "<b>Map Columns</b>" +msgstr "<b>Mapear Columnas</b>" + +#: glade/importdata.glade:1414 +msgid "" +"<small>This is an experiemental import for delimited files\n" +"1) Select a file\n" +"2) Click 'Read File' to analyse the file\n" +"3) Map columns in the file to pytrainer data elements (Date is required)\n" +"4) Click 'Import Data'</small>" +msgstr "" +"<small>Esta é un forma experimental de importar ficheiros delimitados\n" +"1) Seleccione un ficheiro\n" +"2) Prema 'Ler Ficheiro' para analizalo\n" +"3) Asigne as columnas do ficheiro a elementos de datos de Pytrainer (a data é obligatoria)\n" +"4) Prema 'Importar Datos'</small>" + +#: glade/importdata.glade:1427 +msgid "Import Data" +msgstr "Importar Datos" + +#: glade/importdata.glade:1467 +msgid "<b>Import from CSV</b>" +msgstr "<b>Importar dende un ficheiro CSV</b>" + +#: glade/newrecord.glade:7 +#: pytrainer/gui/windowmain.py:1788 +msgid "New Entry" +msgstr "Nova Entrada" + +#: glade/newrecord.glade:61 +msgid "Title:" +msgstr "Título:" + +#: glade/newrecord.glade:85 +msgid "GPX File:" +msgstr "Ficheiro GPX:" + +#: glade/newrecord.glade:129 +msgid "Calculate Values" +msgstr "Calcular Valores:" + +#: glade/newrecord.glade:149 +msgid "Sport:" +msgstr "Deporte:" + +#: glade/newrecord.glade:184 +msgid "<b>Main</b>" +msgstr "<b>Principal</b>" + +#: glade/newrecord.glade:221 +msgid "Distance (Km):" +msgstr "Distancia (km):" + +#: glade/newrecord.glade:230 +#: glade/newrecord.glade:355 +#: glade/newrecord.glade:558 +#: glade/newrecord.glade:697 +#: glade/newrecord.glade:958 +#: glade/profile.glade:1462 +msgid "Calculate" +msgstr "Calcular" + +#: glade/newrecord.glade:248 +msgid "Duration:" +msgstr "Duración:" + +#: glade/newrecord.glade:279 +#: glade/newrecord.glade:306 +msgid ":" +msgstr ":" + +#: glade/newrecord.glade:376 +msgid "Date:" +msgstr "Data:" + +#: glade/newrecord.glade:445 +msgid "Start Time:" +msgstr "Hora de Comezo:" + +#: glade/newrecord.glade:461 +msgid "12:00:00" +msgstr "12:00:00" + +#: glade/newrecord.glade:486 +#: glade/profile.glade:1500 +msgid "<b>General</b>" +msgstr "<b>Xeral</b>" + +#: glade/newrecord.glade:522 +msgid "Max (km/h):" +msgstr "Máx (km/h):" + +#: glade/newrecord.glade:547 +msgid "Average (km/h)" +msgstr "Media (km/h)" + +#: glade/newrecord.glade:593 +msgid "<b>Speed</b>" +msgstr "<b>Velocidade</b>" + +#: glade/newrecord.glade:612 +msgid "Quick Entry" +msgstr "Entrada Rápida" + +#: glade/newrecord.glade:644 +msgid "Max (min/km):" +msgstr "Máx (min/km):" + +#: glade/newrecord.glade:671 +msgid "Pace (min/km):" +msgstr "Ritmo (min/km):" + +#: glade/newrecord.glade:718 +msgid "<b>Pace</b>" +msgstr "<b>Ritmo</b>" + +#: glade/newrecord.glade:754 +msgid "Ascent:" +msgstr "Ascendido:" + +#: glade/newrecord.glade:780 +msgid "Descent:" +msgstr "Descendido:" + +#: glade/newrecord.glade:813 +msgid "<b>Accumulated Altitude Change</b>" +msgstr "<b>Desnivel Acumulado</b>" + +#: glade/newrecord.glade:850 +msgid "Max (bpm):" +msgstr "Máx (ppm):" + +#: glade/newrecord.glade:862 +msgid "Heart rate:" +msgstr "Pulsacións:" + +#: glade/newrecord.glade:902 +#: pytrainer/gui/windowmain.py:115 +#: pytrainer/gui/windowmain.py:1147 +#: pytrainer/monthgraph.py:78 +#: pytrainer/weekgraph.py:123 +#: pytrainer/yeargraph.py:78 +msgid "Calories" +msgstr "Calorías" + +#: glade/newrecord.glade:931 +msgid "<small><b>Note:</b> In order to calculate the calories you must set the sport MET (in Preferences->Sport) </small>" +msgstr "<small><b>Nota:</b> Para o cálculo de calorías é necesario introducir o valor de MET do deporte (en Preferencias->Deportes) </small>" + +#: glade/newrecord.glade:1005 +msgid "<b>Heart Rate</b>" +msgstr "<b>Pulsacións</b>" + +#: glade/newrecord.glade:1027 +msgid "Advanced" +msgstr "Avanzado" + +#: glade/newrecord.glade:1079 +msgid "<b>Comments</b>" +msgstr "<b>Comentarios</b>" + +#: glade/newrecord.glade:1134 +msgid "<i>There are no active equipment items.</i>" +msgstr "<i>Non hai elementos de equipamento activos.</i>" + +#: glade/newrecord.glade:1188 +msgid "<b>Associated Equipment</b>" +msgstr "<b>Equipamento Asociado</b>" + +#: glade/newrecord.glade:1203 +#: glade/profile.glade:1360 +msgid "Equipment" +msgstr "Equipamento" + +#: glade/plugins.glade:53 +msgid "<b>Plugin Details</b>" +msgstr "<b>Detalles do Engadido</b>" + +#: glade/plugins.glade:68 +msgid "description-entry " +msgstr "descrición-entrada" + +#: glade/plugins.glade:129 +msgid "nameEntry" +msgstr "nomeEntrada" + +#: glade/profile.glade:7 +msgid "Preferences" +msgstr "Preferencias" + +#: glade/profile.glade:37 +msgid "Metric system" +msgstr "Sistema métrico" + +#: glade/profile.glade:53 +msgid "U.S. customary units" +msgstr "Sistema americano" + +#: glade/profile.glade:74 +msgid "<b>System of Measurement</b>" +msgstr "<b>Sistema de Medida</b>" + +#: glade/profile.glade:103 +#: glade/pytrainer.glade:2023 +msgid "Google" +msgstr "Google" + +#: glade/profile.glade:118 +#: glade/pytrainer.glade:2006 +msgid "Open Street Map" +msgstr "Open Street Map" + +#: glade/profile.glade:140 +msgid "<b>Default Map Viewer</b>" +msgstr "<b>Visor de Mapas por Defecto</b>" + +#: glade/profile.glade:176 +msgid "Database type:" +msgstr "Tipo de base de datos:" + +#: glade/profile.glade:187 +msgid "Database host:" +msgstr "Servidor da base de datos:" + +#: glade/profile.glade:213 +msgid "Database name:" +msgstr "Nome da base de datos:" + +#: glade/profile.glade:252 +msgid "Database user:" +msgstr "Usuario da base de datos:" + +#: glade/profile.glade:267 +msgid "Database pass:" +msgstr "Contrasinal da base de datos:" + +#: glade/profile.glade:318 +msgid "<b>Database</b>" +msgstr "<b>Base de datos</b>" + +#: glade/profile.glade:340 +msgid "General" +msgstr "Xeral" + +#: glade/profile.glade:369 +msgid "User name:" +msgstr "Nome do usuario:" + +#: glade/profile.glade:380 +msgid "Gender:" +msgstr "Xénero:" + +#: glade/profile.glade:407 +#: glade/pytrainer.glade:5186 +msgid "Height:" +msgstr "Altura:" + +#: glade/profile.glade:447 +#: glade/pytrainer.glade:5173 +msgid "Date of birth:" +msgstr "Data de nacemento:" + +#: glade/profile.glade:520 +msgid "Weight:" +msgstr "Peso:" + +#: glade/profile.glade:542 +#: glade/pytrainer.glade:5248 +msgid "<b>Athlete Details</b>" +msgstr "<b>Datos do Atleta</b>" + +#: glade/profile.glade:557 +#: glade/pytrainer.glade:5351 +msgid "Athlete" +msgstr "Atleta" + +#: glade/profile.glade:671 +msgid "<b>Sport List</b>" +msgstr "<b>Lista de Deportes</b>" + +#: glade/profile.glade:683 +msgid "llist" +msgstr "Lista" + +#: glade/profile.glade:738 +msgid "M.E.T.:" +msgstr "M.E.T.:" + +#: glade/profile.glade:765 +msgid "Extra Weight:" +msgstr "Peso Extra:" + +#: glade/profile.glade:801 +#: glade/profile.glade:1181 +msgid "More information on determining yor M.E.T sport coefficient on Wikipedia" +msgstr "Máis información sobre como determinar o seu coeficiente M.E.T. na Wikipedia" + +#: glade/profile.glade:821 +msgid "Maximum Pace:" +msgstr "Ritmo Máximo:" + +#: glade/profile.glade:913 +msgid "<b>Add New Sport</b>" +msgstr "<b>Engadir Novo Deporte</b>" + +#: glade/profile.glade:928 +msgid "new" +msgstr "novo" + +#: glade/profile.glade:959 +msgid "" +"Deleting a sport removes associated records.\n" +"Continue?" +msgstr "" +"Borrando un deporte tamén elimina as súas entradas asociadas.\n" +"Desexa continuar?" + +#: glade/profile.glade:1041 +msgid "<b>Delete Sport</b>" +msgstr "<b>Borrar Deporte</b>" + +#: glade/profile.glade:1073 +msgid "delete" +msgstr "borrar" + +#: glade/profile.glade:1106 +msgid "Name" +msgstr "Nome" + +#: glade/profile.glade:1129 +msgid "M.E.T." +msgstr "M.E.T." + +#: glade/profile.glade:1156 +#: pytrainer/gui/windowprofile.py:59 +msgid "Extra Weight" +msgstr "Peso Extra" + +#: glade/profile.glade:1201 +msgid "Maxiumum Pace" +msgstr "Ritmo Máximo" + +#: glade/profile.glade:1289 +msgid "<b>Edit Sport</b>" +msgstr "<b>Editar Deporte</b>" + +#: glade/profile.glade:1300 +msgid "<b>lalaal</b>" +msgstr "<b>lalaal</b>" + +#: glade/profile.glade:1322 +msgid "edit" +msgstr "editar" + +#: glade/profile.glade:1338 +msgid "Sports" +msgstr "Deportes" + +#: glade/profile.glade:1393 +msgid "Maximum heart rate:" +msgstr "Pulsacións Máximas:" + +#: glade/profile.glade:1404 +msgid "Resting heart rate:" +msgstr "Pulsacións en repouso:" + +#: glade/profile.glade:1449 +msgid "<small><b>Note:</b> Maximum heart rate is calculated by subtracting the number 220 minus your age. </small>" +msgstr "<small><b>Nota:</b> O número máximo de pulsacións calcúlase coa seguinte fórmula: 220 menos a súa idade. </small>" + +#: glade/profile.glade:1536 +msgid "<small><b>NOTE:</b> in order to use the Karvonen method you must cover the Resting hr field.</small>" +msgstr "<small><b>NOTA:</b> Para empregar o método Karvonen debe cubrir o campo de Pulsacións en repouso.</small>" + +#: glade/profile.glade:1549 +msgid "Percentages based method" +msgstr "Método baseado en porcentaxes" + +#: glade/profile.glade:1565 +#: pytrainer/gui/windowmain.py:614 +msgid "Karvonen method" +msgstr "Método Karvonen" + +#: glade/profile.glade:1586 +msgid "Select how to calculate your heart rate zones." +msgstr "Seleccione como calcular as zonas de frecuencia cardíaca." + +#: glade/profile.glade:1625 +msgid "<b>Heart Rate Zones</b>" +msgstr "<b>Zonas de Frecuencia Cardíaca</b>" + +#: glade/profile.glade:1647 +#: pytrainer/daygraph.py:56 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/lib/activity.py:433 +#: pytrainer/lib/activity.py:435 +#: pytrainer/lib/activity.py:445 +#: pytrainer/lib/activity.py:515 +#: pytrainer/recordgraph.py:140 +msgid "Heart Rate" +msgstr "Pulsacións" + +#: glade/profile.glade:1673 +msgid "Log Level" +msgstr "Nivel Log" + +#: glade/profile.glade:1681 +msgid "" +"Error\n" +"Warning\n" +"Info\n" +"Debug" +msgstr "" +"Error\n" +"Aviso\n" +"Información\n" +"Depuración" + +#: glade/profile.glade:1699 +msgid "<small>What level of detail is written to the log?</small>" +msgstr "<small>Que nivel de detalle está escrito no ficheiro log?</small>" + +#: glade/profile.glade:1713 +msgid "Validate" +msgstr "Validar" + +#: glade/profile.glade:1743 +msgid "<small>Do the plugins attempt to validate the input file before processing?</small>" +msgstr "<small>Intentar validar os engadidos antes de procesar o ficheiro de entrada?</small>" + +#: glade/profile.glade:1759 +msgid "Check" +msgstr "Comprobar" + +#: glade/profile.glade:1788 +msgid "<small>Was a configuration and database check requested?</small>" +msgstr "<small>Houbo unha petición para comprobar a configuración e a base de datos?</small>" + +#: glade/profile.glade:1804 +msgid "New Graph" +msgstr "Novo Gráfico" + +#: glade/profile.glade:1833 +msgid "<small>Want to use experimental new approach to graphing?</small>" +msgstr "<small>Desexa empregar os novos gráficos experimentais?</small>" + +#: glade/profile.glade:1876 +msgid "<small>This screen shows the state of command line configurable options for pytrainer. These options can be changed here which will affect the current instance of pytrainer, they will not be remembered next time though</small>" +msgstr "<small>Esta pantalla amosa o estado das opcións configurables mediante a liña de comandos para PyTrainer. Os cambios que se fagan unicamente terán efecto na presente execución de PyTrainer e non serán recordados no futuro </small>" + +#: glade/profile.glade:1897 +msgid "Startup Parameters" +msgstr "Configuración de Inicio" + +#: glade/pytrainer.glade:24 +msgid "_File" +msgstr "_Ficheiro" + +#: glade/pytrainer.glade:39 +msgid "_Import" +msgstr "_Importar" + +#: glade/pytrainer.glade:60 +msgid "_Export as Text Separated by Commas" +msgstr "_Exportar como Texto Separado por Comas" + +#: glade/pytrainer.glade:94 +msgid "_Edit" +msgstr "_Editar" + +#: glade/pytrainer.glade:114 +msgid "_View" +msgstr "_Vista" + +#: glade/pytrainer.glade:121 +msgid " _Classic View" +msgstr " Vista _Clásica" + +#: glade/pytrainer.glade:131 +msgid " _List View" +msgstr " Vista de _Listado de rexistros" + +#: glade/pytrainer.glade:141 +msgid " _Waypoints Editor" +msgstr "Editor de _Waypoints" + +#: glade/pytrainer.glade:155 +msgid "Tools" +msgstr "Ferramentas" + +#: glade/pytrainer.glade:172 +msgid "GPS Device Plugins" +msgstr "Engadidos do dispositivo GPS" + +#: glade/pytrainer.glade:185 +msgid "_Help" +msgstr "_Axuda" + +#: glade/pytrainer.glade:290 +#: glade/pytrainer.glade:5467 +msgid "All Sports" +msgstr "Todos Deportes" + +#: glade/pytrainer.glade:396 +msgid "<b>Sport:</b>" +msgstr "<b>Deporte:</b>" + +#: glade/pytrainer.glade:408 +#: glade/pytrainer.glade:2726 +#: glade/pytrainer.glade:3337 +#: glade/pytrainer.glade:3960 +#: glade/pytrainer.glade:4659 +msgid "<b>Duration:</b>" +msgstr "<b>Duración:</b>" + +#: glade/pytrainer.glade:423 +#: glade/pytrainer.glade:2816 +#: glade/pytrainer.glade:3416 +#: glade/pytrainer.glade:4039 +#: glade/pytrainer.glade:4761 +msgid "<b>Speed:</b>" +msgstr "<b>Velocidade:</b>" + +#: glade/pytrainer.glade:437 +#: glade/pytrainer.glade:2984 +#: glade/pytrainer.glade:3611 +#: glade/pytrainer.glade:4234 +#: glade/pytrainer.glade:4856 +msgid "<b>Pace:</b>" +msgstr "<b>Ritmo:</b>" + +#: glade/pytrainer.glade:451 +msgid "<b>Ascent:</b>" +msgstr "<b>Ascendido:</b>" + +#: glade/pytrainer.glade:465 +msgid "<b>Calories:</b>" +msgstr "<b>Calorías:</b>" + +#: glade/pytrainer.glade:479 +msgid "<b>Comments:</b>" +msgstr "<b>Comentarios:</b>" + +#: glade/pytrainer.glade:614 +msgid "<b>Date:</b>" +msgstr "<b>Data:</b>" + +#: glade/pytrainer.glade:628 +#: glade/pytrainer.glade:2712 +#: glade/pytrainer.glade:3323 +#: glade/pytrainer.glade:3946 +#: glade/pytrainer.glade:4645 +msgid "<b>Distance:</b>" +msgstr "<b>Distancia:</b>" + +#: glade/pytrainer.glade:645 +msgid "<b>Max Speed</b>" +msgstr "<b>Velocidade Máx</b>" + +#: glade/pytrainer.glade:661 +#: glade/pytrainer.glade:3014 +#: glade/pytrainer.glade:3625 +#: glade/pytrainer.glade:4248 +#: glade/pytrainer.glade:4871 +msgid "<b>Max Pace:</b>" +msgstr "<b>Ritmo Máx:</b>" + +#: glade/pytrainer.glade:677 +msgid "<b>Descent:</b>" +msgstr "<b>Descendido:</b>" + +#: glade/pytrainer.glade:769 +#: glade/pytrainer.glade:2924 +#: glade/pytrainer.glade:3491 +#: glade/pytrainer.glade:4114 +#: glade/pytrainer.glade:4571 +msgid "Cal" +msgstr "Cal" + +#: glade/pytrainer.glade:808 +#: glade/pytrainer.glade:2686 +#: glade/pytrainer.glade:3297 +#: glade/pytrainer.glade:3920 +#: glade/pytrainer.glade:4748 +#: pytrainer/gui/windowmain.py:102 +msgid "Km" +msgstr "Km" + +#: glade/pytrainer.glade:823 +#: glade/pytrainer.glade:2846 +#: glade/pytrainer.glade:2879 +#: glade/pytrainer.glade:3446 +#: glade/pytrainer.glade:3539 +#: glade/pytrainer.glade:4069 +#: glade/pytrainer.glade:4162 +#: glade/pytrainer.glade:4791 +#: glade/pytrainer.glade:4807 +#: pytrainer/gui/windowmain.py:631 +#: pytrainer/gui/windowmain.py:632 +#: pytrainer/gui/windowmain.py:754 +#: pytrainer/gui/windowmain.py:755 +#: pytrainer/gui/windowmain.py:832 +#: pytrainer/gui/windowmain.py:833 +#: pytrainer/lib/activity.py:202 +msgid "km/h" +msgstr "km/h" + +#: glade/pytrainer.glade:838 +msgid "<b>Equipment:</b>" +msgstr "<b>Equipamento:</b>" + +#: glade/pytrainer.glade:1014 +msgid " <b>Title:</b>" +msgstr " <b>Título:</b>" + +#: glade/pytrainer.glade:1064 +#: pytrainer/gui/windowmain.py:1369 +msgid "Show graph display options" +msgstr "Amosa-las opcións do gráfico" + +#: glade/pytrainer.glade:1082 +msgid "" +"Profile\n" +"Speed\n" +"Pace\n" +"Heart Rate\n" +"Cadence\n" +"Percentage\n" +"Zone" +msgstr "" +"Perfil\n" +"Velocidade\n" +"Ritmo\n" +"Pulsacións\n" +"Cadencia\n" +"Porcentaxe\n" +"Zona" + +#: glade/pytrainer.glade:1099 +#: glade/pytrainer.glade:3831 +#: glade/pytrainer.glade:4454 +#: glade/pytrainer.glade:5076 +msgid "Versus" +msgstr "Contra" + +#: glade/pytrainer.glade:1110 +msgid "" +"None\n" +"Profile\n" +"Speed\n" +"Pace\n" +"Heart Rate\n" +"Cadence\n" +msgstr "" +"Nada\n" +"Perfil\n" +"Velocidade\n" +"Ritmo\n" +"Pulsacións\n" +"Cadencia\n" + +#: glade/pytrainer.glade:1257 +msgid "<small>Graph Display Options</small>" +msgstr "<small>Opcións de visualización gráfica</small>" + +#: glade/pytrainer.glade:1282 +#: glade/pytrainer.glade:1497 +#: glade/pytrainer.glade:1717 +msgid "<small>Limits</small>" +msgstr "<small>Límites</small>" + +#: glade/pytrainer.glade:1294 +#: glade/pytrainer.glade:1509 +#: glade/pytrainer.glade:1763 +msgid "<small>Min</small>" +msgstr "<small>Mín</small>" + +#: glade/pytrainer.glade:1306 +#: glade/pytrainer.glade:1521 +#: glade/pytrainer.glade:1775 +msgid "<small>Max</small>" +msgstr "<small>Máx</small>" + +#: glade/pytrainer.glade:1355 +#: glade/pytrainer.glade:1577 +msgid "<small>Color</small>" +msgstr "<small>Cor</small>" + +#: glade/pytrainer.glade:1386 +#: glade/pytrainer.glade:1590 +msgid "<small>Weight</small>" +msgstr "<small>Peso</small>" + +#: glade/pytrainer.glade:1416 +msgid "Y1" +msgstr "Y1" + +#: glade/pytrainer.glade:1427 +#: glade/pytrainer.glade:1638 +msgid "<small>Smoothing</small>" +msgstr "<small>Suavizado</small>" + +#: glade/pytrainer.glade:1567 +msgid "Y2" +msgstr "Y2" + +#: glade/pytrainer.glade:1707 +msgid "X" +msgstr "X" + +#: glade/pytrainer.glade:1788 +msgid "<small>Distance</small>" +msgstr "<small>Distancia</small>" + +#: glade/pytrainer.glade:1817 +msgid "<small>Time</small>" +msgstr "<small>Tempo</small>" + +#: glade/pytrainer.glade:1878 +msgid "Show Laps" +msgstr "Amosar Voltas" + +#: glade/pytrainer.glade:1917 +msgid "Reset Graph" +msgstr "Restablecer o Gráfico" + +#: glade/pytrainer.glade:1979 +msgid "Redraw Map" +msgstr "Restablecer Mapa" + +#: glade/pytrainer.glade:1995 +msgid "<small>Display map using:</small>" +msgstr "<small>Opcións de visualización de mapas:</small>" + +#: glade/pytrainer.glade:2097 +msgid "<b>Beats:</b>" +msgstr "<b>Pulsacións:</b>" + +#: glade/pytrainer.glade:2109 +#: glade/pytrainer.glade:2894 +#: glade/pytrainer.glade:3461 +#: glade/pytrainer.glade:4084 +#: glade/pytrainer.glade:4541 +msgid "<b>Calories: </b>" +msgstr "<b>Calorías: </b>" + +#: glade/pytrainer.glade:2123 +#: glade/pytrainer.glade:3046 +#: glade/pytrainer.glade:3689 +#: glade/pytrainer.glade:4312 +#: glade/pytrainer.glade:4919 +msgid "<b>Max Beats:</b>" +msgstr "<b>Pulsacións Máx:</b>" + +#: glade/pytrainer.glade:2137 +msgid "<b>HR Zones Method:</b>" +msgstr "<b>Método Zonas FC:</b>" + +#: glade/pytrainer.glade:2152 +msgid "<b>HR Zone5:</b>" +msgstr "<b>Máximo esforzo:</b>" + +#: glade/pytrainer.glade:2167 +#: glade/pytrainer.glade:2366 +msgid " bpm" +msgstr " ppm" + +#: glade/pytrainer.glade:2183 +msgid " Cal" +msgstr " Cal" + +#: glade/pytrainer.glade:2241 +msgid "<b>HR Zone4:</b>" +msgstr "<b>Anaeróbica:</b>" + +#: glade/pytrainer.glade:2257 +msgid "<b>HR Zone3:</b>" +msgstr "<b>Aeróbica:</b>" + +#: glade/pytrainer.glade:2273 +msgid "<b>HR Zone2:</b>" +msgstr "<b>Queimagrasas:</b>" + +#: glade/pytrainer.glade:2289 +msgid "<b>HR Zone1:</b>" +msgstr "<b>Recuperación:</b>" + +#: glade/pytrainer.glade:2391 +#: glade/pytrainer.glade:2403 +#: glade/pytrainer.glade:2418 +#: glade/pytrainer.glade:2433 +#: glade/pytrainer.glade:2448 +#: glade/pytrainer.glade:2969 +#: glade/pytrainer.glade:3063 +#: glade/pytrainer.glade:3596 +#: glade/pytrainer.glade:3674 +#: glade/pytrainer.glade:4219 +#: glade/pytrainer.glade:4297 +#: glade/pytrainer.glade:4616 +#: glade/pytrainer.glade:4823 +#: pytrainer/lib/activity.py:435 +#: pytrainer/lib/activity.py:515 +msgid "bpm" +msgstr "bpm" + +#: glade/pytrainer.glade:2493 +msgid " <b>Heart Rate:</b>" +msgstr " <b>Pulsacións:</b>" + +#: glade/pytrainer.glade:2653 +msgid "Record" +msgstr "Rexistro" + +#: glade/pytrainer.glade:2862 +#: glade/pytrainer.glade:3507 +#: glade/pytrainer.glade:4130 +#: glade/pytrainer.glade:4839 +msgid "<b>Max Speed:</b>" +msgstr "<b>Velocidade Máxima:</b>" + +#: glade/pytrainer.glade:2939 +#: glade/pytrainer.glade:3566 +#: glade/pytrainer.glade:4189 +#: glade/pytrainer.glade:4586 +msgid "<b>Beats avg:</b>" +msgstr "<b>Pulsacións media:</b>" + +#: glade/pytrainer.glade:2999 +#: glade/pytrainer.glade:3031 +#: glade/pytrainer.glade:3642 +#: glade/pytrainer.glade:3658 +#: glade/pytrainer.glade:4265 +#: glade/pytrainer.glade:4281 +#: glade/pytrainer.glade:4888 +#: glade/pytrainer.glade:4904 +#: pytrainer/gui/windowmain.py:633 +#: pytrainer/gui/windowmain.py:634 +#: pytrainer/gui/windowmain.py:756 +#: pytrainer/gui/windowmain.py:757 +#: pytrainer/gui/windowmain.py:834 +#: pytrainer/gui/windowmain.py:835 +#: pytrainer/lib/activity.py:203 +msgid "min/km" +msgstr "min/km" + +#: glade/pytrainer.glade:3153 +msgid " <b>Date:</b>" +msgstr " <b>Data:</b>" + +#: glade/pytrainer.glade:3196 +msgid "Value" +msgstr "Valor" + +#: glade/pytrainer.glade:3207 +msgid "" +"Stage Profile\n" +"Stage Velocity\n" +"Stage Profile/velocity" +msgstr "" +"Perfil de Etapa\n" +"Velocidade de Etapa\n" +"Perfil/Velocidade de Etapa" + +#: glade/pytrainer.glade:3263 +msgid "Day" +msgstr "Día" + +#: glade/pytrainer.glade:3765 +msgid " <b>Week:</b>" +msgstr " <b>Semana:</b>" + +#: glade/pytrainer.glade:3816 +#: glade/pytrainer.glade:4439 +#: glade/pytrainer.glade:5061 +msgid "" +"Distance\n" +"Time\n" +"Average Heart Rate\n" +"Average Speed\n" +"Calories" +msgstr "" +"Distancia\n" +"Tempo\n" +"Pulsacións medias\n" +"Velocidade Media\n" +"Calorias" + +#: glade/pytrainer.glade:3842 +#: glade/pytrainer.glade:4465 +#: glade/pytrainer.glade:5087 +msgid "" +"None\n" +"Distance\n" +"Time\n" +"Average Heart Rate\n" +"Average Speed\n" +"Calories" +msgstr "" +"Nada\n" +"Distancia\n" +"Tempo\n" +"Pulsacións Medias\n" +"Velocidade Media\n" +"Calorías" + +#: glade/pytrainer.glade:3886 +msgid "Week" +msgstr "Semana" + +#: glade/pytrainer.glade:4388 +msgid " <b>Month:</b>" +msgstr " <b>Mes:</b>" + +#: glade/pytrainer.glade:4508 +msgid "Month" +msgstr "Mes" + +#: glade/pytrainer.glade:5010 +msgid " <b>Year:</b>" +msgstr " <b>Ano:</b>" + +#: glade/pytrainer.glade:5130 +msgid "Year" +msgstr "Ano" + +#: glade/pytrainer.glade:5284 +msgid "<b>Graph</b>" +msgstr "<b>Gráfico</b>" + +#: glade/pytrainer.glade:5325 +msgid "<b>History</b>" +msgstr "<b>Historial</b>" + +#: glade/pytrainer.glade:5389 +msgid "<b>Title:</b>" +msgstr "<b>Título:</b>" + +#: glade/pytrainer.glade:5412 +msgid "Search" +msgstr "Buscar" + +#: glade/pytrainer.glade:5436 +msgid "All Distances" +msgstr "Todas as Distancias" + +#: glade/pytrainer.glade:5445 +msgid "All Durations" +msgstr "Todas as Duracións" + +#: glade/pytrainer.glade:5455 +msgid "" +"All time\n" +"Last 4 weeks\n" +"Last 6 months\n" +"Last 12 months" +msgstr "" +"Todo o Tempo\n" +"Últimas 4 semanas\n" +"Últimos 6 meses\n" +"Últimos 12 meses" + +#: glade/pytrainer.glade:5492 +msgid "Columns" +msgstr "Columnas" + +#: glade/pytrainer.glade:5619 +msgid "Type:" +msgstr "Tipo:" + +#: glade/pytrainer.glade:5632 +msgid "" +"Font\n" +"Restaurant\n" +"Scenic Area\n" +"Summit" +msgstr "" +"Fonte\n" +"Restaurante\n" +"Mirador\n" +"Cima" + +#: glade/pytrainer.glade:5648 +msgid "Latitude: " +msgstr "Latitude: " + +#: glade/pytrainer.glade:5662 +msgid " Name:" +msgstr " Nome:" + +#: glade/pytrainer.glade:5674 +msgid "Longitude:" +msgstr "Lonxitude:" + +#: glade/pytrainer.glade:5766 +msgid "<b> Waypoint: </b>" +msgstr "<b> Waypoint: </b>" + +#: glade/pytrainer.glade:5950 +msgid "Edit Record" +msgstr "Editar Entrada" + +#: glade/pytrainer.glade:5964 +msgid "Show graph in classic view" +msgstr "Amosa-lo gráfico en vista clásica" + +#: glade/pytrainer.glade:5978 +msgid "Merge tracks" +msgstr "Unir entrenamentos" + +#: glade/selecttrackdialog.glade:7 +msgid "Select track record" +msgstr "Seleccionar un dos entrenamentos" + +#: glade/warning.glade:7 +#: glade/warning.glade:38 +msgid "Warning" +msgstr "Aviso" + +#: extensions/fixelevation/fixelevation.py:94 +msgid "Elevation Correction Complete" +msgstr "Corrección de Altura Completada" + +#: extensions/openstreetmap/openstreetmap.py:30 +msgid "Must have username and password configured" +msgstr "É obrigatorio ter o nome de usuario e contrasinal configurados" + +#: extensions/openstreetmap/openstreetmap.py:32 +msgid "Openstreetmap Extension Error" +msgstr "Erro da Extensión OpenStreetMap" + +#: extensions/openstreetmap/openstreetmap.py:79 +msgid "" +"Posting GPX trace to Openstreetmap\n" +"\n" +"Please wait this could take several minutes" +msgstr "" +"Enviando traza GPX a OpenStreetMap\n" +"\n" +"Por favor espere, pode durar varios minutos" + +#: extensions/openstreetmap/openstreetmap.py:81 +msgid "Openstreetmap Extension Processing" +msgstr "Procesando Extensión OpenStreetMap" + +#: extensions/openstreetmap/openstreetmap.py:99 +msgid "Openstreetmap Extension Upload Complete" +msgstr "Envío Completo da Extensión OpenStreetMap" + +#: extensions/openstreetmap/openstreetmap.py:109 +msgid "Please add any additional information for this upload" +msgstr "Por favor engada información adicional para este envío" + +#: extensions/wordpress/wordpress.py:46 +msgid "Posting to Wordpress blog" +msgstr "Subir a un blogue de Wordpress" + +#: extensions/wordpress/wordpress.py:48 +msgid "Wordpress Extension Processing" +msgstr "Procesando Extensión Wordpress" + +#: extensions/wordpress/wordpress.py:144 +msgid "Wordpress Extension Upload Complete" +msgstr "E" + +#: import/file_garmintcxv1.py:45 +msgid "Garmin training center database file version 1" +msgstr "Versión 1 do ficheiro Garmin training center database" + +#: import/file_garmintcxv2.py:43 +msgid "Garmin training center database file version 2" +msgstr "Versión 2 do ficheiro Garmin training center database" + +#: import/file_garmintools.py:44 +msgid "Garmin tools dump file" +msgstr "Ficheiro de volcado de datos de Garmintools" + +#: import/file_gpxplus.py:41 +msgid "GPS eXchange file" +msgstr "Ficheiro de intercambio GPS" + +#: import/file_gpxplusNokia.py:41 +msgid "Nokia Export - GPS eXchange file" +msgstr "Exportar a Nokia - Ficheiro de intercambio GPS" + +#: import/file_kml20.py:47 +msgid "Geodistance kml version 2.0 file" +msgstr "Ficheiro kml versión 2.0" + +#: import/tool_gant.py:37 +msgid "Gant" +msgstr "Gant" + +#: import/tool_garmintools.py:37 +msgid "Garmintools" +msgstr "FerramentasGarmin" + +#: import/tool_gpsbabel.py:37 +msgid "GPSBabel" +msgstr "GPSBabel" + +#: pytrainer/athlete.py:52 +msgid "lb" +msgstr "lb" + +#: pytrainer/athlete.py:54 +msgid "kg" +msgstr "kg" + +#: pytrainer/daygraph.py:52 +#: pytrainer/recordgraph.py:134 +msgid "Height (m)" +msgstr "Altura (m)" + +#: pytrainer/daygraph.py:52 +#: pytrainer/recordgraph.py:134 +msgid "Stage Profile" +msgstr "Perfil Etapa" + +#: pytrainer/daygraph.py:54 +msgid "Velocity (Km/h)" +msgstr "Velocidade (Km/h)" + +#: pytrainer/daygraph.py:54 +msgid "Velocity" +msgstr "Velocidade" + +#: pytrainer/daygraph.py:56 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/recordgraph.py:140 +msgid "Beats (bpm)" +msgstr "Pulsacións (ppm)" + +#: pytrainer/extensions/googlemaps.py:69 +#: pytrainer/extensions/osm.py:48 +#: pytrainer/gui/drawArea.py:165 +#: pytrainer/gui/windowmain.py:1698 +msgid "h" +msgstr "h" + +#: pytrainer/extensions/googlemaps.py:69 +#: pytrainer/extensions/osm.py:48 +#: pytrainer/gui/drawArea.py:166 +msgid "min" +msgstr "min" + +#: pytrainer/extensions/googlemaps.py:71 +#: pytrainer/extensions/osm.py:50 +#: pytrainer/gui/windowmain.py:112 +#: pytrainer/gui/windowmain.py:394 +#: pytrainer/gui/windowmain.py:1144 +msgid "Time" +msgstr "Tempo" + +#: pytrainer/extensions/googlemaps.py:71 +#: pytrainer/extensions/osm.py:50 +#: pytrainer/gui/windowimportdata.py:352 +#: pytrainer/gui/windowmain.py:110 +#: pytrainer/gui/windowmain.py:393 +#: pytrainer/gui/windowmain.py:1142 +#: pytrainer/gui/windowrecord.py:210 +#: pytrainer/lib/activity.py:313 +#: pytrainer/lib/activity.py:324 +#: pytrainer/lib/activity.py:396 +#: pytrainer/lib/activity.py:407 +#: pytrainer/lib/activity.py:416 +#: pytrainer/lib/activity.py:425 +#: pytrainer/lib/activity.py:434 +#: pytrainer/lib/activity.py:444 +#: pytrainer/lib/activity.py:453 +#: pytrainer/lib/activity.py:514 +msgid "Distance" +msgstr "Distancia" + +#: pytrainer/gui/dialogselecttrack.py:40 +msgid "Track Name" +msgstr "Nome da Ruta" + +#: pytrainer/gui/drawArea.py:399 +msgid "rest" +msgstr "descanso" + +#: pytrainer/gui/drawGraph.py:170 +msgid "Athlete Data" +msgstr "Datos do Atleta" + +#: pytrainer/gui/windowextensions.py:70 +#: pytrainer/gui/windowimportdata.py:560 +#: pytrainer/gui/windowplugins.py:71 +#: pytrainer/gui/windowplugins.py:95 +msgid "Disable" +msgstr "Deshabilitado" + +#: pytrainer/gui/windowextensions.py:72 +#: pytrainer/gui/windowimportdata.py:561 +#: pytrainer/gui/windowplugins.py:69 +#: pytrainer/gui/windowplugins.py:96 +msgid "Enable" +msgstr "Habilitado" + +#: pytrainer/gui/windowextensions.py:81 +#: pytrainer/gui/windowimportdata.py:545 +#: pytrainer/gui/windowplugins.py:80 +#, python-format +msgid "%s settings" +msgstr "%s opcións" + +#: pytrainer/gui/windowextensions.py:112 +#: pytrainer/gui/windowextensions.py:125 +msgid "OK" +msgstr "Aceptar" + +#: pytrainer/gui/windowimportdata.py:116 +msgid "No file selected" +msgstr "Ningún ficheiro seleccionado" + +#: pytrainer/gui/windowimportdata.py:161 +msgid "Configure" +msgstr "Configurar" + +#: pytrainer/gui/windowimportdata.py:162 +msgid "Run" +msgstr "Executar" + +#: pytrainer/gui/windowimportdata.py:170 +msgid "Disabled" +msgstr "Desactivado" + +#: pytrainer/gui/windowimportdata.py:172 +msgid "Enabled" +msgstr "Activo" + +#: pytrainer/gui/windowimportdata.py:252 +msgid "GPS device found" +msgstr "Encontrado dispositivo GPS" + +#: pytrainer/gui/windowimportdata.py:255 +msgid "GPS device <b>not</b> found" +msgstr "Dispositivo GPS <b>non</b> encontrado" + +#: pytrainer/gui/windowimportdata.py:262 +msgid "This tool was not found on the system" +msgstr "Non se encontrou esta ferramenta no sistema" + +#: pytrainer/gui/windowimportdata.py:264 +msgid " Homepage" +msgstr " Páxina de inicio" + +#: pytrainer/gui/windowimportdata.py:317 +msgid "File" +msgstr "Ficheiro" + +#: pytrainer/gui/windowimportdata.py:317 +msgid "Type" +msgstr "Tipo" + +#: pytrainer/gui/windowimportdata.py:317 +msgid "Activities" +msgstr "Actividades" + +#: pytrainer/gui/windowimportdata.py:352 +#: pytrainer/gui/windowrecord.py:210 +msgid "Start Time" +msgstr "Hora de Comezo:" + +#: pytrainer/gui/windowimportdata.py:352 +#: pytrainer/gui/windowrecord.py:210 +msgid "Duration" +msgstr "Duración" + +#: pytrainer/gui/windowimportdata.py:501 +msgid "Imported into database" +msgstr "Importado á base de datos" + +#: pytrainer/gui/windowimportdata.py:568 +#: pytrainer/gui/windowplugins.py:103 +msgid "Ok" +msgstr "Aceptar" + +#: pytrainer/gui/windowimportdata.py:628 +msgid "Saving options" +msgstr "Gardar configuración" + +#: pytrainer/gui/windowimportdata.py:630 +msgid "Options saved" +msgstr "Configuración gardada" + +#: pytrainer/gui/windowimportdata.py:655 +msgid "Importing one activity" +msgstr "Importando unha actividade" + +#: pytrainer/gui/windowimportdata.py:656 +msgid "Imported one activity" +msgstr "Unha actividade importada" + +#: pytrainer/gui/windowimportdata.py:658 +#, python-format +msgid "Importing %d activities" +msgstr "Importando %d actividades" + +#: pytrainer/gui/windowimportdata.py:659 +#, python-format +msgid "Imported %d activities" +msgstr "%d actividades importadas" + +#: pytrainer/gui/windowimportdata.py:678 +msgid "Choose a file (or files) to import activities from" +msgstr "Seleccione ficheiro/s dende o/os que importar actividades" + +#: pytrainer/gui/windowimportdata.py:693 +#: pytrainer/gui/windowimportdata.py:694 +#, python-format +msgid "Found file of type: %s" +msgstr "Tipo de ficheiro encontrado: %s" + +#: pytrainer/gui/windowimportdata.py:718 +msgid "Found in database" +msgstr "Encontrado na base de datos" + +#: pytrainer/gui/windowimportdata.py:733 +#: pytrainer/gui/windowimportdata.py:734 +#, python-format +msgid "File %s is of unknown or unsupported file type" +msgstr "Ficheiro %s descoñecido ou non soportado" + +#: pytrainer/gui/windowimportdata.py:807 +#, python-format +msgid "Column %d" +msgstr "Columna %d" + +#: pytrainer/gui/windowmain.py:100 +msgid "Miles" +msgstr "Millas" + +#: pytrainer/gui/windowmain.py:103 +#: pytrainer/gui/windowmain.py:107 +#: pytrainer/gui/windowmain.py:120 +#: pytrainer/gui/windowmain.py:123 +msgid "id" +msgstr "id" + +#: pytrainer/gui/windowmain.py:103 +msgid "Start" +msgstr "Comezo" + +#: pytrainer/gui/windowmain.py:113 +msgid "⌀ HR" +msgstr "⌀ FC" + +#: pytrainer/gui/windowmain.py:114 +msgid "⌀ Speed" +msgstr "⌀ Velocidade" + +#: pytrainer/gui/windowmain.py:120 +msgid "Waypoint" +msgstr "Waypoint" + +#: pytrainer/gui/windowmain.py:125 +msgid "Weight" +msgstr "Peso" + +#: pytrainer/gui/windowmain.py:126 +msgid "Body Fat %" +msgstr "Grasa Corporal %" + +#: pytrainer/gui/windowmain.py:127 +msgid "Resting HR" +msgstr "FC en repouso" + +#: pytrainer/gui/windowmain.py:128 +msgid "Max HR" +msgstr "Máx FC" + +#: pytrainer/gui/windowmain.py:395 +msgid "Laps" +msgstr "Voltas" + +#: pytrainer/gui/windowmain.py:396 +msgid "Left Axis Grid" +msgstr "Cuadrícula do Eixe Esquerdo" + +#: pytrainer/gui/windowmain.py:397 +msgid "Right Axis Grid" +msgstr "Cuadrícula do Eixe Dereito" + +#: pytrainer/gui/windowmain.py:398 +msgid "X Axis Grid" +msgstr "Cuadrícula do Eixe X" + +#: pytrainer/gui/windowmain.py:459 +msgid "Reset Limits" +msgstr "Restablecer Límites" + +#: pytrainer/gui/windowmain.py:461 +msgid "Set Limits" +msgstr "Establecer Límites" + +#: pytrainer/gui/windowmain.py:616 +msgid "Percentages method" +msgstr "Baseado en porcentaxes" + +#: pytrainer/gui/windowmain.py:624 +#: pytrainer/gui/windowmain.py:747 +#: pytrainer/gui/windowmain.py:825 +#: pytrainer/lib/activity.py:196 +msgid "miles" +msgstr "millas" + +#: pytrainer/gui/windowmain.py:625 +#: pytrainer/gui/windowmain.py:626 +#: pytrainer/gui/windowmain.py:748 +#: pytrainer/gui/windowmain.py:749 +#: pytrainer/gui/windowmain.py:826 +#: pytrainer/gui/windowmain.py:827 +#: pytrainer/lib/activity.py:197 +msgid "miles/h" +msgstr "millas/h" + +#: pytrainer/gui/windowmain.py:627 +#: pytrainer/gui/windowmain.py:628 +#: pytrainer/gui/windowmain.py:750 +#: pytrainer/gui/windowmain.py:751 +#: pytrainer/gui/windowmain.py:828 +#: pytrainer/gui/windowmain.py:829 +#: pytrainer/lib/activity.py:198 +msgid "min/mile" +msgstr "min/milla" + +#: pytrainer/gui/windowmain.py:630 +#: pytrainer/gui/windowmain.py:753 +#: pytrainer/gui/windowmain.py:831 +#: pytrainer/lib/activity.py:201 +msgid "km" +msgstr "km" + +#: pytrainer/gui/windowmain.py:1145 +#: pytrainer/recordgraph.py:144 +msgid "Beats" +msgstr "Pulsacións" + +#: pytrainer/gui/windowmain.py:1146 +msgid "Average" +msgstr "Media" + +#: pytrainer/gui/windowmain.py:1365 +msgid "Hide graph display options" +msgstr "Ocultar opcións de visualización gráfica" + +#: pytrainer/gui/windowmain.py:1688 +msgid "lap" +msgstr "Volta" + +#: pytrainer/gui/windowmain.py:1698 +#: pytrainer/gui/windowmain.py:1700 +#: pytrainer/lib/activity.py:204 +msgid "m" +msgstr "m" + +#: pytrainer/gui/windowmain.py:1698 +#: pytrainer/gui/windowmain.py:1700 +msgid "s" +msgstr "s" + +#: pytrainer/gui/windowmain.py:1784 +#: pytrainer/record.py:63 +msgid "Edit Entry" +msgstr "Editar Entrada" + +#: pytrainer/gui/windowmain.py:1795 +msgid "Delete Entry" +msgstr "Borrar Entrada" + +#: pytrainer/gui/windowmain.py:1819 +msgid "Create Athlete Entry" +msgstr "Crear Entrada de Atleta" + +#: pytrainer/gui/windowmain.py:1823 +msgid "Edit Athlete Entry" +msgstr "Editar Entrada do Atleta" + +#: pytrainer/gui/windowmain.py:1904 +#: pytrainer/main.py:442 +msgid "Delete this database entry?" +msgstr "Borrar esta entrada da base de datos?" + +#: pytrainer/gui/windowmain.py:1906 +msgid "Are you sure?" +msgstr "Está seguro?" + +#: pytrainer/gui/windowprofile.py:41 +msgid "Male" +msgstr "Home" + +#: pytrainer/gui/windowprofile.py:42 +msgid "Female" +msgstr "Muller" + +#: pytrainer/gui/windowprofile.py:59 +msgid "MET" +msgstr "MET" + +#: pytrainer/gui/windowprofile.py:59 +msgid "Maximum Pace" +msgstr "Ritmo Máximo" + +#: pytrainer/gui/windowrecord.py:210 +msgid "GPX File" +msgstr "Ficheiro GPX" + +#: pytrainer/lib/activity.py:199 +msgid "feet" +msgstr "pés" + +#: pytrainer/lib/activity.py:312 +msgid "Pace by Lap" +msgstr "Ritmo por Volta" + +#: pytrainer/lib/activity.py:314 +#: pytrainer/lib/activity.py:424 +#: pytrainer/lib/activity.py:426 +#: pytrainer/recordgraph.py:138 +msgid "Pace" +msgstr "Ritmo" + +#: pytrainer/lib/activity.py:318 +#: pytrainer/lib/activity.py:329 +#: pytrainer/lib/activity.py:401 +#: pytrainer/lib/activity.py:411 +#: pytrainer/lib/activity.py:420 +#: pytrainer/lib/activity.py:429 +#: pytrainer/lib/activity.py:438 +#: pytrainer/lib/activity.py:448 +#: pytrainer/lib/activity.py:457 +#: pytrainer/lib/activity.py:519 +msgid "Time (seconds)" +msgstr "Tempo (segundos)" + +#: pytrainer/lib/activity.py:323 +msgid "Speed by Lap" +msgstr "Velocidade por Volta" + +#: pytrainer/lib/activity.py:325 +#: pytrainer/lib/activity.py:415 +#: pytrainer/lib/activity.py:417 +#: pytrainer/recordgraph.py:136 +msgid "Speed" +msgstr "Velocidade" + +#: pytrainer/lib/activity.py:395 +#: pytrainer/lib/activity.py:397 +msgid "Elevation" +msgstr "Altura" + +#: pytrainer/lib/activity.py:406 +#: pytrainer/lib/activity.py:408 +msgid "Corrected Elevation" +msgstr "Altura Correxida" + +#: pytrainer/lib/activity.py:443 +#, python-format +msgid "Heart Rate (% of max)" +msgstr "Frecuencia cardíaca (% do máx)" + +#: pytrainer/lib/activity.py:445 +msgid "%" +msgstr "%" + +#: pytrainer/lib/activity.py:452 +#: pytrainer/lib/activity.py:454 +#: pytrainer/recordgraph.py:142 +msgid "Cadence" +msgstr "Cadencia" + +#: pytrainer/lib/activity.py:454 +msgid "rpm" +msgstr "rpm" + +#: pytrainer/lib/activity.py:513 +msgid "Heart Rate zone" +msgstr "Zoa Frecuencia Cardíaca" + +#: pytrainer/lib/gpx.py:122 +msgid "No Name" +msgstr "Sen Nome" + +#: pytrainer/lib/gpx.py:129 +msgid "No Data" +msgstr "Sen Datos" + +#: pytrainer/main.py:458 +msgid "Delete this waypoint?" +msgstr "Borrar este waypoint?" + +#: pytrainer/monthgraph.py:70 +#: pytrainer/monthgraph.py:72 +#: pytrainer/monthgraph.py:74 +#: pytrainer/monthgraph.py:76 +#: pytrainer/monthgraph.py:78 +msgid "day" +msgstr "día" + +#: pytrainer/monthgraph.py:70 +#: pytrainer/weekgraph.py:115 +msgid "Daily Distance" +msgstr "Distancia Diaria" + +#: pytrainer/monthgraph.py:72 +#: pytrainer/weekgraph.py:117 +#: pytrainer/yeargraph.py:72 +msgid "Time (hours)" +msgstr "Tempo (horas)" + +#: pytrainer/monthgraph.py:72 +#: pytrainer/weekgraph.py:117 +msgid "Daily Time" +msgstr "Tempo Diario" + +#: pytrainer/monthgraph.py:74 +#: pytrainer/weekgraph.py:119 +#: pytrainer/yeargraph.py:74 +msgid "Average Heart Rate (bpm)" +msgstr "Pulsacións medias (ppm)" + +#: pytrainer/monthgraph.py:74 +#: pytrainer/weekgraph.py:119 +msgid "Daily Average Heart Rate" +msgstr "Pulsacións Medias Diarias" + +#: pytrainer/monthgraph.py:76 +#: pytrainer/weekgraph.py:121 +msgid "Daily Average Speed" +msgstr "Velocidades Medias Diarias" + +#: pytrainer/monthgraph.py:78 +#: pytrainer/weekgraph.py:123 +msgid "Daily Calories" +msgstr "Calorías Diarias" + +#: pytrainer/profile.py:109 +msgid "Moderate activity" +msgstr "Actividad moderada" + +#: pytrainer/profile.py:110 +msgid "Weight Control" +msgstr "Control do peso" + +#: pytrainer/profile.py:111 +msgid "Aerobic" +msgstr "Aeróbico" + +#: pytrainer/profile.py:112 +msgid "Anaerobic" +msgstr "Anaeróbico" + +#: pytrainer/profile.py:113 +msgid "VO2 MAX" +msgstr "VO2 MAX" + +#: pytrainer/record.py:513 +msgid "pyTrainer can't import data from your gpx file" +msgstr "pyTrainer non pode importar datos dende o seu ficheiro gpx" + +#: pytrainer/recordgraph.py:136 +msgid "Speed (Km/h)" +msgstr "Velocidade (km/h)" + +#: pytrainer/recordgraph.py:142 +msgid "Cadence (rpm)" +msgstr "Cadencia (rpm)" + +#: pytrainer/recordgraph.py:144 +msgid "Beats (%)" +msgstr "Pulsacións (%)" + +#: pytrainer/recordgraph.py:146 +msgid "Zone" +msgstr "Zona" + +#: pytrainer/waypoint.py:87 +msgid "The gpx file seems to be a several days records. Perhaps you will need to edit your gpx file" +msgstr "Parece que o ficheiro gpx contén actividades de varios días. Quizais debería editar o seu ficheiro gpx" + +#: pytrainer/yeargraph.py:70 +#: pytrainer/yeargraph.py:72 +#: pytrainer/yeargraph.py:74 +#: pytrainer/yeargraph.py:76 +#: pytrainer/yeargraph.py:78 +msgid "month" +msgstr "mes" + +#: pytrainer/yeargraph.py:70 +msgid "Monthly Distance" +msgstr "Distancia Mensual" + +#: pytrainer/yeargraph.py:72 +msgid "Monthly Time" +msgstr "Tempo Mensual" + +#: pytrainer/yeargraph.py:74 +msgid "Monthly Average Heart Rate" +msgstr "Pulsacións Medias Mensuais" + +#: pytrainer/yeargraph.py:76 +msgid "Monthly Average Speed" +msgstr "Velocidades Medias Mensuais" + +#: pytrainer/yeargraph.py:78 +msgid "Monthly Calories" +msgstr "Calorías Mensuais" + Modified: pytrainer/trunk/setup.py =================================================================== --- pytrainer/trunk/setup.py 2011-09-08 15:31:16 UTC (rev 884) +++ pytrainer/trunk/setup.py 2011-10-11 07:43:06 UTC (rev 885) @@ -56,6 +56,7 @@ (install_locale("es")), (install_locale("eu")), (install_locale("fr")), + (install_locale("gl")), (install_locale("no")), (install_locale("pl")), (install_locale("ru")), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dg...@us...> - 2011-10-20 17:42:02
|
Revision: 896 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=896&view=rev Author: dgranda Date: 2011-10-20 17:41:53 +0000 (Thu, 20 Oct 2011) Log Message: ----------- Added portuguese localization (thanks to Pedro Ribeiro) Modified Paths: -------------- pytrainer/trunk/pytrainer/gui/aboutdialog.py pytrainer/trunk/setup.py pytrainer/trunk/utils/translator.sh Added Paths: ----------- pytrainer/trunk/locale/pt/ pytrainer/trunk/locale/pt/LC_MESSAGES/ pytrainer/trunk/locale/pt/LC_MESSAGES/Makefile pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer.mo pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer_pt.po Added: pytrainer/trunk/locale/pt/LC_MESSAGES/Makefile =================================================================== --- pytrainer/trunk/locale/pt/LC_MESSAGES/Makefile (rev 0) +++ pytrainer/trunk/locale/pt/LC_MESSAGES/Makefile 2011-10-20 17:41:53 UTC (rev 896) @@ -0,0 +1,8 @@ +default: + msgfmt pytrainer_pt.po -o ./pytrainer.mo + +merge: + msgmerge pytrainer_pt.po pytrainer_pt.po_new > pytrainer_pt.po.tmp + mv pytrainer_pt.po.tmp pytrainer_pt.po + rm pytrainer_pt.po_new + Property changes on: pytrainer/trunk/locale/pt/LC_MESSAGES/Makefile ___________________________________________________________________ Added: svn:executable + * Added: pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer.mo =================================================================== (Binary files differ) Property changes on: pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer.mo ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer_pt.po =================================================================== --- pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer_pt.po (rev 0) +++ pytrainer/trunk/locale/pt/LC_MESSAGES/pytrainer_pt.po 2011-10-20 17:41:53 UTC (rev 896) @@ -0,0 +1,2427 @@ +# Portuguese translation for pytrainer's messages +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the pytrainer package. +# Pedro Ribeiro <p.m...@gm...>, 2011 +# +msgid "" +msgstr "" +"Project-Id-Version: pytrainer 1.9.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-10-20 19:14+0200\n" +"PO-Revision-Date: 2011-10-20 19:14+0100\n" +"Last-Translator: Pedro Ribeiro <p.m...@gm...>\n" +"Language-Team: Portuguese <tr...@de...>\n" +"Language: Portuguese\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: glade/calendar.glade:7 +msgid "Calendar" +msgstr "Calendário" + +#: glade/equipment.glade:109 +msgid "<b>Equipment List</b>" +msgstr "<b>Lista de Equipamento</b>" + +#: glade/equipment.glade:149 +#: glade/equipment.glade:406 +#: pytrainer/gui/equipment.py:98 +msgid "Description" +msgstr "Descrição" + +#: glade/equipment.glade:174 +#: glade/equipment.glade:431 +msgid "Life Expectancy" +msgstr "Esperança de Vida" + +#: glade/equipment.glade:211 +#: glade/equipment.glade:467 +msgid "Prior Usage" +msgstr "Utilização Anterior" + +#: glade/equipment.glade:243 +#: glade/equipment.glade:499 +#: pytrainer/gui/equipment.py:102 +msgid "Active" +msgstr "Activo" + +#: glade/equipment.glade:277 +#: glade/equipment.glade:533 +#: pytrainer/gui/windowimportdata.py:353 +msgid "Notes" +msgstr "Notas" + +#: glade/equipment.glade:366 +msgid "<b>Add New Equipment</b>" +msgstr "<b>Acrescentar Novo Equipamento</b>" + +#: glade/equipment.glade:622 +msgid "<b>Edit Equipment</b>" +msgstr "<b>Editar Equipamento</b>" + +#: glade/equipment.glade:651 +msgid "Really delete the equipment item?" +msgstr "De certeza que quer apagar o equipamento?" + +#: glade/equipment.glade:704 +msgid "<b>Delete Equipment</b>" +msgstr "<b>Apagar Equipamento</b>" + +#: glade/extensions.glade:9 +#: glade/pytrainer.glade:163 +msgid "Extensions" +msgstr "Extensões" + +#: glade/extensions.glade:54 +msgid "<b>Extension Details</b>" +msgstr "<b>Detalhes da Extensão</b>" + +#: glade/extensions.glade:67 +msgid "name-entry" +msgstr "nome" + +#: glade/extensions.glade:82 +#: glade/plugins.glade:144 +#: glade/profile.glade:728 +#: glade/pytrainer.glade:5703 +msgid "Name:" +msgstr "Nome:" + +#: glade/extensions.glade:96 +#: glade/plugins.glade:115 +msgid "Status:" +msgstr "Estado:" + +#: glade/extensions.glade:110 +#: glade/plugins.glade:86 +#: glade/pytrainer.glade:6548 +msgid "Description:" +msgstr "Descrição:" + +#: glade/extensions.glade:124 +#: glade/plugins.glade:100 +msgid "status-entry" +msgstr "estado" + +#: glade/extensions.glade:139 +msgid "description-entry" +msgstr "descrição" + +#: glade/importdata.glade:7 +#: glade/importdata.glade:325 +msgid "Import" +msgstr "Importar" + +#: glade/importdata.glade:36 +msgid "" +"Test 1\n" +"Test 2" +msgstr "" +"Teste 1\n" +"Teste 2" + +#: glade/importdata.glade:46 +msgid "<b>Select your GPS device</b>" +msgstr "<b>Seleccione o seu dispositivo GPS</b>" + +#: glade/importdata.glade:64 +msgid "Import from GPS Device is not yet implemented" +msgstr "Importação a partir do Dispositivo GPS ainda não está implementada" + +#: glade/importdata.glade:92 +msgid "<b>Tools</b>" +msgstr "<b>Ferramentas</b>" + +#: glade/importdata.glade:111 +msgid "Rescan" +msgstr "Rescan" + +#: glade/importdata.glade:115 +msgid "Rescan system for available tools" +msgstr "Fazer o rescan do sistema à procura de ferramentas" + +#: glade/importdata.glade:168 +msgid "<b>Import from GPS Device</b>" +msgstr "<b>Importar do Dispositivo GPS</b>" + +#: glade/importdata.glade:221 +msgid "Remove selected files and the associated activities" +msgstr "Remover ficheiros seleccionados e actividades associadas" + +#: glade/importdata.glade:239 +msgid "Add files to import activities from" +msgstr "Acrescentar ficheiros a partir dos quais importar actividades" + +#: glade/importdata.glade:264 +msgid "<b>Add file(s) to import activities from</b>" +msgstr "<b>Acrescentar ficheiro(s) a partir do(s) qual(is) importar actividades" + +#: glade/importdata.glade:307 +msgid "<b>Select activities to import</b>" +msgstr "<b>Seleccionar actividades a importar</b>" + +#: glade/importdata.glade:330 +msgid "Import selected activities" +msgstr "Importar actividades seleccionadas" + +#: glade/importdata.glade:347 +msgid "Close Import dialog" +msgstr "Fechar diálogo de importação" + +#: glade/importdata.glade:387 +msgid "<b>Import from File</b>" +msgstr "<b>Importar a partir de Ficheiro</b>" + +#: glade/importdata.glade:464 +msgid "<b>Plugins</b>" +msgstr "<b>Plugins</b>" + +#: glade/importdata.glade:491 +msgid "Import from GPS Device" +msgstr "Importar a partir do Dispositivo GPS" + +#: glade/importdata.glade:510 +msgid "Import from File" +msgstr "Importar a partir de Ficheiro" + +#: glade/importdata.glade:525 +msgid "Launch 'File Select' on start" +msgstr "Executar o 'Escolher Ficheiro' ao iniciar" + +#: glade/importdata.glade:530 +msgid "Automatically start the file selection dialog" +msgstr "Executar automaticamente o diálogo de selecção de ficheiro" + +#: glade/importdata.glade:546 +#: glade/plugins.glade:9 +msgid "Plugins" +msgstr "Plugins" + +#: glade/importdata.glade:572 +msgid "<b>Default to Tab</b>" +msgstr "<b>Separador predefinido</b>" + +#: glade/importdata.glade:595 +msgid "Reset" +msgstr "Reset" + +#: glade/importdata.glade:611 +msgid "Save" +msgstr "Guardar" + +#: glade/importdata.glade:670 +msgid "<b>Options</b>" +msgstr "<b>Opções</b>" + +#: glade/importdata.glade:702 +msgid "<b>Select file to import from</b>" +msgstr "<b>Escolher ficheiro a partir do qual importar</b>" + +#: glade/importdata.glade:731 +msgid "Tab" +msgstr "Tab" + +#: glade/importdata.glade:746 +msgid "Comma" +msgstr "Vírgula" + +#: glade/importdata.glade:763 +msgid "Other" +msgstr "Outro" + +#: glade/importdata.glade:799 +msgid "Read File" +msgstr "Ler Ficheiro" + +#: glade/importdata.glade:820 +msgid "<b>File delimiter</b>" +msgstr "<b>Delimitador de campos no ficheiro</b>" + +#: glade/importdata.glade:864 +#: pytrainer/gui/windowmain.py:116 +#: pytrainer/gui/windowmain.py:131 +#: pytrainer/gui/windowmain.py:184 +#: pytrainer/gui/windowmain.py:1494 +#: pytrainer/gui/dialogselecttrack.py:40 +msgid "Date" +msgstr "Data" + +#: glade/importdata.glade:877 +#: glade/importdata.glade:918 +#: glade/importdata.glade:933 +#: glade/importdata.glade:1128 +#: glade/importdata.glade:1143 +#: glade/importdata.glade:1158 +#: glade/importdata.glade:1173 +#: glade/importdata.glade:1188 +#: glade/importdata.glade:1203 +#: glade/importdata.glade:1218 +#: glade/importdata.glade:1233 +#: glade/importdata.glade:1248 +#: glade/importdata.glade:1263 +#: glade/importdata.glade:1278 +#: glade/importdata.glade:1293 +msgid "Exclude" +msgstr "Excluir" + +#: glade/importdata.glade:892 +#: pytrainer/recordgraph.py:134 +#: pytrainer/recordgraph.py:136 +#: pytrainer/recordgraph.py:138 +#: pytrainer/recordgraph.py:140 +#: pytrainer/recordgraph.py:142 +#: pytrainer/recordgraph.py:144 +#: pytrainer/recordgraph.py:146 +#: pytrainer/monthgraph.py:26 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/totalgraph.py:24 +#: pytrainer/yeargraph.py:26 +#: pytrainer/daygraph.py:52 +#: pytrainer/daygraph.py:54 +#: pytrainer/daygraph.py:56 +#: pytrainer/weekgraph.py:28 +msgid "Distance (km)" +msgstr "Distância (km)" + +#: glade/importdata.glade:905 +msgid "Duration (sec)" +msgstr "Duração (s)" + +#: glade/importdata.glade:947 +msgid "<b>PyTrainer Field</b>" +msgstr "<b>Campo do PyTrainer</b>" + +#: glade/importdata.glade:958 +msgid "<b>File Column</b>" +msgstr "<b>Coluna no Ficheiro</b>" + +#: glade/importdata.glade:972 +#: pytrainer/gui/windowmain.py:115 +#: pytrainer/gui/windowmain.py:1493 +msgid "Title" +msgstr "Título" + +#: glade/importdata.glade:985 +msgid "Descent (m)" +msgstr "Descida (m)" + +#: glade/importdata.glade:998 +msgid "Accent (m)" +msgstr "Subida (m)" + +#: glade/importdata.glade:1011 +msgid "Calories (kcal)" +msgstr "Calorias (kcal)" + +#: glade/importdata.glade:1024 +msgid "Max Speed (km/h)" +msgstr "Vel Máxima (km/h)" + +#: glade/importdata.glade:1037 +#: pytrainer/monthgraph.py:29 +#: pytrainer/totalgraph.py:27 +#: pytrainer/yeargraph.py:29 +#: pytrainer/weekgraph.py:31 +msgid "Average Speed (km/h)" +msgstr "Vel Média (km/h)" + +#: glade/importdata.glade:1050 +#: glade/pytrainer.glade:277 +#: pytrainer/gui/windowmain.py:110 +#: pytrainer/gui/windowmain.py:118 +#: pytrainer/gui/windowmain.py:140 +#: pytrainer/gui/windowmain.py:1496 +#: pytrainer/gui/windowimportdata.py:353 +#: pytrainer/gui/windowrecord.py:210 +#: pytrainer/gui/windowprofile.py:65 +msgid "Sport" +msgstr "Desporto" + +#: glade/importdata.glade:1063 +msgid "Average Heartrate (bpm)" +msgstr "Ritmo cardíaco médio (ppm)" + +#: glade/importdata.glade:1076 +msgid "Max Heartrate (bpm)" +msgstr "Ritmo cardíaco máx (ppm)" + +#: glade/importdata.glade:1089 +#: pytrainer/recordgraph.py:138 +msgid "Pace (min/km)" +msgstr "Ritmo (min/km)" + +#: glade/importdata.glade:1102 +msgid "Max Pace (min/km)" +msgstr "Ritmo Máximo (min/km)" + +#: glade/importdata.glade:1115 +#: glade/newrecord.glade:1094 +#: pytrainer/gui/windowmain.py:167 +msgid "Comments" +msgstr "Comentários" + +#: glade/importdata.glade:1309 +msgid "Force sport to:" +msgstr "Forçar desporto a:" + +#: glade/importdata.glade:1323 +#: glade/newrecord.glade:404 +msgid " " +msgstr " " + +#: glade/importdata.glade:1394 +msgid "<b>Map Columns</b>" +msgstr "<b>Mapa de Colunas</b>" + +#: glade/importdata.glade:1414 +msgid "" +"<small>This is an experiemental import for delimited files\n" +"1) Select a file\n" +"2) Click 'Read File' to analyse the file\n" +"3) Map columns in the file to pytrainer data elements (Date is required)\n" +"4) Click 'Import Data'</small>" +msgstr "" +"<small>Esta é uma importação experimental para ficheiros delimitados\n" +"1) Escolher um ficheiro\n" +"2) Clicar em 'Ler Ficheiro' para analisar o ficheiro\n" +"3) Mapear colunas do ficheiro para elementos de dados do pytrainer (são\n" +" necessários dados)\n" +"4) Clicar em 'Importar Dados'</small>" + +#: glade/importdata.glade:1427 +msgid "Import Data" +msgstr "Importar Dados" + +#: glade/importdata.glade:1467 +msgid "<b>Import from CSV</b>" +msgstr "<b>Importar a partir do CSV</b>" + +#: glade/newrecord.glade:7 +#: pytrainer/gui/windowmain.py:2160 +msgid "New Entry" +msgstr "Nova Entrada" + +#: glade/newrecord.glade:61 +msgid "Title:" +msgstr "Título:" + +#: glade/newrecord.glade:85 +msgid "GPX File:" +msgstr "Ficheiro GPX:" + +#: glade/newrecord.glade:129 +msgid "Calculate Values" +msgstr "Calcular Valores" + +#: glade/newrecord.glade:149 +msgid "Sport:" +msgstr "Desporto:" + +#: glade/newrecord.glade:184 +msgid "<b>Main</b>" +msgstr "<b>Principal</b>" + +#: glade/newrecord.glade:221 +msgid "Distance (Km):" +msgstr "Distância (Km):" + +#: glade/newrecord.glade:230 +#: glade/newrecord.glade:355 +#: glade/newrecord.glade:558 +#: glade/newrecord.glade:697 +#: glade/newrecord.glade:958 +#: glade/profile.glade:1548 +msgid "Calculate" +msgstr "Calcular" + +#: glade/newrecord.glade:248 +msgid "Duration:" +msgstr "Duração:" + +#: glade/newrecord.glade:279 +#: glade/newrecord.glade:306 +msgid ":" +msgstr ":" + +#: glade/newrecord.glade:376 +msgid "Date:" +msgstr "Data:" + +#: glade/newrecord.glade:445 +msgid "Start Time:" +msgstr "Hora de Início:" + +#: glade/newrecord.glade:461 +msgid "12:00:00" +msgstr "12:00:00" + +#: glade/newrecord.glade:486 +#: glade/profile.glade:1586 +msgid "<b>General</b>" +msgstr "<b>Geral</b>" + +#: glade/newrecord.glade:522 +msgid "Max (km/h):" +msgstr "Máx. (km/h):" + +#: glade/newrecord.glade:547 +msgid "Average (km/h)" +msgstr "Média (km/h)" + +#: glade/newrecord.glade:593 +msgid "<b>Speed</b>" +msgstr "<b>Velocidade</b>" + +#: glade/newrecord.glade:612 +msgid "Quick Entry" +msgstr "Entrada Rápida" + +#: glade/newrecord.glade:644 +msgid "Max (min/km):" +msgstr "Máx. (min/km):" + +#: glade/newrecord.glade:671 +msgid "Pace (min/km):" +msgstr "Ritmo (min/km):" + +#: glade/newrecord.glade:718 +msgid "<b>Pace</b>" +msgstr "<b>Ritmo</b>" + +#: glade/newrecord.glade:754 +msgid "Ascent:" +msgstr "Subida:" + +#: glade/newrecord.glade:780 +msgid "Descent:" +msgstr "Descida:" + +#: glade/newrecord.glade:813 +msgid "<b>Accumulated Altitude Change</b>" +msgstr "<b>Mudança de Altitude Acumulada</b>" + +#: glade/newrecord.glade:850 +msgid "Max (bpm):" +msgstr "Máx (ppm):" + +#: glade/newrecord.glade:862 +msgid "Heart rate:" +msgstr "Ritmo Cardíaco:" + +#: glade/newrecord.glade:902 +#: pytrainer/gui/windowmain.py:122 +#: pytrainer/gui/windowmain.py:165 +#: pytrainer/gui/windowmain.py:1500 +#: pytrainer/monthgraph.py:30 +#: pytrainer/totalgraph.py:28 +#: pytrainer/yeargraph.py:30 +#: pytrainer/weekgraph.py:32 +msgid "Calories" +msgstr "Calorias" + +#: glade/newrecord.glade:931 +msgid "<small><b>Note:</b> In order to calculate the calories you must set the sport MET (in Preferences->Sport) </small>" +msgstr "<small><b>Nota:</b> Para cálculo das calorias deve ser introduzido o MET do desporto (em Preferências->Desporto) </small>" + +#: glade/newrecord.glade:1005 +msgid "<b>Heart Rate</b>" +msgstr "<b>Ritmo Cardíaco</b>" + +#: glade/newrecord.glade:1027 +msgid "Advanced" +msgstr "Avançado" + +#: glade/newrecord.glade:1079 +msgid "<b>Comments</b>" +msgstr "<b>Comentários</b>" + +#: glade/newrecord.glade:1134 +msgid "<i>There are no active equipment items.</i>" +msgstr "<i>Não há items de equipamento activos.</i>" + +#: glade/newrecord.glade:1188 +msgid "<b>Associated Equipment</b>" +msgstr "<b>Equipamento Associado</b>" + +#: glade/newrecord.glade:1203 +#: glade/profile.glade:1446 +msgid "Equipment" +msgstr "Equipamento" + +#: glade/plugins.glade:53 +msgid "<b>Plugin Details</b>" +msgstr "<b>Detalhes do Plugin</b>" + +#: glade/plugins.glade:68 +msgid "description-entry " +msgstr "descrição" + +#: glade/plugins.glade:129 +msgid "nameEntry" +msgstr "nome" + +#: glade/profile.glade:7 +msgid "Preferences" +msgstr "Preferências" + +#: glade/profile.glade:37 +msgid "Metric system" +msgstr "Sistema métrico" + +#: glade/profile.glade:53 +msgid "U.S. customary units" +msgstr "Unidades habituais nos E.U.A." + +#: glade/profile.glade:74 +msgid "<b>System of Measurement</b>" +msgstr "<b>Sistema de Medidas</b>" + +#: glade/profile.glade:103 +#: glade/pytrainer.glade:2111 +msgid "Google" +msgstr "Google" + +#: glade/profile.glade:118 +#: glade/pytrainer.glade:2094 +msgid "Open Street Map" +msgstr "Open Street Map" + +#: glade/profile.glade:140 +msgid "<b>Default Map Viewer</b>" +msgstr "<b>Visualizador de Mapas Predefinido</b>" + +#: glade/profile.glade:176 +msgid "Database type:" +msgstr "Tipo de Base de Dados:" + +#: glade/profile.glade:187 +msgid "Database host:" +msgstr "Servidor de Base de Dados:" + +#: glade/profile.glade:213 +msgid "Database name:" +msgstr "Nome da Base de Dados:" + +#: glade/profile.glade:252 +msgid "Database user:" +msgstr "Utilizador da Base de Dados:" + +#: glade/profile.glade:267 +msgid "Database pass:" +msgstr "Password para a Base de Dados:" + +#: glade/profile.glade:318 +msgid "<b>Database</b>" +msgstr "<b>Base de Dados</b>" + +#: glade/profile.glade:340 +msgid "General" +msgstr "Geral" + +#: glade/profile.glade:369 +msgid "User name:" +msgstr "Nome de Utilizador:" + +#: glade/profile.glade:380 +msgid "Gender:" +msgstr "Sexo:" + +#: glade/profile.glade:407 +#: glade/pytrainer.glade:5728 +msgid "Height:" +msgstr "Altura" + +#: glade/profile.glade:447 +#: glade/pytrainer.glade:5715 +msgid "Date of birth:" +msgstr "Data de Nascimento:" + +#: glade/profile.glade:520 +msgid "Weight:" +msgstr "Peso:" + +#: glade/profile.glade:542 +#: glade/pytrainer.glade:5790 +msgid "<b>Athlete Details</b>" +msgstr "<b>Detalhes do Atleta</b>" + +#: glade/profile.glade:557 +#: glade/pytrainer.glade:5893 +msgid "Athlete" +msgstr "Atleta" + +#: glade/profile.glade:672 +msgid "<b>Sport List</b>" +msgstr "<b>Lista de Desportos</b>" + +#: glade/profile.glade:684 +msgid "llist" +msgstr "llista" + +#: glade/profile.glade:739 +msgid "M.E.T.:" +msgstr "M.E.T.:" + +#: glade/profile.glade:766 +msgid "Extra Weight:" +msgstr "Peso Extra:" + +#: glade/profile.glade:802 +#: glade/profile.glade:1218 +msgid "More information on determining yor M.E.T sport coefficient on Wikipedia" +msgstr "Mais informações para determinar o seu coeficiente desportivo M.E.T. na Wikipédia" + +#: glade/profile.glade:822 +msgid "Maximum Pace:" +msgstr "Ritmo Máximo:" + +#: glade/profile.glade:950 +msgid "<b>Add New Sport</b>" +msgstr "<b>Acrescentar Novo Desporto</b>" + +#: glade/profile.glade:965 +msgid "new" +msgstr "novo" + +#: glade/profile.glade:996 +msgid "" +"Deleting a sport removes associated records.\n" +"Continue?" +msgstr "" +"Apagar um desporto remove registos associados.\n" +"Continuar?" + +#: glade/profile.glade:1078 +msgid "<b>Delete Sport</b>" +msgstr "<b>Apagar Desporto</b>" + +#: glade/profile.glade:1110 +msgid "delete" +msgstr "apagar" + +#: glade/profile.glade:1143 +msgid "Name" +msgstr "Nome" + +#: glade/profile.glade:1166 +msgid "M.E.T." +msgstr "M.E.T." + +#: glade/profile.glade:1193 +#: pytrainer/gui/windowprofile.py:65 +msgid "Extra Weight" +msgstr "Peso Extra" + +#: glade/profile.glade:1238 +#: pytrainer/gui/windowprofile.py:65 +msgid "Maximum Pace" +msgstr "Ritmo Máximo" + +#: glade/profile.glade:1265 +#: pytrainer/gui/windowmain.py:188 +#: pytrainer/gui/windowprofile.py:65 +msgid "Color" +msgstr "Cor" + +#: glade/profile.glade:1375 +msgid "<b>Edit Sport</b>" +msgstr "<b>Editar Desporto</b>" + +#: glade/profile.glade:1386 +msgid "<b>lalaal</b>" +msgstr "<b>lalaal</b>" + +#: glade/profile.glade:1408 +msgid "edit" +msgstr "editar" + +#: glade/profile.glade:1424 +msgid "Sports" +msgstr "Desportos" + +#: glade/profile.glade:1479 +msgid "Maximum heart rate:" +msgstr "Ritmo cardíaco máximo:" + +#: glade/profile.glade:1490 +msgid "Resting heart rate:" +msgstr "Ritmo cardíaco em repouso:" + +#: glade/profile.glade:1535 +msgid "<small><b>Note:</b> Maximum heart rate is calculated by subtracting the number 220 minus your age. </small>" +msgstr "<small><b>Nota:</b> O ritmo cardíaco máximo é calculado pela subtracção de 220 à sua idade. </small>" + +#: glade/profile.glade:1622 +msgid "<small><b>NOTE:</b> in order to use the Karvonen method you must cover the Resting hr field.</small>" +msgstr "<small><b>NOTA:</b> para poder usar o método de Karvonen tem de preencher o campo 'Ritmo cardíaco em repouso'.</small>" + +#: glade/profile.glade:1635 +msgid "Percentages based method" +msgstr "Método baseado em percentagens" + +#: glade/profile.glade:1651 +#: pytrainer/gui/windowmain.py:790 +msgid "Karvonen method" +msgstr "Método de Karvonen" + +#: glade/profile.glade:1672 +msgid "Select how to calculate your heart rate zones." +msgstr "Seleccione o método de cálculo do seu ritmo cardíaco" + +#: glade/profile.glade:1711 +msgid "<b>Heart Rate Zones</b>" +msgstr "<b>Zonas de Ritmo Cardíaco</b>" + +#: glade/profile.glade:1733 +#: pytrainer/recordgraph.py:140 +#: pytrainer/lib/activity.py:443 +#: pytrainer/lib/activity.py:445 +#: pytrainer/lib/activity.py:455 +#: pytrainer/lib/activity.py:525 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/daygraph.py:56 +msgid "Heart Rate" +msgstr "Ritmo Cardíaco" + +#: glade/profile.glade:1759 +msgid "Log Level" +msgstr "Nível de Registo" + +#: glade/profile.glade:1767 +msgid "" +"Error\n" +"Warning\n" +"Info\n" +"Debug" +msgstr "" +"Erro\n" +"Aviso\n" +"Informativo\n" +"Debug" + +#: glade/profile.glade:1785 +msgid "<small>What level of detail is written to the log?</small>" +msgstr "<small>Que nível de informação quer escrever no registo?</small>" + +#: glade/profile.glade:1799 +msgid "Validate" +msgstr "Validar" + +#: glade/profile.glade:1829 +msgid "<small>Do the plugins attempt to validate the input file before processing?</small>" +msgstr "<small>Os plugins tentam validar os ficheiros de input antes do processamento?</small>" + +#: glade/profile.glade:1845 +msgid "Check" +msgstr "Verificar" + +#: glade/profile.glade:1874 +msgid "<small>Was a configuration and database check requested?</small>" +msgstr "<small>Foi pedida uma verificação da configuração e base de dados?</small>" + +#: glade/profile.glade:1890 +msgid "New Graph" +msgstr "Novo Gráfico" + +#: glade/profile.glade:1919 +msgid "<small>Want to use experimental new approach to graphing?</small>" +msgstr "<small>Quer usar a nova abordagem experimental para os gráficos?</small>" + +#: glade/profile.glade:1962 +msgid "<small>This screen shows the state of command line configurable options for pytrainer. These options can be changed here which will affect the current instance of pytrainer, they will not be remembered next time though</small>" +msgstr "<small>Este écran mostra o estado das opções configuráveis pela linha de comando para o pytrainer. Estas opções podem ser alteradas aqui, de modo a afectarem a execução actual do pytrainer. No entanto, estas alterações não serão guardadas para a próxima execução do programa</small>" + +#: glade/profile.glade:1983 +msgid "Startup Parameters" +msgstr "Parâmetros de Arranque" + +#: glade/pytrainer.glade:24 +msgid "_File" +msgstr "_Ficheiro" + +#: glade/pytrainer.glade:39 +msgid "_Import" +msgstr "_Importar" + +#: glade/pytrainer.glade:60 +msgid "_Export as Text Separated by Commas" +msgstr "_Exportar como Texto Separado por Vírgulas (CSV)" + +#: glade/pytrainer.glade:94 +msgid "_Edit" +msgstr "_Editar" + +#: glade/pytrainer.glade:114 +msgid "_View" +msgstr "_Ver" + +#: glade/pytrainer.glade:121 +msgid " _Classic View" +msgstr "Vista _Clássica" + +#: glade/pytrainer.glade:131 +msgid " _List View" +msgstr "Vista em _Lista" + +#: glade/pytrainer.glade:141 +msgid " _Waypoints Editor" +msgstr "Editor de _Pontos de Interesse" + +#: glade/pytrainer.glade:155 +msgid "Tools" +msgstr "Ferramentas" + +#: glade/pytrainer.glade:172 +msgid "GPS Device Plugins" +msgstr "Plugins para Dispositivos GPS" + +#: glade/pytrainer.glade:185 +msgid "_Help" +msgstr "_Ajuda" + +#: glade/pytrainer.glade:290 +#: glade/pytrainer.glade:6324 +msgid "All Sports" +msgstr "Todos os Desportos" + +#: glade/pytrainer.glade:396 +msgid "<b>Sport:</b>" +msgstr "<b>Desporto:</b>" + +#: glade/pytrainer.glade:408 +#: glade/pytrainer.glade:3144 +#: glade/pytrainer.glade:3786 +#: glade/pytrainer.glade:4440 +#: glade/pytrainer.glade:5170 +msgid "<b>Duration:</b>" +msgstr "<b>Duração:</b>" + +#: glade/pytrainer.glade:423 +#: glade/pytrainer.glade:3234 +#: glade/pytrainer.glade:3865 +#: glade/pytrainer.glade:4519 +#: glade/pytrainer.glade:5272 +msgid "<b>Speed:</b>" +msgstr "<b>Velocidade:</b>" + +#: glade/pytrainer.glade:437 +#: glade/pytrainer.glade:3402 +#: glade/pytrainer.glade:4060 +#: glade/pytrainer.glade:4714 +#: glade/pytrainer.glade:5367 +msgid "<b>Pace:</b>" +msgstr "<b>Ritmo:</b>" + +#: glade/pytrainer.glade:451 +msgid "<b>Ascent:</b>" +msgstr "<b>Subida:</b>" + +#: glade/pytrainer.glade:465 +msgid "<b>Calories:</b>" +msgstr "<b>Calorias:</b>" + +#: glade/pytrainer.glade:479 +msgid "<b>Comments:</b>" +msgstr "<b>Comentários:</b>" + +#: glade/pytrainer.glade:614 +msgid "<b>Date:</b>" +msgstr "<b>Data:</b>" + +#: glade/pytrainer.glade:628 +#: glade/pytrainer.glade:3130 +#: glade/pytrainer.glade:3772 +#: glade/pytrainer.glade:4426 +#: glade/pytrainer.glade:5156 +msgid "<b>Distance:</b>" +msgstr "<b>Distância:</b>" + +#: glade/pytrainer.glade:645 +msgid "<b>Max Speed</b>" +msgstr "<b>Vel. Máxima</b>" + +#: glade/pytrainer.glade:661 +#: glade/pytrainer.glade:3432 +#: glade/pytrainer.glade:4074 +#: glade/pytrainer.glade:4728 +#: glade/pytrainer.glade:5382 +msgid "<b>Max Pace:</b>" +msgstr "<b>Ritmo Máx:</b>" + +#: glade/pytrainer.glade:677 +msgid "<b>Descent:</b>" +msgstr "<b>Descida:</b>" + +#: glade/pytrainer.glade:769 +#: glade/pytrainer.glade:3342 +#: glade/pytrainer.glade:3940 +#: glade/pytrainer.glade:4594 +#: glade/pytrainer.glade:5082 +msgid "Cal" +msgstr "Cal" + +#: glade/pytrainer.glade:809 +#: glade/pytrainer.glade:3104 +#: glade/pytrainer.glade:3746 +#: glade/pytrainer.glade:4400 +#: glade/pytrainer.glade:5259 +#: pytrainer/gui/windowmain.py:109 +msgid "Km" +msgstr "Km" + +#: glade/pytrainer.glade:824 +#: glade/pytrainer.glade:3264 +#: glade/pytrainer.glade:3297 +#: glade/pytrainer.glade:3895 +#: glade/pytrainer.glade:3988 +#: glade/pytrainer.glade:4549 +#: glade/pytrainer.glade:4642 +#: glade/pytrainer.glade:5302 +#: glade/pytrainer.glade:5318 +#: pytrainer/gui/windowmain.py:916 +#: pytrainer/gui/windowmain.py:917 +#: pytrainer/gui/windowmain.py:1045 +#: pytrainer/gui/windowmain.py:1046 +#: pytrainer/gui/windowmain.py:1127 +#: pytrainer/gui/windowmain.py:1128 +#: pytrainer/lib/activity.py:203 +msgid "km/h" +msgstr "km/h" + +#: glade/pytrainer.glade:839 +msgid "<b>Equipment:</b>" +msgstr "<b>Equipamento:</b>" + +#: glade/pytrainer.glade:935 +msgid "<b>Active / Rest:</b>" +msgstr "<b>Activo / Em Repouso:</b>" + +#: glade/pytrainer.glade:962 +msgid "h:m:s" +msgstr "h:m:s" + +#: glade/pytrainer.glade:1007 +msgid "<b>Laps</b>" +msgstr "<b>Voltas</b>" + +#: glade/pytrainer.glade:1078 +msgid " <b>Title:</b>" +msgstr "<b>Título:</b>" + +#: glade/pytrainer.glade:1128 +#: pytrainer/gui/windowmain.py:1722 +msgid "Show graph display options" +msgstr "Mostrar opções de gráficos" + +#: glade/pytrainer.glade:1146 +msgid "" +"Profile\n" +"Speed\n" +"Pace\n" +"Heart Rate\n" +"Cadence\n" +"Percentage\n" +"Zone" +msgstr "" +"Perfil\n" +"Velocidade\n" +"Ritmo\n" +"Ritmo Cardíaco\n" +"Cadência\n" +"Percentagem\n" +"Zona" + +#: glade/pytrainer.glade:1163 +#: glade/pytrainer.glade:4311 +#: glade/pytrainer.glade:4965 +#: glade/pytrainer.glade:5618 +#: glade/pytrainer.glade:5984 +msgid "Versus" +msgstr "vs." + +#: glade/pytrainer.glade:1174 +msgid "" +"None\n" +"Profile\n" +"Speed\n" +"Pace\n" +"Heart Rate\n" +"Cadence\n" +msgstr "" +"Nenhum\n" +"Perfil\n" +"Velocidade\n" +"Ritmo\n" +"Ritmo Cardíaco\n" +"Cadência\n" + +#: glade/pytrainer.glade:1320 +msgid "<small>Graph Display Options</small>" +msgstr "<small>Opções de Visualização de Gráficos</small>" + +#: glade/pytrainer.glade:1345 +#: glade/pytrainer.glade:1560 +#: glade/pytrainer.glade:1780 +msgid "<small>Limits</small>" +msgstr "<small>Limites</small>" + +#: glade/pytrainer.glade:1357 +#: glade/pytrainer.glade:1572 +#: glade/pytrainer.glade:1826 +msgid "<small>Min</small>" +msgstr "<small>Mín.</small>" + +#: glade/pytrainer.glade:1369 +#: glade/pytrainer.glade:1584 +#: glade/pytrainer.glade:1838 +msgid "<small>Max</small>" +msgstr "<small>Máx.</small>" + +#: glade/pytrainer.glade:1418 +#: glade/pytrainer.glade:1640 +msgid "<small>Color</small>" +msgstr "<small>Cor</small>" + +#: glade/pytrainer.glade:1449 +#: glade/pytrainer.glade:1653 +msgid "<small>Weight</small>" +msgstr "<small>Peso</small>" + +#: glade/pytrainer.glade:1479 +msgid "Y1" +msgstr "Y1" + +#: glade/pytrainer.glade:1490 +#: glade/pytrainer.glade:1701 +msgid "<small>Smoothing</small>" +msgstr "<small>Suavização</small>" + +#: glade/pytrainer.glade:1630 +msgid "Y2" +msgstr "Y2" + +#: glade/pytrainer.glade:1770 +msgid "X" +msgstr "X" + +#: glade/pytrainer.glade:1851 +msgid "<small>Distance</small>" +msgstr "<small>Distância</small>" + +#: glade/pytrainer.glade:1880 +msgid "<small>Time</small>" +msgstr "<small>Tempo</small>" + +#: glade/pytrainer.glade:1941 +msgid "Show Laps" +msgstr "Mostrar Voltas" + +#: glade/pytrainer.glade:1980 +msgid "Reset Graph" +msgstr "Reset do Gráfico" + +#: glade/pytrainer.glade:2042 +msgid "Redraw Map" +msgstr "Redesenhar Mapa" + +#: glade/pytrainer.glade:2058 +msgid "<small>Show:</small>" +msgstr "<small>Mostrar:</small>" + +#: glade/pytrainer.glade:2069 +msgid "" +"None\n" +"Speed\n" +"Heart rate\n" +"Cadence" +msgstr "" +"Nenhum\n" +"Velocidade\n" +"Ritmo Cardíaco\n" +"Cadência" + +#: glade/pytrainer.glade:2084 +msgid "<small>Display map using:</small>" +msgstr "<small>Mostrar mapa usando:</small>" + +#: glade/pytrainer.glade:2185 +msgid "<b>Beats:</b>" +msgstr "<b>Pulsações:</b>" + +#: glade/pytrainer.glade:2197 +#: glade/pytrainer.glade:3312 +#: glade/pytrainer.glade:3910 +#: glade/pytrainer.glade:4564 +#: glade/pytrainer.glade:5052 +msgid "<b>Calories: </b>" +msgstr "<b>Calorias: </b>" + +#: glade/pytrainer.glade:2211 +#: glade/pytrainer.glade:3464 +#: glade/pytrainer.glade:4138 +#: glade/pytrainer.glade:4792 +#: glade/pytrainer.glade:5430 +msgid "<b>Max Beats:</b>" +msgstr "<b>Máx. de Pulsações:</b>" + +#: glade/pytrainer.glade:2225 +msgid "<b>HR Zones Method:</b>" +msgstr "<b>Método das Zonas HR:</b>" + +#: glade/pytrainer.glade:2240 +msgid "<b>HR Zone5:</b>" +msgstr "<b>Zona5 HR:</b>" + +#: glade/pytrainer.glade:2255 +#: glade/pytrainer.glade:2454 +msgid " bpm" +msgstr " ppm" + +#: glade/pytrainer.glade:2271 +msgid " Cal" +msgstr " Cal" + +#: glade/pytrainer.glade:2329 +msgid "<b>HR Zone4:</b>" +msgstr "<b>Zona4 HR:</b>" + +#: glade/pytrainer.glade:2345 +msgid "<b>HR Zone3:</b>" +msgstr "<b>Zona3 HR:</b>" + +#: glade/pytrainer.glade:2361 +msgid "<b>HR Zone2:</b>" +msgstr "<b>Zona2 HR:</b>" + +#: glade/pytrainer.glade:2377 +msgid "<b>HR Zone1:</b>" +msgstr "<b>Zona1 HR:</b>" + +#: glade/pytrainer.glade:2479 +#: glade/pytrainer.glade:2491 +#: glade/pytrainer.glade:2506 +#: glade/pytrainer.glade:2521 +#: glade/pytrainer.glade:2536 +#: glade/pytrainer.glade:3387 +#: glade/pytrainer.glade:3481 +#: glade/pytrainer.glade:4045 +#: glade/pytrainer.glade:4123 +#: glade/pytrainer.glade:4699 +#: glade/pytrainer.glade:4777 +#: glade/pytrainer.glade:5127 +#: glade/pytrainer.glade:5334 +#: pytrainer/lib/activity.py:445 +#: pytrainer/lib/activity.py:525 +msgid "bpm" +msgstr "ppm" + +#: glade/pytrainer.glade:2581 +msgid " <b>Heart Rate:</b>" +msgstr " <b>Ritmo Cardíaco:</b>" + +#: glade/pytrainer.glade:2769 +msgid "<b>Projected times</b>" +msgstr "<b>Tempos projectados</b>" + +#: glade/pytrainer.glade:2799 +msgid "<i>Rank relative to same-sport activities for distances ± x%:</i>" +msgstr "<i>Classificação relativa para actividades do mesmo desporto para distâncias ± x%:</i>" + +#: glade/pytrainer.glade:2832 +msgid "<b>Range:</b>" +msgstr "<b>Alcance:</b>" + +#: glade/pytrainer.glade:2845 +msgid "<b>Rank:</b>" +msgstr "<b>Classificação:</b>" + +#: glade/pytrainer.glade:2858 +msgid "<b>Speed: </b>" +msgstr "<b>Velocidade: </b>" + +#: glade/pytrainer.glade:2871 +msgid "<b>Avg speed:</b>" +msgstr "<b>Vel. média:</b>" + +#: glade/pytrainer.glade:2884 +msgid "<b>Standard deviation:</b>" +msgstr "<b>Desvio padrão:</b>" + +#: glade/pytrainer.glade:2898 +msgid "<b>Deviation:</b>" +msgstr "<b>Desvio:</b>" + +#: glade/pytrainer.glade:3017 +msgid "<b>Ranking</b>" +msgstr "<b>Classificação</b>" + +#: glade/pytrainer.glade:3043 +msgid "<b>Analytics:</b>" +msgstr "<b>Analítica:</b>" + +#: glade/pytrainer.glade:3071 +msgid "Record" +msgstr "Registo" + +#: glade/pytrainer.glade:3280 +#: glade/pytrainer.glade:3956 +#: glade/pytrainer.glade:4610 +#: glade/pytrainer.glade:5350 +msgid "<b>Max Speed:</b>" +msgstr "<b>Vel. Máxima:</b>" + +#: glade/pytrainer.glade:3357 +#: glade/pytrainer.glade:4015 +#: glade/pytrainer.glade:4669 +#: glade/pytrainer.glade:5097 +msgid "<b>Beats avg:</b>" +msgstr "<b>Média pulsação:</b>" + +#: glade/pytrainer.glade:3417 +#: glade/pytrainer.glade:3449 +#: glade/pytrainer.glade:4091 +#: glade/pytrainer.glade:4107 +#: glade/pytrainer.glade:4745 +#: glade/pytrainer.glade:4761 +#: glade/pytrainer.glade:5399 +#: glade/pytrainer.glade:5415 +#: pytrainer/gui/windowmain.py:918 +#: pytrainer/gui/windowmain.py:919 +#: pytrainer/gui/windowmain.py:1047 +#: pytrainer/gui/windowmain.py:1048 +#: pytrainer/gui/windowmain.py:1129 +#: pytrainer/gui/windowmain.py:1130 +#: pytrainer/lib/activity.py:204 +msgid "min/km" +msgstr "min/km" + +#: glade/pytrainer.glade:3556 +#: glade/pytrainer.glade:4199 +#: glade/pytrainer.glade:4853 +#: glade/pytrainer.glade:5506 +msgid "<b>Total Asc/Desc:</b>" +msgstr "<b>Total Sub/Desc:</b>" + +#: glade/pytrainer.glade:3583 +#: glade/pytrainer.glade:4226 +#: glade/pytrainer.glade:4880 +#: glade/pytrainer.glade:5533 +#: pytrainer/gui/windowmain.py:2066 +#: pytrainer/gui/windowmain.py:2068 +#: pytrainer/lib/activity.py:205 +msgid "m" +msgstr "m" + +#: glade/pytrainer.glade:3602 +msgid " <b>Date:</b>" +msgstr " <b>Data:</b>" + +#: glade/pytrainer.glade:3645 +msgid "Value" +msgstr "Valor" + +#: glade/pytrainer.glade:3656 +msgid "" +"Stage Profile\n" +"Stage Velocity\n" +"Stage Profile/velocity" +msgstr "" +"Definir Perfil\n" +"Definir Velocidade\n" +"Definir Perfil/velocidade" + +#: glade/pytrainer.glade:3712 +msgid "Day" +msgstr "Dia" + +#: glade/pytrainer.glade:4245 +msgid " <b>Week:</b>" +msgstr " <b>Semana:</b>" + +#: glade/pytrainer.glade:4296 +#: glade/pytrainer.glade:4950 +#: glade/pytrainer.glade:5603 +#: glade/pytrainer.glade:5969 +msgid "" +"Distance\n" +"Time\n" +"Average Heart Rate\n" +"Average Speed\n" +"Calories" +msgstr "" +"Distância\n" +"Tempo\n" +"Ritmo Cardíaco Médio\n" +"Velocidade Média\n" +"Calorias" + +#: glade/pytrainer.glade:4322 +#: glade/pytrainer.glade:4976 +#: glade/pytrainer.glade:5629 +#: glade/pytrainer.glade:5995 +msgid "" +"None\n" +"Distance\n" +"Time\n" +"Average Heart Rate\n" +"Average Speed\n" +"Calories" +msgstr "" +"Nenhum\n" +"Distância\n" +"Tempo\n" +"Ritmo Cardíaco Médio\n" +"Velocidade Média\n" +"Calorias" + +#: glade/pytrainer.glade:4366 +msgid "Week" +msgstr "Semana" + +#: glade/pytrainer.glade:4899 +msgid " <b>Month:</b>" +msgstr " <b>Mês:</b>" + +#: glade/pytrainer.glade:5019 +msgid "Month" +msgstr "Mês" + +#: glade/pytrainer.glade:5552 +msgid " <b>Year:</b>" +msgstr " <b>Ano:</b>" + +#: glade/pytrainer.glade:5672 +msgid "Year" +msgstr "Ano" + +#: glade/pytrainer.glade:5826 +msgid "<b>Graph</b>" +msgstr "<b>Gráfico</b>" + +#: glade/pytrainer.glade:5867 +msgid "<b>History</b>" +msgstr "<b>Histórico</b>" + +#: glade/pytrainer.glade:5937 +msgid "<b>Sports</b>" +msgstr "<b>Desportos</b>" + +#: glade/pytrainer.glade:6018 +msgid "Total distance:" +msgstr "Distância total:" + +#: glade/pytrainer.glade:6032 +msgid "Total duration:" +msgstr "Duração total:" + +#: glade/pytrainer.glade:6081 +msgid "Start date:" +msgstr "Data de início:" + +#: glade/pytrainer.glade:6096 +msgid "End date:" +msgstr "Data de fim:" + +#: glade/pytrainer.glade:6182 +msgid "<b>All</b>" +msgstr "<b>Todos</b>" + +#: glade/pytrainer.glade:6208 +msgid "Totals" +msgstr "Totais" + +#: glade/pytrainer.glade:6246 +msgid "<b>Title:</b>" +msgstr "<b>Título:</b>" + +#: glade/pytrainer.glade:6269 +msgid "Search" +msgstr "Pesquisar" + +#: glade/pytrainer.glade:6293 +#: pytrainer/lib/listview.py:3 +#: pytrainer/lib/listview.py:10 +msgid "All Distances" +msgstr "Todas as Distâncias" + +#: glade/pytrainer.glade:6302 +#: pytrainer/lib/listview.py:35 +msgid "All Durations" +msgstr "Todas as durações" + +#: glade/pytrainer.glade:6312 +msgid "" +"All time\n" +"Last 4 weeks\n" +"Last 6 months\n" +"Last 12 months" +msgstr "" +"Todos os registos\n" +"Últimas 4 semanas\n" +"Últimos 6 meses\n" +"Últimos 12 meses" + +#: glade/pytrainer.glade:6349 +msgid "Columns" +msgstr "Colunas" + +#: glade/pytrainer.glade:6477 +msgid "Type:" +msgstr "Tipo:" + +#: glade/pytrainer.glade:6490 +msgid "" +"Font\n" +"Restaurant\n" +"Scenic Area\n" +"Summit" +msgstr "" +"Fonte\n" +"Restaurante\n" +"Paisagem\n" +"Monte" + +#: glade/pytrainer.glade:6506 +msgid "Latitude: " +msgstr "Latitude: " + +#: glade/pytrainer.glade:6520 +msgid " Name:" +msgstr " Nome:" + +#: glade/pytrainer.glade:6532 +msgid "Longitude:" +msgstr "Longitude:" + +#: glade/pytrainer.glade:6624 +msgid "<b> Waypoint: </b>" +msgstr "<b> Ponto de Interesse: </b>" + +#: glade/pytrainer.glade:6808 +msgid "Edit Record" +msgstr "Editar Registo" + +#: glade/pytrainer.glade:6822 +msgid "Show graph in classic view" +msgstr "Mostrar gráfico na vista clássica" + +#: glade/pytrainer.glade:6836 +msgid "Merge tracks" +msgstr "Juntar percursos" + +#: glade/selecttrackdialog.glade:7 +msgid "Select track record" +msgstr "Escolher registo de percurso" + +#: glade/warning.glade:7 +#: glade/warning.glade:38 +msgid "Warning" +msgstr "Aviso" + +#: pytrainer/gui/windowmain.py:107 +msgid "Miles" +msgstr "Milhas" + +#: pytrainer/gui/windowmain.py:110 +#: pytrainer/gui/windowmain.py:114 +#: pytrainer/gui/windowmain.py:127 +#: pytrainer/gui/windowmain.py:130 +#: pytrainer/gui/windowmain.py:139 +#: pytrainer/gui/windowmain.py:173 +#: pytrainer/gui/windowmain.py:182 +msgid "id" +msgstr "id" + +#: pytrainer/gui/windowmain.py:110 +msgid "Start" +msgstr "Início" + +#: pytrainer/gui/windowmain.py:117 +#: pytrainer/gui/windowmain.py:157 +#: pytrainer/gui/windowmain.py:175 +#: pytrainer/gui/windowmain.py:185 +#: pytrainer/gui/windowmain.py:569 +#: pytrainer/gui/windowmain.py:1495 +#: pytrainer/gui/windowimportdata.py:353 +#: pytrainer/gui/windowrecord.py:210 +#: pytrainer/lib/activity.py:323 +#: pytrainer/lib/activity.py:334 +#: pytrainer/lib/activity.py:406 +#: pytrainer/lib/activity.py:417 +#: pytrainer/lib/activity.py:426 +#: pytrainer/lib/activity.py:435 +#: pytrainer/lib/activity.py:444 +#: pytrainer/lib/activity.py:454 +#: pytrainer/lib/activity.py:463 +#: pytrainer/lib/activity.py:524 +#: pytrainer/extensions/googlemaps.py:129 +#: pytrainer/extensions/osm.py:105 +msgid "Distance" +msgstr "Distância" + +#: pytrainer/gui/windowmain.py:119 +#: pytrainer/gui/windowmain.py:158 +#: pytrainer/gui/windowmain.py:176 +#: pytrainer/gui/windowmain.py:186 +#: pytrainer/gui/windowmain.py:570 +#: pytrainer/gui/windowmain.py:1497 +#: pytrainer/extensions/googlemaps.py:129 +#: pytrainer/extensions/osm.py:104 +msgid "Time" +msgstr "Tempo" + +#: pytrainer/gui/windowmain.py:120 +msgid "⌀ HR" +msgstr "⌀ Bat. Card." + +#: pytrainer/gui/windowmain.py:121 +msgid "⌀ Speed" +msgstr "⌀ Vel." + +#: pytrainer/gui/windowmain.py:127 +msgid "Waypoint" +msgstr "Ponto de interesse" + +#: pytrainer/gui/windowmain.py:132 +msgid "Weight" +msgstr "Peso" + +#: pytrainer/gui/windowmain.py:133 +msgid "Body Fat %" +msgstr "% Gordura Corporal" + +#: pytrainer/gui/windowmain.py:134 +msgid "Resting HR" +msgstr "Bat. Card. em Repouso" + +#: pytrainer/gui/windowmain.py:135 +#: pytrainer/gui/windowmain.py:147 +#: pytrainer/gui/windowmain.py:164 +msgid "Max HR" +msgstr "Máx Bat. Card." + +#: pytrainer/gui/windowmain.py:141 +msgid "Records" +msgstr "Registos" + +#: pytrainer/gui/windowmain.py:142 +msgid "Total duration" +msgstr "Duração total" + +#: pytrainer/gui/windowmain.py:143 +msgid "Total distance" +msgstr "Distância total" + +#: pytrainer/gui/windowmain.py:144 +#: pytrainer/gui/windowmain.py:159 +msgid "Avg speed" +msgstr "Vel. Média" + +#: pytrainer/gui/windowmain.py:145 +#: pytrainer/gui/windowmain.py:160 +msgid "Max speed" +msgstr "Vel. Máx." + +#: pytrainer/gui/windowmain.py:146 +#: pytrainer/gui/windowmain.py:163 +msgid "Avg HR" +msgstr "Ritmo Card. Médio" + +#: pytrainer/gui/windowmain.py:148 +msgid "Max duration" +msgstr "Duração Máx." + +#: pytrainer/gui/windowmain.py:149 +msgid "Max distance" +msgstr "Distância Máx." + +#: pytrainer/gui/windowmain.py:155 +msgid "Lap" +msgstr "Volta" + +#: pytrainer/gui/windowmain.py:156 +msgid "Trigger" +msgstr "Gatilho" + +#: pytrainer/gui/windowmain.py:161 +msgid "Avg pace" +msgstr "Ritmo Méd." + +#: pytrainer/gui/windowmain.py:162 +msgid "Max pace" +msgstr "Ritmo Máx." + +#: pytrainer/gui/windowmain.py:166 +msgid "Intensity" +msgstr "Intensidade" + +#: pytrainer/gui/windowmain.py:174 +msgid "Race" +msgstr "Corrida" + +#: pytrainer/gui/windowmain.py:183 +msgid "Rank" +msgstr "Posição" + +#: pytrainer/gui/windowmain.py:187 +#: pytrainer/recordgraph.py:136 +#: pytrainer/lib/activity.py:335 +#: pytrainer/lib/activity.py:425 +#: pytrainer/lib/activity.py:427 +msgid "Speed" +msgstr "Velocidade" + +#: pytrainer/gui/windowmain.py:319 +msgid "d" +msgstr "d" + +#: pytrainer/gui/windowmain.py:559 +msgid "Show on X Axis" +msgstr "Mostrar no eixo X" + +#: pytrainer/gui/windowmain.py:560 +msgid "Show on Y1 Axis" +msgstr "Mostrar no eixo Y1" + +#: pytrainer/gui/windowmain.py:561 +msgid "Show on Y2 Axis" +msgstr "Mostrar no eixo Y2" + +#: pytrainer/gui/windowmain.py:562 +msgid "Axis Limits" +msgstr "Limites do Eixo" + +#: pytrainer/gui/windowmain.py:571 +msgid "Laps" +msgstr "Voltas" + +#: pytrainer/gui/windowmain.py:572 +msgid "Left Axis Grid" +msgstr "Grelha do Eixo Esquerdo" + +#: pytrainer/gui/windowmain.py:573 +msgid "Right Axis Grid" +msgstr "Grelha do Eixo Direito" + +#: pytrainer/gui/windowmain.py:574 +msgid "X Axis Grid" +msgstr "Grelha do Eixo X" + +#: pytrainer/gui/windowmain.py:635 +msgid "Reset Limits" +msgstr "Repor Limites" + +#: pytrainer/gui/windowmain.py:637 +msgid "Set Limits" +msgstr "Definir Limites" + +#: pytrainer/gui/windowmain.py:792 +msgid "Percentages method" +msgstr "Método das percentagens" + +#: pytrainer/gui/windowmain.py:805 +msgid "800 m" +msgstr "800 m" + +#: pytrainer/gui/windowmain.py:806 +msgid "1500 m" +msgstr "1500 m" + +#: pytrainer/gui/windowmain.py:807 +msgid "5K" +msgstr "5K" + +#: pytrainer/gui/windowmain.py:808 +msgid "7K" +msgstr "7K" + +#: pytrainer/gui/windowmain.py:809 +msgid "10K" +msgstr "10K" + +#: pytrainer/gui/windowmain.py:810 +msgid "Half marathon" +msgstr "Meia maratona" + +#: pytrainer/gui/windowmain.py:811 +msgid "Marathon" +msgstr "Maratona" + +#: pytrainer/gui/windowmain.py:812 +msgid "100K" +msgstr "100K" + +#: pytrainer/gui/windowmain.py:909 +#: pytrainer/gui/windowmain.py:1038 +#: pytrainer/gui/windowmain.py:1120 +#: pytrainer/lib/activity.py:197 +msgid "miles" +msgstr "milhas" + +#: pytrainer/gui/windowmain.py:910 +#: pytrainer/gui/windowmain.py:911 +#: pytrainer/gui/windowmain.py:1039 +#: pytrainer/gui/windowmain.py:1040 +#: pytrainer/gui/windowmain.py:1121 +#: pytrainer/gui/windowmain.py:1122 +#: pytrainer/lib/activity.py:198 +msgid "miles/h" +msgstr "milhas/h" + +#: pytrainer/gui/windowmain.py:912 +#: pytrainer/gui/windowmain.py:913 +#: pytrainer/gui/windowmain.py:1041 +#: pytrainer/gui/windowmain.py:1042 +#: pytrainer/gui/windowmain.py:1123 +#: pytrainer/gui/windowmain.py:1124 +#: pytrainer/lib/activity.py:199 +msgid "min/mile" +msgstr "min/milha" + +#: pytrainer/gui/windowmain.py:915 +#: pytrainer/gui/windowmain.py:1044 +#: pytrainer/gui/windowmain.py:1126 +#: pytrainer/lib/activity.py:202 +msgid "km" +msgstr "km" + +#: pytrainer/gui/windowmain.py:1498 +#: pytrainer/recordgraph.py:144 +msgid "Beats" +msgstr "Pulsações" + +#: pytrainer/gui/windowmain.py:1499 +msgid "Average" +msgstr "Média" + +#: pytrainer/gui/windowmain.py:1718 +msgid "Hide graph display options" +msgstr "Esconder opções de visualização de gráficos" + +#: pytrainer/gui/windowmain.py:2056 +msgid "lap" +msgstr "volta" + +#: pytrainer/gui/windowmain.py:2066 +#: pytrainer/gui/drawArea.py:165 +#: pytrainer/extensions/googlemaps.py:127 +#: pytrainer/extensions/osm.py:102 +msgid "h" +msgstr "h" + +#: pytrainer/gui/windowmain.py:2066 +#: pytrainer/gui/windowmain.py:2068 +msgid "s" +msgstr "s" + +#: pytrainer/gui/windowmain.py:2156 +#: pytrainer/record.py:67 +msgid "Edit Entry" +msgstr "Editar Registo" + +#: pytrainer/gui/windowmain.py:2167 +msgid "Delete Entry" +msgstr "Apagar Registo" + +#: pytrainer/gui/windowmain.py:2191 +msgid "Create Athlete Entry" +msgstr "Criar Registo de Atleta" + +#: pytrainer/gui/windowmain.py:2195 +msgid "Edit Athlete Entry" +msgstr "Editar Registo de Atleta" + +#: pytrainer/gui/windowmain.py:2208 +msgid "<b>Date</b>" +msgstr "<b>Data</b>" + +#: pytrainer/gui/windowmain.py:2224 +msgid "<b>Weight</b>" +msgstr "<b>Peso</b>" + +#: pytrainer/gui/windowmain.py:2232 +msgid "<b>Body Fat</b>" +msgstr "<b>Gordura Corporal</b>" + +#: pytrainer/gui/windowmain.py:2240 +msgid "<b>Resting Heart Rate</b>" +msgstr "<b>Ritmo Cardíaco em Repouso</b>" + +#: pytrainer/gui/windowmain.py:2248 +msgid "<b>Max Heart Rate</b>" +msgstr "<b>Ritmo Cardíaco Máximo</b>" + +#: pytrainer/gui/windowmain.py:2276 +#: pytrainer/main.py:451 +msgid "Delete this database entry?" +msgstr "Apagar este registo?" + +#: pytrainer/gui/windowmain.py:2278 +msgid "Are you sure?" +msgstr "De certeza?" + +#: pytrainer/gui/windowimportdata.py:117 +msgid "No file selected" +msgstr "Nenhum ficheiro seleccionado" + +#: pytrainer/gui/windowimportdata.py:162 +msgid "Configure" +msgstr "Configurar" + +#: pytrainer/gui/windowimportdata.py:163 +msgid "Run" +msgstr "Executar" + +#: pytrainer/gui/windowimportdata.py:171 +msgid "Disabled" +msgstr "Desactivado" + +#: pytrainer/gui/windowimportdata.py:173 +msgid "Enabled" +msgstr "Activado" + +#: pytrainer/gui/windowimportdata.py:253 +msgid "GPS device found" +msgstr "Encontrado dispositivo GPS" + +#: pytrainer/gui/windowimportdata.py:256 +msgid "GPS device <b>not</b> found" +msgstr "<b>Não foi</b> encontrado dispositivo GPS" + +#: pytrainer/gui/windowimportdata.py:263 +msgid "This tool was not found on the system" +msgstr "Esta ferramenta não foi encontrada no sistema" + +#: pytrainer/gui/windowimportdata.py:265 +msgid " Homepage" +msgstr " Homepage" + +#: pytrainer/gui/windowimportdata.py:318 +msgid "File" +msgstr "Ficheiro" + +#: pytrainer/gui/windowimportdata.py:318 +msgid "Type" +msgstr "Tipo" + +#: pytrainer/gui/windowimportdata.py:318 +msgid "Activities" +msgstr "Actividades" + +#: pytrainer/gui/windowimportdata.py:353 +#: pytrainer/gui/windowrecord.py:210 +msgid "Start Time" +msgstr "Hora de Início" + +#: pytrainer/gui/windowimportdata.py:353 +#: pytrainer/gui/windowrecord.py:210 +msgid "Duration" +msgstr "Duração" + +#: pytrainer/gui/windowimportdata.py:499 +msgid "Imported into database" +msgstr "Importado para a base de dados" + +#: pytrainer/gui/windowimportdata.py:543 +#: pytrainer/gui/windowextensions.py:81 +#: pytrainer/gui/windowplugins.py:80 +#, python-format +msgid "%s settings" +msgstr "configurações %s" + +#: pytrainer/gui/windowimportdata.py:558 +#: pytrainer/gui/windowextensions.py:70 +#: pytrainer/gui/windowplugins.py:71 +#: pytrainer/gui/windowplugins.py:95 +msgid "Disable" +msgstr "Desligar" + +#: pytrainer/gui/windowimportdata.py:559 +#: pytrainer/gui/windowextensions.py:72 +#: pytrainer/gui/windowplugins.py:69 +#: pytrainer/gui/windowplugins.py:96 +msgid "Enable" +msgstr "Ligar" + +#: pytrainer/gui/windowimportdata.py:566 +#: pytrainer/gui/windowplugins.py:103 +msgid "Ok" +msgstr "Ok" + +#: pytrainer/gui/windowimportdata.py:626 +msgid "Saving options" +msgstr "Opções de gravação" + +#: pytrainer/gui/windowimportdata.py:628 +msgid "Options saved" +msgstr "Opções guardadas" + +#: pytrainer/gui/windowimportdata.py:653 +msgid "Importing one activity" +msgstr "A importar uma actividade" + +#: pytrainer/gui/windowimportdata.py:654 +msgid "Imported one activity" +msgstr "Uma actividade importada" + +#: pytrainer/gui/windowimportdata.py:656 +#, python-format +msgid "Importing %d activities" +msgstr "A importar %d actividades" + +#: pytrainer/gui/windowimportdata.py:657 +#, python-format +msgid "Imported %d activities" +msgstr "%d actividades importadas" + +#: pytrainer/gui/windowimportdata.py:676 +msgid "Choose a file (or files) to import activities from" +msgstr "Escolha um ficheiro (ou ficheiros) para fonte de actividades" + +#: pytrainer/gui/windowimportdata.py:691 +#: pytrainer/gui/windowimportdata.py:692 +#, python-format +msgid "Found file of type: %s" +msgstr "Encontrado ficheiro do tipo: %s" + +#: pytrainer/gui/windowimportdata.py:716 +msgid "Found in database" +msgstr "Encontrado na base de dados" + +#: pytrainer/gui/windowimportdata.py:731 +#: pytrainer/gui/windowimportdata.py:732 +#, python-format +msgid "File %s is of unknown or unsupported file type" +msgstr "O ficheiro %s é de um tipo desconhecido ou não suportado" + +#: pytrainer/gui/windowimportdata.py:805 +#, python-format +msgid "Column %d" +msgstr "Coluna %d" + +#: pytrainer/gui/drawArea.py:166 +#: pytrainer/extensions/googlemaps.py:127 +#: pytrainer/extensions/osm.py:102 +msgid "min" +msgstr "min" + +#: pytrainer/gui/drawArea.py:410 +msgid "rest" +msgstr "repouso" + +#: pytrainer/gui/windowrecord.py:210 +msgid "GPX File" +msgstr "Ficheiro GPX" + +#: pytrainer/gui/windowprofile.py:47 +msgid "Male" +msgstr "Masculino" + +#: pytrainer/gui/windowprofile.py:48 +msgid "Female" +msgstr "Feminino" + +#: pytrainer/gui/windowprofile.py:65 +msgid "MET" +msgstr "MET" + +#: pytrainer/gui/windowprofile.py:314 +msgid "Sport Creation Error" +msgstr "Erro ao Criar Desporto" + +#: pytrainer/gui/equipment.py:101 +msgid "Usage" +msgstr "Utilização" + +#: pytrainer/gui/dialogselecttrack.py:40 +msgid "Track Name" +msgstr "Nome da Pista" + +#: pytrainer/gui/drawGraph.py:170 +msgid "Athlete Data" +msgstr "Dados de Atleta" + +#: pytrainer/gui/windowextensions.py:112 +#: pytrainer/gui/windowextensions.py:125 +msgid "OK" +msgstr "OK" + +#: pytrainer/recordgraph.py:134 +#: pytrainer/daygraph.py:52 +msgid "Height (m)" +msgstr "Altura (m)" + +#: pytrainer/recordgraph.py:134 +#: pytrainer/daygraph.py:52 +msgid "Stage Profile" +msgstr "Definir Perfil" + +#: pytrainer/recordgraph.py:136 +msgid "Speed (Km/h)" +msgstr "Velocidade (Km/h)" + +#: pytrainer/recordgraph.py:138 +#: pytrainer/lib/activity.py:324 +#: pytrainer/lib/activity.py:434 +#: pytrainer/lib/activity.py:436 +msgid "Pace" +msgstr "Ritmo" + +#: pytrainer/recordgraph.py:140 +#: pytrainer/heartrategraph.py:36 +#: pytrainer/daygraph.py:56 +msgid "Beats (bpm)" +msgstr "Pulsações (ppm)" + +#: pytrainer/recordgraph.py:142 +msgid "Cadence (rpm)" +msgstr "Cadência (rpm)" + +#: pytrainer/recordgraph.py:142 +#: pytrainer/lib/activity.py:462 +#: pytrainer/lib/activity.py:464 +msgid "Cadence" +msgstr "Cadência" + +#: pytrainer/recordgraph.py:144 +msgid "Beats (%)" +msgstr "Batidas (%)" + +#: pytrainer/recordgraph.py:146 +msgid "Zone" +msgstr "Zona" + +#: pytrainer/profile.py:114 +msgid "Moderate activity" +msgstr "Actividade moderada" + +#: pytrainer/profile.py:115 +msgid "Weight Control" +msgstr "Controlo de Peso" + +#: pytrainer/profile.py:116 +msgid "Aerobic" +msgstr "Aeróbica" + +#: pytrainer/profile.py:117 +msgid "Anaerobic" +msgstr "Anaeróbica" + +#: pytrainer/profile.py:118 +msgid "VO2 MAX" +msgstr "Máx VO2" + +#: pytrainer/lib/listview.py:32 +msgid "All Time" +msgstr "Desde sempre" + +#: pytrainer/lib/listview.py:32 +msgid "Last 4 Weeks" +msgstr "Últimas 4 Semanas" + +#: pytrainer/lib/listview.py:33 +msgid "Last 6 Months" +msgstr "Últimos 6 Meses" + +#: pytrainer/lib/listview.py:33 +msgid "Last 12 Months" +msgstr "Últimos 12 Meses" + +#: pytrainer/lib/listview.py:36 +msgid "<1 Hour" +msgstr "<1 Hora" + +#: pytrainer/lib/listview.py:37 +msgid "1-2 Hours" +msgstr "1-2 Horas" + +#: pytrainer/lib/listview.py:38 +msgid ">2 Hours" +msgstr ">2 Horas" + +#: pytrainer/lib/gpx.py:129 +msgid "No Name" +msgstr "Sem Nome" + +#: pytrainer/lib/gpx.py:137 +msgid "No Data" +msgstr "Sem Dados" + +#: pytrainer/lib/activity.py:200 +msgid "feet" +msgstr "pés" + +#: pytrainer/lib/activity.py:322 +msgid "Pace by Lap" +msgstr "Ritmo por Volta" + +#: pytrainer/lib/activity.py:328 +#: pytrainer/lib/activity.py:339 +#: pytrainer/lib/activity.py:411 +#: pytrainer/lib/activity.py:421 +#: pytrainer/lib/activity.py:430 +#: pytrainer/lib/activity.py:439 +#: pytrainer/lib/activity.py:448 +#: pytrainer/lib/activity.py:458 +#: pytrainer/lib/activity.py:467 +#: pytrainer/lib/activity.py:529 +msgid "Time (seconds)" +msgstr "Tempo (segundos)" + +#: pytrainer/lib/activity.py:333 +msgid "Speed by Lap" +msgstr "Velocidade por Volta" + +#: pytrainer/lib/activity.py:405 +#: pytrainer/lib/activity.py:407 +msgid "Elevation" +msgstr "Elevação" + +#: pytrainer/lib/activity.py:416 +#: pytrainer/lib/activity.py:418 +msgid "Corrected Elevation" +msgstr "Elevação Corrigida" + +#: pytrainer/lib/activity.py:453 +#, python-format +msgid "Heart Rate (% of max)" +msgstr "Ritmo Cardíaco (% do máx.)" + +#: pytrainer/lib/activity.py:455 +msgid "%" +msgstr "%" + +#: pytrainer/lib/activity.py:464 +msgid "rpm" +msgstr "rpm" + +#: pytrainer/lib/activity.py:523 +msgid "Heart Rate zone" +msgstr "Zona do Ritmo Cardíaco" + +#: pytrainer/monthgraph.py:26 +#: pytrainer/monthgraph.py:27 +#: pytrainer/monthgraph.py:28 +#: pytrainer/monthgraph.py:29 +#: pytrainer/monthgraph.py:30 +msgid "day" +msgstr "dia" + +#: pytrainer/monthgraph.py:26 +#: pytrainer/weekgraph.py:28 +msgid "Daily Distance" +msgstr "Distância Diária" + +#: pytrainer/monthgraph.py:27 +#: pytrainer/totalgraph.py:25 +#: pytrainer/yeargraph.py:27 +#: pytrainer/weekgraph.py:29 +msgid "Time (hours)" +msgstr "Tempo (horas)" + +#: pytrainer/monthgraph.py:27 +#: pytrainer/weekgraph.py:29 +msgid "Daily Time" +msgstr "Tempo Diário" + +#: pytrainer/monthgraph.py:28 +#: pytrainer/totalgraph.py:26 +#: pytrainer/yeargraph.py:28 +#: pytrainer/weekgraph.py:30 +msgid "Average Heart Rate (bpm)" +msgstr "Ritmo Cardíaco Médio (ppm)" + +#: pytrainer/monthgraph.py:28 +#: pytrainer/weekgraph.py:30 +msgid "Daily Average Heart Rate" +msgstr "Ritmo Cardíaco Médio Diário" + +#: pytrainer/monthgraph.py:29 +#: pytrainer/weekgraph.py:31 +msgid "Daily Average Speed" +msgstr "Velocidade Média Diária" + +#: pytrainer/monthgraph.py:30 +#: pytrainer/weekgraph.py:32 +msgid "Daily Calories" +msgstr "Calorias Diárias" + +#: pytrainer/totalgraph.py:24 +#: pytrainer/totalgraph.py:25 +#: pytrainer/totalgraph.py:26 +#: pytrainer/totalgraph.py:27 +#: pytrainer/totalgraph.py:28 +msgid "year" +msgstr "ano" + +#: pytrainer/totalgraph.py:24 +msgid "Annual Distance" +msgstr "Distância Anual" + +#: pytrainer/totalgraph.py:25 +msgid "Annual Time" +msgstr "Tempo anual" + +#: pytrainer/totalgraph.py:26 +msgid "Annual Average Heart Rate" +msgstr "Ritmo Cardíaco Médio Anual" + +#: pytrainer/totalgraph.py:27 +msgid "Annual Average Speed" +msgstr "Velocidade Média Anual" + +#: pytrainer/totalgraph.py:28 +msgid "Annual Calories" +msgstr "Calorias Anuais" + +#: pytrainer/athlete.py:52 +msgid "lb" +msgstr "lb" + +#: pytrainer/athlete.py:54 +msgid "kg" +msgstr "kg" + +#: pytrainer/record.py:56 +msgid "Modify details before importing" +msgstr "Modificar detalhes antes de importar" + +#: pytrainer/record.py:466 +msgid "pyTrainer can't import data from your gpx file" +msgstr "O pyTrainer não pode importar dados do seu ficheiro gpx" + +#: pytrainer/yeargraph.py:26 +#: pytrainer/yeargraph.py:27 +#: pytrainer/yeargraph.py:28 +#: pytrainer/yeargraph.py:29 +#: pytrainer/yeargraph.py:30 +msgid "month" +msgstr "mês" + +#: pytrainer/yeargraph.py:26 +msgid "Monthly Distance" +msgstr "Distância Mensal" + +#: pytrainer/yeargraph.py:27 +msgid "Monthly Time" +msgstr "Tempo Mensal" + +#: pytrainer/yeargraph.py:28 +msgid "Monthly Average Heart Rate" +msgstr "Ritmo Cardíaco Médio Mensal" + +#: pytrainer/yeargraph.py:29 +msgid "Monthly Average Speed" +msgstr "Velocidade Média Mensal" + +#: pytrainer/yeargraph.py:30 +msgid "Monthly Calories" +msgstr "Calorias Médias" + +#: pytrainer/waypoint.py:87 +msgid "The gpx file seems to be a several days records. Perhaps you will need to edit your gpx file" +msgstr "O ficheiro gpx contém aparentemente registos de vários dias. Talvez seja necessário editar o ficheiro gpx" + +#: pytrainer/daygraph.py:54 +msgid "Velocity (Km/h)" +msgstr "Velocidade (Km/h)" + +#: pytrainer/daygraph.py:54 +msgid "Velocity" +msgstr "Velocidade" + +#: pytrainer/main.py:467 +msgid "Delete this waypoint?" +msgstr "Apagar este ponto?" + +#: import/file_gpxplus.py:41 +msgid "GPS eXchange file" +msgstr "Ficheiro GPS eXchange" + +#: import/file_kml20.py:47 +msgid "Geodistance kml version 2.0 file" +msgstr "Ficheiro Geodistance kml versão 2.0" + +#: import/file_garmintcxv1.py:45 +msgid "Garmin training center database file version 1" +msgstr "Ficheiro de base de dados do centro de treino Garmin, versão 1" + +#: import/file_gpxplusNokia.py:41 +msgid "Nokia Export - GPS eXchange file" +msgstr "Nokia Export - ficheiro GPS eXchange" + +#: import/tool_gpsbabel.py:37 +msgid "GPSBabel" +msgstr "GPSBabel" + +#: import/tool_garmintools.py:37 +msgid "Garmintools" +msgstr "Garmintools" + +#: import/tool_gant.py:37 +msgid "Gant" +msgstr "Gant" + +#: import/file_garmintools.py:44 +msgid "Garmin tools dump file" +msgstr "Ficheiro dump de Garmin tools" + +#: import/file_garmintcxv2.py:44 +msgid "Garmin training center database file version 2" +msgstr "Ficheiro de base de dados do centro de treino Garmin, versão 2" + +#: extensions/fixelevation/fixelevation.py:129 +msgid "Elevation Correction Complete" +msgstr "Correcção de Elevações Completa" + +#: extensions/openstreetmap/openstreetmap.py:82 +msgid "" +"Posting GPX trace to Openstreetmap\n" +"\n" +"Please wait this could take several minutes" +msgstr "" +"A enviar percurso GPX para o Openstreetmap\n" +"\n" +"Por favor aguarde, isto pode demorar alguns minutos" + +#: extensions/openstreetmap/openstreetmap.py:84 +msgid "Openstreetmap Extension Processing" +msgstr "A processar Extensão Openstreetmap" + +#: extensions/openstreetmap/openstreetmap.py:102 +msgid "Openstreetmap Extension Upload Complete" +msgstr "Envio da Extensão Openstreetmap completo" + +#: extensions/openstreetmap/openstreetmap.py:107 +msgid "Error while uploading file to OSM: " +msgstr "Erro ao enviar ficheiro para o OSM: " + +#: extensions/openstreetmap/openstreetmap.py:109 +msgid "Openstreetmap Extension Error" +msgstr "Erro na Extensão Openstreetmap" + +#: extensions/openstreetmap/openstreetmap.py:117 +msgid "Please add any additional information for this upload" +msgstr "Por favor, acrescente informações adicionais para este envio" + +#: extensions/openstreetmap/openstreetmap.py:204 +msgid "Error" +msgstr "Erro" + +#: extensions/openstreetmap/openstreetmap.py:247 +msgid "Error while saving extension configuration" +msgstr "Erro ao guardar configuração da extensão" + +#: extensions/wordpress/wordpress.py:46 +msgid "Posting to Wordpress blog" +msgstr "A enviar para blog Wordpress" + +#: extensions/wordpress/wordpress.py:48 +msgid "Wordpress Extension Processing" +msgstr "A processar Extensão Wordpress" + +#: extensions/wordpress/wordpress.py:144 +msgid "Wordpress Extension Upload Complete" +msgstr "Envio da Extensão Wordpress Completo" + Modified: pytrainer/trunk/pytrainer/gui/aboutdialog.py =================================================================== --- pytrainer/trunk/pytrainer/gui/aboutdialog.py 2011-10-16 17:12:12 UTC (rev 895) +++ pytrainer/trunk/pytrainer/gui/aboutdialog.py 2011-10-20 17:41:53 UTC (rev 896) @@ -33,11 +33,11 @@ def run(self): authors = ["Fiz Vázquez <vu...@si...>\nDavid García Granda <dg...@gm...>\nJohn Blance <joh...@gm...>\nArnd Zapletal <a.z...@gm...>\nNathan Jones <na...@nc...>\n\n-Package maintainers:\n\nRedHat/Fedora: Douglas E. Warner <sil...@si...>\nDebian: Noèl Köthe <no...@de...>\nUbuntu: Kevin Dwyer <ke...@ph...>, Alessio Treglia <qua...@ub...>"] - translator_credits = "Euskara: Jabier Santamaria <men...@gm...>\nCatalà: Eloi Crespillo Itchart <el...@ik...>\nČesky: Lobus Pokorny <sp...@se...>\nFrançais: Dj <dj...@dj...>, Pierr... [truncated message content] |
From: <nc...@us...> - 2011-10-22 10:43:50
|
Revision: 900 http://pytrainer.svn.sourceforge.net/pytrainer/?rev=900&view=rev Author: ncjones Date: 2011-10-22 10:43:44 +0000 (Sat, 22 Oct 2011) Log Message: ----------- Add support for sqlalchemy-migrate 0.6.1+ ticket:132 Modified Paths: -------------- pytrainer/trunk/INSTALL pytrainer/trunk/pytrainer/upgrade/migratedb.py Modified: pytrainer/trunk/INSTALL =================================================================== --- pytrainer/trunk/INSTALL 2011-10-22 07:41:34 UTC (rev 899) +++ pytrainer/trunk/INSTALL 2011-10-22 10:43:44 UTC (rev 900) @@ -24,7 +24,7 @@ libxslt-1.1.26 sqlite-3.7.5 (sqlite is preferred as db than mysql, MySQL-python has been reported to be installed if so) python-sqlalchemy-0.6.8 (sqlalchemy >= 0.6.4) -python-migrate-0.6 (sqlalchemy-migrate-0.6.0) -- In 0.6.1 migrate.*.exceptions are moved to migrate.exceptions -- +python-migrate-0.6 (sqlalchemy-migrate >=0.6.0) - Only needed if correspondent plugin is enabled: gpsbabel-1.3.5 ("Garmin via GPSBabel 1.3.5" aka garmin_hr) - No longer available in official F15 repositories Modified: pytrainer/trunk/pytrainer/upgrade/migratedb.py =================================================================== --- pytrainer/trunk/pytrainer/upgrade/migratedb.py 2011-10-22 07:41:34 UTC (rev 899) +++ pytrainer/trunk/pytrainer/upgrade/migratedb.py 2011-10-22 10:43:44 UTC (rev 900) @@ -17,7 +17,12 @@ #Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. from migrate.versioning.api import db_version, upgrade, version, version_control -from migrate.versioning.exceptions import DatabaseNotControlledError +# sqlalchemy-migrate 0.6.1 broke backwards compatibility +# so we need to try importing exceptions from one of two packages +try: + from migrate.exceptions import DatabaseNotControlledError +except: + from migrate.versioning.exceptions import DatabaseNotControlledError import logging import os import sys This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |