]> git.itanic.dy.fi Git - emerge-timer/blobdiff - emerge-timer.py
Fix correct newline behaviour in python3
[emerge-timer] / emerge-timer.py
index f2885c164ab5029c79878105aeb7af0bbd8ab7b4..d782b34996fac6c9b6c791a58a069adace19de77 100755 (executable)
 #!/usr/bin/python
 
 
-import sys, subprocess, datetime, os
+import sys, datetime, os
 
 
 PORTDIR = "/usr/portage/"
 LOGFILE = "/var/log/emerge.log"
 
 
-
 green_start = "\033[32m"
+red_start = "\033[1;31m"
 color_stop = "\033[m"
 
+QUIET = False
+
+PACKAGES = []
+
+def GREEN(string):
+    return green_start + string + color_stop
+
+def RED(string):
+    return red_start + string + color_stop
+
+
+class package:
+    def __init__(self, name, version=0):
+        self.name = name
+        self.version = version
+        self.versions = []
+        self.emerge_time = "infinity"
+
+        self.plotData = []
+
+
+    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 seek_versions(self, whatToSeek, Input):
+
+        if whatToSeek == "date":
+            for i in self.versions:
+                if i[1] == Input:
+                    return i[2]
+
+        if whatToSeek == "version":
+            for i in self.versions:
+                if i[1] == Input:
+                    return i[0]
+
+
+    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 min_max_time(self, setting):
+        """Return maximum or minimum time from class version list"""
+
+        emerge_times = []
+        for i in self.versions:
+            emerge_times.append(i[1])
+
+        emerge_times.sort()
+
+        if setting == "max":
+            return emerge_times[-1]
+        elif setting == "min":
+            return emerge_times[0]
+
+
+
+
+    def print_current_emerge(self):
+        """Function used to print all the current emerge stuff"""
+
+        print("\t" + GREEN(self.name + '-' + self.version) +
+              "\n\t current time: " + give_time(self.emerge_time))
+
+
+        if len(self.versions) == 1:
+            print("\t last time:   "),
+            print(give_time(self.average_time()), end='')
+
+        elif len(self.versions) > 1:
+            print("\t average time: ", end='')
+            print(give_time(self.average_time()), end='')
+
+        else:
+            print("\t average time: " + GREEN("unknown\n")),
+            return
+
+        print("\n\t " + '-'*45, end='')
+
+        print("\n\t time to finish: ", end='')
+
+        if type(self.emerge_time) != str:
+
+            finish_time = self.average_time() - self.emerge_time
+
+            if finish_time > 0:
+                print(give_time(finish_time), "\n")
+            else:
+                print(GREEN("any time now"))
+        else:
+            print(GREEN("unknown"))
+        print
+
+
+    def print_versions(self):
+        """This prints the emerge times for different versions in the
+        'package' operating mode of the script"""
+
+        if QUIET == False:
+
+            version_length = 0
+            time_length = 0
+
+            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))
 
+                # Create the data for plotting in the format (emerge time, emerge date)
+                self.plotData.append((p[1], p[2]))
 
+            dots =  (version_length + time_length + len(self.name)
+                     + len(give_date(self.versions[0][2])) + 14)
 
+            for p in self.versions:
 
-def organize_times(time):
-    """Takes the emerge time in seconds, organizes it into
-    days, hours, minutes or seconds and finally prints that out."""
+                pad = time_length - len(give_time(p[1], True))
 
