Skip to content

Commit

Permalink
filtragem de componenetes e dados voláteis com base em menores preços…
Browse files Browse the repository at this point in the history
… dentro de um intervalo. #52
  • Loading branch information
wesleyvitor11000 committed Mar 27, 2023
1 parent ab60f1f commit 925200e
Show file tree
Hide file tree
Showing 17 changed files with 437 additions and 196 deletions.
8 changes: 6 additions & 2 deletions src/Scraper/application/ScraperOrchestration/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from framework.infrastructure.connection_util import _get_engine
from framework.infrastructure.db_management.db_connection import create_session
from Scraper.domain.commands import *
from framework.domain.components import Component
from framework.application.handler import MessageBus


Expand Down Expand Up @@ -76,12 +77,15 @@ async def run_scraping(self):
component_manager = SQLAlchemyRepository(
self.session
) # placeholder
component = component_manager.get(filters_gt={"consumption": -1})[
0
component: Component = component_manager.get(
filters_gt={"consumption": -1}
)[
int(uniform(0, 10))
] # placeholder
volatile_data = VolatileData(
_id=UUIDv5(url.url),
component_id=component.uid,
component_type=component.type,
url=url,
cost=cost,
availability=availability,
Expand Down
56 changes: 52 additions & 4 deletions src/Scraper/application/handlers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Dict, Type, List
from typing import Type, Dict, List
from smtplib import SMTP
from ssl import create_default_context
from email.mime.text import MIMEText
Expand All @@ -12,10 +12,11 @@
)
from SearchEngine.application.unit_of_work import SQLAlchemyUnitOfWork
from framework.domain.components import Component
from framework.application.handler import MessageHandler, Command
from ..domain.repositories import ICategoryURLRepository
from framework.domain.events import Command, DomainEvent
from framework.application.handler import MessageHandler
from Scraper.domain.aggragate import VolatileData
from ..domain.repositories import ICategoryURLRepository, IVolatileDataRepository
from ..domain.events import LowerPriceRegisteredEvent
from framework.domain.events import DomainEvent
from ..domain.commands import *


Expand Down Expand Up @@ -81,6 +82,14 @@ def __call__(self, cmd: GetVolatileDataByMaxCost):
return self.uow.repository.get(filters_lt={"cost": cmd.cost})


class GetLowerCostVolatileDatasHandler(MessageHandler):
def __call__(self, cmd: GetLowerCostVolatileDatas):
with self.uow:
cost_filter = {} if cmd.cost >= 0 else {"filters_lt": {"cost": cmd.cost}}
if isinstance(self.uow.repository, IVolatileDataRepository):
return self.uow.repository.get_lower_costs(**cost_filter)


class GetVolatileDataByComponentUIDHandler(MessageHandler):
def __call__(self, cmd: GetVolatileDataByComponentUID):
with self.uow:
Expand All @@ -89,6 +98,42 @@ def __call__(self, cmd: GetVolatileDataByComponentUID):
)


def _get_components_from_volatile_data(volatile_data: list[VolatileData]):
message_bus = get_message_bus(
SE_EVENT_HANDLER_MAPPER, SE_COMMAND_HANDLER_MAPPER, SQLAlchemyUnitOfWork
)

components = [
message_bus.handle(GetComponentByUID(vd.component_id)) for vd in volatile_data
]

return components


class GetComponentsFromVolatileDataHandler(MessageHandler):
def __call__(self, cmd: GetComponentsFromVolatileData):
with self.uow:
return _get_components_from_volatile_data(cmd.volatile_data)


class GetVolatileDataByCostIntervalHandler(MessageHandler):
def __call__(self, cmd: GetVolatileDataByCostInterval):
with self.uow:
filters_lt = {"cost": cmd.max_cost}
filters_gt = {"cost": cmd.min_cost}
filters_eq = {"component_type": cmd.component_type}

volatile_data = self.uow.repository.get_lower_costs(
filters_eq=filters_eq,
filters_lt=filters_lt,
filters_gt=filters_gt,
)

components = _get_components_from_volatile_data(volatile_data)

return components, volatile_data


CURL_COMMAND_HANDLER_MAPPER: Dict[Type[Command], Type[MessageHandler]] = {
AddCategoryURL: AddCategoryURLHandler,
GetCategoryURLByDomain: GetVolatileDataByDomainHandler,
Expand All @@ -101,6 +146,9 @@ def __call__(self, cmd: GetVolatileDataByComponentUID):
AddVolatileData: AddVolatileDataHandler,
GetVolatileDataByMaxCost: GetVolatileDataByMaxCostHandler,
GetVolatileDataByComponentUID: GetVolatileDataByComponentUIDHandler,
GetLowerCostVolatileDatas: GetLowerCostVolatileDatasHandler,
GetComponentsFromVolatileData: GetComponentsFromVolatileDataHandler,
GetVolatileDataByCostInterval: GetVolatileDataByCostIntervalHandler,
}

