]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
print_versions(): Pad strings correctly when colors are used
[emerge-timer] / emerge-timer.py
1 #!/usr/bin/python
2
3
4 import sys, datetime, os
5
6
7 PORTDIR = "/usr/portage/"
8 LOGFILE = "/var/log/emerge.log"
9
10
11 green_start = "\033[32m"
12 color_stop = "\033[m"
13
14 QUIET = False
15
16 PACKAGES = []
17
18
19
20 class package:
21     def __init__(self, name, version=0):
22         self.name = name
23         self.version = version
24         self.versions = []
25         self.emerge_time = 12*3600
26
27
28     def add_version(self, version, emerge_time, emerge_date):
29         """Add version to the class version list"""
30         self.versions.append((version, emerge_time, emerge_date))
31
32
33     def average_time(self):
34         """Return average time from class version list"""
35         total_time = 0
36         for i in self.versions:
37             total_time += i[1]
38
39         average_time = total_time/len(self.versions)
40
41         return average_time
42
43
44     def total_time(self):
45         """Return total time from class version list"""
46         total_time = 0
47         for i in self.versions:
48             total_time += i[1]
49
50         return total_time
51
52
53     def max_time(self):
54         """Return maximum time from class version list"""
55         self.versions.sort()
56
57         return self.versions[len(self.versions)-1][1]
58
59
60     def min_time(self):
61         """Return minimum time from class version list"""
62         self.versions.sort()
63
64         return self.versions[0][1]
65
66
67     def print_current_emerge(self):
68         """Function used to print all the current emerge stuff"""
69
70         print("\t" + green_start + self.name + '-' + self.version +
71               color_stop + "\n\t current time: " + give_time(self.emerge_time) +
72               "\n\t average time: "),
73
74         if len(self.versions) > 1:
75             print(give_time(self.average_time())),
76         else:
77             print("unknown"),
78
79         print("\n\t " + '-'*45),
80
81         finish_time = self.average_time() - self.emerge_time
82
83         print("\n\t time to finish: "),
84
85         if finish_time > 0:
86             print(give_time(finish_time))
87         else:
88             print("any time now")
89         print
90
91
92     def print_versions(self):
93         """This prints the emerge times for different versions in the
94         normal operating mode of the script"""
95
96         if QUIET == False:
97
98             version_length = 0
99             time_length = 0
100
101             for p in self.versions:
102                 if len(p[0]) > version_length:
103                     version_length = len(p[0])
104
105                 if len(give_time(p[1], True)) > time_length:
106                     time_length = len(give_time(p[1], True))
107
108             for p in self.versions:
109
110                 pad = time_length - len(give_time(p[1], True))
111
112                 print('-'*90 + "\n" +
113                       green_start + self.name + (p[0]).ljust(version_length) +
114                       color_stop + "  >>>  " + (give_time(p[1])) + " "*pad +
115                       "  >>>  " + give_date(p[2]))
116
117         print('-'*90 + "\n" + "Package " + green_start +
118               self.name + color_stop + " emerged " +
119               str(len(self.versions)) + " times.")
120
121         print
122
123
124     def print_pretended_times(self):
125         """This is used the print all the pretended times"""
126
127         if QUIET == False:
128             print("\t" + green_start + self.name + '-' + self.version + color_stop),
129
130         if len(self.versions) > 1:
131             aver_time = self.average_time()
132
133             if QUIET == False:
134                 print("\n\taverage time: " + give_time(aver_time))
135
136             return aver_time
137
138         else:
139             if QUIET == False:
140                 print("\n\t no previous emerges")
141
142             return 0
143
144
145     def print_min_max_ave(self):
146         maxi = self.max_time()
147         mini = self.min_time()
148         average = self.average_time()
149         total = self.total_time()
150
151         print("Max time: \t" + give_time(maxi) +
152               "\nMin time: \t" + give_time(mini) +
153               "\nAverage time: \t" + give_time(average) +
154               "\nIn total spent " + give_time(total) +
155               " emerging " + green_start + self.name + color_stop)
156
157
158
159 def give_time(time, nocolor=False):
160     """Converts time in seconds to human readable form"""
161     global green_start, color_stop
162
163     if nocolor == True:
164         green_start = ""
165         color_stop = ""
166
167     days = time/(3600*24)
168     hours = (days - int(days))*24
169     minutes = (hours - int(hours))*60
170     seconds = (minutes - int(minutes))*60
171
172     days = int(days)
173     hours = int(hours)
174     minutes = int(minutes)
175     seconds = int(round(seconds))
176
177     pdays = str()
178     phours = str()
179     pminutes = str()
180     pseconds = str()
181
182     if days > 0:
183         pdays = (green_start + str(days) +
184                  color_stop + " day ")
185         if days != 1:
186             pdays = (green_start + str(days) +
187                          color_stop + " days ")
188
189     if hours > 0:
190         phours = (green_start + str(hours) +
191                   color_stop + " hour ")
192         if hours != 1:
193             phours = (green_start + str(hours) +
194                           color_stop + " hours ")
195
196     if minutes > 0:
197         pminutes = (green_start + str(minutes) +
198                     color_stop + " minute ")
199         if minutes != 1:
200             pminutes = (green_start + str(minutes) +
201                         color_stop + " minutes ")
202
203     pseconds = (green_start + str(seconds) +
204                 color_stop + " second ")
205     if seconds != 1:
206         pseconds = (green_start + str(seconds) +
207                     color_stop + " seconds ")
208
209     green_start = "\033[32m"
210     color_stop = "\033[m"
211
212     return (pdays + phours + pminutes + pseconds)
213
214
215
216 def give_date(emerge_date):
217     """Returns a date string from a standard POSIX time"""
218     date = datetime.datetime.fromtimestamp(emerge_date)
219
220     date = "{:%d.%m.%Y %H:%M:%S}".format(date)
221
222     return date
223
224
225
226 def open_log():
227     """Attempt to open the LOGFILE."""
228
229     try:
230         f = open(LOGFILE, 'r')
231     except IOError as detail:
232         print detail
233         sys.exit(1)
234
235     return f
236
237
238
239 def search_log_for_package(package_class):
240     """Searchs emerge log for given package and adds all found
241     versions with their emerge times to the class"""
242
243     log = open_log()
244
245     for line in log:
246         if ((">>>" in line) and ("emerge" in line)):
247             if package_class.name in line:
248
249                 version = line.partition(package_class.name)[2].partition(' ')[0]
250                 digit = version.strip('-')[0].isdigit()
251
252                 if digit:
253                     time_string = line.partition(">>>")
254                     start_time = float(time_string[0].strip().strip(':'))
255
256         elif ((":::" in line) and ("completed emerge" in line)):
257
258             if package_class.name in line:
259                 if digit:
260                     time_string = line.partition(":::")
261                     stop_time = float(time_string[0].strip().strip(':'))
262
263                     emerge_time = stop_time - start_time
264
265                     package_class.add_version(version, emerge_time, start_time)
266
267
268
269 def get_package(name):
270     """Take the user-input package name and search for it
271     in PORTDIR. """
272
273     dirlist = os.listdir(PORTDIR)
274     possible_package = []
275
276
277     # If the given name is in the format xxx/zzz
278     # assume that xxx is the package group
279     if '/' in name:
280         group = name.partition('/')[0]
281         pkg = name.partition('/')[2]
282         directory = PORTDIR + group
283
284         if group in dirlist:
285             dirs = os.listdir(directory)
286             if pkg in dirs:
287                 possible_package.append(name)
288
289
290     # Go through the directory listing searching for anything
291     # that matches the given name
292     for i in dirlist:
293         directory = PORTDIR + i
294         if os.path.isdir(directory):
295             dirs = os.listdir(directory)
296             if name in dirs:
297                 possible_package.append(i + '/' + name)
298
299
300     if len(possible_package) > 1:
301         print("Multiple packages found for '" + name + "'.")
302         print("Possible packages: ")
303         for value in possible_package:
304             print("\t" + value)
305
306
307     elif len(possible_package) == 1:
308         package = possible_package[0]
309         return package
310
311
312     else:
313         print("No package '" + name + "' found")
314
315
316     sys.exit(1)
317
318
319
320 def list_pretended():
321     """List all the pretended packages given by emerge -p
322     output. Create a class out of each of those packages and add them
323     to the list."""
324
325     log = open_log()
326
327     for line in sys.stdin:
328         if "[ebuild" in line:
329             full_name = line.partition("] ")[2].partition(' ')[0]
330
331             version = full_name.partition('/')[2].partition('-')[2]
332             while not version[0].isdigit():
333                 version = version.partition('-')[2]
334             package_name = full_name[:-len(version)-1]
335
336             PACKAGES.append(package(package_name, version))
337
338
339
340 def list_emerge_processes():
341     """Look for the ebuild process with ps. If the process is found parse
342     the command for the package. With this package search the LOGFILE for
343     the emerge startup time."""
344
345     f = open_log()
346
347     now = datetime.datetime.today()
348
349     for i in os.popen("ps ax"):
350         if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
351             pack = i.partition('[')[2].partition(']')[0]
352
353             version = pack.partition('/')[2].partition('-')[2]
354
355             while not version[0].isdigit():
356                 version = version.partition('-')[2]
357
358             package_name = pack[:-len(version)-1]
359
360             PACKAGES.append(package(package_name, version))
361
362
363     if len(PACKAGES) == 0:
364         print "No current emerge process found."
365         sys.exit(0)
366
367
368     for line in f:
369         if ((">>>" in line) and ("emerge" in line)):
370             for p in PACKAGES:
371                 if (p.name + '-' + p.version in line):
372
373                     time = float(line.partition(' ')[0].strip(":"))
374
375                     timestamp = datetime.datetime.fromtimestamp(time)
376                     difference = (now - timestamp).total_seconds()
377
378                     if difference < p.emerge_time:
379                         p.emerge_time = difference
380
381
382
383 def main(status, user_package=None):
384     """Main function. Hanlde all the different modes of operation."""
385
386     if status == "package":
387         user_package = get_package(user_package)
388
389         pack = package(user_package)
390
391         search_log_for_package(pack)
392         pack.print_versions()
393         pack.print_min_max_ave()
394
395
396     elif status == "current":
397         list_emerge_processes()
398
399         print "Currently emerging:"
400
401         for p in PACKAGES:
402             search_log_for_package(p)
403             p.print_current_emerge()
404
405
406     elif status == "pretended":
407         list_pretended()
408
409         print "This is how long these packages would take to emerge"
410
411         total_pretended_time = 0
412
413         for p in PACKAGES:
414             search_log_for_package(p)
415
416             total_pretended_time += p.print_pretended_times()
417
418             print
419
420         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
421               color_stop + " package(s): "+ give_time(total_pretended_time))
422
423
424 def usage():
425     usage = """Usage: emerge-timer.py [package] [options]
426
427 Calculate emerge times from emerge log.
428
429 Options:
430 \t-c, --current \t Show time until currently compiling package finishes
431 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
432 \t-h, --help \t Show this helpscreen
433 \t-q, --quiet \t Be less verbose
434 \t--no-color \t Use colorless output"""
435
436     print usage
437
438     sys.exit(0)
439
440
441 if __name__ == "__main__":
442
443     # Set the default mode as "package"
444     mode = "package"
445     input_package = None
446
447
448     for arg in sys.argv[1:]:
449
450         if arg == "-p" or arg == "--pretended":
451             mode = "pretended"
452
453         if arg == "-c" or arg == "--current":
454             mode = "current"
455
456         if arg == "-h" or arg == "--help":
457             usage()
458
459         if arg == "-q" or arg == "--quiet":
460             QUIET = True
461
462         if arg == "--no-color":
463             green_start = ""
464             color_stop = ""
465
466
467     if len(sys.argv) > 1:
468         input_package = sys.argv[1]
469     else:
470         usage()
471
472     main(mode, input_package)