Source code for trackers.parser

"""This module provides a class for parsing social media messages."""

import re

from utils.constants.core import REWARDS_COLLECTION


[docs] class MessageParser: """A parser for social media messages to extract type, level, and title.""" def __init__(self): """Initialize the MessageParser.""" self.alias_to_code_map, self.sorted_aliases = self._build_alias_maps() def _build_alias_maps(self): """Build alias maps from the rewards collection. :return: A tuple containing the alias-to-code map and sorted aliases. :rtype: tuple """ alias_map = {} for entry in REWARDS_COLLECTION: parts = entry[0].split("]") code = parts[0].strip("[]") full_name = parts[1].strip().lower() alias_map[full_name] = code for word in full_name.split(): alias_map[word] = code alias_map[code.lower()] = code sorted_aliases = sorted(alias_map.keys(), key=len, reverse=True) return alias_map, sorted_aliases def _clean_message(self, message, arg): """Remove argument, whitespace and dot from the message. :param message: The original message string. :type message: str :param arg: The argument to remove from the message. :type arg: str :return: The cleaned message. :rtype: str """ work_message = message.replace(arg, "").strip().strip(".") return " ".join(work_message.split()) def _full_type_from_parsed_type(self, parsed_type): """Return full request type from provided parsed type/code. :param parsed_type: request type code :type parsed_type: str :rtype: str """ return next( entry[0] for entry in REWARDS_COLLECTION if f"[{parsed_type}]" in entry[0] ) def _parse_combined_type_level(self, message): """Parse combined type and level from the message (e.g., F1, CT2). :param message: The message string to parse. :type message: str :return: A tuple containing the parsed type, level, and the remaining message. :rtype: tuple """ type_words = [alias for alias in self.sorted_aliases if " " not in alias] type_pattern = "|".join(re.escape(word) for word in type_words) match = re.search(rf"\b({type_pattern})([1-3])\b", message, re.IGNORECASE) if match: type_alias = match.group(1).lower() parsed_type = self.alias_to_code_map.get(type_alias) level = int(match.group(2)) remaining_message = message.replace(match.group(0), "", 1).strip() return parsed_type, level, remaining_message return None, None, message def _parse_explicit_level(self, message): """Parse explicit level from the message (e.g., level:1, l2). :param message: The message string to parse. :type message: str :return: A tuple containing the parsed level and the remaining message. :rtype: tuple """ level_match = re.search( r"\b(level|l)\s*[:\s]?\s*([1-3])\b", message, re.IGNORECASE ) if level_match: level = int(level_match.group(2)) remaining_message = message.replace(level_match.group(0), "", 1).strip() return level, remaining_message return None, message def _parse_explicit_type(self, message): """Parse explicit type from the message. :param message: The message string to parse. :type message: str :return: A tuple containing the parsed type and the remaining message. :rtype: tuple """ for alias in self.sorted_aliases: type_match = re.search(rf"\b{re.escape(alias)}\b", message, re.IGNORECASE) if type_match: parsed_type = self.alias_to_code_map[alias] remaining_message = message.replace(type_match.group(0), "", 1).strip() return parsed_type, remaining_message return None, message def _parse_title(self, message): """Parse the title from the message. :param message: The message string to parse. :type message: str :return: The parsed title. :rtype: str """ title_match = re.search( r"\b(title|subject|s)\s*:\s*(.+)", message, re.IGNORECASE ) if title_match: title = title_match.group(2).strip() else: title = message.strip() return " ".join(title.split())
[docs] def parse(self, message, arg): """Parse a social media message to extract type, level, and title. :param message: The message string to parse. :type message: str :param arg: The argument to remove from the message (e.g., a username). :type arg: str :return: A dictionary containing the parsed type, level, and title. :rtype: dict """ result = {"type": None, "level": 1, "comment": ""} work_message = self._clean_message(message, arg) parsed_type, level, work_message = self._parse_combined_type_level(work_message) if parsed_type: result["type"] = self._full_type_from_parsed_type(parsed_type) result["level"] = level explicit_level, work_message = self._parse_explicit_level(work_message) if explicit_level: result["level"] = explicit_level if not result["type"]: explicit_type, work_message = self._parse_explicit_type(work_message) if explicit_type: result["type"] = self._full_type_from_parsed_type(explicit_type) result["comment"] = self._parse_title(work_message) if result["type"] is None: result["type"] = "[F] Feature Request" return result