import logging import os import traceback from pathlib import Path from threading import Thread from typing import Dict, List import requests from bauh.api.abstract.controller import SoftwareManager from bauh.api.http import HttpClient class CategoriesDownloader(Thread): def __init__(self, id_: str, http_client: HttpClient, logger: logging.Logger, manager: SoftwareManager, url_categories_file: str, disk_cache_dir: str, categories_path: str, before=None, after=None): super(CategoriesDownloader, self).__init__(daemon=True) self.id_ = id_ self.http_client = http_client self.logger = logger self.manager = manager self.url_categories_file = url_categories_file self.disk_cache_dir = disk_cache_dir self.categories_path = categories_path self.before = before self.after = after def _msg(self, msg: str): return '{}({}): {}'.format(self.__class__.__name__, self.id_, msg) def _read_categories_from_disk(self) -> Dict[str, List[str]]: if os.path.exists(self.categories_path): self.logger.info(self._msg("Reading cached categories from the disk")) with open(self.categories_path) as f: categories = f.read() return self._map_categories(categories) return {} def _map_categories(self, categories: str) -> Dict[str, List[str]]: categories_map = {} for l in categories.split('\n'): if l: data = l.split('=') categories_map[data[0]] = [c.strip() for c in data[1].split(',') if c] return categories_map def _cache_categories_to_disk(self, categories: str): self.logger.info(self._msg('Caching categories to the disk')) try: Path(self.disk_cache_dir).mkdir(parents=True, exist_ok=True) with open(self.categories_path, 'w+') as f: f.write(categories) self.logger.info(self._msg("Categories cached to the disk as '{}'".format(self.categories_path))) except: self.logger.error(self._msg("Could not cache categories to the disk as '{}'".format(self.categories_path))) traceback.print_exc() def download_categories(self) -> Dict[str, List[str]]: self.logger.info(self._msg('Downloading category definitions from {}'.format(self.url_categories_file))) try: res = self.http_client.get(self.url_categories_file) if res: try: categories = self._map_categories(res.text) self.logger.info(self._msg('Loaded categories for {} applications'.format(len(categories)))) if categories: Thread(target=self._cache_categories_to_disk, args=(res.text,), daemon=True).start() return categories except: self.logger.error(self._msg("Could not parse categories definitions")) traceback.print_exc() else: self.logger.info(self._msg('Could not download {}'.format(self.url_categories_file))) except requests.exceptions.ConnectionError: self.logger.warning(self._msg('The internet connection seems to be off.')) return {} def _set_categories(self, categories: dict): if categories: self.logger.info(self._msg("Settings {} categories to {}".format(len(categories), self.manager.__class__.__name__))) self.manager.categories = categories def _download_and_set(self): self._set_categories(self.download_categories()) def run(self): if self.before: self.before() cached = self._read_categories_from_disk() if cached: self._set_categories(cached) Thread(target=self._download_and_set, daemon=True).start() else: self._download_and_set() if self.after: self.after() self.logger.info(self._msg('Finished'))