Coverage for py_tools_ds/processing/progress_mon.py: 82%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

74 statements  

1# -*- coding: utf-8 -*- 

2 

3# py_tools_ds - A collection of geospatial data analysis tools that simplify standard 

4# operations when handling geospatial raster and vector data as well as projections. 

5# 

6# Copyright (C) 2016-2021 

7# - Daniel Scheffler (GFZ Potsdam, daniel.scheffler@gfz-potsdam.de) 

8# - Helmholtz Centre Potsdam - GFZ German Research Centre for Geosciences Potsdam, 

9# Germany (https://www.gfz-potsdam.de/) 

10# 

11# This software was developed within the context of the GeoMultiSens project funded 

12# by the German Federal Ministry of Education and Research 

13# (project grant code: 01 IS 14 010 A-C). 

14# 

15# Licensed under the Apache License, Version 2.0 (the "License"); 

16# you may not use this file except in compliance with the License. 

17# You may obtain a copy of the License at 

18# 

19# http://www.apache.org/licenses/LICENSE-2.0 

20# 

21# Unless required by applicable law or agreed to in writing, software 

22# distributed under the License is distributed on an "AS IS" BASIS, 

23# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

24# See the License for the specific language governing permissions and 

25# limitations under the License. 

26 

27import sys 

28from time import time 

29from datetime import timedelta 

30 

31__author__ = "Daniel Scheffler" 

32 

33 

34class Timer(object): 

35 def __init__(self, timeout=None, use_as_callback=False): 

36 self.starttime = time() 

37 self.endtime = self.starttime + timeout if timeout else None 

38 self.timeout = timeout 

39 self.use_as_cb = use_as_callback 

40 

41 @property 

42 def timed_out(self): 

43 if self.endtime: 

44 if time() > self.endtime: 

45 if self.use_as_cb: 

46 # raise a KeyBoardInterrupt instead of a TimeOutError 

47 # as this is catchable by gdal.GetLastException() 

48 raise KeyboardInterrupt() 

49 else: 

50 return True 

51 else: 

52 if self.use_as_cb: 

53 pass 

54 else: 

55 return False 

56 else: 

57 return False 

58 

59 @property 

60 def elapsed(self): 

61 return str(timedelta(seconds=time() - self.starttime)).split('.')[0] 

62 # return '%.2f sek' %(time()-self.starttime) 

63 

64 def __call__(self, percent01, message, user_data): 

65 """Allow Timer instances to be callable and thus to be used as callback function, e.g., for GDAL. 

66 

67 :param percent01: this is not used but expected when used as GDAL callback 

68 :param message: this is not used but expected when used as GDAL callback 

69 :param user_data: this is not used but expected when used as GDAL callback 

70 :return: 

71 """ 

72 return self.timed_out 

73 

74 

75class ProgressBar(object): 

76 def __init__(self, prefix='', suffix='Complete', decimals=1, barLength=50, show_elapsed=True, 

77 timeout=None, use_as_callback=False, out=sys.stderr): 

78 """Call an instance of this class in a loop to create terminal progress bar. 

79 

80 NOTE: This class can also be used as callback function, e.g. for GDAL. 

81 Just pass an instance of ProgressBar to the respective callback keyword. 

82 

83 :param prefix: prefix string (Str) 

84 :param suffix: suffix string (Str) 

85 :param decimals: positive number of decimals in percent complete (Int) 

86 :param barLength: character length of bar (Int) 

87 :param show_elapsed: displays the elapsed time right after the progress bar (bool) 

88 :param timeout: breaks the process after a given time in seconds (float) 

89 

90 http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console 

91 """ 

92 self.prefix = prefix 

93 self.suffix = suffix 

94 self.decimals = decimals 

95 self.barLength = barLength 

96 self.show_elapsed = show_elapsed 

97 self.timeout = timeout 

98 self.Timer = Timer(timeout=timeout) 

99 self.use_as_cb = use_as_callback 

100 self.out = out 

