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