Index: ow/models/workout.py
===================================================================
--- ow/models/workout.py	(revision 8c2b0949ae91540ee5c8101b9d5ff39003773da3)
+++ ow/models/workout.py	(revision 5ec3a0b5cf5640304a0953320daef2a28bbca9a5)
@@ -1,21 +1,10 @@
-import os
-import textwrap
+
 from datetime import datetime, timedelta, timezone
 from decimal import Decimal
 
-import pytz
 import gpxpy
 from repoze.folder import Folder
-from pyramid.security import Allow, Deny, Everyone, ALL_PERMISSIONS
-
-from ow.utilities import (
-    GPXMinidomParser,
-    copy_blob,
-    create_blob,
-    mps_to_kmph,
-    timedelta_to_hms
-)
-
-from ow.fit import Fit
+from pyramid.security import Allow, Everyone
+from ow.utilities import GPXMinidomParser
 
 
@@ -29,11 +18,17 @@
         it (for now). If not, everybody can view it, only admins can edit it.
         """
-        uid = self.__parent__.uid
+        # Default permissions
         permissions = [
-            (Allow, str(uid), 'view'),
-            (Allow, str(uid), 'edit'),
-            (Allow, str(uid), 'delete'),
-            (Deny, Everyone, ALL_PERMISSIONS)
+            (Allow, Everyone, 'view'),
+            (Allow, 'group:admins', 'edit')
         ]
+
+        uid = getattr(self.__parent__, 'uid', None)
+        if uid is not None:
+            # Change permissions in case this workout has an owner
+            permissions = [
+                (Allow, str(uid), 'view'),
+                (Allow, str(uid), 'edit'),
+            ]
         return permissions
 
@@ -47,5 +42,4 @@
         self.duration = kw.get('duration', None)  # a timedelta object
         self.distance = kw.get('distance', None)  # kilometers, Decimal
-        self.speed = kw.get('speed', {})
         self.hr_min = kw.get('hr_min', None)  # bpm, Decimal
         self.hr_max = kw.get('hr_max', None)  # bpm, Decimal
@@ -61,16 +55,8 @@
         self.tracking_file = kw.get('tracking_file', None)  # Blob
         self.tracking_filetype = ''  # unicode string
-        # attr to store ANT fit files. For now this file is used to
-        # generate a gpx-encoded tracking file we then use through
-        # the whole app
-        self.fit_file = kw.get('fit_file', None)  # Blob
 
     @property
     def workout_id(self):
         return self.__name__
-
-    @property
-    def owner(self):
-        return self.__parent__
 
     @property
@@ -88,22 +74,8 @@
         return self.start.strftime('%H:%M')
 
-    def start_in_timezone(self, timezone):
-        """
-        Return a string representation of the start date and time,
-        localized into the given timezone
-        """
-        _start = self.start.astimezone(pytz.timezone(timezone))
-        return _start.strftime('%d/%m/%Y %H:%M (%Z)')
-
-    def end_in_timezone(self, timezone):
-        """
-        Return a string representation of the end date and time,
-        localized into the given timezone
-        """
-        _end = self.end.astimezone(pytz.timezone(timezone))
-        return _end.strftime('%d/%m/%Y %H:%M (%Z)')
-
     def split_duration(self):
-        return timedelta_to_hms(self.duration)
+        hours, remainder = divmod(int(self.duration.total_seconds()), 3600)
+        minutes, seconds = divmod(remainder, 60)
+        return hours, minutes, seconds
 
     @property
@@ -120,10 +92,4 @@
 
     @property
-    def _duration(self):
-        return ':'.join(
-            [self.duration_hours, self.duration_minutes, self.duration_seconds]
-        )
-
-    @property
     def rounded_distance(self):
         """
@@ -132,30 +98,6 @@
         """
         if self.distance:
-            return round(self.distance, 2)
+            return round(self.distance, 1)
         return '-'
-
-    @property
-    def hashed(self):
-        """
-        Return a unique hash that we use to look for duplicated workouts.
-        The hash contains: owner's uid, start time, duration and distance
-        """
-        hashed = ''
-        if self.owner is not None:
-            hashed += str(self.owner.uid)
-        hashed += self.start.strftime('%Y%m%d%H%M%S')
-        if self.duration is not None:
-            hashed += str(self.duration.seconds)
-        if self.distance is not None:
-            hashed += str(self.distance)
-        return hashed
-
-    @property
-    def trimmed_notes(self):
-        """
-        Return a string with a reduced version of the full notes for this
-        workout.
-        """
-        return textwrap.shorten(self.notes, width=225, placeholder=' ...')
 
     @property
@@ -219,38 +161,4 @@
         return None
 
-    @property
-    def tracking_file_path(self):
-        """
-        Get the path to the blob file attached as a tracking file.
-
-        First check if the file was not committed to the db yet (new workout
-        not saved yet) and use the path to the temporary file on the fs.
-        If none is found there, go for the real blob file in the blobs
-        directory
-        """
-        path = None
-        if self.tracking_file:
-            path = self.tracking_file._uncommitted()
-            if path is None:
-                path = self.tracking_file.committed()
-        return path
-
-    @property
-    def fit_file_path(self):
-        """
-        Get the path to the blob file attached as a fit file.
-
-        First check if the file was not committed to the db yet (new workout
-        not saved yet) and use the path to the temporary file on the fs.
-        If none is found there, go for the real blob file in the blobs
-        directory
-        """
-        path = None
-        if self.fit_file:
-            path = self.fit_file._uncommitted()
-            if path is None:
-                path = self.fit_file.committed()
-        return path
-
     def load_from_file(self):
         """
