lab4 / app /api /topics /parser.py
brestok's picture
init
a08f988
import base64
from datetime import datetime
from typing import List
from uuid import UUID
from app.api.topics.schemas import Topic, TopicMessage
from app.core.config import (
ENCODING_ASCII,
ENCODING_UTF8,
TABLE_PARTICIPANTS,
TOPIC_EXPECTED_PARTS,
TOPIC_INNER_SEPARATOR,
TOPIC_LIST_SEPARATOR,
TOPIC_MESSAGE_PART_SEPARATOR,
TOPIC_PARSE_EMPTY_ERROR,
TOPIC_PARSE_INVALID_ERROR,
TOPIC_SEPARATOR,
)
from app.core.link import Link, ParticipantLink
class TopicLineParser:
SEPARATOR = TOPIC_SEPARATOR
EXPECTED_PARTS = TOPIC_EXPECTED_PARTS
LIST_SEPARATOR = TOPIC_LIST_SEPARATOR
INNER_SEPARATOR = TOPIC_INNER_SEPARATOR
MESSAGE_PART_SEPARATOR = TOPIC_MESSAGE_PART_SEPARATOR
@staticmethod
def _decode_message(encoded: str) -> str:
return base64.b64decode(encoded.encode(ENCODING_ASCII)).decode(ENCODING_UTF8)
@staticmethod
def _encode_message(value: str) -> str:
return base64.b64encode(value.encode(ENCODING_UTF8)).decode(ENCODING_ASCII)
@classmethod
def _parse_participants(cls, raw: str) -> List[ParticipantLink]:
if not raw:
return []
links: List[ParticipantLink] = []
for value in raw.split(cls.INNER_SEPARATOR):
if not value:
continue
links.append(ParticipantLink.from_raw(value))
return links
@classmethod
def _parse_messages(cls, raw: str) -> List[TopicMessage]:
if not raw:
return []
messages: List[TopicMessage] = []
for chunk in raw.split(cls.LIST_SEPARATOR):
if not chunk:
continue
try:
participant_raw, encoded_content = chunk.split(cls.MESSAGE_PART_SEPARATOR, 1)
except ValueError:
continue
messages.append(
TopicMessage(
participant_id=ParticipantLink.from_raw(participant_raw),
content=cls._decode_message(encoded_content),
)
)
return messages
@classmethod
def _serialize_participants(cls, participants: List[ParticipantLink]) -> str:
return cls.INNER_SEPARATOR.join(participant.as_path() for participant in participants)
@classmethod
def _serialize_messages(cls, messages: List[TopicMessage]) -> str:
return cls.LIST_SEPARATOR.join(
cls.MESSAGE_PART_SEPARATOR.join(
[message.participant_id.as_path(), cls._encode_message(message.content)]
)
for message in messages
)
@classmethod
def parse(cls, line: str) -> Topic:
raw = line.strip()
if not raw:
raise ValueError(TOPIC_PARSE_EMPTY_ERROR)
parts = raw.split(cls.SEPARATOR)
if len(parts) != cls.EXPECTED_PARTS:
raise ValueError(TOPIC_PARSE_INVALID_ERROR)
(
topic_id,
title,
description,
created_at_value,
participants_raw,
messages_raw,
) = parts
participants = cls._parse_participants(participants_raw)
messages = cls._parse_messages(messages_raw)
return Topic(
id=UUID(topic_id),
title=title,
description=description,
created_at=datetime.fromisoformat(created_at_value),
participants=participants,
messages=messages,
)
@classmethod
def serialize(cls, topic: Topic) -> str:
return cls.SEPARATOR.join(
[
str(topic.id),
topic.title,
topic.description,
topic.created_at.isoformat(),
cls._serialize_participants(topic.participants),
cls._serialize_messages(topic.messages),
]
)