]> git.itanic.dy.fi Git - emerge-timer/blobdiff - emerge-timer.py
Use float number when dealing with the time simplification
[emerge-timer] / emerge-timer.py
index 7967574b97bf55a4f2a79ad525c12082dcd9399e..e1d1ec5d3582d889638c1bba7ffd04c31eef4dc5 100755 (executable)
 #!/usr/bin/python
 
-import sys, subprocess, datetime, os
+
+import sys, datetime, os
+
 
 PORTDIR = "/usr/portage/"
 LOGFILE = "/var/log/emerge.log"
 
-emerge_number = 0
-times = []
 
 green_start = "\033[32m"
+red_start = "\033[1;31m"
 color_stop = "\033[m"
 
-def write_times(emerge_times_list):
-    with open('times', 'w') as g:
+QUIET = False
+
+PACKAGES = []
+
+
+
+class package:
+    def __init__(self, name, version=0):
+        self.name = name
+        self.version = version
+        self.versions = []
+        self.emerge_time = "infinity"
+
+
+    def add_version(self, version, emerge_time, emerge_date):
+        """Add version to the class version list"""
+        self.versions.append((version, emerge_time, emerge_date))
+
+
+    def average_time(self):
+        """Return average time from class version list"""
+        total_time = 0
+        for i in self.versions:
+            total_time += i[1]
+
+        average_time = total_time/len(self.versions)
+
+        return average_time
+
+
+    def total_time(self):
+        """Return total time from class version list"""
+        total_time = 0
+        for i in self.versions:
+            total_time += i[1]
+
+        return total_time
+
+
+    def max_time(self):
+        """Return maximum time from class version list"""
+        self.versions.sort()
+
+        return self.versions[len(self.versions)-1][1]
+
+
+    def min_time(self):
+        """Return minimum time from class version list"""
+        self.versions.sort()
+
+        return self.versions[0][1]
+
+
+    def print_current_emerge(self):
+        """Function used to print all the current emerge stuff"""
+
+        print("\t" + green_start + self.name + '-' + self.version +
+              color_stop + "\n\t current time: " + give_time(self.emerge_time))
+
+
+        if len(self.versions) == 1:
+            print("\t last time:   "),
+            print(give_time(self.average_time())),
+
+        elif len(self.versions) > 1:
+            print("\t average time:"),
+            print(give_time(self.average_time())),
+
+        else:
+            print("\t average time: " + green_start + "unknown\n" + color_stop),
+            return
+
+        print("\n\t " + '-'*45),
+
+        print("\n\t time to finish:"),
+
+        if type(self.emerge_time) != str:
+
+            finish_time = self.average_time() - self.emerge_time
+
+            if finish_time > 0:
+                print(give_time(finish_time))
+            else:
+                print(green_start + "any time now" + color_stop)
+        else:
+            print(green_start + "unknown" + color_stop)
+        print
+
 
-        for i in emerge_times_list:
-            line = str(i[0]) + " " + str(i[1]) + "\n"
-            g.write(line)
+    def print_versions(self):
+        """This prints the emerge times for different versions in the
+        'package' operating mode of the script"""
 
-    g.close()
+        if QUIET == False:
 
+            version_length = 0
+            time_length = 0
 
