Пример #1
0
def main(debug_mode=DEBUG_MODE):
    # Imports are at this level, in case modules are reloaded.
    from tools.helpers.interface import MainTk
    # import functions here:
    from scripts import FUNCTIONS
    # import debug functions here:
    DEBUG_FUNCTIONS = []
    parts = OrderedDict([("sample functions", FUNCTIONS),
                        ])  # add functions here
    if debug_mode:
        parts['Debugging (developer only)'] = DEBUG_FUNCTIONS  # add debug functions here
    logger_level = logging.DEBUG if debug_mode else logging.INFO
    root = MainTk(parts, logger_level, help_msg=__help__, title=TITLE)
    if debug_mode:
        reload_but = ttk.Button(master=root.info_frame, text="Reload interface and scripts",
                                command=lambda: reload_main(root, debug_mode=debug_mode))
        reload_but.grid(row=2, column=2, sticky='se')
        ttk.Button(master=root.info_frame, text="logger INFO",
                   command=lambda: change_logger_to_info(root)).grid(row=2, column=0, sticky='e')
        ttk.Button(master=root.info_frame, text="logger DEBUG",
                   command=lambda: change_logger_to_debug(root)).grid(row=2, column=1, sticky='e')
        ttk.Button(master=root.info_frame, text="Standard mode",
                   command=lambda: reload_main(root, debug_mode=False)).grid(row=3, column=0, sticky='w')
        ttk.Button(master=root.info_frame, text="Open debugger",
                   command=debugger).grid(row=3, column=1, sticky='w')
        change_logger_to_debug(root)
    else:
        ttk.Button(master=root.info_frame, text="Advanced mode",
                   command=lambda: reload_main(root, debug_mode=True)).grid(row=3, column=0, sticky='w')
        change_logger_to_info(root)
    root.mainloop()
    logger.debug('Main window closed.')
Пример #2
0
 def load(self, path=None, force_load=None, auto_cast=None, load_empty=None, merge_how='right'):
     """Load configuration from file.
     If force-load, reload_default values on error.."""
     path = self._path if path is None else Path(path)
     auto_cast = self._auto_cast if auto_cast is None else auto_cast
     force_load = self._force_load if force_load is None else force_load
     load_empty = self._load_empty if load_empty is None else load_empty
     if not path.isfile:
         if force_load:
             self.reload_default()
         return None
     config_dict = self.read_config(path, auto_cast=auto_cast)
     if config_dict is None:
         if force_load:
             self.reload_default()
         return None
     elif path.isfile:  # Change path
         self._path = path
         logger.debug("Path changed to '{}' with 'load' method.".format(self.path))
     elif config_dict.isempty:
         if not load_empty:
             logger.info("Configuration loaded is empty! It won't replace the existing one.")
             logger.debug("Use 'clear' method to empty the ConfigDict")
             return None
     else:
         raise UnknownError("bad case in 'load'")
     # config_dict.section = self.section
     # config_dict.default_section = self.default_section
     # config_dict._conversion_dict = self._conversion_dict
     self.merge(config_dict, how=merge_how, inplace=True)
     logger.info("Configuration loaded!")
Пример #3
0
def _handle_duplicates(dico, key, value, flag='first', inplace=False):
    """Handle duplicates in dico.

    :param dico: dico to update
    :param key: key to check
    :param value: value to set
    :param flag: 'first', 'last', 'rename' or 'error' (or whatever, which means 'error')
    :param inplace: modification of dico inplace if True
    :return: None if inplace is True, else dico updated
    """
    n_dico = type(dico)()
    if key in dico:
        logger.debug("Key '{}' is duplicated.".format(key))
        if flag == 'first':
            pass
        elif flag == 'last':
            n_dico[key] = value
        elif flag == 'rename':
            i = 0
            exists = True
            while exists:
                i += 1
                n_key = "{}_{}".format(key, i)
                exists = True if n_key in dico else False
            n_dico[n_key] = value
        else:
            err_msg = "Duplicate keys '{}' found! Conversion process aborting."
            logger.error(err_msg)
            raise ValueError(err_msg)
    else:
        n_dico[key] = value
    if inplace:
        dico.update(n_dico)
        return
    return n_dico
