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