Skip to content

Commit

Permalink
➕ Added average ETA (progress) + several tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
VForiel committed Oct 22, 2022
1 parent 78359b0 commit 80c1711
Show file tree
Hide file tree
Showing 33 changed files with 810 additions and 221 deletions.
13 changes: 13 additions & 0 deletions LRFutils.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Metadata-Version: 2.1
Name: LRFutils
Version: 0.0.11
Summary: Just a custom library to share with some colleagues. Use it at your own risks.
Home-page: https://github.com/LeiRoF/Utils
Author: Leirof
Author-email: [email protected]
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Requires-Python: >=3.10.0
License-File: LICENSE
14 changes: 14 additions & 0 deletions LRFutils.egg-info/SOURCES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
LICENSE
README.md
setup.py
LRFutils/__init__.py
LRFutils/archive.py
LRFutils/color.py
LRFutils/logs.py
LRFutils/progress.py
LRFutils/term.py
LRFutils.egg-info/PKG-INFO
LRFutils.egg-info/SOURCES.txt
LRFutils.egg-info/dependency_links.txt
LRFutils.egg-info/requires.txt
LRFutils.egg-info/top_level.txt
1 change: 1 addition & 0 deletions LRFutils.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions LRFutils.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gitpython
regex
1 change: 1 addition & 0 deletions LRFutils.egg-info/top_level.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
LRFutils
2 changes: 1 addition & 1 deletion LRFutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
from .color import *
from .term import *
from .progress import *
from .log import *
from .logs import *
103 changes: 51 additions & 52 deletions LRFutils/color.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
import re

class Color:
NC = '\033[0m' # No Color, reset all

Bold = '\033[1m'
Underlined = '\033[4m'
Blink = '\033[5m'
Inverted = '\033[7m'
Hidden = '\033[8m'

Black = '\033[30m'
Red = '\033[31m'
Green = '\033[32m'
Yellow = '\033[33m'
Blue = '\033[34m'
Purple = '\033[35m'
Cyan = '\033[36m'
LightGray = '\033[37m'

Gray = "\u001b[30;1m"
LightRed = "\u001b[31;1m"
LightGreen = "\u001b[32;1m"
LightYellow = "\u001b[33;1m"
LightBlue = "\u001b[34;1m"
LightMagenta = "\u001b[35;1m"
LightCyan = "\u001b[36;1m"
White = "\u001b[37;1m"


on_Black = "\u001b[40m"
on_Red = "\u001b[41m"
on_Green = "\u001b[42m"
on_Yellow = "\u001b[43m"
on_Blue = "\u001b[44m"
on_Magenta = "\u001b[45m"
on_Cyan = "\u001b[46m"
on_LightGray = "\u001b[47m"


on_Gray = "\u001b[40;1m"
on_LightRed = "\u001b[41;1m"
on_LightGreen = "\u001b[42;1m"
on_LightYellow = "\u001b[43;1m"
on_LightBlue = "\u001b[44;1m"
on_LightMagenta = "\u001b[45;1m"
on_LightCyan = "\u001b[46;1m"
on_White = "\u001b[47;1m"

def clear(txt):
txt = re.sub("\033\[[0-9][0-9]?m", "", txt)
txt = re.sub("\\u001b\[[0-9][0-9]?m", "", txt)
txt = re.sub("\\u001b\[[0-9][0-9]?;1m", "", txt)
return txt
NC = '\033[0m' # No Color, reset all

Bold = '\033[1m'
Underlined = '\033[4m'
Blink = '\033[5m'
Inverted = '\033[7m'
Hidden = '\033[8m'

Black = '\033[30m'
Red = '\033[31m'
Green = '\033[32m'
Yellow = '\033[33m'
Blue = '\033[34m'
Purple = '\033[35m'
Cyan = '\033[36m'
LightGray = '\033[37m'

Gray = "\u001b[30;1m"
LightRed = "\u001b[31;1m"
LightGreen = "\u001b[32;1m"
LightYellow = "\u001b[33;1m"
LightBlue = "\u001b[34;1m"
LightMagenta = "\u001b[35;1m"
LightCyan = "\u001b[36;1m"
White = "\u001b[37;1m"


on_Black = "\u001b[40m"
on_Red = "\u001b[41m"
on_Green = "\u001b[42m"
on_Yellow = "\u001b[43m"
on_Blue = "\u001b[44m"
on_Magenta = "\u001b[45m"
on_Cyan = "\u001b[46m"
on_LightGray = "\u001b[47m"


on_Gray = "\u001b[40;1m"
on_LightRed = "\u001b[41;1m"
on_LightGreen = "\u001b[42;1m"
on_LightYellow = "\u001b[43;1m"
on_LightBlue = "\u001b[44;1m"
on_LightMagenta = "\u001b[45;1m"
on_LightCyan = "\u001b[46;1m"
on_White = "\u001b[47;1m"

