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