-    days = time/(3600*24)
-    hours = (days - int(days))*24
-    minutes = (hours - int(hours))*60
-    seconds = (minutes - int(minutes))*60
+                name = self.name
+                p_time = give_time(p[1])
+                p_date = give_date(p[2])
+
+                print('-' * dots + "\n" +
+                      GREEN(name + (p[0]).ljust(version_length))
+                      + "  >>>  " + p_time + " "*pad + "  >>>  " + p_date)
+
+        print("\n" + "Package " + GREEN(self.name) + " emerged"),
+
+        if len(self.versions) > 1:
+            print(str(len(self.versions)) + " times.\n")
+        elif len(self.versions) == 1:
+            print("once.\n")
+
+
+
+    def print_pretended_times(self):
+        """This is used the print all the pretended times"""
+
+        if QUIET == False:
+            print("\t" + GREEN(self.name + '-' + self.version)),
+
+        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):
+
+        if len(self.versions) == 1:
+            return
+
+        maxi = self.min_max_time("max")
+        mini = self.min_max_time("min")
+        average = self.average_time()
+        total = self.total_time()
+
+        print("-"*20 +
+              "\nMaximum emerge time:\n" +
+              give_time(maxi) + "for version " +
+              GREEN(self.seek_versions("version", maxi).lstrip("-"))
+              + " on " +
+              give_date(self.seek_versions("date", maxi)))
+
+        print("-"*20 +
+              "\nMinimum emerge time:\n" +
+              give_time(mini) + "for version " +
+              GREEN(self.seek_versions("version", mini).lstrip("-"))
+              + " on " +
+              give_date(self.seek_versions("date", mini)))
+
+        print("-"*20 +
+              "\nAverage emerge time:\n" +
+              give_time(average))
+
+        print("-"*20 +
+              "\nIn total spent:\n" +
+              give_time(total))
+
+
+    def plotToScreen(self):
+        dates = []
+        times = []
+
+        for i in self.plotData:
+            dates.append(datetime.date.fromtimestamp(i[1]))
+            times.append(i[0])
+
+        fig = plt.figure()
+
+        plt.plot_date(dates, times, xdate=True, ydate=False)
+
+        plt.ylabel("Emerge time [s]")
+        plt.suptitle(self.name)
+        plt.grid()
+        fig.autofmt_xdate()
+        plt.show()
+
+
+
+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("unknown"))
+
+
+    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(str(days)) + " day ")
         if days != 1:
-            print_days += "s"
-        print print_days,
+            pdays = (GREEN(str(days)) + " days ")
 
     if hours > 0:
-        print_hours = (green_start + str(hours) + color_stop + " hour")
+        phours = (GREEN(str(hours)) + " hour ")
         if hours != 1:
-            print_hours += "s"
-        print print_hours,
+            phours = (GREEN(str(hours)) + " hours ")
 
     if minutes > 0:
-        print_minutes = (green_start + str(minutes) + color_stop + " minute")
+        pminutes = (GREEN(str(minutes)) + " minute ")
         if minutes != 1:
-            print_minutes += "s"
-        print print_minutes,
+            pminutes = (GREEN(str(minutes)) + " minutes ")
 
-    printable_sec = (green_start + str(seconds) + color_stop + " second")
-    if seconds != 1:
-        printable_sec += "s"
-    print printable_sec,
+    if seconds > 0:
+        pseconds = (GREEN(str(seconds)) + " second ")
+        if seconds != 1:
+            pseconds = (GREEN(str(seconds)) + " seconds ")
 
+    if nocolor == True:
+        green_start = "\033[32m"
+        color_stop = "\033[m"
 
+    return (pdays + phours + pminutes + pseconds)
 
-def get_date(emerge_start):
-    """Take the emerge startup time in seconds and turn it into a
-    correct date."""
 
-    date = datetime.datetime.fromtimestamp(emerge_start)
 
-    year = str(date.year)
-    month = str(date.month)
-    day = str(date.day)
-    weekday = date.weekday()
+def give_date(emerge_date):
+    """Returns a date string from a standard POSIX time"""
+    date = datetime.datetime.fromtimestamp(emerge_date)
 
-    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 '
+    date = "{:%d.%m.%Y %H:%M:%S}".format(date)
 
-    hour = str(date.hour)
-    minute = str(date.minute)
-    second = str(date.second)
+    return date
 
-    # This is in the format 'Mon 23.05.2011 00:20:14'
 
-    date = weekday + "{:%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 list_all_packages():
-    """Go through PORTDIR and create a list of all the packages in portage"""
 
-    root = os.listdir(PORTDIR)
+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 search_log_for_all_packages():
+    """Goes through the emerge.log and lists all packages in there"""
+
+    log = open_log()
+
     all_packages = []
 
-    for package_group in root:
-        group_dir = PORTDIR + package_group
-        if (os.path.isdir(group_dir)
-            and (package_group != "licenses")
-            and (package_group != "metadata")):
+    total_emerge_time = 0
+    emerge_number = 0
+
+    for line in log:
+        if ((">>>" in line) and ("emerge" in line)):
+            pack = line.partition(')')[2].strip().partition(' ')[0]
+            start_time = float(line.partition(':')[0])
+
+            all_packages.append((pack, start_time))
 