def clear(txt):
txt = re.sub("\033\[[0-9][0-9]?m", "", txt)
txt = re.sub("\\u001b\[[0-9][0-9]?m", "", txt)
txt = re.sub("\\u001b\[[0-9][0-9]?;1m", "", txt)
return txt
22 changes: 11 additions & 11 deletions LRFutils/log.py → LRFutils/logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import datetime
import os
import platform
from LRFutils.color import Color
from LRFutils import color as C
import sys
import traceback

Expand All @@ -15,25 +15,25 @@ def now(human=False, color=False):
time = str(datetime.now())
if human:
if color:
return f"{Color.Blue}{time[8:10]}/{time[5:7]}/{time[0:4]}{Color.NC} at {Color.Purple}{time[11:13]}:{time[14:16]}:{time[17:19]}{Color.NC}"
return f"{C.Blue}{time[8:10]}/{time[5:7]}/{time[0:4]}{C.NC} at {C.Purple}{time[11:13]}:{time[14:16]}:{time[17:19]}{C.NC}"
else:
return f"{time[8:10]}/{time[5:7]}/{time[0:4]} at {time[11:13]}:{time[14:16]}:{time[17:19]}"
else:
return time.replace(" ", "_").replace(":", ".")

startTime = now()
filename = f"logs/{startTime}.log"
filename = f"logs/{startTime}.logs"
if not os.path.isdir("logs"): os.makedirs(f"logs/")

# Getting the current environment
with open(filename, "a") as logFile:
logFile.write(f"ENVIRONMENT: {platform.uname()}\n\n")
with open(filename, "a") as logsFile:
logsFile.write(f"ENVIRONMENT: {platform.uname()}\n\n")

# Print message in log file
def logSave(message):
currentTime = now(human = True, color=False)
with open(filename, "a", encoding="utf-8") as logFile:
logFile.write(f"{currentTime} | {message}\n")
with open(filename, "a", encoding="utf-8") as logsFile:
logsFile.write(f"{currentTime} | {message}\n")

# Print message in terminal
def logPrint(message):
Expand All @@ -43,27 +43,27 @@ def logPrint(message):
# Info-styled messages
def info(message):
logSave(f"[INFO] {message}")
message = Color.Green + "[INFO] " + Color.NC + message
message = C.Green + "[INFO] " + C.NC + message
logPrint(message)

# Warning-styled messages
def warn(message):
message = f"[WARNING] {message}"
logSave(message)
message = Color.Yellow + message + Color.NC
message = C.Yellow + message + C.NC
logPrint(message)

# Error-styled messages
def error(message, etype = None, value = None, tb=None):
message = f"[ERROR] {message}"
logSave(message)
message = Color.on_Red + message + Color.NC
message = C.on_Red + message + C.NC

if etype is None or value is None or tb is None: tb = traceback.format_exc()
else: tb = ''.join(traceback.format_exception(etype, value, tb))
logSave(f"Full traceback below.\n\n{tb}")

logPrint(message + f"\n -> Look at {Color.Green}{filename}{Color.NC} for more information.\n")
logPrint(message + f"\n -> Look at {C.Green}{filename}{C.NC} for more information.\n")

# Catch unexpected crashes
def myexcepthook(etype, value, tb):
Expand Down
107 changes: 76 additions & 31 deletions LRFutils/progress.py
Original file line number Diff line number Diff line change
@@ -1,74 +1,119 @@
from .color import Color
from . import color
from time import time
import datetime

class Bar():
def __init__(self, max = 1, width = 80, prefix = "", eta = True, decimals = 0, duration = True):



# ________________________________________________________________________________
# Create a progress bar

def __init__(self, max:float|int = 1, width:int = 80, prefix:str = "", eta:bool = True, decimals:int = 0, show_duration:bool = True, average_ETA:int = 10):
self.max = max
self.width = width if type(width) == int else 80
self.width = width if isinstance(width,int) else 80
self.prefix = prefix
self.eta = eta
self.decimals = decimals
self.lastProgress = 0
self.lastUpdate = None
self.previous_progress = [0]
self.previous_update = []
self.lastETA = "-"
self.start_at = time()
self.duration = duration
self.duration = show_duration
self.average_ETA = average_ETA


# ________________________________________________________________________________
# Compute the ETA

def update_eta(self, progress):
if self.lastUpdate is not None and time()-self.lastUpdate < 1: return self.lastETA

progression = progress - self.lastProgress
left = (self.max-progress)
# Get index of the i-th revious update to average the ETA on i updates
if len(self.previous_progress) <= self.average_ETA:
i = 0
else:
i = -self.average_ETA

# Compute progression since the i-th previous update
progression = progress - self.previous_progress[i]
todo = self.max - progress

if progression == 0 or self.lastUpdate is None:
self.lastUpdate = time()
self.lastProgress = progress
# If there is no progression or the bar was just created, it update the data but return the same ETA
if progression == 0 or self.previous_update == []:
self.previous_update.append(time())
self.previous_progress.append(progress)
self.lastETA = "-"
return self.lastETA