Пример #4
0
    def __init__(self,
                 master: 'MainTk',
                 text,
                 answers,
                 labelanchor='n',
                 geometry_manager='pack',
                 geometry_options=None):
        """Main part of the MainTk window. Should not be instantiated outside a MainTk window.

        :param answers: list of functions or list of tuples withe the format [(function_name, function, tooltip), ...]
        """
        super().__init__(master, text=text, labelanchor=labelanchor)
        self.master = master
        answers = self._format_answers(answers)
        logger.debug("Possible functions: {}".format(answers))
        self.question_frame = CustomQuestionFrame(
            master=self,
            choices=answers,
            auto_quit=False,
            geometry_manager=geometry_manager,
            geometry_options=geometry_options)
        self.question_frame.pack(fill=tk.BOTH)
        self.question_frame.change_res.trace(
            mode='w', callback=self.exec_func)  # exec func on click
        self.queue = None
Пример #5
0
def reload_functions(module, functions: Union[list, tuple, Callable]):
    """Reloads a function or a list/tuple of functions."""
    if not isinstance(functions, (list, tuple)):
        functions = [functions]
    for func in functions:
        if isinstance(
                func,
            (list, tuple)):  # format  [(_, func1, _), (_, func2, _), ...]
            fn = func[1] if len(func) > 1 else None
        else:  # format [func1, func2, ...]
            fn = func
        if '__name__' in dir(fn):  # case of functions and classes
            name = fn.__name__
            if name == '<lambda>':
                continue
        elif isinstance(
                fn,
                str) and fn in dir(module):  # particular case of __all__ list
            name = fn
        else:
            logger.error("object '{}' not reloaded, wrong type or name.")
            return
        getattr(module,
                name)  # access to function seems sufficient to reload it.
        logger.debug("object '{}' reloaded".format(name))
Пример #6
0
    def reload_recursive_ex(module):
        try:
            importlib.reload(module)
        except ImportError as err:
            logger.exception(err)
            logger.error("module '{}' could not be reloaded.".format(module))
            return
        if reload_func:
            for func_names in ls_func_names:
                if func_names in dir(module):
                    ls_functions = getattr(module, func_names)
                    reload_functions(module, ls_functions)
        logger.debug("module '{}' reloaded!".format(module.__name__))

        for module_child in vars(
                module).values():  # search subpackages in vars(module)
            if isinstance(module_child, types.ModuleType):  # if it is a module
                fn_child = getattr(module_child, "__file__", None)
                if (fn_child is not None
                    ) and fn_child.startswith(fn_dir):  # if it is a subpackage
                    if fn_child not in module_visit:  # if module has not benn reloaded yet
                        # print("reloading:", fn_child, "from", module)
                        module_visit.add(fn_child)
                        reload_recursive_ex(
                            module_child)  # reload subpackages of this module
Пример #7
0
def handle_file_error(err, func, path, args=None, kwargs=None,
                      pos_path=0, key_path=None, change_path_func=save_file,
                      title='', msg='', return_if_ignore=None):
    """If PermissionError when opening/saving file, propose to retry, change file path or cancel

    :param err: exception
    :param func: function to execute if the user wants to retry
    :param path: file path
    :param args: args to pass to func
    :param kwargs: kwargs to pass to func
    :param pos_path: position of the positional argument path in func (only if key_path is None)
    :param key_path: name of the keyword argument path in func (if None, positional argument is used)
    :param change_path_func: function to get a new path, with no positional argument and 'initialdir' keyword argument
    :param title: title of the error
    :param msg: message of the error
    :param return_if_ignore: return if Ignore option is selected
    :return:
    """
    logger.debug(err)
    args = args or []
    kwargs = kwargs or {}
    title = title or 'File error!'
    msg = msg or "Unknown error with file '{}'. \nOriginal error: {}".format(path, err)
    logger.warning('User action needed!')
    res = messagebox.askcustomquestion(title=title, message=msg,
                                       choices=["Retry", "Rename automatically", "Change file path",
                                                "Ignore", "Debug (developer only)", "Cancel"])
    if res == "Retry":
        if key_path is not None:
            kwargs[key_path] = path
        else:
            args.insert(pos_path, path)
        return func(*args, **kwargs)
    if res == "Rename automatically":
        n_path = _handle_existing_file_conflict(path=path, overwrite='rename')
        if key_path is not None:
            kwargs[key_path] = n_path
        else:
            args.insert(pos_path, n_path)
        return func(*args, **kwargs)
    elif res == "Change file path":
        initialdir = Path(path).dirname if Path(path).dirname.exists else None
        if key_path is not None:
            kwargs[key_path] = change_path_func(initialdir=initialdir)
        else:
            args.insert(pos_path, change_path_func(initialdir=initialdir))
        return func(*args, **kwargs)
    elif res == "Ignore":
        logger.warning("Function ignored!")
        logger.debug("Function '{}' with path '{}' ignored!")
        return return_if_ignore
    elif res == "Debug (developer only)":
        pdb.set_trace()
    elif res in [None, "Cancel"]:
        err = UnknownError if not isinstance(err, BaseException) else err
        logger.exception(err)
        raise err.__class__(err)
    else:
        raise TypeError("Bad return of function 'messagebox.askcustomquestion': '{}'".format(res))
