]> git.itanic.dy.fi Git - emerge-timer/blob - emerge-timer.py
Change how current emerge time is calculated a bit
[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 = 0
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                 difference = "infinity"
377                 new_difference = 0
378
379                 if (p.name + '-' + p.version in line):
380
381                     time = float(line.partition(' ')[0].strip(":"))
382
383                     timestamp = datetime.datetime.fromtimestamp(time)
384                     new_difference = (now - timestamp).total_seconds()
385
386                     if ((new_difference < difference) or
387                         (difference == "infinity")):
388                         difference = new_difference
389
390             p.emerge_time = difference
391
392     return 0
393
394
395
396 def main(status, user_package=None):
397     """Main function. Hanlde all the different modes of operation."""
398
399     if status == "package":
400         user_package = get_package(user_package)
401
402         pack = package(user_package)
403
404         search_log_for_package(pack)
405
406         if len(pack.versions) != 0:
407             pack.print_versions()
408             pack.print_min_max_ave()
409
410         else:
411             print("Package " + green_start + pack.name +
412                   color_stop + " has never been emerged.")
413
414
415     elif status == "current":
416         if list_emerge_processes():
417             return
418
419         print "Currently emerging:"
420
421         for p in PACKAGES:
422             search_log_for_package(p)
423             p.print_current_emerge()
424
425
426     elif status == "pretended":
427         list_pretended()
428
429         print "This is how long these packages would take to emerge"
430
431         total_pretended_time = 0
432
433         for p in PACKAGES:
434             search_log_for_package(p)
435
436             total_pretended_time += p.print_pretended_times()
437
438             print
439
440         print("Total emerge time of " + green_start + str(len(PACKAGES)) +
441               color_stop + " package(s): "+ give_time(total_pretended_time))
442
443
444 def usage():
445     usage = """Usage: emerge-timer.py [package] [options]
446
447 Calculate emerge times from emerge log.
448
449 Options:
450 \t-c, --current \t Show time until currently compiling package finishes
451 \t-p, --pretended  Calculate compile time from piped 'emerge -p' output
452 \t-h, --help \t Show this helpscreen
453 \t-q, --quiet \t Be less verbose
454 \t--no-color \t Use colorless output"""
455
456     print usage
457
458     sys.exit(0)
459
460
461 if __name__ == "__main__":
462
463     # Set the default mode as "package"
464     mode = "package"
465     input_package = None
466
467
468     for arg in sys.argv[1:]:
469
470         if arg == "-p" or arg == "--pretended":
471             mode = "pretended"
472
473         if arg == "-c" or arg == "--current":
474             mode = "current"
475
476         if arg == "-h" or arg == "--help":
477             usage()
478
479         if arg == "-q" or arg == "--quiet":
480             QUIET = True
481
482         if arg == "--no-color":
483             green_start = ""
484             color_stop = ""
485
486
487     if len(sys.argv) > 1:
488         input_package = sys.argv[1]
489     else:
490         usage()
491
492     main(mode, input_package)