Skip to content

Utilities

tibiawikisql.utils

Utility functions used for parsing information.

Classes:

Name Description
Elapsed

Holds the elapsed time measured by the [tibiawikisql.utils.timed][timed] context manager.

Functions:

Name Description
timed

Context manager that measures the time taken to execute a block of code.

clean_question_mark

Remove question marks from strings, returning None if one is found.

clean_links

Remove any links from the string, changing them for their plain version.

convert_tibiawiki_position

Convert from TibiaWiki position system to regular numeric coordinates.

find_template

Find a template in a string containing wiki code.

find_templates

Create a generator to find templates in a wikicode string.

parse_boolean

Parse a boolean value from a string.

parse_date

Parse a date from the formats used in TibiaWiki.

parse_float

From a string, parses a floating value.

parse_integer

Parse an integer from a string. Extra characters are ignored.

parse_loot_statistics

Get every dropped item from a creature's loot statistics.

parse_min_max

Parse the minimum and maximum amounts of a loot drop.

parse_sounds

Parse a list of sounds, using Template:Sound_List.

client_color_to_rgb

Convert a color number from Tibia's client data to a RGB value.

parse_templatates_data

Parse the attributes of an Infobox template.

strip_code

Strip code from Wikicode elements into plain strings.

Elapsed

Elapsed()

Holds the elapsed time measured by the [tibiawikisql.utils.timed][timed] context manager.

Attributes:

Name Type Description
elapsed float

Time duration in seconds. Initially 0.0, set after the context ends.

Source code in tibiawikisql/utils.py
def __init__(self) -> None:
    """Create an instance of the class."""
    self.elapsed: float = 0.0

timed

timed() -> Generator[Elapsed, None, None]

Context manager that measures the time taken to execute a block of code.

Yields:

Name Type Description
Elapsed Elapsed

An object where the elapsed attribute is set to the duration after exiting the block.

Source code in tibiawikisql/utils.py
@contextmanager
def timed() -> Generator[Elapsed, None, None]:
    """Context manager that measures the time taken to execute a block of code.

    Yields:
        Elapsed: An object where the `elapsed` attribute is set to the duration after exiting the block.
    """
    start = time.perf_counter()
    e = Elapsed()
    yield e
    e.elapsed = time.perf_counter() - start

clean_question_mark

clean_question_mark(content: str) -> str | None

Remove question marks from strings, returning None if one is found.

Parameters:

Name Type Description Default
content str

A string to clean.

required

Returns:

Type Description
str | None