Пример #8
0
 def path(self, path):
     if self._path.isnone:
         title = "Select a configuration file to load."
     else:
         title = "Bad configuration file set. Please select a valid configuration file."
     title += " [Cancel for default configuration]"
     self._path = open_file(path, title=title, ask_path=self._ask_path, behavior_on_cancellation='ignore')
     logger.debug("Config path set to '{}'".format(self._path))
Пример #9
0
def to_list(v, sep=','):  # deprecated, replaced by ast.literal_eval
    logger.debug(
        "DeprecationWarning: to_list function is deprecated. Use ast.literal_eval instead."
    )
    assert isinstance(sep, str)
    _s = str(v).strip().lstrip('[').rstrip(']').strip()
    _l = _s.split(sep)
    _l = [ele.strip() for ele in _l if ele]
    return _l
Пример #10
0
 def users_list(self, usernames, only_new=True):
     usernames = [u.lower() for u in usernames]
     if only_new:
         for username in usernames.copy():  # Todo: verify copy() is necessary
             if get_a_profile(username):
                 logger.debug(f'Username {username} already exists')
                 usernames.remove(username)
     self.usersnames_df = pd.DataFrame(usernames, columns=['username'])
     return self
Пример #11
0
def q_save_a_tweet(tweet):
    collection = get_collection()
    try:
        result = collection.insert_one(tweet)
    except DuplicateKeyError as e:
        logger.debug(f"Duplicate: {tweet['tweet_id']} - {tweet['date']} - {tweet['name']}")
    except:
        logger.error(f'Unknown error: {sys.exc_info()[0]}')
        raise
Пример #12
0
def _set_writable(path: str):
    """Make a file writable if it exists and is read-only."""
    if Path(path).isfile and not os.access(path, os.W_OK):
        try:
            os.chmod(path, stat.S_IWUSR | stat.S_IREAD)  # if read-only existing file, make it writable
        except PermissionError:
            logger.debug("Failed to change file properties of '{}'".format(path))
            return
        logger.debug("File '{}' is now writable.")
Пример #13
0
def q_update_a_tweet(tweet):
    collection = get_collection()
    f = {'tweet_id': tweet['tweet_id']}
    u = {'$set': tweet}
    try:
        result = collection.update_one(f, u, upsert=True)
        logger.debug(f"Updated: {result.raw_result} - {tweet['tweet_id']} - {tweet['date']} - {tweet['name']}")
    except DuplicateKeyError as e:
        logger.debug(f"Duplicate: {tweet['tweet_id']} - {tweet['date']} - {tweet['name']}")
Пример #14
0
 def on_closing(self):
     res = messagebox.askyesnocancel(
         message="Do you want to select this date? "
         "Click 'Cancel' to end the program, "
         "click 'No' to come back to the selection window.")
     if res is None:
         logger.debug("Program terminated by the user ('Cancel' button).")
         self.close()
         raise KeyboardInterrupt(
             "Program terminated by the user ('Cancel' button).")
     if res:
         self.close()
Пример #15
0
 def _check_section(self, section: Union[str, list], search_in_default_config: bool = None):
     """If the section doesn't exist and search_in_default_config,
     append the section from default config to current configuration"""
     section = None if section is None else ConfigDict.TO_KEY_FUNC(section)
     search_in_default_config = self._search_in_default_config if search_in_default_config is None \
         else search_in_default_config
     if section not in self.sections() and section is not None and search_in_default_config:
         # if the section doesn't exist, append the default configuration to the configuration
         self.add_default_config_sections(sections=section)
         # self.reload_default(write=False, how='append')  # old method
         logger.debug("Section(s) '{}' of default configuration appended to config.".format(section))
     return section