-def organize_times(time):
-    days = time/(3600*24)
-    hours = (days - int(days))*24
-    minutes = (hours - int(hours))*60
-    seconds = (minutes - int(minutes))*60
+            for p in self.versions:
+                if len(p[0]) > version_length:
+                    version_length = len(p[0])
+
+                if len(give_time(p[1], True)) > time_length:
+                    time_length = len(give_time(p[1], True))
+
+            for p in self.versions:
+
+                pad = time_length - len(give_time(p[1], True))
+
+                print('-'*90 + "\n" +
+                      green_start + self.name + (p[0]).ljust(version_length) +
+                      color_stop + "  >>>  " + (give_time(p[1])) + " "*pad +
+                      "  >>>  " + give_date(p[2]))
+
+        print('-'*90 + "\n" + "Package " + green_start +
+              self.name + color_stop + " emerged " +
+              str(len(self.versions)) + " times.")
+
+        print
+
+
+    def print_pretended_times(self):
+        """This is used the print all the pretended times"""
+
+        if QUIET == False:
+            print("\t" + green_start + self.name + '-' + self.version + color_stop),
+
+        if len(self.versions) > 1:
+            aver_time = self.average_time()
+
+            if QUIET == False:
+                print("\n\taverage time: " + give_time(aver_time))
+
+            return aver_time
+
+        else:
+            if QUIET == False:
+                print("\n\t no previous emerges")
+
+            return 0
+
+
+    def print_min_max_ave(self):
+        maxi = self.max_time()
+        mini = self.min_time()
+        average = self.average_time()
+        total = self.total_time()
+
+        print("Max time:\t" + give_time(maxi) +
+              "\nMin time:\t" + give_time(mini) +
+              "\nAverage time:\t" + give_time(average) +
+              "\nIn total spent:\t" + give_time(total) +
+              "emerging " + green_start + self.name + color_stop)
+
+
+
+def give_time(time, nocolor=False):
+    """Converts time in seconds to human readable form"""
+    global green_start, color_stop
+
+    if green_start == "":
+        nocolor = False
+
+    if nocolor == True:
+        green_start = ""
+        color_stop = ""
+
+    if type(time) == str:
+        return(green_start + "unknown" + color_stop)
+
+
+    days = time/(3600.0*24.0)
+    hours = (days - int(days))*24.0
+    minutes = (hours - int(hours))*60.0
+    seconds = (minutes - int(minutes))*60.0
 
     days = int(days)
     hours = int(hours)
     minutes = int(minutes)
     seconds = int(round(seconds))
 
+    pdays = str()
+    phours = str()
+    pminutes = str()
+    pseconds = str()
+
     if days > 0:
-        print_days = (green_start + str(days) + color_stop + " day")
+        pdays = (green_start + str(days) +
+                 color_stop + " day ")
         if days != 1:
-            print_days += "s"
-        print print_days,
+            pdays = (green_start + str(days) +
+                         color_stop + " days ")
 
     if hours > 0:
-        print_hours = (green_start + str(hours) + color_stop + " hour")
+        phours = (green_start + str(hours) +
+                  color_stop + " hour ")
         if hours != 1:
-            print_hours += "s"
-        print print_hours,
+            phours = (green_start + str(hours) +
+                          color_stop + " hours ")
 
     if minutes > 0:
-        print_minutes = (green_start + str(minutes) + color_stop + " minute")
+        pminutes = (green_start + str(minutes) +
+                    color_stop + " minute ")
         if minutes != 1:
-            print_minutes += "s"
-        print print_minutes,
+            pminutes = (green_start + str(minutes) +
+                        color_stop + " minutes ")
 
-    printable_sec = (green_start + str(seconds) + color_stop + " second")
+    pseconds = (green_start + str(seconds) +
+                color_stop + " second ")
     if seconds != 1:
-        printable_sec += "s"
-    print printable_sec,
+        pseconds = (green_start + str(seconds) +
+                    color_stop + " seconds ")
 
+    if nocolor == True:
+        green_start = "\033[32m"
+        color_stop = "\033[m"
 
-def date_printer(package, emerge_start, emerge_time):
-    date = datetime.datetime.fromtimestamp(emerge_start)
+    return (pdays + phours + pminutes + pseconds)
 
-    year = str(date.year)
-    month = str(date.month)
-    day = str(date.day)
-    weekday = date.weekday()
 
-    if weekday == 0: weekday = 'Mon '
-    if weekday == 1: weekday = 'Tue '
-    if weekday == 2: weekday = 'Wed '
-    if weekday == 3: weekday = 'Thu '
-    if weekday == 4: weekday = 'Fri '
-    if weekday == 5: weekday = 'Sat '
-    if weekday == 6: weekday = 'Sun '
 
-    hour = str(date.hour)
-    minute = str(date.minute)
-    second = str(date.second)
+def give_date(emerge_date):
+    """Returns a date string from a standard POSIX time"""
+    date = datetime.datetime.fromtimestamp(emerge_date)
 
-    date = weekday + "{:%d.%m.%Y %H:%M:%S}".format(date)
+    date = "{:%d.%m.%Y %H:%M:%S}".format(date)
 
     return date
 
 