The string, or `None if it was a question mark.

Source code in tibiawikisql/utils.py
def clean_question_mark(content: str) -> str | None:
    """Remove question marks from strings, returning ``None`` if one is found.

    Args:
        content: A string to clean.

    Returns:
        The string, or ``None` if it was a question mark.

    """
    if not content:
        return None
    if "?" in content:
        return None
    return content.strip()
clean_links(content: str) -> str
clean_links(
    content: str, strip_question_mark: Literal[False]
) -> str
clean_links(
    content: str, strip_question_mark: Literal[True]
) -> str | None
clean_links(
    content: str, strip_question_mark: bool = False
) -> str | None

Remove any links from the string, changing them for their plain version.

Parameters:

Name Type Description Default
content str

The string to clean.

required
strip_question_mark bool

If the content is a question mark, return None.

False

Returns:

Type Description
str | None

The clean string, with no links.

Source code in tibiawikisql/utils.py
def clean_links(content: str, strip_question_mark: bool = False) -> str | None:
    """Remove any links from the string, changing them for their plain version.

    Args:
        content: The string to clean.
        strip_question_mark: If the content is a question mark, return None.

    Returns:
        The clean string, with no links.

    """
    img = re.compile("(File|Image):", re.IGNORECASE)
    clean_content = re.sub(r"</?[bB][rR] ?/?>", "\n", content)

    # Convert lists to Markdown lists so they are not removed  by `strip_code`.

    lines = clean_content.splitlines()
    converted_lines = []
    for line in lines:
        stripped = line.lstrip()
        match = re.match(r"^(\*+)\s*(.*)", stripped)
        if match:
            indent = "\t" * (len(match.group(1)) - 1)
            content_line = match.group(2).strip()
            converted_lines.append(f"{indent}- {content_line}")
        else:
            converted_lines.append(line)
    clean_content = "\n".join(converted_lines)

    parsed = mwparserfromhell.parse(clean_content)
    # Remove image links as well
    remove_img = [f for f in parsed.ifilter_wikilinks() if img.match(str(f.title))]
    for f in remove_img:
        parsed.remove(f)
    for template in parsed.ifilter_templates():
        if template.name:
            parsed.replace(template, template.params[0])
    clean_content = parsed.strip_code().strip()
    if strip_question_mark and clean_content == "?":
        return None
    return clean_content

convert_tibiawiki_position

convert_tibiawiki_position(pos: str) -> int

Convert from TibiaWiki position system to regular numeric coordinates.

TibiaWiki takes the coordinates and splits in two bytes, represented in decimal, separated by a period.

Parameters:

Name Type Description Default
pos str

A string containing a coordinate.

required

Returns:

Type Description
int

The coordinate value.

Source code in tibiawikisql/utils.py
def convert_tibiawiki_position(pos: str) -> int:
    """Convert from TibiaWiki position system to regular numeric coordinates.

    TibiaWiki takes the coordinates and splits in two bytes, represented in decimal, separated by a period.

    Args:
        pos: A string containing a coordinate.

    Returns:
        The coordinate value.

    """
    position_splits = pos.strip().split(".")
    try:
        coordinate = int(position_splits[0]) << 8
        if len(position_splits) > 1 and position_splits[1].strip():
            coordinate += int(position_splits[1])
        return coordinate
    except (ValueError, IndexError):
        return 0

find_template

find_template(
    content: str,
    template_name: str,
    partial: bool = False,
    recursive: bool = False,
) -> Template | None

Find a template in a string containing wiki code.

If there are multiple matches, the first one will be returned.

Parameters:

Name Type Description Default
content str

A string containing wiki code.

required
template_name str

The name of the template to match. Case-insensitive.

required
partial bool

Whether to match the entire template name or just a substring of it. e.g. match "Loot Table" when searching for "Loot"

False
recursive bool

Whether to search for templates recursively, by going inside nested templates.

False

Returns:

Type Description
Template | None

The first template found in the content, if any. Otherwise, None is returned.

Source code in tibiawikisql/utils.py
def find_template(content: str, template_name: str, partial: bool = False, recursive: bool = False) -> Template | None:
    """Find a template in a string containing wiki code.

    If there are multiple matches, the first one will be returned.

    Args:
        content: A string containing wiki code.
        template_name: The name of the template to match. Case-insensitive.
        partial: Whether to match the entire template name or just a substring of it.
            e.g. match "Loot Table" when searching for "Loot"
        recursive: Whether to search for templates recursively, by going inside nested templates.

    Returns:
        The first template found in the content, if any. Otherwise, ``None`` is returned.

    """
    return next(find_templates(content, template_name, partial, recursive), None)

find_templates

find_templates(
    content: str,
    template_name: str,
    partial: bool = False,
    recursive: bool = False,
) -> Generator[Template]

Create a generator to find templates in a wikicode string.

Parameters:

Name Type Description Default
content str

A string containing wiki code.

required
template_name str

The name of the template to match. Case insensitive.

required
partial bool

Whether to match the entire template name or just a substring of it. e.g. match "Loot Table" when searching for "Loot"

False
recursive bool

Whether to search for templates recursively, by going inside nested templates.

False

Yields:

Type Description
Generator[Template]

Templates matching provided string.

Source code in tibiawikisql/utils.py
def find_templates(content: str, template_name: str, partial: bool = False, recursive: bool = False) -> Generator[
    Template]:
    """Create a generator to find templates in a wikicode string.

    Args:
        content: A string containing wiki code.
        template_name: The name of the template to match. Case insensitive.
        partial: Whether to match the entire template name or just a substring of it.
            e.g. match "Loot Table" when searching for "Loot"
        recursive: Whether to search for templates recursively, by going inside nested templates.

    Yields:
        Templates matching provided string.

    """
    parsed = mwparserfromhell.parse(content)
    templates: list[Template] = parsed.ifilter_templates(recursive=recursive)
    template_name = template_name.strip().lower().replace("_", " ")
    for template in templates:
        name = strip_code(template.name).lower().replace("_", " ")
        if (partial and template_name in name) or (not partial and template_name == name):
            yield template

parse_boolean

parse_boolean(
    value: str, default: bool = False, invert: bool = False
) -> bool

Parse a boolean value from a string.

String must contain "yes" to be considered True.

Parameters:

Name Type Description Default
value str

The string containing an integer.

required
default bool

The value to return if no boolean string is found.

False
invert bool

Whether to invert the value or not.

False

Returns:

Type Description
bool

The boolean value parsed in the string, or default if it doesn't match yes or no.

Source code in tibiawikisql/utils.py
def parse_boolean(value: str, default: bool = False, invert: bool = False) -> bool:
    """Parse a boolean value from a string.

    String must contain "yes" to be considered True.

    Args:
        value: The string containing an integer.
        default: The value to return if no boolean string is found.
        invert: Whether to invert the value or not.

    Returns:
        The boolean value parsed in the string, or default if it doesn't match yes or no.

    """
    value = value.strip().lower()
    if value == "yes":
        return not invert
    if value == "no":
        return invert
    return default

parse_date

parse_date(value: str) -> date

Parse a date from the formats used in TibiaWiki.

  • June 28, 2019
  • Aug 21, 2014
  • May 14, 2024 17:45

Parameters:

Name Type Description Default
value str

The string containing the date.

required

Returns:

Type Description
date

The date represented by the string.

Source code in tibiawikisql/utils.py
def parse_date(value: str) -> datetime.date:
    """Parse a date from the formats used in TibiaWiki.

    - June 28, 2019
    - Aug 21, 2014
    - May 14, 2024 17:45

    Args:
        value: The string containing the date.

    Returns:
        The date represented by the string.

    """
    value = value.strip()
    date_formats = [
        "%B %d, %Y",
        "%b %d, %Y",
        "%Y",
        "%B %d, %Y %H:%M",
        "%b %d, %Y %H:%M",
        "%Y %H:%M",
    ]
    for date_format in date_formats:
        try:
            dt = datetime.datetime.strptime(value, date_format).replace(tzinfo=datetime.timezone.utc)
            return dt.date()
        except ValueError:
            continue

    msg = f"Date format for value '{value}' not recognized"
    raise ValueError(msg)

parse_float

parse_float(value: str, default: float = 0.0) -> float

From a string, parses a floating value.

Parameters:

Name Type Description Default
value str

The string containing the floating number.

required
default float

The value to return if no float is found.

0.0

Returns:

Type Description
float

The floating number found, or the default value provided.

Source code in tibiawikisql/utils.py
def parse_float(value: str, default: float = 0.0) -> float:
    """From a string, parses a floating value.

    Args:
        value: The string containing the floating number.
        default: The value to return if no float is found.

    Returns:
        The floating number found, or the default value provided.

    """
    match = float_pattern.search(value)
    if match:
        return float(match.group(0))
    return default

parse_integer

parse_integer(value: str, default: int = 0) -> int

Parse an integer from a string. Extra characters are ignored.

Parameters:

Name Type Description Default
value str

The string containing an integer.

required
default int

The value to return if no integer is found.

0

Returns:

Type Description
int

The numeric value found, or the default value provided.

Source code in tibiawikisql/utils.py
def parse_integer(value: str, default: int = 0) -> int:
    """Parse an integer from a string. Extra characters are ignored.

    Args:
        value: The string containing an integer.
        default: The value to return if no integer is found.

    Returns:
        The numeric value found, or the default value provided.

    """
    match = int_pattern.search(value)
    if match:
        return int(match.group(0))
    return default

parse_loot_statistics

parse_loot_statistics(value: str) -> tuple[int, list[Any]]

Get every dropped item from a creature's loot statistics.

Parameters:

Name Type Description Default
value str

A string containing a creature's loot statistics.

required

Returns:

Type Description
tuple[int, list[Any]]

A tuple containing the total kills and a list of entries.

Source code in tibiawikisql/utils.py
def parse_loot_statistics(value: str) -> tuple[int, list[Any]]:
    """Get every dropped item from a creature's loot statistics.

    Args:
        value: A string containing a creature's loot statistics.

    Returns:
        A tuple containing the total kills and a list of entries.

    """
    template = find_template(value, "Loot2", partial=True)
    if not template:
        return 0, []
    kills = parse_integer(strip_code(template.get("kills", 0)))
    entries = [_parse_loot_entry(param.value.strip_code()) for param in template.params if not param.showkey]
    return kills, entries

parse_min_max

parse_min_max(value: str) -> tuple[int, int]

Parse the minimum and maximum amounts of a loot drop.

They consist of two numbers separated by a hyphen, e.g. 0-40

Parameters:

Name Type Description Default
value str

A string containing minimum and maximum values.

required

Returns:

Type Description
tuple[int, int]

The minimum and maximum amounts.

Source code in tibiawikisql/utils.py
def parse_min_max(value: str) -> tuple[int, int]:
    """Parse the minimum and maximum amounts of a loot drop.

    They consist of two numbers separated by a hyphen, e.g. ``0-40``

    Args:
        value: A string containing minimum and maximum values.

    Returns:
        The minimum and maximum amounts.

    """
    match = min_max_pattern.search(value)
    if match:
        return int(match.group(1)), int(match.group(2))
    return 0, parse_integer(value, 1)

parse_sounds

parse_sounds(value: str) -> list[str]

Parse a list of sounds, using Template:Sound_List.

Parameters:

Name Type Description Default
value str

A string containing the list of sounds.

required

Returns:

Type Description
list[str]

A list of sounds.

Source code in tibiawikisql/utils.py
def parse_sounds(value: str) -> list[str]:
    """Parse a list of sounds, using Template:Sound_List.

    Args:
        value: A string containing the list of sounds.

    Returns:
        A list of sounds.

    """
    template = find_template(value, "Sound", partial=True)
    if not template:
        return []
    return [strip_code(param) for param in template.params if param]

client_color_to_rgb

client_color_to_rgb(value: int) -> int

Convert a color number from Tibia's client data to a RGB value.

Parameters:

Name Type Description Default
value int

A numeric value representing a color.

required

Returns:

Type Description
int

The hexadecimal color represented.

Source code in tibiawikisql/utils.py
def client_color_to_rgb(value: int) -> int:
    """Convert a color number from Tibia's client data to a RGB value.

    Args:
        value: A numeric value representing a color.

    Returns:
        The hexadecimal color represented.

    """
    if value < 0 or value > 215:
        return 0
    return ((value // 36 * 0x33) << 16) + ((value // 6 % 6 * 0x33) << 8) + ((value % 6 * 0x33) & 0xFF)

parse_templatates_data

parse_templatates_data(
    content: str,
) -> dict[str, dict[str, str]]

Parse the attributes of an Infobox template.

Parameters:

Name Type Description Default
content str

A string containing an Infobox template.

required

Returns:

Type Description
dict[str, dict[str, str]]

A dictionary with every attribute as key.

Source code in tibiawikisql/utils.py
def parse_templatates_data(content: str) -> dict[str, dict[str, str]]:
    """Parse the attributes of an Infobox template.

    Args:
        content: A string containing an Infobox template.

    Returns:
        A dictionary with every attribute as key.

    """
    parsed = mwparserfromhell.parse(content)
    templates = parsed.filter_templates(recursive=False)
    if not templates:
        return {}
    data = defaultdict(dict)
    for template in templates:
        template_name = str(template.name).strip().replace(" ", "_")
        for param in template.params:
            key = param.name.strip()
            if not param.showkey:
                key = int(key)
            value = param.value.strip()
            if value:
                data[template_name][key] = value
    return data

strip_code

strip_code(value: Any) -> str | int | dict | None

Strip code from Wikicode elements into plain strings.

Parameters:

Name Type Description Default
value Any

A string or object containing wiki code.

required

Returns:

Type Description
str | int | dict | None

A string representing the plain text content.

Source code in tibiawikisql/utils.py
def strip_code(value: Any) -> str | int | dict | None:
    """Strip code from Wikicode elements into plain strings.

    Args:
        value: A string or object containing wiki code.

    Returns:
        A string representing the plain text content.

    """
    if value is None or isinstance(value, int):
        return value
    if isinstance(value, str):
        return value.strip()
    if isinstance(value, Parameter):
        return value.value.strip_code().strip()
    if isinstance(value, Wikicode):
        return value.strip_code().strip()
    if isinstance(value, dict):
        for key, val in value.items():
            value[key] = strip_code(val)
        return value
    return None