@@ -260,6 +168,4 @@
         if self.tracking_filetype == 'gpx':
             self.load_from_gpx()
-        elif self.tracking_filetype == 'fit':
-            self.load_from_fit()
 
     def load_from_gpx(self):
@@ -282,9 +188,8 @@
                 self.duration = timedelta(seconds=track.get_duration())
                 # length_3d returns meters
-                self.distance = round(
-                    Decimal(track.length_3d()) / Decimal(1000.00), 2)
+                self.distance = Decimal(track.length_3d()) / Decimal(1000.00)
                 ud = track.get_uphill_downhill()
-                self.uphill = round(Decimal(ud.uphill), 0)
-                self.downhill = round(Decimal(ud.downhill), 0)
+                self.uphill = Decimal(ud.uphill)
+                self.downhill = Decimal(ud.downhill)
                 # If the user did not provide us with a title, and the gpx has
                 # one, use that
@@ -351,80 +256,4 @@
         return parser.tracks
 
-    def load_from_fit(self):
-        """
-        Try to load data from an ANT-compatible .fit file (if any has been
-        added to this workout).
-
-        "Load data" means:
-
-        1. Copy over the uploaded fit file to self.fit_file, so we can keep
-           that copy around for future use
-
-        2. generate a gpx object from the fit file
-
-        3. save the gpx object as the tracking_file, which then will be used
-           by the current code to display and gather data to be displayed/shown
-           to the user.
-
-        4. Grab some basic info from the fit file and store it in the Workout
-        """
-
-        # we can call load_from_fit afterwards for updates. In such case, check
-        # if the tracking file is a fit file uploaded to override the previous
-        # one. If not, just reuse the existing fit file
-        if self.tracking_filetype == 'fit':
-            # backup the fit file
-            self.fit_file = copy_blob(self.tracking_file)
-
-        # create an instance of our Fit class
-        fit = Fit(self.fit_file_path)
-        fit.load()
-
-        # fit -> gpx and store that as the main tracking file
-        self.tracking_file = create_blob(fit.gpx, 'gpx')
-        self.tracking_filetype = 'gpx'
-
-        # grab the needed data from the fit file, update the workout
-        self.sport = fit.data['sport']
-        self.start = fit.data['start']
-        # ensure this datetime start object is timezone-aware
-        self.start = self.start.replace(tzinfo=timezone.utc)
-        # duration comes in seconds, store a timedelta
-        self.duration = timedelta(seconds=fit.data['duration'])
-        if fit.data['distance']:
-            # distance comes in meters
-            self.distance = Decimal(fit.data['distance']) / Decimal(1000.00)
-        if fit.data['uphill']:
-            self.uphill = Decimal(fit.data['uphill'])
-        if fit.data['downhill']:
-            self.downhill = Decimal(fit.data['downhill'])
-        # If the user did not provide us with a title, build one from the
-        # info in the fit file
-        if not self.title:
-            self.title = fit.name
-
-        if fit.data['max_speed']:
-            self.speed['max'] = mps_to_kmph(fit.data['max_speed'])
-
-        if fit.data['avg_speed']:
-            self.speed['avg'] = mps_to_kmph(fit.data['avg_speed'])
-
-        if fit.data['avg_hr']:
-            self.hr_avg = Decimal(fit.data['avg_hr'])
-            self.hr_min = Decimal(fit.data['min_hr'])
-            self.hr_max = Decimal(fit.data['max_hr'])
-
-        if fit.data['avg_cad']:
-            self.cad_avg = Decimal(fit.data['avg_cad'])
-            self.cad_min = Decimal(fit.data['min_cad'])
-            self.cad_max = Decimal(fit.data['max_cad'])
-
-        if fit.data['avg_atemp']:
-            self.atemp_avg = Decimal(fit.data['avg_atemp'])
-            self.atemp_min = Decimal(fit.data['min_atemp'])
-            self.atemp_max = Decimal(fit.data['max_atemp'])
-
-        return True
-
     @property
     def has_tracking_file(self):
@@ -434,33 +263,2 @@
     def has_gpx(self):
         return self.has_tracking_file and self.tracking_filetype == 'gpx'
-
-    @property
-    def has_fit(self):
-        return self.fit_file is not None
-
-    @property
-    def map_screenshot_name(self):
-        return os.path.join(
-            str(self.owner.uid), str(self.workout_id) + '.png')
-
-    @property
-    def map_screenshot_path(self):
-        current_path = os.path.abspath(os.path.dirname(__file__))
-        return os.path.join(
-            current_path, '../static/maps', self.map_screenshot_name)
-
-    @property
-    def map_screenshot(self):
-        """
-        Return the static path to the screenshot image of the map for
-        this workout (works only for workouts with gps tracking)
-        """
-        if not self.has_gpx:
-            return None
-
-        if not os.path.exists(self.map_screenshot_path):
-            return None
-
-        static_path = os.path.join('static/maps', self.map_screenshot_name)
-        # return a string we can use with request.static_url
-        return 'ow:' + static_path
