141 - Support for Package Management in SO
[osm/SO.git] / common / python / rift / downloader / base.py
diff --git a/common/python/rift/downloader/base.py b/common/python/rift/downloader/base.py
new file mode 100644 (file)
index 0000000..c87839f
--- /dev/null
@@ -0,0 +1,180 @@
+#
+#   Copyright 2016 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+# Author(s): Varun Prasad
+# Creation Date: 09/25/2016
+#
+
+import abc
+import enum
+import os
+import uuid
+import time
+
+
+class InvalidDestinationError(Exception):
+    pass
+
+
+class DownloaderProtocol:
+    """Listener of this class can implement the following method to get a
+    callback
+    """
+    def on_download_started(self, job):
+        """Called when the download starts
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+    def on_download_progress(self, job):
+        """Called after each chunk is downloaded
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+    def on_download_succeeded(self, job):
+        """Called when the download is completed successfully
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+    def on_download_failed(self, job):
+        """Called when the download fails
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+    def on_download_cancelled(self, job):
+        """Called when the download is canceled
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+    def on_download_finished(self, job):
+        """Called when the download finishes regardless of the status of the
+        download (success, failed or canceled)
+
+        Args:
+            job (DownloadJob): Yang Model
+
+        """
+        pass
+
+
+class DownloadStatus(enum.Enum):
+    STARTED = 1
+    IN_PROGRESS = 2
+    COMPLETED = 3
+    FAILED = 4
+    CANCELLED = 5
+
+
+class DownloadMeta:
+    """Model data used by the downloader.
+    """
+    def __init__(self, url, dest_file):
+        self.url = url
+        self.filepath = dest_file
+        self.download_id = str(uuid.uuid4())
+        self.bytes_total = 0
+        self.progress_percent = 0
+        self.bytes_downloaded = 0
+        self.bytes_per_second = 0
+
+
+        self.start_time = 0
+        self.stop_time = 0
+        self.detail = ""
+
+    @property
+    def filename(self):
+        return os.path.basename(self.filepath)
+
+    def start_download(self):
+        self.start_time = time.time()
+
+    def end_download(self):
+        self.end_time = time.time()
+
+    def set_state(self, state):
+        self.status = state
+
+    def update_with_data(self, downloaded_chunk):
+        self.bytes_downloaded += len(downloaded_chunk)
+
+        if self.bytes_total != 0:
+            self.progress_percent = \
+                int((self.bytes_downloaded / self.bytes_total) * 100)
+
+        # compute bps
+        seconds_elapsed = time.time() - self.start_time
+        self.bytes_per_second = self.bytes_downloaded // seconds_elapsed
+
+    def update_data_with_head(self, headers):
+        """Update the model from the header of HEAD request
+
+        Args:
+            headers (dict): headers from HEAD response
+        """
+        if 'Content-Length' in headers:
+            self.bytes_total = int(headers['Content-Length'])
+
+    def as_dict(self):
+        return self.__dict__
+
+
+class AbstractDownloader:
+
+    def __init__(self):
+        self._delegate = None
+
+    @property
+    def delegate(self):
+        return self._delegate
+
+    @delegate.setter
+    def delegate(self, delegate):
+        self._delegate = delegate
+
+    @abc.abstractproperty
+    def download_id(self):
+        pass
+
+    @abc.abstractmethod
+    def cancel_download(self):
+        pass
+
+    @abc.abstractmethod
+    def close(self):
+        pass
+
+    @abc.abstractmethod
+    def download(self):
+        pass