Merge commit 'adb86c891b17171889c2c30380fe8d0b9054e449' as 'utils_v2'
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,168 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Saturday, 18th May, 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide a quick set of functions to work with fuzzy logic.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
1) https://www.w3schools.com/python/python_json.asp
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# To apply fuzzy logic:
|
||||
from thefuzz import fuzz, process
|
||||
|
||||
# To work with tabulated data:
|
||||
import pandas as pd
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
def get_best_match(
|
||||
target,
|
||||
choices,
|
||||
threshold = 0.70,
|
||||
partial = False
|
||||
):
|
||||
|
||||
if partial: scorer = fuzz.partial_token_sort_ratio
|
||||
else: scorer = fuzz.ratio
|
||||
|
||||
result = process.extractOne(
|
||||
target,
|
||||
choices,
|
||||
score_cutoff = threshold * 100,
|
||||
scorer = scorer
|
||||
)
|
||||
|
||||
try: return result[0]
|
||||
except: return None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def rank(target, choices, partial = True):
|
||||
|
||||
if partial: scorer = fuzz.partial_token_sort_ratio
|
||||
else: scorer = fuzz.ratio
|
||||
|
||||
result = process.extract(
|
||||
target,
|
||||
choices,
|
||||
limit = len(choices),
|
||||
scorer = scorer
|
||||
)
|
||||
|
||||
result = pd.DataFrame(result, columns = ["choice", "closeness"])
|
||||
result["closeness"] = result["closeness"] / 100.0
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def match(targets, choices, threshold = 0.7, partial = False, allow_null = False):
|
||||
|
||||
all_matches_df = None
|
||||
all_matches = {target: None for target in targets}
|
||||
something_is_null = False
|
||||
|
||||
for target in targets:
|
||||
match_df = rank(target, choices, partial = partial)
|
||||
match_df["target"] = target
|
||||
if all_matches_df is None: all_matches_df = match_df
|
||||
else: all_matches_df = pd.concat([all_matches_df, match_df])
|
||||
|
||||
all_matches_df = all_matches_df.sort_values(by = ["closeness"], ascending = False).reset_index(drop = True)
|
||||
|
||||
for target in targets:
|
||||
target_df = all_matches_df[all_matches_df["target"] == target].reset_index(drop = True)
|
||||
if target_df.empty: continue
|
||||
if target_df.at[0, "closeness"] >= threshold:
|
||||
choice = target_df.at[0, "choice"]
|
||||
all_matches[target] = choice
|
||||
all_matches_df = all_matches_df[all_matches_df["choice"] != choice]
|
||||
else:
|
||||
all_matches[target] = None
|
||||
something_is_null = True
|
||||
|
||||
# print(all_matches)
|
||||
if something_is_null and not allow_null: return None
|
||||
else: return all_matches
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import async_json_utils
|
||||
|
||||
awb_numbers = [
|
||||
"SF1111BIC",
|
||||
"SF2222BIC",
|
||||
"SF3333BIC",
|
||||
"SF4444BIC",
|
||||
]
|
||||
|
||||
chat_text = "SF1112BIC"
|
||||
|
||||
# print(chat_text == names[0])
|
||||
best_match = get_best_match(chat_text, awb_numbers, threshold = 0.60, partial = False)
|
||||
print(f"Best match for '{chat_text}' is '{best_match}'")
|
||||
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Saturday, 24th Aug., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide an overview of any function or class in a string.
|
||||
The generated overview can then either be shown on the terminal, or transmitted over some other medium for
|
||||
collaborative work.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
N/A
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# System-level activities:
|
||||
import io
|
||||
import inspect
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
def get_help_for_class(cls, skip_methods = None):
|
||||
|
||||
"""
|
||||
Returns the help documentation to use this class.
|
||||
:param cls: The class whose help string is desired.
|
||||
:param skip_methods: A list of methods to NOT include in the help text.
|
||||
:return: This help documentation.
|
||||
"""
|
||||
|
||||
separator = "\n\n" + ("=" * 120) + "\n\n"
|
||||
if skip_methods is None: skip_methods = []
|
||||
elif not isinstance(skip_methods, list): skip_methods = [skip_methods]
|
||||
|
||||
# Get class name and docstring:
|
||||
class_name = cls.__name__
|
||||
docstring = inspect.getdoc(cls) or ""
|
||||
help_string = "HELP FOR:\n\n"
|
||||
help_string += class_name + "\n\n"
|
||||
help_string += "This document has upto 120 chars per line.\n"
|
||||
help_string += "Best viewed with monospaced font :)"
|
||||
help_string += docstring + separator
|
||||
|
||||
# Get all methods and their docstrings.
|
||||
# Then note the documentation of the methods while ignoring the blacklisted ones:
|
||||
members = inspect.getmembers(cls, predicate = inspect.isfunction)
|
||||
func_help = []
|
||||
for name, method in members:
|
||||
|
||||
# Ignore if asked, or extract the details:
|
||||
if name in skip_methods or name.startswith(f"_{class_name}__"): continue
|
||||
else: func_help.append(get_help_for_function(method))
|
||||
|
||||
# Put all the things together:
|
||||
func_help = separator.join(func_help)
|
||||
help_string += func_help
|
||||
|
||||
# Done here:
|
||||
return help_string
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def get_help_for_function(func):
|
||||
|
||||
"""
|
||||
Get the help string for one function.
|
||||
It could be a standalone function, or a method of a class.
|
||||
:param func: The function (or method) whose help string is needed.
|
||||
:return: The help string of the function.
|
||||
"""
|
||||
|
||||
# Get the name and documentation:
|
||||
func_name = func.__name__
|
||||
async_indicator = " (async)" if inspect.iscoroutinefunction(func) else ""
|
||||
func_doc = inspect.getdoc(func) or ""
|
||||
|
||||
# Create the decorated header:
|
||||
func_decorator = "-" * (len(func_name) + 2)
|
||||
func_head = "." + func_decorator + f".\n| {func_name} |{async_indicator}\n`" + func_decorator + "`\n\n"
|
||||
|
||||
# Add the 'args' and 'kwargs':
|
||||
func_args = []
|
||||
for name, param in inspect.signature(func).parameters.items():
|
||||
default = param.default
|
||||
if isinstance(default, str): default = f"\"{default}\""
|
||||
if default == inspect.Parameter.empty: func_args.append(f"{name}")
|
||||
else: func_args.append(f"{name}: {type(default).__name__} = {default}")
|
||||
if len(func_args) > 0: func_args = f"{func_name} (\n\t" + "\n\t".join(func_args) + "\n):\n\n"
|
||||
else: func_args = f"{func_name} ():\n\n"
|
||||
|
||||
# Get the params and return value part from the doc:
|
||||
params_start = func_doc.find(":param")
|
||||
return_start = func_doc.find(":return")
|
||||
func_params = "\n" + func_doc[params_start:return_start] if params_start >= 0 else ""
|
||||
func_return = "\n" + func_doc[return_start:] if return_start >= 0 else ""
|
||||
|
||||
# Isolate the documentation part:
|
||||
if params_start >= 0: func_doc = func_doc[:params_start]
|
||||
elif return_start >= 0: func_doc = func_doc[:return_start]
|
||||
|
||||
# Done here:
|
||||
return func_head + func_args + func_doc + func_params + func_return
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def get_help(entity, skip_methods = None):
|
||||
|
||||
"""
|
||||
Get the help documentation for anything from its docstring.
|
||||
:param entity: The entity you want to get help for.
|
||||
:param skip_methods: A list of methods to ignore if inspecting a class. Not valid for standalone functions.
|
||||
:return: The help string.
|
||||
"""
|
||||
|
||||
if inspect.isclass(entity): return get_help_for_class(entity, skip_methods = skip_methods)
|
||||
else: return get_help_for_function(entity)
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,202 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Create: Saturday, 18th May, 2022
|
||||
Update: Thursday, 22nd Aug. 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide an easy way to work with '.json' data and files.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
1) https://www.w3schools.com/python/python_json.asp
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# System-level activities:
|
||||
import io
|
||||
|
||||
# To work with the JSON standard:
|
||||
import json
|
||||
|
||||
# To work with files:
|
||||
from utils_v2.system import files
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
def from_string(json_data):
|
||||
|
||||
"""
|
||||
Decodes a JSON string to a pythonic variable like a dict.
|
||||
:param json_data: The JSON string to decode.
|
||||
:return: The decoded pythonic variable.
|
||||
"""
|
||||
|
||||
python_data = json.loads(json_data)
|
||||
return python_data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def to_string(
|
||||
python_data,
|
||||
indent = 4,
|
||||
default = None,
|
||||
separators = None,
|
||||
no_space = False
|
||||
):
|
||||
|
||||
"""
|
||||
Converts the given pythonic data to a JSON string.
|
||||
:param python_data: The input data like a dict.
|
||||
:param indent: The tab-width for pretty presentation.
|
||||
:param default: The function to use on something that cannot be directly parsed into a JSON string.
|
||||
:param separators: Custom separators to use.
|
||||
:param no_space: If you want a dense JSON string that saves memory by not using spaces or tabs or line-breaks. Not
|
||||
good for human readability, very good for saving memory. WARNING: THIS OVERRIDES EVERY OTHER PARAMETER EXCEPT
|
||||
'default'.
|
||||
:return: The JSON string representation of the input pythonic data.
|
||||
"""
|
||||
|
||||
if no_space:
|
||||
json_data = json.dumps(
|
||||
python_data,
|
||||
default = default,
|
||||
separators = (',', ':')
|
||||
)
|
||||
|
||||
else:
|
||||
json_data = json.dumps(
|
||||
python_data,
|
||||
indent = indent,
|
||||
default = default,
|
||||
separators = separators
|
||||
)
|
||||
|
||||
return json_data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def from_file(file):
|
||||
|
||||
"""
|
||||
Reads a JSON file and returns it as a pythonic variable like a dict.
|
||||
:param file: The path to the file on the disk or a file held in RAM as a BytesIO object.
|
||||
:return: The decoded pythonic variable.
|
||||
"""
|
||||
|
||||
if isinstance(file, io.BytesIO):
|
||||
file.seek(0)
|
||||
json_data = file.getvalue()
|
||||
else: json_data = files.read_file(file)
|
||||
python_data = from_string(json_data)
|
||||
return python_data
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def to_file(
|
||||
file,
|
||||
python_data,
|
||||
indent = 4,
|
||||
default = None,
|
||||
separators = None,
|
||||
no_space = False
|
||||
):
|
||||
|
||||
"""
|
||||
|
||||
:param file: Either a path to a file on disk, or a buffer in RAM in the form of a BytesIO object.
|
||||
:param python_data: The pythonic data to be converted to the JSON string.
|
||||
:param indent: The tab-width for pretty presentation.
|
||||
:param default: The function to use on something that cannot be directly parsed into a JSON string.
|
||||
:param separators: Custom separators to use.
|
||||
:param no_space: If you want a dense JSON string that saves memory by not using spaces or tabs or line-breaks. Not
|
||||
good for human readability, very good for saving memory. WARNING: THIS OVERRIDES EVERY OTHER PARAMETER EXCEPT
|
||||
'default'.
|
||||
:return: True/False if a path was given, else the same BytesIO object with the written JSON data.
|
||||
"""
|
||||
|
||||
json_data = to_string(
|
||||
python_data,
|
||||
indent = indent,
|
||||
default = default,
|
||||
separators = separators,
|
||||
no_space = no_space
|
||||
)
|
||||
|
||||
if isinstance(file, io.BytesIO):
|
||||
file.write(json_data.encode("utf-8"))
|
||||
file.seek(0)
|
||||
return file
|
||||
|
||||
else:
|
||||
try:
|
||||
files.write_file(file, json_data, mode = "w")
|
||||
return True
|
||||
except: return False
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
pass
|
||||
@@ -0,0 +1,290 @@
|
||||
"""
|
||||
|
||||
AUTHOR:
|
||||
|
||||
Khushal P Soonderji
|
||||
|
||||
DATE:
|
||||
|
||||
Sunday, 28th Apr., 2024
|
||||
|
||||
OBJECTIVE:
|
||||
|
||||
To provide a convenient way to perform RegEx operations like finding patterns and substituting them.
|
||||
|
||||
REFERENCES:
|
||||
|
||||
1) https://www.w3schools.com/python/python_regex.asp
|
||||
|
||||
DOWNLOADS:
|
||||
|
||||
N/A
|
||||
|
||||
"""
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** IMPORT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# To make sibling directories accessible for imports:
|
||||
import sys
|
||||
sys.path.append(".")
|
||||
sys.path.append("..")
|
||||
|
||||
# To work with RegEx:
|
||||
import re
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MACROS / ONE-TIME INIT ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# Common RegEx patterns:
|
||||
REGEX_EMAIL_ID = r"[\d\w\-_.+]*@[\d\w\-_]*.[\d\w]{2,}"
|
||||
REGEX_URL = r"^http[s]?:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[a-zA-Z0-9-_.~%]*)*(?:\?[a-zA-Z0-9-_&=%.]*)?$"
|
||||
REGEX_HTTPS_URL = r"^https:\/\/([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[a-zA-Z0-9-_.~%]*)*(?:\?[a-zA-Z0-9-_&=%.]*)?$"
|
||||
REGEX_PASSWORD = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[!@#$%^&*()_+{}\[\]:;<>,.?~\\\/-]).{8,}$"
|
||||
REGEX_NAME = r"^[\d\w .\-]{1,30}$"
|
||||
REGEX_USERNAME = r"^[\d\w_]{8,25}$"
|
||||
REGEX_CONTACT_NUMBER = r"\+?\d{0,3}\s*\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}"
|
||||
REGEX_DATE = r"\b(?:\d{4}-\d{2}-\d{2}|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2},?\s+\d{4}|\d{1,2}\/\d{1,2}\/\d{4}|\d{1,2}-\d{1,2}-\d{2}|\d{1,2}(?:st|nd|rd|th)\s+(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?),?\s+\d{4})\b"
|
||||
REGEX_GSTIN = r"[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}"
|
||||
REGEX_PAN = r"[A-Z]{5}[0-9]{4}[A-Z]{1}"
|
||||
REGEX_IPV4 = (r"[0-9]{1,3}\." * 3) + r"[0-9]{1,3}"
|
||||
REGEX_IPV6 = (r"[0-9a-fA-F]{1,4}:" * 7) + r"[0-9a-fA-F]{1,4}"
|
||||
REGEX_IFSC = r"[A-Z]{4}0[A-Z0-9]{6}"
|
||||
REGEX_UPI = r"[a-zA-Z0-9\.\-]{2,256}@[a-zA-Z][a-zA-Z]{2,64}"
|
||||
REGEX_MAC_ADDRESS = r"([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})|([0-9a-fA-F]{4}\\.[0-9a-fA-F]{4}\\.[0-9a-fA-F]{4})"
|
||||
REGEX_METRIC_WEIGHT = r"[\d\.]+[ ]?[k]?g"
|
||||
|
||||
|
||||
# RegEx chars (append them to the patterns if needed):
|
||||
REGEX_START = "^"
|
||||
REGEX_END = "$"
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** VARIABLES ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
# --- Nothing Yet
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** FUNCTIONS ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
def find(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Returns a list of substrings that match the given RegEx pattern in the input text.
|
||||
:param text: The text in which the pattern needs to be found.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: An array (list) of substring that match the pattern. Can be an empty list as well.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and clean the results:
|
||||
matches = [match if type(match) is str else match[1] for match in re.findall(pattern, text, flags = flags)]
|
||||
matches = [match for match in matches if len(match) > 0]
|
||||
|
||||
# Return the results:
|
||||
return matches
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def find_first(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Returns the first substring that matches the given RegEx pattern in the input text.
|
||||
:param text: The text in which the pattern needs to be found.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: The first match as a string, or None if no match was found..
|
||||
"""
|
||||
|
||||
matches = find(
|
||||
text = text,
|
||||
pattern = pattern,
|
||||
case_sensitive = case_sensitive,
|
||||
dot_all = dot_all
|
||||
)
|
||||
|
||||
if not matches: return None
|
||||
else: return matches[0]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def replace(text, pattern, substitute_text, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Replaces any substring in the text that matches the RegEx pattern.
|
||||
:param text: The text in which the substitutions need to be made.
|
||||
:param pattern: The RegEx pattern that needs to be substituted.
|
||||
:param substitute_text: The text that will replace the matches that were found.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: The text with the substitutions. If no matches are found, the original string is returned.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and return the results:
|
||||
return re.sub(pattern, substitute_text, text, flags = flags)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def search(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Checks if the given RegEx pattern occurs ANYWHERE in the text that was provided.
|
||||
:param text: The text that needs to be matched against the pattern.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: True if the pattern matches, else False.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and return the results:
|
||||
if re.search(pattern, text, flags = flags): return True
|
||||
else: return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def match(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Checks if the given text matches the RegEx pattern that was provided. The check is made only at the start of the
|
||||
input string.
|
||||
:param text: The text that needs to be matched against the pattern.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: True if the pattern matches, else False.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and return the results:
|
||||
if re.match(pattern, text, flags = flags): return True
|
||||
else: return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def split(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Splits an input string based on the pattern that is being matched.
|
||||
:param text: The text that needs to be matched against the pattern.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: True if the pattern matches, else False.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and return the results:
|
||||
substrings = re.split(pattern, text, flags = flags)
|
||||
if len(substrings) > 0 and substrings[0] == "": substrings.pop(0)
|
||||
return substrings
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def to_json(text, pattern, case_sensitive = True, dot_all = False):
|
||||
|
||||
"""
|
||||
Gives out a dict from the extracted features in a string. It is based on the concept of Named Groups.
|
||||
Consider the following example (assuming the search is case-insensitive):
|
||||
TEXT: "UPI/309258561479/14:17:35/UPI/omsainurses@okhdfc"
|
||||
PATTERN: "upi/.*/(?P<time>.*)/.*/(?P<ref>.*)"
|
||||
RESULT: {'time': '14:17:35', 'ref': 'omsainurses@okhdfc'}
|
||||
:param text: The text that needs to be matched against the pattern.
|
||||
:param pattern: The RegEx pattern to look for.
|
||||
:param case_sensitive: Whether, or not, you want the operation to be case-sensitive.
|
||||
:param dot_all: Allow all characters to be matched in ".".
|
||||
:return: A dict with all the extracted features.
|
||||
"""
|
||||
|
||||
# Prepare the flags:
|
||||
flags = 0
|
||||
if not case_sensitive: flags |= re.IGNORECASE
|
||||
if dot_all: flags |= re.DOTALL
|
||||
|
||||
# Perform the RegEx operation, and return the results:
|
||||
matches = re.search(pattern, text, flags = flags)
|
||||
regex_json = matches.groupdict() if matches else {}
|
||||
return regex_json
|
||||
|
||||
|
||||
# *****************************************************************************************************************
|
||||
# ***** ****
|
||||
# *** MAIN PROGRAM ***
|
||||
# ***** ****
|
||||
# *****************************************************************************************************************
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
import json_utils
|
||||
|
||||
for narr in [
|
||||
# r"UPI/309258561479/14:17:35/UPI/omsainurses@okhdf cb",
|
||||
# r"UPI/309258561479/14:17:35/omsainurses@okhdf cb",
|
||||
# r"NEFT-N095232403538009-RELIGARE BROKING LIMITED MAI",
|
||||
r"Product listing - My product - 75g - Super combo pack",
|
||||
r"Product listing - My product - 750 g",
|
||||
r"0.5 kgs mini pack"
|
||||
]:
|
||||
|
||||
result = to_json(
|
||||
narr,
|
||||
r"upi/.*/(?P<time>.*)/.*/(?P<ref>.*)",
|
||||
case_sensitive = False
|
||||
)
|
||||
print(result)
|
||||
Reference in New Issue
Block a user