-            name_dir = os.listdir(group_dir)
+        elif ((":::" in line) and ("completed emerge" in line)):
+            for p in all_packages:
+                if p[0] in line:
+                    print("\t" + give_date(p[1]) + " >>> " + GREEN(p[0]))
 
-            for package_name in name_dir:
-                if ".xml" not in package_name:
+                    emerge_number += 1
 
-                    all_packages.append((package_group +
-                                         '/' + package_name))
+                    all_packages.pop(all_packages.index(p))
 
-    return all_packages
 
+    print("\n" + GREEN(str(emerge_number)) + " emerges in total found.")
 
 
 
@@ -162,284 +434,240 @@ def get_package(name):
 
 
 
-def print_times(package, times, silent):
-    """Print the maximum/minimum/average times of the given emerge package.
-    If we're in the 'current emerge stage' (the 'silent' flag is True)
-    print the appropriate comment for that 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."""
 
+    log = open_log()
 
-    times.sort()
-    times.reverse()
-
-
-    # This should be True if we're in current emerge stage
-    if silent == True:
-        if len(times) == 0:
-            print("\t  no previous emerges found for this package"),
-            return 0
-
-        elif len(times) == 1:
-            print("\t  previous emerge time:\t"),
-            organize_times(times[0][0])
-            print("(only one emerge previously)"),
-            return times[0][0]
-
-        else:
-            print("\t  average emerge time:\t"),
-            all_times = 0
-            for i in times:
-                all_times += i[0]
-
-            organize_times(all_times/len(times))
-
-        return all_times/len(times)
-
-
-    if len(times) == 1:
-        print(green_start + package + color_stop + " emerged once")
-
-    elif len(times) > 1:
-        print(green_start + package + color_stop + " emerged " + green_start +
-              str(len(times)) + color_stop + " times\n")
+    for line in sys.stdin:
+        if "[ebuild" in line:
+            full_name = line.partition("] ")[2].partition(' ')[0]
 
-        print "Max time:\t",
-        organize_times(times[0][0])
-        print "at", times[0][1]
+            version = full_name.partition('/')[2].partition('-')[2]
+            while not version[0].isdigit():
+                version = version.partition('-')[2]
+            package_name = full_name[:-len(version)-1]
 
-        print "Min time:\t",
-        organize_times(times[len(times)-1][0])
-        print "at", times[len(times)-1][1]
+            PACKAGES.append(package(package_name, version))
 
-        all_times = 0
-        for i in times:
-            all_times += i[0]
 
-        print "Average time\t",
-        organize_times(all_times/len(times))
-        print
 