VD_EVENT_HANDLER_MAPPER: Dict[Type[DomainEvent], List[Type[MessageHandler]]] = {
Expand Down
15 changes: 13 additions & 2 deletions src/Scraper/domain/aggragate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,26 @@
from dataclasses import dataclass, field
from typing import List

from framework.domain.components_enums import EComponentType
from framework.domain.entity import AggregateRoot
from framework.domain.value_object import UUID, Money, URL

_AttrsVolatileData = ["_id", "url", "component_id", "cost", "availability", "timestamp"]
_AttrsVolatileData = [
"_id",
"url",
"component_id",
"component_type",
"cost",
"availability",
"timestamp",
]


@dataclass(kw_only=True)
class VolatileData(AggregateRoot):
# url_id: UUID
url: URL
component_id: UUID
component_type: EComponentType
cost: Money
availability: bool

Expand All @@ -29,13 +38,15 @@ def generateVolatileDataPoint(
self,
_id: UUID,
component_id: UUID,
component_type: EComponentType,
url: URL,
cost: Money,
availability: bool,
):
return VolatileData(
_id=_id,
component_id=component_id,
component_type=component_type,
url=url,
cost=cost,
availability=availability,
Expand Down
24 changes: 24 additions & 0 deletions src/Scraper/domain/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from Scraper.domain.entity import CategoryURL
from Scraper.domain.aggragate import VolatileData
from framework.domain.value_object import UUID, Money
from framework.domain.components import Component
from framework.domain.components_enums import *

__all__ = [
"AddCategoryURL",
Expand All @@ -12,6 +14,9 @@
"AddVolatileData",
"GetVolatileDataByMaxCost",
"GetVolatileDataByComponentUID",
"GetLowerCostVolatileDatas",
"GetComponentsFromVolatileData",
"GetVolatileDataByCostInterval",
]


Expand Down Expand Up @@ -45,5 +50,24 @@ class GetVolatileDataByMaxCost(Command):
cost: float


@dataclass
class GetLowerCostVolatileDatas(Command):
cost: float = -1
pass


@dataclass
class GetVolatileDataByComponentUID(Command):
component_uid: UUID


@dataclass
class GetComponentsFromVolatileData(Command):
volatile_data: list[VolatileData]


@dataclass
class GetVolatileDataByCostInterval(Command):
component_type: EComponentType
min_cost: float
max_cost: float
7 changes: 6 additions & 1 deletion src/Scraper/domain/repositories.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from framework.domain.repository import (
AbstractRepository,
AbstractCategoryURLRepository,
AbstractVolatileDataRepository,
)
from framework.domain.exception import DomainException
from Scraper.domain.aggragate import VolatileData
Expand Down Expand Up @@ -62,7 +63,7 @@ def __post_init__(self):
f"URL de categoria com UID {self.entity_id} já existe."


class IVolatileDataRepository(AbstractRepository, metaclass=ABCMeta):
class IVolatileDataRepository(AbstractVolatileDataRepository, metaclass=ABCMeta):
@abstractmethod
def __init__(self, session):
raise NotImplemented
Expand All @@ -79,6 +80,10 @@ def _get_by_uid(self, ref: UUID):
def _get(self, **kwargs):
raise NotImplemented

@abstractmethod
def _get_lower_costs(self, **kwargs):
raise NotImplemented

def __repr__(self):
raise NotImplemented

Expand Down
82 changes: 66 additions & 16 deletions src/Scraper/infrastructure/SQL_alchemy_volatile_data.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from sqlalchemy.orm.session import Session
from sqlalchemy.exc import NoResultFound
from sqlalchemy import update
from sqlalchemy import func
from sqlalchemy import Column
from datetime import datetime, timedelta

from framework.domain.value_object import UUID, Money
from framework.infrastructure.db_management.db_mapping import (
Expand All @@ -10,6 +12,7 @@
)
from framework.infrastructure.db_management.db_structure import (
VolatileDataInstance,
LowerCostsInstance,
AttrsVolatileData,
)
from Scraper.domain.events import LowerPriceRegisteredEvent
Expand Down Expand Up @@ -45,6 +48,18 @@ def _db_object_to_volatile_data(

return volatile_data

def _db_volatile_data_to_lower_cost(
self, volatile_data: VolatileData
) -> LowerCostsInstance:
lower = LowerCostsInstance()
lower.component_uid = volatile_data.component_id
lower.component_type = volatile_data.component_type
lower.volatile_data_uid = volatile_data.uid
lower.cost = volatile_data.cost.amount
lower.timestamp = volatile_data.timestamp

return lower

def _get_instance_by_uid(self, ref: UUID) -> VolatileDataInstance:
query_filter = [VolatileDataInstance.url_id == ref]

Expand All @@ -58,40 +73,65 @@ def _get_instance_by_uid(self, ref: UUID) -> VolatileDataInstance:

return vol_data_inst

def _add(self, volatile_data: VolatileData):
db_volatile_data: VolatileDataInstance = self._volatile_data_to_db_object(
volatile_data
)

db_volatile_data.url = volatile_data.url.url # TODO modificar dicionário
db_volatile_data.cost = volatile_data.cost.amount
def _get_lower_cost_by_uid(self, ref: UUID | Column) -> LowerCostsInstance | None:
query_filter = [LowerCostsInstance.component_uid == ref]

try:
current_volatile_data = self._get_instance_by_uid(volatile_data.uid)
vol_data_inst: LowerCostsInstance = (
self._session.query(LowerCostsInstance).filter(*query_filter).one()
)

except Exception:
return None

return vol_data_inst

def _update_lower_cost(self, volatile_data: VolatileData) -> None:
current_lower = self._get_lower_cost_by_uid(volatile_data.component_id)
min_timestamp = datetime.now() - timedelta(days=1)

if current_lower is None:
lower = self._db_volatile_data_to_lower_cost(volatile_data)
self._session.add(lower)
return

if (
db_volatile_data.cost + 0.1 < current_volatile_data.cost
and db_volatile_data.availability
):
price_reduced = volatile_data.cost.amount + 0.1 < current_lower.cost
current_outdate = current_lower.timestamp < min_timestamp.date()

if (
(price_reduced or current_outdate) and volatile_data.availability
) or current_lower.volatile_data_uid == volatile_data:
current_lower.cost = volatile_data.cost.amount
current_lower.volatile_data_uid = volatile_data.uid
current_lower.timestamp = volatile_data.timestamp

if price_reduced:
volatile_data.events.append(
LowerPriceRegisteredEvent(
volatile_data.component_id, volatile_data.cost
)
)

print("redução do preço")
def _add(self, volatile_data: VolatileData):
db_volatile_data: VolatileDataInstance = self._volatile_data_to_db_object(
volatile_data
)

db_volatile_data.url = volatile_data.url.url # TODO modificar dicionário
db_volatile_data.cost = volatile_data.cost.amount

try:
current_volatile_data = self._get_instance_by_uid(volatile_data.uid)
current_volatile_data.cost = db_volatile_data.cost
current_volatile_data.timestamp = db_volatile_data.timestamp

except EntityUIDNotFoundException:
self._session.add(db_volatile_data)

self._update_lower_cost(volatile_data)
self._session.commit()

def _get(self, **kwargs):
cost = kwargs.get("cost", None)

filters = parse_filters(VolatileDataInstance, **kwargs)

volatile_datas_instances = filter_instance_from_db(
Expand All @@ -109,3 +149,13 @@ def _get_by_uid(self, ref: UUID):
volatile_data = self._db_object_to_volatile_data(volatile_data_instance)

return volatile_data

def _get_lower_costs(self, **kwargs):
filters = parse_filters(LowerCostsInstance, **kwargs)

lower_costs = filter_instance_from_db(
self._session, LowerCostsInstance.volatile_data_uid, filters
)
volatile_datas = [self._get_by_uid(vd_uid[0]) for vd_uid in lower_costs]

return volatile_datas
6 changes: 5 additions & 1 deletion src/SearchEngine/domain/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
from framework.domain.value_object import UUID, UUIDv4
from framework.domain.events import Command

__all__ = ["GetComponentByUID", "ListComponentsByType", "AddComponent"]
__all__ = [
"GetComponentByUID",
"ListComponentsByType",
"AddComponent",
]


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import List

from framework.domain.components import *
from framework.domain.components_enums import *
from framework.domain.value_object import UUID
from SearchEngine.domain.repositories import (
ISQLAlchemyRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import List
from framework.domain.components import *
from framework.domain.components_enums import *
from framework.infrastructure.db_management.db_mapping import map_from_to
from framework.infrastructure.db_management.db_structure import (
ComponentInstance,
Expand Down
Loading

0 comments on commit 925200e

Please sign in to comment.