blob: c87839f604f7f8f982a98129a33dbfbb45feedc5 [file] [log] [blame]
velandy6364d012017-01-04 19:25:07 +00001#
2# Copyright 2016 RIFT.IO Inc
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16# Author(s): Varun Prasad
17# Creation Date: 09/25/2016
18#
19
20import abc
21import enum
22import os
23import uuid
24import time
25
26
27class InvalidDestinationError(Exception):
28 pass
29
30
31class DownloaderProtocol:
32 """Listener of this class can implement the following method to get a
33 callback
34 """
35 def on_download_started(self, job):
36 """Called when the download starts
37
38 Args:
39 job (DownloadJob): Yang Model
40
41 """
42 pass
43
44 def on_download_progress(self, job):
45 """Called after each chunk is downloaded
46
47 Args:
48 job (DownloadJob): Yang Model
49
50 """
51 pass
52
53 def on_download_succeeded(self, job):
54 """Called when the download is completed successfully
55
56 Args:
57 job (DownloadJob): Yang Model
58
59 """
60 pass
61
62 def on_download_failed(self, job):
63 """Called when the download fails
64
65 Args:
66 job (DownloadJob): Yang Model
67
68 """
69 pass
70
71 def on_download_cancelled(self, job):
72 """Called when the download is canceled
73
74 Args:
75 job (DownloadJob): Yang Model
76
77 """
78 pass
79
80 def on_download_finished(self, job):
81 """Called when the download finishes regardless of the status of the
82 download (success, failed or canceled)
83
84 Args:
85 job (DownloadJob): Yang Model
86
87 """
88 pass
89
90
91class DownloadStatus(enum.Enum):
92 STARTED = 1
93 IN_PROGRESS = 2
94 COMPLETED = 3
95 FAILED = 4
96 CANCELLED = 5
97
98
99class DownloadMeta:
100 """Model data used by the downloader.
101 """
102 def __init__(self, url, dest_file):
103 self.url = url
104 self.filepath = dest_file
105 self.download_id = str(uuid.uuid4())
106 self.bytes_total = 0
107 self.progress_percent = 0
108 self.bytes_downloaded = 0
109 self.bytes_per_second = 0
110
111
112 self.start_time = 0
113 self.stop_time = 0
114 self.detail = ""
115
116 @property
117 def filename(self):
118 return os.path.basename(self.filepath)
119
120 def start_download(self):
121 self.start_time = time.time()
122
123 def end_download(self):
124 self.end_time = time.time()
125
126 def set_state(self, state):
127 self.status = state
128
129 def update_with_data(self, downloaded_chunk):
130 self.bytes_downloaded += len(downloaded_chunk)
131
132 if self.bytes_total != 0:
133 self.progress_percent = \
134 int((self.bytes_downloaded / self.bytes_total) * 100)
135
136 # compute bps
137 seconds_elapsed = time.time() - self.start_time
138 self.bytes_per_second = self.bytes_downloaded // seconds_elapsed
139
140 def update_data_with_head(self, headers):
141 """Update the model from the header of HEAD request
142
143 Args:
144 headers (dict): headers from HEAD response
145 """
146 if 'Content-Length' in headers:
147 self.bytes_total = int(headers['Content-Length'])
148
149 def as_dict(self):
150 return self.__dict__
151
152
153class AbstractDownloader:
154
155 def __init__(self):
156 self._delegate = None
157
158 @property
159 def delegate(self):
160 return self._delegate
161
162 @delegate.setter
163 def delegate(self, delegate):
164 self._delegate = delegate
165
166 @abc.abstractproperty
167 def download_id(self):
168 pass
169
170 @abc.abstractmethod
171 def cancel_download(self):
172 pass
173
174 @abc.abstractmethod
175 def close(self):
176 pass
177
178 @abc.abstractmethod
179 def download(self):
180 pass