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