-        print "In total spent\t",
-        organize_times(all_times)
-        print("emerging " + green_start +
-              package + color_stop)
+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."""
 
+    f = open_log()
 
+    now = datetime.datetime.today()
 
-def open_log():
-    """Attempt to open the LOGFILE."""
+    for i in os.popen("ps ax"):
+        if (("ebuild.sh" in i) and ("/bin/bash" not in i)):
+            pack = i.partition('[')[2].partition(']')[0]
 
-    try:
-        f = open(LOGFILE, 'r')
-    except IOError as detail:
-        print detail
-        sys.exit(1)
-    finally:
-        return f
+            version = pack.partition('/')[2].partition('-')[2]
 
+            while not version[0].isdigit():
+                version = version.partition('-')[2]
 
+            package_name = pack[:-len(version)-1]
 
-def list_emerge_processes(f):
-    """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."""
+            PACKAGES.append(package(package_name, version))
 
-    now = datetime.datetime.today()
-    packages = []
 
-    for i in os.popen("ps ax"):
-        if (("ebuild" in i) and ("/bin/bash" not in i)):
-             pack = i.partition('[')[2].partition(']')[0]
+    if len(PACKAGES) == 0:
+        print("No current emerge process found.")
 
-             packages.append([pack, 12*3600])
+        return 1
 
 
     for line in f:
         if ((">>>" in line) and ("emerge" in line)):
-            for p in packages:
-                if (p[0] in line):
+            for p in PACKAGES:
+                difference = 0
+
+                if (p.name + '-' + p.version in line):
 
                     time = float(line.partition(' ')[0].strip(":"))
 
                     timestamp = datetime.datetime.fromtimestamp(time)
                     difference = (now - timestamp).total_seconds()
 
-                    if difference < p[1]:
-                        p[1] = difference
-
-
-    if len(packages) == 0:
-        print "No current emerge process found."
-        return
-
-    print_current_emerges(f, packages)
+                    if ((p.emerge_time == "infinity") or
+                        (difference < p.emerge_time)):
 
+                        p.emerge_time = difference
 
+    return 0
 
 
-def print_current_emerges(f, packages):
-    """Print the current packages that are being merged with the
-    current emerge time."""
+def search_syncs():
+    f = open_log()
 
-    all_packages = list_all_packages()
+    print("These emerge syncs found")
+    print("\tDate                    Server")
+    print("\t------------------------------")
 
-    print("Currently emerging: ")
+    a = 0
+    for line in f:
+        if "=== Sync completed with" in line:
+            time = float(line.partition(' ')[0].strip(":"))
+            server = line.rpartition(' ')[2]
 
-    for p in packages:
-        print("\t" + green_start + p[0] + color_stop),
-        print("\n\t  current emerge time:\t"),
+            print("\t" + GREEN(give_date(time)) +
+                  " === " + server),
+            a += 1
 
-        organize_times(p[1])
-        print
+    print("\n" + GREEN(str(a)) + " emerge syncs found. ")
 
 
-        for i in all_packages:
-            if i in p[0]:
-                average_time = main_loop(f, i, True)
+def main(status, user_package=None):
+    try:
+        _main(status, user_package)
+    except IOError:
+        sys.exit()
 
-        if average_time != 0:
-            print("\n\t  " + '-'*45 + "\n\t  time to finish: \t"),
-            if (average_time - p[1]) < 0:
-                print(green_start + "Any time now" + color_stop),
-            else:
-                organize_times(average_time - p[1])
-            print "\n"
 
+def _main(status, user_package=None):
+    """Main function. Handle all the different modes of operation."""
 
+    if status == "package":
+        for p in user_package:
+            pack = get_package(p)
 
-def list_pretended(f):
-    """Print the average times of pretended packages"""
+            pack = package(pack)
 
-    packages = []
-    for line in sys.stdin:
-        if "[ebuild" in line:
-            full_name = line.partition('] ')[2].partition(' ')[0]
+            search_log_for_package(pack)
 
-            version = full_name.partition('/')[2].partition('-')[2]
-            while not version[0].isdigit():
-                version = version.partition('-')[2]
+            if len(pack.versions) != 0:
+                pack.print_versions()
+                pack.print_min_max_ave()
 
-            package_name = full_name[:-len(version)-1]
+                if matplotWorking:
+                    pack.plotToScreen()
 
-            packages.append((package_name, '-' + version))
+            else:
+                print("Package " + GREEN(pack.name)
+                      + " has never been emerged.")
 
 
-    if len(packages) == 0:
+    elif status == "sync":
+        search_syncs()
         return
 
-    print "This is how long these packages would take to emerge"
-
-    all_time = 0
-    for pack in packages:
-
-        print('\t' + green_start + pack[0] + pack[1] + color_stop)
-
-        all_time += main_loop(f, pack[0], True)
-
-        print "\n"
-
-
-    if all_time != 0:
-        print("Total emerge time of " + green_start + str(len(packages)) +
-              color_stop + " packages:"),
-        organize_times(all_time)
 
+    elif status == "list":
+        search_log_for_all_packages()
+        return
 
 
+    elif status == "current":
+        if list_emerge_processes():
+            return
 
-def main_loop(f, package, silent):
-    """The main loop which parses the LOGFILE and if needed prints out emerge times."""
+        print("Currently emerging:")
 
-    f.seek(0) # Seek to the beginning of the file
-    times = []
+        for p in PACKAGES:
+            search_log_for_package(p)
+            p.print_current_emerge()
 
-    # MAIN LOOP
-    for line in f:
-        if ((">>>" in line) and ("emerge" in line)):
-            if package in line:
-                version = line.partition(package)[2].partition(' ')[0]
 
-                if version.strip('-')[0].isdigit():
-                    full_package = package + version
+    elif status == "pretended":
+        list_pretended()
 
-                    time_string = line.partition(">>>")
-                    start_time = float(time_string[0].strip().strip(':'))
+        print("This is how long these packages would take to emerge")
 
+        total_pretended_time = 0
 
-        elif ((":::" in line) and ("completed emerge" in line)):
-            if package in line:
-                if version.strip('-')[0].isdigit():
+        for p in PACKAGES:
+            search_log_for_package(p)
 
-                    time_string = line.partition(":::")
-                    end_time = float(time_string[0].strip().strip(':'))
+            total_pretended_time += p.print_pretended_times()
 
+            print
 
-                    emerge_time = end_time - start_time
+        print("Total emerge time of " + GREEN(str(len(PACKAGES)))
+              + " package(s): "+ give_time(total_pretended_time))
 
-                    date = get_date(start_time)
 
+def usage():
+    usage = """Usage: emerge-timer.py [package] [options]
 