Пример #16
0
def q_update_a_tweet(tweet):  # Todo: replace tweet with its fields
    collection = get_collection()
    f = {'tweet_id': tweet['tweet_id']}
    tweet.update({'timestamp': datetime.now()})
    u = {'$set': tweet}
    try:
        result = collection.update_one(f, u, upsert=True)
        logger.debug(
            f"Updated: {result.raw_result} | {tweet['date']} {tweet['name']}")
    except DuplicateKeyError as e:
        logger.error(
            f"Duplicate: {tweet['tweet_id']} - {tweet['date']} - {tweet['name']}"
        )
Пример #17
0
 def __getitem__(self, item):
     if item in self._temp_config:  # 1st, try to find the key in temp config
         logger.debug("temporary config used for key '{}'".format(item))
         return self._temp_config[item]
     try:
         return self._cfg[item]  # 2nd, try to find the key in current config
     except KeyError as _err_msg:
         try:  # 3rd, try to find the key in default config TODO: use search in default config
             res = self._default_config[item]
             logger.debug("Item '{}' found in default configuration instead of current configuration.".format(item))
             self._cfg[item] = res  # set item to current configuration
             return res
         except KeyError as err_msg:
             raise KeyError(err_msg)
Пример #18
0
 def __init__(self,
              parts: OrderedDict,
              logger_level=logging.INFO,
              nb_columns=3,
              help_msg="No help_msg available.",
              title="main",
              **options):
     super().__init__(title=title, **options)
     self.help_text = help_msg
     self.help_win = None
     self.load_help()
     self.bind('<F1>', func=self.open_help)
     self.protocol("WM_DELETE_WINDOW", self.on_closing)
     self.parts = OrderedDict()
     for i, (title, answers) in enumerate(parts.items()):
         q_f = _MainPart(master=self,
                         text=title,
                         answers=answers,
                         geometry_manager='grid',
                         geometry_options=dict(sticky='ew',
                                               nb_columns=nb_columns))
         q_f.pack(fill=tk.BOTH)
         self.parts[title] = q_f
     # Info frame
     self.info_frame = ttk.Frame(master=self)
     self.info_frame.pack(fill=tk.BOTH, expand=True)
     self.func_text = tk.StringVar()
     self.func_label = ttk.Label(master=self.info_frame,
                                 textvariable=self.func_text)
     self.func_label.grid(row=0, column=0, sticky=tk.W)
     self.progressbar = ttk.Progressbar(master=self.info_frame,
                                        mode='indeterminate',
                                        length=200,
                                        value=0)
     self.help_button = ttk.Button(master=self.info_frame,
                                   text="Help",
                                   command=self.open_help)
     self.help_button.grid(row=0, column=2, sticky='e')
     self.logger_frame = LoggingHandlerFrame(master=self.info_frame,
                                             level=logger_level)
     self.logger_frame.grid(row=1, column=0, columnspan=3, sticky='nsew')
     self.info_frame.grid_rowconfigure(
         1, weight=1)  # make logger frame expandable vertically
     self.info_frame.grid_columnconfigure(0, weight=1)  # idem horizontally
     self.active_thread = None
     logger.addHandler(self.logger_frame.logging_handler)
     logger.debug("Window loaded.")
     logger.info("Main window loaded. Please select an option or quit.")
Пример #19
0
def _no_flag_handling(key, no_flag='ignore'):
    if no_flag == 'ignore':
        return []
    elif no_flag == 'drop':
        logger.debug(
            "Key '{}' will be dropped because it has no flag.".format(key))
        return None
    elif no_flag == 'auto-conversion':
        logger.debug(
            "Auto conversion will be performed for key '{}' because it has no flag."
            .format(key))
        return [AUTO_FLAG]
    else:
        err_msg = "No flag for the key '{}'".format(key)
        logger.error(err_msg)
        raise ValueError(err_msg)
Пример #20
0
    def download_xml(self, download_url, item_id):
        response = requests.get(download_url,
                                auth=HTTPBasicAuth(
                                    os.environ['PLANET_API_KEY'], ''))
        status = check_response(response)

        output_file = os.path.join(self.indir,
                                   item_id.rstrip() + '_metadata.xml')
        logger.debug(output_file)
        if not "<?xml" in response.text:
            status = False

        with open(output_file, 'w') as f:
            f.write(response.text)

        return status
