Skip to content
This repository has been archived by the owner on Jan 18, 2025. It is now read-only.

Commit

Permalink
refactor: Whatever will be, will be 8{
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinNitroG committed Jan 1, 2025
1 parent 1a4efc4 commit c3547d6
Show file tree
Hide file tree
Showing 16 changed files with 90 additions and 78 deletions.
4 changes: 2 additions & 2 deletions schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@
"title": "Data",
"type": "array"
},
"notify": {
"notifications": {
"description": "Danh sách các thiết lập để thông báo",
"items": {
"$ref": "#/$defs/TelegramNotifyDTO"
},
"title": "Notify",
"title": "Notifications",
"type": "array"
},
"unpaid_only": {
Expand Down
29 changes: 17 additions & 12 deletions src/check_phat_nguoi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
from logging import Logger, getLogger

from check_phat_nguoi.config import config
from check_phat_nguoi.config.config_reader import _config_reader
from check_phat_nguoi.context.plate_context.models.plate import PlatesModel
from check_phat_nguoi.config.dto.notify.base_notify import BaseNotifyDTO
from check_phat_nguoi.config.dto.notify.telegram_notify import TelegramNotifyDTO
from check_phat_nguoi.context import PlatesModel
from check_phat_nguoi.get_data.check_phat_nguoi import GetDataCheckPhatNguoi
from check_phat_nguoi.notify.message import Message
from check_phat_nguoi.notify.noti_engine import NotificationEngine
from check_phat_nguoi.notify.telegram import Telegram

from .utils.setup_logger import setup_logger
Expand All @@ -16,17 +18,20 @@
async def _main():
setup_logger()
logger.debug(config)
config_object = _config_reader()
get_data_object = GetDataCheckPhatNguoi(config_object.data)
data = await get_data_object.get_data()
plate_object = PlatesModel(plates=data)
message_object = Message(
plate_context_object=plate_object, config_object=config_object
plates: PlatesModel = PlatesModel(
plates=await GetDataCheckPhatNguoi(config.data).get_data()
)
message_dict = message_object.format_messages()
for notify_object in config_object.notify:
telegram_object = Telegram(notify_object, message_dict)
await telegram_object.send_messages()
message_dict = Message(plates=plates).format_messages()
notifications: filter[BaseNotifyDTO] = filter(
lambda notify: notify.enabled, config.notifications
)
for notification in notifications:
noti_engine: NotificationEngine
if isinstance(notification, TelegramNotifyDTO):
noti_engine = Telegram(notification.telegram, message_dict)
else:
continue # Unknown notification engine
await noti_engine.send_messages()


def main():
Expand Down
2 changes: 0 additions & 2 deletions src/check_phat_nguoi/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
from .config import Config
from .config_reader import config
from .dto import *

__all__ = [
"config",
"Config",
"ConfigDTO",
"PlateInfoDTO",
"LogLevelDTO",
Expand Down
2 changes: 1 addition & 1 deletion src/check_phat_nguoi/config/config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Final

from check_phat_nguoi.config.exceptions.no_config_found import NoConfigFoundException
from check_phat_nguoi.modules.constants.config import CONFIG_PATHS
from check_phat_nguoi.constants.config import CONFIG_PATHS

from .dto import ConfigDTO

Expand Down
2 changes: 1 addition & 1 deletion src/check_phat_nguoi/config/dto/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ConfigDTO(BaseModel):
data: tuple[PlateInfoDTO, ...] = Field(
description="Danh sách các biển xe", default_factory=tuple
)
notify: tuple[TelegramNotifyDTO, ...] = Field(
notifications: tuple[TelegramNotifyDTO, ...] = Field(
description="Danh sách các thiết lập để thông báo", default_factory=tuple
)
unpaid_only: bool = Field(
Expand Down
31 changes: 18 additions & 13 deletions src/check_phat_nguoi/get_data/check_phat_nguoi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,39 @@
from aiohttp import ClientConnectionError, ClientSession, ClientTimeout

from check_phat_nguoi.config import PlateInfoDTO
from check_phat_nguoi.constants import (
DATETIME_FORMAT_CHECKPHATNGUOI as DATETIME_FORMAT,
)
from check_phat_nguoi.constants import (
GET_DATA_API_URL_CHECKPHATNGUOI as API_URL,
)
from check_phat_nguoi.constants import OFFICE_NAME_PATTERN
from check_phat_nguoi.context import PlateInfoModel, ViolationModel
from check_phat_nguoi.context.plate_context.models.resolution_office import (
ResolutionOfficeModel,
)

from ..modules.constants.get_data import (
DATETIME_FORMAT_CHECKPHATNGUOI as DATETIME_FORMAT,
)
from ..modules.constants.get_data import GET_DATA_API_URL_CHECKPHATNGUOI as API_URL
from ..modules.constants.get_data import OFFICE_NAME_PATTERN
from .get_data_base import GetDataBase

logger = getLogger(__name__)


class GetDataCheckPhatNguoi(GetDataBase):
# TODO: Refactor timeout to constant
def __init__(
self, plate_infos: tuple[PlateInfoDTO, ...], timeout: int = 10
) -> None:
super().__init__(plate_infos)
# NOTE: Can we specify the Dict???
self.data_dict: Dict[PlateInfoDTO, None | Dict] = {}
self.timeout = timeout
self.headers = {"Content-Type": "application/json"}
self.session: ClientSession = ClientSession()

async def _get_data_request(self, plate_info_object: PlateInfoDTO) -> None:
payload: dict[str, str] = {"bienso": plate_info_object.plate}
session = ClientSession()
try:
async with session.post(
async with self.session.post(
API_URL,
headers=self.headers,
json=payload,
Expand All @@ -47,6 +51,7 @@ async def _get_data_request(self, plate_info_object: PlateInfoDTO) -> None:
response_data = await response.read()
response_data = json.loads(response_data)
self.data_dict[plate_info_object] = response_data
logger.debug(f"Successfully get data for plate: {plate_info_object.plate}")
except asyncio.TimeoutError:
logger.error(
f"Time out of {self.timeout} seconds from URL {API_URL} for plate: {plate_info_object.plate}"
Expand All @@ -55,12 +60,11 @@ async def _get_data_request(self, plate_info_object: PlateInfoDTO) -> None:
logger.error(
f"Error occurs while connecting to {API_URL} for plate: {plate_info_object.plate}"
)
finally:
await session.close()

async def _get_data(self) -> None:
tasks = [self._get_data_request(plate_info) for plate_info in self._plate_infos]
tasks = (self._get_data_request(plate_info) for plate_info in self._plate_infos)
await asyncio.gather(*tasks)
await self.session.close()

@staticmethod
def get_plate_violation(
Expand All @@ -76,6 +80,7 @@ def _create_resolution_office_mode(
) -> tuple[ResolutionOfficeModel, ...]:
parsed_office_dict: Dict[str, Dict] = {}
current_name = None
# FIXME: Declare Type for typesafety, use ResolutionOfficeModel
for office_info in resolution_offices:
if re.match(OFFICE_NAME_PATTERN, office_info):
current_name = office_info.split(".", 1)[1].strip()
Expand Down Expand Up @@ -123,13 +128,13 @@ async def get_data(self) -> tuple[PlateInfoModel, ...]:
await self._get_data()
plate_infos: tuple[PlateInfoModel, ...] = tuple(
PlateInfoModel(
plate=plate_info_object.plate,
owner=plate_info_object.owner,
plate=plate_info.plate,
owner=plate_info.owner,
violation=GetDataCheckPhatNguoi.get_plate_violation(
plate_violation_dict=plate_violation_dict
),
)
for plate_info_object, plate_violation_dict in self.data_dict.items()
for plate_info, plate_violation_dict in self.data_dict.items()
)

return plate_infos
3 changes: 1 addition & 2 deletions src/check_phat_nguoi/get_data/get_data_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ def __init__(self, plate_infos: tuple[PlateInfoDTO, ...]) -> None:
self._plate_infos: tuple[PlateInfoDTO, ...] = plate_infos

@abstractmethod
async def get_data(self) -> tuple[PlateInfoModel, ...]:
pass
async def get_data(self) -> tuple[PlateInfoModel, ...]: ...
1 change: 0 additions & 1 deletion src/check_phat_nguoi/modules/__init__.py

This file was deleted.

32 changes: 17 additions & 15 deletions src/check_phat_nguoi/notify/message.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
from check_phat_nguoi.config.dto.config import ConfigDTO
from check_phat_nguoi.config import config
from check_phat_nguoi.context import PlateInfoModel, PlatesModel
from check_phat_nguoi.context.plate_context.models.resolution_office import (
ResolutionOfficeModel,
)

from ..modules.constants.notify import (
from ..constants.notify import (
MESSAGE_MARKDOWN_PATTERN,
RESOLUTION_LOCATION_MARKDOWN_PATTERN,
)


class Message:
def __init__(self, plate_context_object: PlatesModel, config_object: ConfigDTO):
self._plate_context_object: PlatesModel = plate_context_object
self._config_object: ConfigDTO = config_object
def __init__(self, plates: PlatesModel):
self._plates: PlatesModel = plates

@staticmethod
def format_location(
def _format_location(
locations_info: tuple[ResolutionOfficeModel, ...] | None,
) -> str:
if locations_info is None:
Expand All @@ -35,32 +34,35 @@ def format_location(
return resolution_markdown

@staticmethod
def format_message(
plate_info_context: PlateInfoModel, unpaid_paid_only: bool
def _format_message(
plate_info_context: PlateInfoModel, unpaid_only: bool
) -> tuple[str, ...]:
return tuple(
[
MESSAGE_MARKDOWN_PATTERN.substitute(
plate=plate_info_context.plate,
owner="Không biết"
owner="Không"
if not plate_info_context.owner
else plate_info_context.owner,
action=vio.action,
status="Đã nộp phạt" if vio.status else "Chưa nộp phạt",
status="Đã xử phạt" if vio.status else "Chưa xử phạt",
date=f"{vio.date}",
location=vio.location,
enforcement_unit=vio.enforcement_unit,
resolution_locations=Message.format_location(vio.resolution_office),
resolution_locations=Message._format_location(
vio.resolution_office
),
)
for vio in plate_info_context.violation
if not vio.status or unpaid_paid_only
if not vio.status or unpaid_only
]
)

# FIXME: complex object!!
def format_messages(self) -> dict[str, tuple[str, ...]]:
message_dict: dict[str, tuple[str, ...]] = {}
for plate_info_context in self._plate_context_object.plates:
message_dict[plate_info_context.plate] = Message.format_message(
plate_info_context, self._config_object.unpaid_only
for plate_info_context in self._plates.plates:
message_dict[plate_info_context.plate] = Message._format_message(
plate_info_context, unpaid_only=config.unpaid_only
)
return message_dict
6 changes: 6 additions & 0 deletions src/check_phat_nguoi/notify/noti_engine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from abc import abstractmethod


class NotificationEngine:
@abstractmethod
async def send_messages(self) -> None: ...
54 changes: 26 additions & 28 deletions src/check_phat_nguoi/notify/telegram.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,68 @@

from aiohttp import ClientConnectionError, ClientSession, ClientTimeout

from check_phat_nguoi.config import TelegramNotifyDTO
from check_phat_nguoi.config.dto.notify.telegram import TelegramDTO

from ..modules.constants.notify import SEND_MESSAGE_API_URL_TELEGRAM as API_URL
from ..constants.notify import SEND_MESSAGE_API_URL_TELEGRAM as API_URL
from .noti_engine import NotificationEngine

logger = getLogger(__name__)


class Telegram:
class Telegram(NotificationEngine):
# FIXME: The message_dict is so ... bruh
def __init__(
self,
telegram_notify: TelegramNotifyDTO,
telegram: TelegramDTO,
message_dict: dict[str, tuple[str, ...]],
):
self._telegram_notify_object: TelegramNotifyDTO = telegram_notify
self._telegram: TelegramDTO = telegram
self._message_dict: dict[str, tuple[str, ...]] = message_dict
# FIXME: Heyyyyy refactor timeout hehe
self.timeout = 10
self.session: ClientSession = ClientSession()

async def _send_message(self, message: str) -> None:
if not self._telegram_notify_object.enabled:
logger.info("Not enable to sending")
return
url = API_URL.format(bot_token=self._telegram_notify_object.telegram.bot_token)
url = API_URL.format(bot_token=self._telegram.bot_token)
payload = {
"chat_id": self._telegram_notify_object.telegram.chat_id,
"chat_id": self._telegram.chat_id,
"text": message,
"parse_mode": "Markdown",
}
session = ClientSession()
# session: ClientSession = ClientSession()
try:
async with session.post(
async with self.session.post(
url, json=payload, timeout=ClientTimeout(self.timeout)
) as response:
response.raise_for_status()
logger.info(
"Sending message completed for chat_id:{chat_id} and bot_token:{bot_token}".format(
chat_id=self._telegram_notify_object.telegram.chat_id,
bot_token=self._telegram_notify_object.telegram.bot_token,
chat_id=self._telegram.chat_id,
bot_token=self._telegram.bot_token,
)
)
except asyncio.TimeoutError:
logger.error(
"Time out of {self.timeout} seconds for chat_id:{chat_id} and bot_token:{bot_token}".format(
chat_id=self._telegram_notify_object.telegram.chat_id,
bot_token=self._telegram_notify_object.telegram.bot_token,
chat_id=self._telegram.chat_id,
bot_token=self._telegram.bot_token,
)
)
except ClientConnectionError:
logger.error(
"Unable to sending message for chat_id:{chat_id} and bot_token:{bot_token}".format(
chat_id=self._telegram_notify_object.telegram.chat_id,
bot_token=self._telegram_notify_object.telegram.bot_token,
chat_id=self._telegram.chat_id,
bot_token=self._telegram.bot_token,
)
)
except Exception as e:
logger.error(e)
finally:
await session.close()

async def send_messages(self) -> None:
async def _concurent_send_messages():
tasks = []
for _, message_tuple in self._message_dict.items():
for message in message_tuple:
tasks.append(self._send_message(message))
await asyncio.gather(*tasks)

await _concurent_send_messages()
tasks = (
self._send_message(message)
for message_tuple in self._message_dict.values()
for message in message_tuple
)
await asyncio.gather(*tasks)
await self.session.close()
2 changes: 1 addition & 1 deletion src/check_phat_nguoi/utils/setup_logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from logging import basicConfig

from check_phat_nguoi.config import config
from check_phat_nguoi.modules.constants.config import (
from check_phat_nguoi.constants.config import (
DETAIL_LOG_MESSAGE,
SIMPLE_LOG_MESSAGE,
)
Expand Down

0 comments on commit c3547d6

Please sign in to comment.