+
+def open_log():
+    """Attempt to open the LOGFILE."""
+
+    try:
+        f = open(LOGFILE, 'r')
+    except IOError as detail:
+        print detail
+        sys.exit(1)
+
+    return f
+
+
+
+def search_log_for_package(package_class):
+    """Searchs emerge log for given package and adds all found
+    versions with their emerge times to the class"""
+
+    log = open_log()
+
+    for line in log:
+        if ((">>>" in line) and ("emerge" in line)):
+            if package_class.name in line:
+
+                version = line.partition(package_class.name)[2].partition(' ')[0]
+                digit = version.strip('-')[0].isdigit()
+
+                if digit:
+                    time_string = line.partition(">>>")
+                    start_time = float(time_string[0].strip().strip(':'))
+
+        elif ((":::" in line) and ("completed emerge" in line)):
+
+            if package_class.name in line:
+                if digit:
+                    time_string = line.partition(":::")
+                    stop_time = float(time_string[0].strip().strip(':'))
+
+                    emerge_time = stop_time - start_time
+
+                    package_class.add_version(version, emerge_time, start_time)
+
+
+
 def get_package(name):
+    """Take the user-input package name and search for it
+    in PORTDIR. """
+
     dirlist = os.listdir(PORTDIR)
     possible_package = []
 
+
+    # If the given name is in the format xxx/zzz
+    # assume that xxx is the package group
     if '/' in name:
         group = name.partition('/')[0]
         pkg = name.partition('/')[2]
@@ -95,6 +306,9 @@ def get_package(name):
             if pkg in dirs:
                 possible_package.append(name)
 
+
+    # Go through the directory listing searching for anything
+    # that matches the given name
     for i in dirlist:
         directory = PORTDIR + i
         if os.path.isdir(directory):
@@ -102,200 +316,242 @@ def get_package(name):
             if name in dirs:
                 possible_package.append(i + '/' + name)
 
+
     if len(possible_package) > 1:
         print("Multiple packages found for '" + name + "'.")
         print("Possible packages: ")
         for value in possible_package:
             print("\t" + value)
 
+
     elif len(possible_package) == 1:
         package = possible_package[0]
         return package
 
+
     else:
         print("No package '" + name + "' found")
 
+
     sys.exit(1)
 
 
-def print_times(package, times, silent):
-    times.sort()
-    times.reverse()
 
-    if silent == True:
-        if emerge_number == 0:
-            print("\t  no previous emerges found for this package"),
+def list_pretended():
+    """List all the pretended packages given by emerge -p
+    output. Create a class out of each of those packages and add them
+    to the list."""
 
-        elif emerge_number == 1:
-            print("\t  previous emerge time"),
-            organize_times(times[0][0])
-            print("(only one emerge previously)"),
+    log = open_log()
 
-        else:
-            print("\t  average emerge time: "),
-            all_times = 0
-            for i in times:
-                all_times += i[0]
+    for line in sys.stdin:
+        if "[ebuild" in line:
+            full_name = line.partition("] ")[2].partition(' ')[0]
 
-            organize_times(all_times/len(times))
+            version = full_name.partition('/')[2].partition('-')[2]
+            while not version[0].isdigit():
+                version = version.partition('-')[2]
+            package_name = full_name[:-len(version)-1]
 
-        print('\n')
-        return
+            PACKAGES.append(package(package_name, version))
 
 
-    if emerge_number == 1:
-        print(green_start + package + color_stop + " emerged once")
 
-    else:
-        print(green_start + package + color_stop + " emerged " + green_start +
-              str(emerge_number) + color_stop + " times\n")
+def list_emerge_processes():
+    """Look for the ebuild process with ps. If the process is found parse
+    the command for the package. With this package search the LOGFILE for
+    the emerge startup time."""
 
-        print "Max time:\t",
-        organize_times(times[0][0])
-        print "at", times[0][1]
+    f = open_log()
 
-        print "Min time:\t",
-        organize_times(times[len(times)-1][0])
-        print "at", times[len(times)-1][1]
+    now = datetime.datetime.today()
 