seconds = left/progression * (time() - self.lastUpdate)
self.lastUpdate = time()
self.lastProgress = progress
if seconds < 0: self.lastETA = "-"
else: self.lastETA = str(datetime.timedelta(seconds=seconds)).split(".")[0]
# If the bar was updated less than one second before,
# we don't update it again to not slow the program
if time()-self.previous_update[-1] < 1:
return self.lastETA

# Compute the ETA in seconds
try:
seconds = todo / progression * (time() - self.previous_update[i])
except:
print(len(self.previous_update), i)

# Update bar data
self.previous_update.append(time())
self.previous_progress.append(progress)

# If the ETA is negative, then we show an undefined ETA
if seconds < 0:
self.lastETA = "-"
# Else we return the number of seconds
else:
self.lastETA = str(datetime.timedelta(seconds=seconds)).split(".")[0]

return self.lastETA



# ________________________________________________________________________________
# Update the progress bar

def __call__(self, progress: float, prefix=None, stop=False):
if prefix is None: prefix = self.prefix
else: prefix = str(prefix)

progress_normed = progress / self.max
if progress == self.max : stop = True
color = Color.Yellow if stop and progress_normed != 1 else Color.LightGreen
color_bar = color.Yellow if stop and progress_normed != 1 else color.LightGreen

if stop: end = "\n"
else: end = "\r"

if type(progress) == float: progress = round(progress, self.decimals)
percent = f" {color}{round(progress_normed*100,self.decimals) if self.decimals > 0 else int(progress_normed*100)}%"
frac = f" {Color.LightRed}{progress}/{self.max}" if self.max is not None else ''
eta = f" {Color.NC}eta {Color.Blue}{self.update_eta(progress)}" if not stop else ''
duration = f" {Color.Purple}{str(datetime.timedelta(seconds=time() - self.start_at)).split('.')[0]}" if self.duration else ''
percent = f" {color_bar}{round(progress_normed*100,self.decimals) if self.decimals > 0 else int(progress_normed*100)}%"
frac = f" {color.LightRed}{progress}/{self.max}" if self.max is not None else ''
eta = f" {color.NC}eta {color.Blue}{self.update_eta(progress)}" if not stop else ''
duration = f" {color.Purple}{str(datetime.timedelta(seconds=time() - self.start_at)).split('.')[0]}" if self.duration else ''

prefix = '' if prefix == '' else Color.NC + prefix + ' '
prefix = '' if prefix == '' else color.NC + prefix + ' '
suffix = f"{percent}{frac}{duration}{eta}"

barwidth = self.width - len(Color.clear(suffix)) - len(Color.clear(prefix))
barwidth = self.width - len(color.clear(suffix)) - len(color.clear(prefix))
barwidth = max(barwidth,10)

currentBar = int(round(min(progress_normed*barwidth,barwidth)))
minBar = int(min(progress_normed*barwidth,barwidth))

if progress_normed == 0: bar = Color.White + '━' * barwidth
elif progress_normed == 1: bar = Color.LightGreen + '━' * barwidth
elif currentBar == minBar: bar = color + '━' * currentBar + Color.White + '╺' + Color.White + '━' * (barwidth - currentBar - 1)
else: bar = color + '━' * (currentBar - 1) + color + '╸' + Color.White + '━' * (barwidth - currentBar)
if progress_normed == 0: bar = color.White + '━' * barwidth
elif progress_normed == 1: bar = color.LightGreen + '━' * barwidth
elif currentBar == minBar: bar = color_bar + '━' * currentBar + color.White + '╺' + color.White + '━' * (barwidth - currentBar - 1)
else: bar = color_bar + '━' * (currentBar - 1) + color_bar + '╸' + color.White + '━' * (barwidth - currentBar)

msg = f"{prefix}{bar}{suffix}{Color.NC}"
msg = f"{prefix}{bar}{suffix}{color.NC}"
print(msg, end=end)



# ________________________________________________________________________________
# Stop the progress bar

def stop(self):
self(self.lastProgress, stop=True)
self.lastProgress = 0
self(self.previous_progress[-1], stop=True)
self.previous_progress = [0]
self.lastUpdate = None
15 changes: 15 additions & 0 deletions Leirof.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Metadata-Version: 2.1
Name: Leirof
Version: 0.0.11
Summary: Just a custom library to share with some colleagues. Use it at your own risks.
Home-page: https://github.com/LeiRoF/LRFutils
License: MIT
Project-URL: Bug Tracker, https://github.com/LeiRoF/LRFutils/issues
Classifier: [
Classifier: "Programming Language :: Python :: 3",
Classifier: "License :: OSI Approved :: MIT License",
Classifier: "Operating System :: OS Independent",
Classifier: 'Intended Audience :: Developers'
Classifier: ]
Requires-Python: >=3.10
License-File: LICENSE
Empty file added Leirof.egg-info/SOURCES.txt
Empty file.
1 change: 1 addition & 0 deletions Leirof.egg-info/dependency_links.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions Leirof.egg-info/requires.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gitpython
regex
Loading

0 comments on commit 80c1711

Please sign in to comment.