View Single Post
Old 03-26-2024, 09:59 AM   #63
lomkiri
Zealot
lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.lomkiri ought to be getting tired of karma fortunes by now.
 
lomkiri's Avatar
 
Posts: 136
Karma: 1000102
Join Date: Jul 2021
Device: N/A
Quote:
Originally Posted by moldy View Post
For my purposes I would have liked to have been able to add regex to the keys individually in the json file rather than globally
Modify your entries in the json in this way:
"old_name": ["new_name", "regex"]
If you don't provide a regex, the one from the function will be used. In this case, the entry shall be either (at your choice) :
"old_name": ["new_name"]
or "old_name": "new_name"
or "old_name": ["new_name", ""]
Example :
Code:
{
  "John": ["Mike", "\\b{}(?![^<>]*>)"],
  "Paul": "Keith",
  "George": ["Ronnie",  "{}\\b(?![^<>]*>)"],
  "Ringo": ["Charlie"]
}
Inside the regex, {} will be replaced in the function by old_name using str.format()
You must double all the antislashes in the json
You cannot use the regex to search curly brackets in the text, since format() will try to interpret them, if it's necessary, a work-around must be used.

The function is:
Code:
def replace(match, number, file_name, metadata, dictionaries, data, functions, *args, **kwargs):
    # Replace words using a dict in a json file, with possibility of counters
    # The regex for replacing each word may (or may not) be provided with the word

    ### Parameters
    # Put the file 'change_words.json' in the config-folder of calibre
    # If you choose another name for the json, change it here:
    fname = 'change_words.json'
    #
    # If do_count is True, will write the total of changes.
    do_count = True    # put False if you don't want any counter
    #
    # It count_by_name is also True, the function will write the counters by name in the
    # file "change_words_counters.json" (in the config-folder of calibre)
    count_by_name = True
    counters_fname = 'change_words_counters.json'
    #
    # The regex to be applied for the names that have no regex provided in the json:
    rgx_default = r'\b{}\b(?![^<>]*>)'  # Find the key, excluding everything between <...> :
    ### End Parameters

    from calibre.utils.config import JSONConfig
    import regex

    # === Last passage: if counters were asked in the heading of this function
    if match == None:
        if data['total'] == 0:
            print('No occurrence found.\n'
                  f"There is {len(data['equiv'])} entries in the file '{fname}'")
            return

        print(f"There is {len(data['equiv'])} entries in the file '{fname}'\n"
              f"In this dict, {len(data['counters'])} words had at least one occurrence\n"
              f"=== The total of all changes is: {data['total']} ===\n\n"
              f"The file {counters_fname} has been written with the counters by word" if count_by_name else '')

        if count_by_name and counters_fname:
            json = JSONConfig(counters_fname)
            json.clear()
            json.update(data['counters'])
            json.commit()
        return

    # === First passage
    # Load the json file only at first passage
    # The dict "data" retains its values throught all the passages when "replace all"
    if number == 1:
        data['equiv'] = JSONConfig(fname).copy()    # .copy() is here to avoid the json file to be formated or modified
        if not data['equiv']:
            print(f'Problem loading {fname}, no treatment will be done')
            return
        # Prepare the dict for the treatment :
        for key, val in data['equiv'].items():
            if isinstance(val, str):
                val = [val, rgx_default]                
            elif len(val) == 1:
                 val.append(rgx_default)                
            elif not val[1]:
                val[1] = rgx_default                
            data['equiv'][key] = val
        # Activate the counters:
        if do_count:
            replace.call_after_last_match = True    # Ask for last passage
            data['total'] = 0
            data['counters']= {}

    # === normal passage
    m = match.group()
    for key, val in data['equiv'].items():
        [new_name, rgx] = val
        m, n = regex.subn(rgx.format(key), new_name, m)
        if do_count and n:
            data['total'] += n
            data['counters'][key] = data['counters'].get(key, 0) + n
    return m

Last edited by lomkiri; 03-28-2024 at 03:02 PM. Reason: Corrected another bug
lomkiri is offline   Reply With Quote