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