-                    if silent == False:
-                        print(str(len(times)+1) + ". " +
-                              green_start + full_package + color_stop +
-                              "  >>>  " + date + "  >>>  "),
+Calculate emerge times from emerge log.
 
-                        organize_times(emerge_time)
+Options:
+\t-c, --current \t Show time until currently compiling package finishes
+\t-p, --pretended  Calculate compile time from piped 'emerge -p' output
+\t-l, --list \t List all emerged packages
+\t-s, --sync \t Show emerge sync history
+\t-h, --help \t Show this helpscreen
+\t-q, --quiet \t Be less verbose
+\t--no-color \t Use colorless output
+\t--plot \t\t Plot emerge times into a 2D scatter plot
+\t\t\t (needs matlibplot installed for this to work)"""
 
-                        print("\n" + '-'*90)
 
+    print(usage)
 
-                    times.append((emerge_time, date))
+    sys.exit(0)
 
 
-    average_time = print_times(package, times, silent)
-    return average_time
+if __name__ == "__main__":
 
+    # Set the default mode as "package"
+    mode = "package"
+    input_packages = None
+    simulation = False
+    matplotWorking = False
 
+    for arg in sys.argv[1:]:
 
-def main(status):
-    """Change between current emerge stage and normal operating stage."""
+        if arg == "-p" or arg == "--pretended":
+            mode = "pretended"
 
-    f = open_log()
+        if arg == "-c" or arg == "--current":
+            mode = "current"
 
-    if status == 'current':
-        list_emerge_processes(f)
-        return
-    elif status == 'pretended':
-        list_pretended(f)
-        return
-    else:
-        pass
+        if arg == "-h" or arg == "--help":
+            usage()
 
-    package = get_package(package_name)
+        if arg == "-l" or arg == "--list":
+            mode = "list"
 
-    print('-'*90)
+        if arg == "-s" or arg == "--sync":
+            mode = "sync"
 
-    main_loop(f, package, False)
+        if arg == "-q" or arg == "--quiet":
+            QUIET = True
 
-    f.close()
+            sys.argv.pop(sys.argv.index(arg))
 
+        if arg == "--plot":
+            try:
+                import matplotlib.pyplot as plt
+                matplotWorking = True
+            except ImportError:
+                print(RED("Cannot initialize matplotlib!"))
+                print(RED("Check that you have properly installed matplotlib.\n"))
+                matplotWorking = False
 
+            sys.argv.pop(sys.argv.index(arg))
 
-if __name__ == "__main__":
+        if arg == "--no-color":
+            green_start = ""
+            color_stop = ""
 
-    if len(sys.argv) == 1:
-        main('current')
-        sys.exit(1)
+            sys.argv.pop(sys.argv.index(arg))
 
-    elif sys.argv[1] == "-p":
-        main('pretended')
-        sys.exit(1)
+        if arg == "--simulate":
+            simulation = True
 
-    elif ((sys.argv[1] == "-h") or (sys.argv[1] == "--help")):
-        print("Usage: emerge-timer.py [options] [package]\n\nOptions:\n"
-              + green_start + "\t-p" + color_stop +
-              "\tcalculate compile time from piped 'emerge -p' output\n" +
-              green_start + "\t[none]" + color_stop +
-              "\tShow average emerge times for currently compiling packages.")
-        sys.exit(1)
 
     if len(sys.argv) > 1:
-        package_name = sys.argv[1]
-        main(0)
-
+        input_packages = sys.argv[1:]
+    else:
+        usage()
 
+    main(mode, input_packages)