Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add color to messages in the console (Issue #62) #65

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 75 additions & 8 deletions Log.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
from datetime import datetime
import logging
from time import asctime
import ctypes
import platform
import sys
import os
from Config import Config


# Log class to handle log messages
class Log:
def __init__(self, config):
self.config = config
file_name = "log-" + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + ".log"

def log_config(self, in_terminal: bool = False):
# Setup log configration
config_obj = Config(self.config)
system_config = config_obj.system_config()
log_location = system_config["logs_location"]

file_name = "log-" + datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + ".log"
if (log_location[-1] != "/"):
log_location = log_location + '/'
file_path = log_location + file_name
self.file_path = log_location + file_name

self.ansi_terminal = _check_ansi()

def log_config(self, in_terminal: bool = False):
if (in_terminal):
file_path = ''
logging.basicConfig(filename=file_path,
format="%(asctime)s:%(levelname)s:%(name)s: %(message)s")
f = ''
logging.addLevelName(logging.WARNING, self._format_messagetype_ansi('WARNING'))
logging.addLevelName(logging.ERROR, self._format_messagetype_ansi('ERROR'))
else:
f = self.file_path
logging.basicConfig(filename=f, force=True,
format='%(asctime)s:%(levelname)s: %(message)s')

def show_log_in_terminal(self, type, message, stop_script=False):
# Show log in terminal
Expand All @@ -35,7 +45,7 @@ def write_log_in_file(self, type, message, show_in_terminal=False, stop_script=F
# Show log in file
self.log_config(False)
if (show_in_terminal is True):
print(asctime() + ":" + type.upper() + ":Log - " + message)
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":" + self._format_messagetype_ansi(type.upper()) + ": " + message)
self.message(type, message)
if (stop_script is True):
exit()
Expand All @@ -61,3 +71,60 @@ def message(self, type, message):
logger.setLevel(logging.ERROR)
logger.error(message)
del logger

def _format_messagetype_ansi(self, type):
'''
Returns a colorized version of the given message type string. If no ANSI support is detected, the same string is returned unchanged.
'''
if not self.ansi_terminal:
return type
if (type.lower() == 'error'):
return '\033[2;30;41m' + type + '\033[0;0m'
elif (type.lower() == 'warning'):
return '\033[2;31;43m' + type + '\033[0;0m'
elif (type.lower() == 'info'):
return type
elif (type.lower() == 'debug'):
return type
else:
return type


def _check_ansi():
'''
Returns True if the terminal the script is being run in supports ANSI escape sequences
Based on: https://gist.github.com/ssbarnea/1316877
'''
for handle in [sys.stdout, sys.stderr]:
if (hasattr(handle, "isatty") and handle.isatty()) or ('TERM' in os.environ and os.environ['TERM'] == 'ANSI'):
if platform.system() == 'Windows' and not ('TERM' in os.environ and os.environ['TERM'] == 'ANSI'):
if _is_wt():
# Windows terminal does support ANSI
return True
else:
# Assume the console does not support ANSI
return False
else:
# Assume ANSI available
return True
else:
# no ANSI available
return False


def _is_wt():
'''
Returns True if the script is run in the Windows Terminal 'wt.exe'
Source: https://github.com/cvzi/AssertWT/blob/3125863ef823d5eaad1bc55155d90d9ca83f4aec/assertwt.py#L74-L88
'''

if platform.system() != 'Windows' or 'idlelib' in sys.modules:
return False

window = ctypes.windll.kernel32.GetConsoleWindow()
if not window:
return False
ctypes.windll.kernel32.CloseHandle(window)
WM_GETICON = 0x7F
ret = ctypes.windll.user32.SendMessageW(window, WM_GETICON, 0, 0)
return not ret
29 changes: 15 additions & 14 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from version import __version__, __commit__
from Log import Log
from figshare.Article import Article
from time import asctime
from datetime import datetime
from Config import Config
from figshare.Collection import Collection
from pathlib import Path
Expand Down Expand Up @@ -41,12 +41,14 @@ def check_logs_path_access(config_file):

logs_access = os.access(log_location, os.W_OK)
if (logs_access is False):
print(asctime() + ":ERROR: Log - " + "The logs location specified in the config file could not be reached or read.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":ERROR: "
+ "The logs location specified in the config file could not be reached or read.")
exit()

except OSError as error:
print(error)
print(asctime() + ":ERROR: Log - " + "The logs location specified in the config file could not be reached or read.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":ERROR: "
+ "The logs location specified in the config file could not be reached or read.")
exit()


Expand All @@ -56,15 +58,15 @@ def main():
Setting up required variables and conditions.
"""
global args
print(asctime() + ":Info: Log - ReBACH script has started.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":INFO: ReBACH script has started.")

# Check .env file exists.
if not args.xfg.is_file():
print(asctime() + ":ERROR: Log - " + "Configuration file is missing or cannot be read.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":ERROR: " + "Configuration file is missing or cannot be read.")
exit()
env_file = str(args.xfg)
print(asctime() + ":Info: Log - " + "Env file:" + env_file)
print(asctime() + ":Info: Log - " + "Checking configuration file.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":INFO: " + "Env file:" + env_file)
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":INFO: " + "Checking configuration file.")
config_obj = Config(env_file)

figshare_config = config_obj.figshare_config()
Expand All @@ -80,10 +82,10 @@ def main():

# Check required env variables exist.
if (log_location == ""):
print(asctime() + ":ERROR: Log - " + "Logs file path missing in .env.ini file.")
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S,%f")[:-3] + ":ERROR: " + "Logs file path missing in .env.ini file.")
exit()

log.write_log_in_file('info', "Logs location is accessible. Logging will now start.", True)
log.write_log_in_file('info', "Logs location is accessible. Logging to file will now start.", True)

if (figshare_api_url == "" or figshare_api_token == ""):
log.write_log_in_file('error', "Figshare API URL and Token is required.", True, True)
Expand Down Expand Up @@ -129,18 +131,17 @@ def main():
+ " not be reached or read.",
True, False)

return env_file
return env_file, log


if __name__ == "__main__":
get_args()
config_file_path = main()
log = Log(config_file_path)
config_file_path, log = main()

log.write_log_in_file('info',
"Fetching articles...",
True)
article_obj = Article(config_file_path, args.ids)
article_obj = Article(config_file_path, log, args.ids)
article_data = article_obj.get_articles()
log.write_log_in_file('info',
f"Total articles fetched: {len(article_data)}.",
Expand All @@ -150,7 +151,7 @@ def main():
log.write_log_in_file('info',
"Fetching collections...",
True)
collection_obj = Collection(config_file_path, args.ids)
collection_obj = Collection(config_file_path, log, args.ids)
collection_data = collection_obj.get_collections()
log.write_log_in_file('info',
f"Total collections fetched: {len(collection_data)}.",
Expand Down
5 changes: 2 additions & 3 deletions figshare/Article.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import requests
import hashlib
import re
from Log import Log
from Config import Config
from figshare.Integration import Integration
from slugify import slugify
Expand All @@ -23,15 +22,15 @@ class Article:
:param config: configuration
:param ids: a list of ids to process. If None or an empty list is passed, all will be processed
"""
def __init__(self, config, ids):
def __init__(self, config, log, ids):
self.config_obj = Config(config)
figshare_config = self.config_obj.figshare_config()
self.system_config = self.config_obj.system_config()
self.api_endpoint = figshare_config["url"]
self.api_token = figshare_config["token"]
self.retries = int(figshare_config["retries"]) if figshare_config["retries"] is not None else 3
self.retry_wait = int(figshare_config["retries_wait"]) if figshare_config["retries_wait"] is not None else 10
self.logs = Log(config)
self.logs = log
self.errors = []
self.exclude_dirs = [".DS_Store"]
self.total_all_articles_file_size = 0
Expand Down
7 changes: 3 additions & 4 deletions figshare/Collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import requests
import hashlib
import re
from Log import Log
from Config import Config
from figshare.Article import Article
from figshare.Integration import Integration
Expand All @@ -18,7 +17,7 @@ class Collection:
:param config: configuration
:param ids: list of ids to process. If None or an empty list is passed, all collections will be processed
"""
def __init__(self, config, ids):
def __init__(self, config, log, ids):
self.config_obj = Config(config)
figshare_config = self.config_obj.figshare_config()
self.system_config = self.config_obj.system_config()
Expand All @@ -27,9 +26,9 @@ def __init__(self, config, ids):
self.retries = int(figshare_config["retries"]) if figshare_config["retries"] is not None else 3
self.retry_wait = int(figshare_config["retries_wait"]) if figshare_config["retries_wait"] is not None else 10
self.institution = int(figshare_config["institution"])
self.logs = Log(config)
self.logs = log
self.errors = []
self.article_obj = Article(config, ids)
self.article_obj = Article(config, log, ids)
self.preservation_storage_location = self.system_config["preservation_storage_location"]
if self.preservation_storage_location[-1] != "/":
self.preservation_storage_location = self.preservation_storage_location + "/"
Expand Down
Loading