Пример #21
0
 def run(self):
     logger.debug("start task with func '{}'".format(self.func_name))
     try:
         self.func(*self.args, **self.kwargs)
         msg = "'{}' successfully ended.".format(self.func_name)
         logger.info(
             "Function '{}' successfully ended.\n***************\n".format(
                 self.func_name))
     except Exception as err:
         logger.exception(err)
         msg = "'{}' ended with error.".format(self.func_name)
         logger.error(
             "Function '{}' ended with error.\n***************\n".format(
                 self.func_name))
     self.queue.put(
         msg
     )  # add func name to queue to indicate that the task is finished
     logger.debug("end task with func '{}'".format(self.func_name))
Пример #22
0
 def download_udm(self, download_url, item_id):
     vsicurl_url = '/vsicurl/' + download_url
     logger.debug(vsicurl_url)
     output_file = os.path.join(self.indir,
                                item_id.rstrip() + '_subarea_udm.tif')
     logger.debug(output_file)
     status = True
     try:
         # GDAL Warp crops the image by our AOI, and saves it
         gdal.Warp(output_file,
                   vsicurl_url,
                   dstSRS='EPSG:4326',
                   cutlineDSName=self.aoi_geojson,
                   cropToCutline=True)
     except Exception as exc:
         logger.error(exc)
         status = False
     return status
Пример #23
0
    def discover(self, base_directory, only=None):
        # Check if there are tests
        if not os.path.isdir(base_directory):
            logger.critical(
                'The {} directory is absent!'.format(base_directory))
            exit(1)

        if only is not None and isinstance(only, list):
            only = set(only)

        # Discover the tests
        nb_tests_before = len(self.tests)
        for test_directory in ['.'] + list(sorted(os.listdir(base_directory))):
            logger.debug('Looking for tests in:', test_directory)
            if os.path.isdir(os.path.join(base_directory, test_directory)):
                for test_file in sorted(
                        os.listdir(os.path.join(base_directory,
                                                test_directory))):
                    if only is None or test_file in only:
                        if only is not None:
                            only.remove(test_file)
                        test_path = os.path.join(base_directory,
                                                 test_directory, test_file)
                        if os.path.isfile(test_path):
                            self.add_test(test_file, test_path,
                                          test_directory == 'invalid')

        # Consider remaining test files as relative paths
        if only is not None:
            for test_path in only:
                if not os.path.isfile(test_path):
                    logger.fatal('Could not find test: {}'.format(test_path))
                    exit(1)
                self.add_test(test_path.split('/')[-1], test_path, False)

        logger.info(
            'Discovered',
            colored('{} tests.'.format(len(self.tests) - nb_tests_before),
                    color='yellow'))
Пример #24
0
def try_it():
    """Try something! (this message is shown by default as tooltip)"""
    # Test logger
    logger.debug('Test debug message')
    logger.info('Test info message')
    logger.error('Test error message')

    # Print directories
    logger.info("Working directory: {}".format(os.getcwd()))
    logger.info("File path: {}".format(__file__))

    # Test message box
    res = messagebox.askyesno(title="This is a message box!",
                              message="Do you want to raise an error?")
    if res:
        err_msg = "This is the error you asked!"
        logger.error(err_msg)
        raise ValueError(err_msg)
    logger.info("No error was raised!")

    # Test get_user_date
    message = "This is the end of this sample script ending at {}".format(
        get_user_date(bypass_dialog=True))
    messagebox.showinfo(title="This is already the end...", message=message)
Пример #25
0
    def __init__(self, *args, **kwargs):
        """Initialisation of a _Config instance.

        :param args:  see 'init' method
        :param kwargs: see 'init' method
        """
        super().__init__()
        self._cfg = ConfigDict()  # current configuration
        self._default_config = ConfigDict()  # default configuration
        self._temp_config = OrderedDict()  # temporary configuration
        self._path = Path()  # current configuration path
        self._default_path = Path()  # default configuration path
        self._conversion_dict = None
        self._auto_cast = None
        self._write_flags = None
        self._force_load = None
        self._load_empty = None
        self._ask_path = None
        self._search_in_default_config = None
        self._init_count = 0
        self._policies = defaultdict(bool)  # by default every modification is forbidden  # WIP
        if args or kwargs:
            self.init(*args, **kwargs)
        logger.debug("Config object created.")