101 

102 self._percdone = list(range(10, 110, 10)) 

103 

104 def print_progress(self, percent): 

105 """Print progress. 

106 

107 - based on http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console. 

108 

109 :param percent: <float> a number between 0 and 100 

110 :return: 

111 """ 

112 if self.Timer.timed_out: 

113 if self.out is not None: 

114 self.out.flush() 

115 if self.use_as_cb: 

116 # raise a KeyBoardInterrupt instead of a TimeOutError 

117 # as this is catchable by gdal.GetLastException() 

118 raise KeyboardInterrupt() 

119 else: 

120 raise TimeoutError(f'No progress for {self.timeout} seconds.') 

121 

122 formatStr = "{0:." + str(self.decimals) + "f}" 

123 percents = formatStr.format(percent) 

124 filledLength = int(round(self.barLength * percent / 100)) 

125 # bar = '█' * filledLength + '-' * (barLength - filledLength) # this is not compatible to shell console 

126 bar = '=' * filledLength + '-' * (self.barLength - filledLength) 

127 

128 if self.out is not None: 

129 # reset the cursor to the beginning of the line and allows to write over what was previously on the line 

130 self.out.write('\r') 

131 

132 # [%s/%s] numberDone 

133 suffix = self.suffix if not self.show_elapsed else '%s => %s' % (self.suffix, self.Timer.elapsed) 

134 self.out.write('%s |%s| %s%s %s' % (self.prefix, bar, percents, '%', suffix)) 

135 

136 if percent >= 100.: 

137 self.out.write('\n') 

138 

139 self.out.flush() 

140 

141 else: 

142 # in some environments, sys.stderr can also be None 

143 # pydocs: usually Windows GUI apps that aren’t connected to a console and Python apps started with pythonw 

144 try: 

145 percnext = self._percdone[0] 

146 if percent >= percnext: 

147 print(f'{percents} %') 

148 self._percdone.pop(0) 

149 

150 except IndexError: # pragma: no cover 

151 pass 

152 

153 def __call__(self, percent01, message, user_data): 

154 """Allow ProgressBar instances to be callable and thus to be used as callback function, e.g., for GDAL. 

155 

156 :param percent01: a float number between 0 and 1 

157 :param message: this is not used but expected when used as GDAL callback 

158 :param user_data: this is not used but expected when used as GDAL callback 

159 :return: 

160 """ 

161 self.print_progress(percent01 * 100) 

162 

163 

164def tqdm_hook(t): 

165 """ 

166 

167 Wraps tqdm instance. Don't forget to close() or __exit__() 

168 the tqdm instance once you're done with it (easiest using `with` syntax). 

169 

170 Example 

171 ------- 

172 

173 > with tqdm(...) as t: 

174 ... reporthook = my_hook(t) 

175 ... urllib.urlretrieve(..., reporthook=reporthook) 

176 

177 http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console 

178 """ 

179 last_b = [0] 

180 

181 def inner(b=1, bsize=1, tsize=None): 

182 """ 

183 b : int, optional 

184 Number of blocks just transferred [default: 1]. 

185 bsize : int, optional 

186 Size of each block (in tqdm units) [default: 1]. 

187 tsize : int, optional 

188 Total size (in tqdm units). If [default: None] remains unchanged. 

189 """ 

190 if tsize is not None: 

191 t.total = tsize 

192 t.update((b - last_b[0]) * bsize) 

193 last_b[0] = b 

194 

195 return inner 

196 

197 

198def printPercentage(i, i_total): 

199 """Print a percentage counter from within a loop. 

200 

201 Example: 

202 for i in range(100+1): 

203 time.sleep(0.1) 

204 printPercentage(i) 

205 

206 :param i: 

207 :param i_total: 

208 :return: 

209 

210 http://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console 

211 """ 

212 sys.stdout.write(('=' * i) + ('' * (i_total - i)) + ("\r [ %d" % i + "% ] ")) 

213 sys.stdout.flush()