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