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