refactoring modules structure | improving README and CHANGELOG

This commit is contained in:
Vinicius Moreira
2019-09-17 17:35:33 -03:00
parent 01ca7414e2
commit 18238eb269
49 changed files with 94 additions and 63 deletions

View File

107
bauh/view/util/cache.py Normal file
View File

@@ -0,0 +1,107 @@
import datetime
import time
from threading import Lock, Thread
from bauh.api.abstract.cache import MemoryCache, MemoryCacheFactory
class DefaultMemoryCache(MemoryCache):
"""
A synchronized cache implementation
"""
def __init__(self, expiration_time: int):
super(DefaultMemoryCache, self).__init__()
self.expiration_time = expiration_time
self._cache = {}
self.lock = Lock()
def is_enabled(self):
return self.expiration_time < 0 or self.expiration_time > 0
def add(self, key: str, val: object):
if key and self.is_enabled():
self.lock.acquire()
self._add(key, val)
self.lock.release()
def _add(self, key: str, val: object):
if key:
self._cache[key] = {'val': val, 'expires_at': datetime.datetime.utcnow() + datetime.timedelta(seconds=self.expiration_time) if self.expiration_time > 0 else None}
def add_non_existing(self, key: str, val: object):
if key and self. is_enabled():
self.lock.acquire()
cur_val = self.get(key)
if cur_val is None:
self._add(key, val)
self.lock.release()
def get(self, key: str):
if key and self.is_enabled():
val = self._cache.get(key)
if val:
expiration = val.get('expires_at')
if expiration and expiration <= datetime.datetime.utcnow():
self.lock.acquire()
del self._cache[key]
self.lock.release()
return None
return val['val']
def delete(self, key):
if key and self.is_enabled():
if key in self._cache:
self.lock.acquire()
del self._cache[key]
self.lock.release()
def keys(self):
return set(self._cache.keys()) if self.is_enabled() else set()
def clean_expired(self):
if self.is_enabled():
for key in self.keys():
self.get(key)
class CacheCleaner(Thread):
def __init__(self, check_interval: int = 15):
super(CacheCleaner, self).__init__(daemon=True)
self.caches = []
self.check_interval = check_interval
def register(self, cache: MemoryCache):
if cache.is_enabled():
self.caches.append(cache)
def run(self):
if self.caches:
while True:
for cache in self.caches:
cache.clean_expired()
time.sleep(self.check_interval)
class DefaultMemoryCacheFactory(MemoryCacheFactory):
def __init__(self, expiration_time: int, cleaner: CacheCleaner):
"""
:param expiration_time: default expiration time for all instantiated caches
:param cleaner
"""
super(DefaultMemoryCacheFactory, self).__init__()
self.expiration_time = expiration_time
self.cleaner = cleaner
def new(self, expiration: int = None) -> MemoryCache:
instance = DefaultMemoryCache(expiration if expiration is not None else self.expiration_time)
self.cleaner.register(instance)
return instance

82
bauh/view/util/disk.py Normal file
View File

@@ -0,0 +1,82 @@
import json
import logging
import os
import time
from threading import Thread, Lock
from typing import Type, Dict
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.disk import DiskCacheLoader, DiskCacheLoaderFactory
from bauh.api.abstract.model import SoftwarePackage
class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
def __init__(self, enabled: bool, cache_map: Dict[Type[SoftwarePackage], MemoryCache], logger: logging.Logger):
super(AsyncDiskCacheLoader, self).__init__(daemon=True)
self.pkgs = []
self._work = True
self.lock = Lock()
self.cache_map = cache_map
self.enabled = enabled
self.logger = logger
self.processed = 0
def fill(self, pkg: SoftwarePackage):
"""
Adds a package which data must be read from the disk to a queue.
:param pkg:
:return:
"""
if self.enabled and pkg and pkg.supports_disk_cache():
self.pkgs.append(pkg)
def stop_working(self):
self._work = False
def run(self):
if self.enabled:
last = 0
while True:
time.sleep(0.00001)
if len(self.pkgs) > self.processed:
pkg = self.pkgs[last]
self._fill_cached_data(pkg)
self.processed += 1
last += 1
elif not self._work:
break
def _fill_cached_data(self, pkg: SoftwarePackage) -> bool:
if self.enabled:
if os.path.exists(pkg.get_disk_data_path()):
with open(pkg.get_disk_data_path()) as f:
cached_data = json.loads(f.read())
if cached_data:
pkg.fill_cached_data(cached_data)
cache = self.cache_map.get(pkg.__class__)\
if cache:
cache.add_non_existing(pkg.id, cached_data)
return True
return False
class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
def __init__(self, disk_cache_enabled: bool, logger: logging.Logger):
super(DefaultDiskCacheLoaderFactory, self).__init__()
self.disk_cache_enabled = disk_cache_enabled
self.logger = logger
self.cache_map = {}
def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache):
if pkg_type:
if pkg_type not in self.cache_map:
self.cache_map[pkg_type] = cache
def new(self) -> AsyncDiskCacheLoader:
return AsyncDiskCacheLoader(enabled=self.disk_cache_enabled, cache_map=self.cache_map, logger=self.logger)

22
bauh/view/util/logs.py Normal file
View File

@@ -0,0 +1,22 @@
import logging
from logging import INFO
FORMAT = '%(asctime)s %(levelname)s [%(module_path)s:%(lineno)s - %(funcName)s()] - %(message)s'
class FilePathFilter(logging.Filter):
def filter(self, record):
record.module_path = record.pathname.split('site-packages/')[1] if 'site-packages' in record.pathname else str(record.pathname)
return True
def new_logger(name: str, enabled: bool) -> logging.Logger:
instance = logging.Logger(name, level=INFO)
instance.addFilter(FilePathFilter())
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter(FORMAT))
instance.addHandler(stream_handler)
instance.disabled = not enabled
return instance

5
bauh/view/util/resource.py Executable file
View File

@@ -0,0 +1,5 @@
from bauh import ROOT_DIR
def get_path(resource_path):
return ROOT_DIR + '/view/resources/' + resource_path

63
bauh/view/util/util.py Normal file
View File

@@ -0,0 +1,63 @@
import glob
import locale
import os
import subprocess
import sys
from PyQt5.QtCore import QCoreApplication
from bauh import __app_name__
from bauh.view.util import resource
def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale')):
locale_path = None
if key is None:
current_locale = locale.getdefaultlocale()
else:
current_locale = [key.strip().lower()]
if current_locale:
current_locale = current_locale[0]
for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1]
if current_locale == name or current_locale.startswith(name + '_'):
locale_path = locale_file
break
if not locale_path:
locale_path = resource.get_path('locale/en')
with open(locale_path, 'r') as f:
locale_keys = f.readlines()
locale_obj = {}
for line in locale_keys:
if line:
keyval = line.strip().split('=')
locale_obj[keyval[0].strip()] = keyval[1].strip()
return locale_obj
def notify_user(msg: str, icon_path: str = resource.get_path('img/logo.svg')):
os.system("notify-send -a {} {} '{}'".format(__app_name__, "-i {}".format(icon_path) if icon_path else '', msg))
def restart_app(show_panel: bool):
"""
:param show_panel: if the panel should be displayed after the app restart
:return:
"""
restart_cmd = [sys.executable, *sys.argv]
if show_panel:
restart_cmd.append('--show-panel')
subprocess.Popen(restart_cmd)
QCoreApplication.exit()