-        all_times = 0
-        for i in times:
-            all_times += i[0]
+    for i in os.popen("ps ax"):
+        if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
+            pack = i.partition('[')[2].partition(']')[0]
 
-        print "Average time\t",
-        organize_times(all_times/len(times))
-        print
+            version = pack.partition('/')[2].partition('-')[2]
 
-        print "In total spent\t",
-        organize_times(all_times)
-        print("emerging " + green_start +
-              package + color_stop)
+            while not version[0].isdigit():
+                version = version.partition('-')[2]
 
+            package_name = pack[:-len(version)-1]
 
-def open_log():
-    try:
-        f = open(LOGFILE, 'r')
-    except IOError:
-        print "Permission denied: ", LOGFILE
-        sys.exit(1)
-    finally:
-        return f
+            PACKAGES.append(package(package_name, version))
 
 
-def list_emerge_processes(f):
+    if len(PACKAGES) == 0:
+        print "No current emerge process found."
 
-    packages = []
-    for i in os.popen("ps ax"):
-        if (("ebuild" in i) and ("/bin/bash" not in i)):
-             pack = i.partition('[')[2].partition(']')[0]
+        return 1
 
-             packages.append([pack, 0])
 
     for line in f:
-        for p in packages:
-            if ((p[0] in line) and (">>>" in line) and ("emerge" in line)):
-
-                time = float(line.partition(' ')[0].strip(":"))
-                if time > p[1]:
-                    p[1] = time
-
-    a = 1
-    for p in packages:
-        now = datetime.datetime.today()
-        timestamp = datetime.datetime.fromtimestamp(p[1])
-        difference = now - timestamp
-
-        if difference.total_seconds() < 12*60*60:
-            if a:
-                print("Currently emerging: ")
-                a = 0
-            print("\t" + green_start + p[0] + color_stop),
-            print('\n\t  current emerge time: '),
-            organize_times(difference.total_seconds())
-            print
+        if ((">>>" in line) and ("emerge" in line)):
+            for p in PACKAGES:
+                difference = 0
 
-            name = (p[0].partition('-')[0] + '-' +
-                    p[0].partition('-')[2].partition('-')[0])
-            main_loop(f, name, True)
+                if (p.name + '-' + p.version in line):
 
+                    time = float(line.partition(' ')[0].strip(":"))
 
+                    timestamp = datetime.datetime.fromtimestamp(time)
+                    difference = (now - timestamp).total_seconds()
 
-def main_loop(f, package, silent):
-    global emerge_number
-    emerge_number = 0
-    f.seek(0)
+                    if ((difference < p.emerge_time) or
+                        (p.emerge_time == "infinity")):
 
-    # MAIN LOOP
-    for line in f:
-        if package in line:
-            st = line.split(' ')
-            for string in st:
-                if ((package in string) and (len(string) > len(package)+1)
-                    and (string[len(package)+1].isdigit())):
-                    full_package = st[st.index(string)]
+                        p.emerge_time = difference
 
-                    if ((">>>" in line) and ("emerge" in line)):
-                        time_string = line.partition(">>>")
-                        time = float(time_string[0].strip().strip(":"))
+    return 0
 
-                    try:
-                        if (":::" in line) and ("completed emerge" in line):
-                            emerge_number += 1
 
-                            time_string = line.partition(":::")
-                            time2 = float(time_string[0].strip().strip(":"))
 
-                            emerge_time = time2-time
+def main(status, user_package=None):
+    """Main function. Hanlde all the different modes of operation."""
 
-                            date = date_printer(full_package, time, emerge_time)
+    if status == "package":
+        user_package = get_package(user_package)
 
-                            if silent == False:
-                                print(str(len(times)+1) + "."),
-                                print(green_start + full_package + color_stop + "  >>>  "
-                                      + date + "  >>> "),
+        pack = package(user_package)
 
-                                organize_times(emerge_time)
+        search_log_for_package(pack)
 
-                                print("\n" + '-'*90)
+        if len(pack.versions) != 0:
+            pack.print_versions()
+            pack.print_min_max_ave()
 
-                            times.append((emerge_time, date))
+        else:
+            print("Package " + green_start + pack.name +
+                  color_stop + " has never been emerged.")
 
