38
"""Contains various statistics, provide string conversion functions"""
38
u"""Contains various statistics, provide string conversion functions"""
39
39
# used when quoting files in get_stats_line
40
space_regex = re.compile(" ")
40
space_regex = re.compile(u" ")
42
stat_file_attrs = ('SourceFiles',
52
stat_misc_attrs = ('Errors',
53
'TotalDestinationSizeChange')
54
stat_time_attrs = ('StartTime',
57
stat_attrs = (('Filename',) + stat_time_attrs +
42
stat_file_attrs = (u'SourceFiles',
52
stat_misc_attrs = (u'Errors',
53
u'TotalDestinationSizeChange')
54
stat_time_attrs = (u'StartTime',
57
stat_attrs = ((u'Filename',) + stat_time_attrs +
58
58
stat_misc_attrs + stat_file_attrs)
60
60
# Below, the second value in each pair is true iff the value
61
61
# indicates a number of bytes
62
stat_file_pairs = (('SourceFiles', False),
63
('SourceFileSize', True),
65
('NewFileSize', True),
66
('DeletedFiles', False),
67
('ChangedFiles', False),
68
('ChangedFileSize', True),
69
('ChangedDeltaSize', True),
70
('DeltaEntries', False),
71
('RawDeltaSize', True))
62
stat_file_pairs = ((u'SourceFiles', False),
63
(u'SourceFileSize', True),
65
(u'NewFileSize', True),
66
(u'DeletedFiles', False),
67
(u'ChangedFiles', False),
68
(u'ChangedFileSize', True),
69
(u'ChangedDeltaSize', True),
70
(u'DeltaEntries', False),
71
(u'RawDeltaSize', True))
73
73
# This is used in get_byte_summary_string below
74
byte_abbrev_list = ((1024 * 1024 * 1024 * 1024, "TB"),
75
(1024 * 1024 * 1024, "GB"),
74
byte_abbrev_list = ((1024 * 1024 * 1024 * 1024, u"TB"),
75
(1024 * 1024 * 1024, u"GB"),
79
79
def __init__(self):
80
"""Set attributes to None"""
80
u"""Set attributes to None"""
81
81
for attr in self.stat_attrs:
82
82
self.__dict__[attr] = None
84
84
def get_stat(self, attribute):
85
u"""Get a statistic"""
86
86
return self.__dict__[attribute]
88
88
def set_stat(self, attr, value):
89
"""Set attribute to given value"""
89
u"""Set attribute to given value"""
90
90
self.__dict__[attr] = value
92
92
def increment_stat(self, attr):
93
"""Add 1 to value of attribute"""
93
u"""Add 1 to value of attribute"""
94
94
self.__dict__[attr] += 1
96
96
def get_total_dest_size_change(self):
97
"""Return total destination size change
97
u"""Return total destination size change
99
99
This represents the total increase in the size of the
100
100
duplicity destination directory, or None if not available.
103
103
return 0 # this needs to be re-done for duplicity
105
105
def get_stats_line(self, index, use_repr=1):
106
"""Return one line abbreviated version of full stats string"""
106
u"""Return one line abbreviated version of full stats string"""
107
107
file_attrs = [str(self.get_stat(a)) for a in self.stat_file_attrs]
111
111
filename = os.path.join(*index)
113
113
# use repr to quote newlines in relative filename, then
114
114
# take of leading and trailing quote and quote spaces.
115
filename = self.space_regex.sub("\\x20", repr(filename)[1:-1])
116
return " ".join([filename, ] + file_attrs)
115
filename = self.space_regex.sub(u"\\x20", repr(filename)[1:-1])
116
return u" ".join([filename, ] + file_attrs)
118
118
def set_stats_from_line(self, line):
119
"""Set statistics from given line"""
119
u"""Set statistics from given line"""
121
raise StatsException("Bad line '%s'" % line)
121
raise StatsException(u"Bad line '%s'" % line)
122
if line[-1] == u"\n":
124
lineparts = line.split(" ")
124
lineparts = line.split(u" ")
125
125
if len(lineparts) < len(self.stat_file_attrs):
127
127
for attr, val_string in zip(self.stat_file_attrs,
139
139
def get_stats_string(self):
140
"""Return extended string printing out statistics"""
141
return "%s%s%s" % (self.get_timestats_string(),
142
self.get_filestats_string(),
143
self.get_miscstats_string())
140
u"""Return extended string printing out statistics"""
141
return u"%s%s%s" % (self.get_timestats_string(),
142
self.get_filestats_string(),
143
self.get_miscstats_string())
145
145
def get_timestats_string(self):
146
"""Return portion of statistics string dealing with time"""
146
u"""Return portion of statistics string dealing with time"""
148
148
if self.StartTime is not None:
149
timelist.append("StartTime %.2f (%s)\n" %
149
timelist.append(u"StartTime %.2f (%s)\n" %
150
150
(self.StartTime, dup_time.timetopretty(self.StartTime)))
151
151
if self.EndTime is not None:
152
timelist.append("EndTime %.2f (%s)\n" %
152
timelist.append(u"EndTime %.2f (%s)\n" %
153
153
(self.EndTime, dup_time.timetopretty(self.EndTime)))
154
154
if self.ElapsedTime or (self.StartTime is not None and
155
155
self.EndTime is not None):
156
156
if self.ElapsedTime is None:
157
157
self.ElapsedTime = self.EndTime - self.StartTime
158
timelist.append("ElapsedTime %.2f (%s)\n" %
158
timelist.append(u"ElapsedTime %.2f (%s)\n" %
159
159
(self.ElapsedTime, dup_time.inttopretty(self.ElapsedTime)))
160
return "".join(timelist)
160
return u"".join(timelist)
162
162
def get_filestats_string(self):
163
"""Return portion of statistics string about files and bytes"""
163
u"""Return portion of statistics string about files and bytes"""
164
164
def fileline(stat_file_pair):
165
"""Return zero or one line of the string"""
165
u"""Return zero or one line of the string"""
166
166
attr, in_bytes = stat_file_pair
167
167
val = self.get_stat(attr)
171
return "%s %s (%s)\n" % (attr, val,
172
self.get_byte_summary_string(val))
171
return u"%s %s (%s)\n" % (attr, val,
172
self.get_byte_summary_string(val))
174
return "%s %s\n" % (attr, val)
174
return u"%s %s\n" % (attr, val)
176
return "".join(map(fileline, self.stat_file_pairs))
176
return u"".join(map(fileline, self.stat_file_pairs))
178
178
def get_miscstats_string(self):
179
"""Return portion of extended stat string about misc attributes"""
179
u"""Return portion of extended stat string about misc attributes"""
181
181
tdsc = self.TotalDestinationSizeChange
182
182
if tdsc is not None:
183
misc_string += ("TotalDestinationSizeChange %s (%s)\n" %
183
misc_string += (u"TotalDestinationSizeChange %s (%s)\n" %
184
184
(tdsc, self.get_byte_summary_string(tdsc)))
185
185
if self.Errors is not None:
186
misc_string += "Errors %d\n" % self.Errors
186
misc_string += u"Errors %d\n" % self.Errors
187
187
return misc_string
189
189
def get_byte_summary_string(self, byte_count):
190
"""Turn byte count into human readable string like "7.23GB" """
190
u"""Turn byte count into human readable string like "7.23GB" """
191
191
if byte_count < 0:
193
193
byte_count = -byte_count
197
197
for abbrev_bytes, abbrev_string in self.byte_abbrev_list:
198
198
if byte_count >= abbrev_bytes:
207
return "%s%%.%df %s" % (sign, precision, abbrev_string) \
207
return u"%s%%.%df %s" % (sign, precision, abbrev_string) \
208
208
% (abbrev_count,)
209
209
byte_count = round(byte_count)
210
210
if byte_count == 1:
211
return sign + "1 byte"
211
return sign + u"1 byte"
213
return "%s%d bytes" % (sign, byte_count)
213
return u"%s%d bytes" % (sign, byte_count)
215
215
def get_stats_logstring(self, title):
216
"""Like get_stats_string, but add header and footer"""
217
header = "--------------[ %s ]--------------" % title
218
footer = "-" * len(header)
219
return "%s\n%s%s\n" % (header, self.get_stats_string(), footer)
216
u"""Like get_stats_string, but add header and footer"""
217
header = u"--------------[ %s ]--------------" % title
218
footer = u"-" * len(header)
219
return u"%s\n%s%s\n" % (header, self.get_stats_string(), footer)
221
221
def set_stats_from_string(self, s):
222
"""Initialize attributes from string, return self for convenience"""
222
u"""Initialize attributes from string, return self for convenience"""
224
raise StatsException("Bad line '%s'" % line)
224
raise StatsException(u"Bad line '%s'" % line)
226
for line in s.split("\n"):
226
for line in s.split(u"\n"):
229
229
line_parts = line.split()
249
249
def write_stats_to_path(self, path):
250
"""Write statistics string to given path"""
250
u"""Write statistics string to given path"""
251
fin = path.open(u"w")
252
252
fin.write(self.get_stats_string())
253
253
assert not fin.close()
255
255
def read_stats_from_path(self, path):
256
"""Set statistics from path, return self for convenience"""
256
u"""Set statistics from path, return self for convenience"""
258
258
self.set_stats_from_string(fp.read())
259
259
assert not fp.close()
262
262
def stats_equal(self, s):
263
"""Return true if s has same statistics as self"""
263
u"""Return true if s has same statistics as self"""
264
264
assert isinstance(s, StatsObj)
265
265
for attr in self.stat_file_attrs:
266
266
if self.get_stat(attr) != s.get_stat(attr):
309
309
self.files_changed = []
311
311
def add_new_file(self, path):
312
"""Add stats of new file path to statistics"""
312
u"""Add stats of new file path to statistics"""
313
313
filesize = path.getsize()
314
314
self.SourceFiles += 1
315
315
# SourceFileSize is added-to incrementally as read
316
316
self.NewFiles += 1
317
317
self.NewFileSize += filesize
318
318
self.DeltaEntries += 1
319
self.add_delta_entries_file(path, 'new')
319
self.add_delta_entries_file(path, u'new')
321
321
def add_changed_file(self, path):
322
"""Add stats of file that has changed since last backup"""
322
u"""Add stats of file that has changed since last backup"""
323
323
filesize = path.getsize()
324
324
self.SourceFiles += 1
325
325
# SourceFileSize is added-to incrementally as read
326
326
self.ChangedFiles += 1
327
327
self.ChangedFileSize += filesize
328
328
self.DeltaEntries += 1
329
self.add_delta_entries_file(path, 'changed')
329
self.add_delta_entries_file(path, u'changed')
331
331
def add_deleted_file(self, path):
332
"""Add stats of file no longer in source directory"""
332
u"""Add stats of file no longer in source directory"""
333
333
self.DeletedFiles += 1 # can't add size since not available
334
334
self.DeltaEntries += 1
335
self.add_delta_entries_file(path, 'deleted')
335
self.add_delta_entries_file(path, u'deleted')
337
337
def add_unchanged_file(self, path):
338
"""Add stats of file that hasn't changed since last backup"""
338
u"""Add stats of file that hasn't changed since last backup"""
339
339
filesize = path.getsize()
340
340
self.SourceFiles += 1
341
341
self.SourceFileSize += filesize
344
"""End collection of data, set EndTime"""
344
u"""End collection of data, set EndTime"""
345
345
self.EndTime = time.time()
347
347
def add_delta_entries_file(self, path, action_type):