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