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