c87839f604f7f8f982a98129a33dbfbb45feedc5
[osm/SO.git] / common / python / rift / downloader / base.py
1 #
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
20 import abc
21 import enum
22 import os
23 import uuid
24 import time
25
26
27 class InvalidDestinationError(Exception):
28 pass
29
30
31 class 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
91 class DownloadStatus(enum.Enum):
92 STARTED = 1
93 IN_PROGRESS = 2
94 COMPLETED = 3
95 FAILED = 4
96 CANCELLED = 5
97
98
99 class 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
153 class 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