Пример #26
0
 def on_closing(self):
     # Case of an active thread
     if self.active_thread and self.active_thread.is_alive():
         logger.warning("Process running in background!")
         logger.debug("Thread '{}: {}' still alive.".format(
             self.active_thread.name, self.active_thread))
         logger.debug(
             "number of active threads: {}, current thread: {}, main thread: {}."
             .format(threading.active_count(), threading.current_thread(),
                     threading.main_thread()))
         self.active_thread.join(timeout=1)  # wait 1 second
         if self.active_thread.is_alive(
         ):  # if the thread is still alive after 1 second
             if not self.ask_exit(self.active_thread.func_name):
                 logger.debug("Exit cancelled")
                 return False
     # Remove logger handler attached to tkinter frame
     self.logger_frame.quit()
     # Destroy the window
     self.destroy()
     logger.debug("Main window destroyed.")
     self.quit()
     logger.debug("Main window quited.")
     return True
Пример #27
0
def _handle_existing_file_conflict(path: Path, overwrite='ask', backup=False, **kwargs) -> Union[Path, None]:
    """Handle conflict if a file already exist by opening adapted dialog."""
    # overwrite 'ask': ask user to modify overwrite arg into 'overwrite' (Yes) or 'rename' (No) or return None (Cancel)
    if overwrite == 'ask':
        logger.warning('User action needed!')
        res = messagebox.askyesnocancel(title="File existing",
                                        message="File {} already exists.\nDo you want to overwrite it?"
                                                "\n\nIf you select 'No', the file will be "
                                                "renamed automatically.".format(path))
        if res is None:
            logger.info("'Save file' operation cancelled by the user.".format(path))
            logger.debug("The path 'None' will be returned.")

            return None
        if res:
            overwrite = 'overwrite'
        else:
            overwrite = 'rename'

    # overwrite 'rename' or False: add '-i' at the end of the path to make it unique, where 'i' is an integer.
    if overwrite == 'rename' or overwrite is False:
        r_path, r_ext = path.splitext
        # def rename_method1(r_path, sep='-'):#todo
        ls_end = re.findall(r'-(\d+)$', r_path)
        if ls_end:  # if the path already ends by '-i', change end to 'i+1'
            end = ls_end[0]
            r_path = r_path[:-(len(end) + 1)]
            added_ending = "-{}".format(int(end) + 1)
        else:
            added_ending = "-1"
        n_path = r_path + added_ending + r_ext
        logger.debug("Path {} changed to {} (renaming)".format(path, n_path))
        return save_file(n_path, overwrite=overwrite, backup=backup, **kwargs)

    # backup True: backup the old file
    if backup and path.isfile:
        suffix = datetime.datetime.now().strftime("-%Y-%m-%d_%H-%M_") + uuid.uuid4().hex[:5]
        try:
            shutil.copyfile(path, path.radix + suffix + path.ext)
        except (PermissionError, FileNotFoundError) as err:
            logger.exception(err)
            logger.error("Failed to backup previous configuration file.")

    # overwrite 'overwrite' or True: do not modify the path and make the old file writable to allow overwriting
    if overwrite == 'overwrite' or overwrite is True:
        logger.debug("File {} will be overwritten".format(path))
        _set_writable(path)

    # overwrite 'ignore': do nothing
    elif overwrite == 'ignore':
        pass
    # other case of overwrite: do nothing (same as 'ignore')
    else:
        logger.warning("Unexpected argument 'overwrite'! File {} will be overwritten".format(path))
    return path
Пример #28
0
    def edit(self, **kwargs):
        """Edit attributes of the configuration."""
        for attr in self.EDITABLE_ATTR:
            kwarg = kwargs.pop(attr, self._WILDCARD)
            if kwarg is not self._WILDCARD:
                setattr(self, attr, kwarg)
                logger.debug("Attribute '{}' changed to '{}'.".format(attr, kwarg))

        for p_attr in self.EDITABLE_PRIVATE_ATTR:
            kwarg = kwargs.pop(p_attr, self._WILDCARD)
            if kwarg is not self._WILDCARD:
                setattr(self, "_" + p_attr, kwarg)
                logger.debug("Private attribute '{}' changed to '{}'.".format(p_attr, kwarg))
        logger.debug("Configuration edited.")