-                    except IndexError: pass
 
-    print_times(package, times, silent)
+    elif status == "current":
+        if list_emerge_processes():
+            return
 
+        print "Currently emerging:"
 
-def main(status):
-    global emerge_number
+        for p in PACKAGES:
+            search_log_for_package(p)
+            p.print_current_emerge()
 
-    f = open_log()
 
-    if status == 'current':
-        list_emerge_processes(f)
-        return
-    else:
-        pass
+    elif status == "pretended":
+        list_pretended()
 
-    package = get_package(package_name)
+        print "This is how long these packages would take to emerge"
 
-    print('-'*90)
+        total_pretended_time = 0
 
-    main_loop(f, package, False)
+        for p in PACKAGES:
+            search_log_for_package(p)
 
-    if emerge_number == 0:
-        print("Package '" + package + "' has never been emerged")
-        sys.exit(1)
+            total_pretended_time += p.print_pretended_times()
+
+            print
 
-    f.close()
+        print("Total emerge time of " + green_start + str(len(PACKAGES)) +
+              color_stop + " package(s): "+ give_time(total_pretended_time))
+
+
+def usage():
+    usage = """Usage: emerge-timer.py [package] [options]
+
+Calculate emerge times from emerge log.
+
+Options:
+\t-c, --current \t Show time until currently compiling package finishes
+\t-p, --pretended  Calculate compile time from piped 'emerge -p' output
+\t-h, --help \t Show this helpscreen
+\t-q, --quiet \t Be less verbose
+\t--no-color \t Use colorless output
+\t--simulate \t Do a simulation run"""
+
+    print usage
+
+    sys.exit(0)
 
 
 if __name__ == "__main__":
 
+    # Set the default mode as "package"
+    mode = "package"
+    input_package = None
+    simulation = False
+
+    for arg in sys.argv[1:]:
+
+        if arg == "-p" or arg == "--pretended":
+            mode = "pretended"
+
+        if arg == "-c" or arg == "--current":
+            mode = "current"
+
+        if arg == "-h" or arg == "--help":
+            usage()
+
+        if arg == "-q" or arg == "--quiet":
+            QUIET = True
+
+        if arg == "--no-color":
+            green_start = ""
+            color_stop = ""
+
+        if arg == "--simulate":
+            simulation = True
+
+
     if len(sys.argv) > 1:
-        package_name = sys.argv[1]
-        main(0)
+        input_package = sys.argv[1]
     else:
-        main('current')
-        sys.exit(1)
+        usage()
+
+    if simulation == True:
+
+        print(red_start + "\n" + '*'*25 + "\n" + "THIS IS A SIMULATION RUN\n"
+              + '*'*25 + "\n")
+
+        print("Beginning 'package' mode check")
 
-    write_times(times)
+        print("Checking for one emerge\n" + color_stop)
+
+        LOGFILE = "simulate/fake_emerge.log"
+        PORTDIR = "simulate/"
+
+        main("package", "first_fake_package")
+
+        print(red_start + "\nChecking for three emerges\n" + color_stop)
+
+        main("package", "second_fake_package")
+
+        print(red_start + "\n'package' mode check complete\n")
+
+        print(30*'*')
+
+        print("\nBeginning 'current' mode check")
+        print("Current emerge with no emerge process\n" + color_stop)
+
+        main("current", None)
+
+        print(red_start + "\nCurrent emerge with emerge processes\n" + color_stop)
+
+        PACKAGES.append(package("test-group/second_fake_package", "2.9-r2"))
+        PACKAGES.append(package("test-group/first_fake_package", "1.10.2-r1"))
+
+        main("current", None)
+
+        print(red_start + "\nCurrent emerge with incomplete emerge log" +
+              "(causes error in some cases)\n" + color_stop)
+
+        PACKAGES = []
+        PACKAGES.append(package("test-group/third_fake_package", "2.9-r2"))
+
+        main("current", None)
+
+        print(red_start + "\n" + '*'*20 + "\n" + "SIMULATION FINISHED\n" +
+              '*'*20 + color_stop)
+
+
+    else:
+        main(mode, input_package)