lab4 / app /api /topics /parser.py
brestok's picture
init
a08f988
Raw
History Blame Contribute Delete
3.85 kB
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),
]
)