Пример #29
0
def _write_excel(path, dataframes, sheet_names, index=False, **kwargs):
    """Save dataframes to an Excel workbook.

    :param path: output path
    :param dataframes: list of dataframes
    :param sheet_names: list of sheet names (same index as dataframes)
    :param index: if True, index names are exported
    :param kwargs: keyword arguments for pd.DataFrame.to_excel function
    :return: final output path
    """
    if path is None or path.isnone:
        logger.warning('No path set to write data to Excel! No file has been created.')
        return None
    try:
        logger.debug("Trying to open file '{}'".format(path))
        with pd.ExcelWriter(path) as writer:
            logger.debug("File {} opened.".format(path))
            for df, sheet_name in zip(dataframes, sheet_names):
                if isinstance(df.columns, pd.MultiIndex):
                    index = True  # index must be True if MultiIndex columns, otherwise NotImplementedError is raised
                df.to_excel(writer, sheet_name=sheet_name, index=index, **kwargs)
                logger.debug("Sheet {} written.".format(sheet_name))
        logger.debug("File {} closed.".format(path))
    except PermissionError as err:
        logger.exception(err)
        path = handle_permission_error(err, func=_write_excel, path=path, args=[dataframes, sheet_names, index],
                                       kwargs=kwargs, change_path_func=save_file, handle_read_only_error=True)
    except ValueError as err:
        if str(err).startswith("No engine for filetype"):
            path = handle_bad_extension_error(err, func=_write_excel, path=path, args=[dataframes, sheet_names, index],
                                              kwargs=kwargs, change_path_func=save_file, extension=".xlsx")
        else:
            raise err
    except FileNotFoundError as err:
        logger.exception(err)
        path = handle_file_not_found_error(err, func=_write_excel, path=path, args=[dataframes, sheet_names, index],
                                           kwargs=kwargs, change_path_func=save_file)
    return path
    async def parse_item_page(self, content=None, detail=None, rate=None):

        if content:
            sku_map = re.search('skuMap.*?(\{.*)', content)
            shop_id = store_trans(self.fromStore, 'code_2_id')
            doc = PyQuery(content)
            items = doc("li[data-value]").items()
            logger.debug(items)
            attr_map = {}
            if items:
                for item in items:
                    attr_map[item.attr('data-value')] = item.find(
                        'span').text().replace("(", "(").replace(")", ")")
            if sku_map:
                sku_dict = json.loads(sku_map.group(1))
                for k, v in sku_dict.items():
                    for price_tb_item in self.price_tb_items:
                        if price_tb_item.skuId == v.get('skuId'):
                            price_tb_item.price_tb = v.get('price')
                            price_tb_item.shop_id = shop_id
                            price_tb_item.attribute_map = k
                            price_tb_item.attribute = "-".join([
                                attr_map.get(r)
                                for r in re.sub('^;|;$', "", k).split(";")
                            ])
            else:
                self.price_tb_items[0].shop_id = shop_id
                self.price_tb_items[0].price_tb = doc(
                    'input[name="current_price"]').val()
            self.completed = 2
        if detail:
            while 1:
                if self.completed == 2:
                    break
                await asyncio.sleep(1)
            logger.debug(detail)
            detail = re.sub(r'span class=\"wl-yen\"',
                            r'span class=\\"wl-yen\\"', detail)
            data = re.search('uccess\((.*?)\);', detail)
            if data:
                x = json.loads(data.group(1))
            else:
                await self.login.slider(self.item_page)
                return
            promo_data = jsonpath(x, '$..promoData')
            for price_tb_item in self.price_tb_items:
                price_tb_item.sales = jsonpath(x, '$..soldTotalCount')[0]
                price_tb_item.typeabbrev = self.fromStore
                if promo_data and promo_data[0]:
                    if price_tb_item.attribute_map:
                        for k, v in promo_data[0].items():
                            if k == price_tb_item.attribute_map:
                                price_tb_item.promotionprice = jsonpath(
                                    v, '$..price')[0]
                    else:
                        price_tb_item.promotionprice = jsonpath(
                            x, '$..promoData..price')[0]
            self.completed = 3
        if rate:
            while 1:
                if self.completed == 3:
                    break
                await asyncio.sleep(1)
            logger.debug(rate)
            ms = MySql()
            for price_tb_item in self.price_tb_items:
                count = re.search('count.*?(\d+)', rate)
                if count:
                    price_tb_item.rates = count.group(1)
                price_tb_item.need_to_update = 0
                price_tb_item.save(ms)
                # print(price_tb_item)
            self.price_tb_items[0].delete(ms)
            del ms
            self.completed = 4