349 lines
13 KiB
Python
349 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
from typing import Optional, Self, Any, Callable, Sequence, TypeVar
|
|
from abc import abstractmethod
|
|
from AnP.Interfaces.Application.AnPInterface import AnPInterface
|
|
from AnP.DSLs.ScrapDSL import (
|
|
Execute, Action, Open, Close, Switch, Wait, Do, Go, Get, ForEach, And, Or, Not,
|
|
If, While, DoWhile, Download, Selector, Condition
|
|
)
|
|
from AnP.Utils.Common import Common
|
|
from AnP.Abstracts.ModelAbstract import ModelAbstract
|
|
|
|
T = TypeVar("T")
|
|
|
|
class BrowserAbstract(ModelAbstract):
|
|
|
|
def __init__(self:Self, anp:AnPInterface, key:str, TabType:T, inputs:Optional[dict[str, Any|None]|Sequence[Any|None]] = None) -> None:
|
|
self.anp:AnPInterface = anp
|
|
self.key:str = key
|
|
self.working:bool = True
|
|
self.tabs:dict[str, T] = {}
|
|
self.tab_selected:str = ""
|
|
self.human_time:float|int|Sequence[float|int] = Common.get_value("human_time", inputs, 0.0)
|
|
self.actions:dict[Action, Callable[[Action, dict[str, Any|None], Optional[Any]], None]] = {
|
|
Execute : self.execute,
|
|
Open : self.open,
|
|
Close : self.close,
|
|
Switch : self.switch,
|
|
Wait : self.wait,
|
|
Do : self.do,
|
|
Go : self.go,
|
|
Get : self.get,
|
|
ForEach : self.foreach,
|
|
And : self._and,
|
|
Or : self._or,
|
|
Not : self._not
|
|
}
|
|
|
|
def start(self:Self) -> None:
|
|
self.working = True
|
|
|
|
def close(self:Self) -> None:
|
|
self.working = False
|
|
self.tabs.clear()
|
|
self.tab_selected = ""
|
|
|
|
def is_working(self:Self) -> bool:
|
|
return self.working and self.anp.working()
|
|
|
|
@abstractmethod
|
|
def run(self:Self, actions:Sequence[Action], shared:dict[str, Any|None] = {}) -> dict[str, Any|None]:pass
|
|
|
|
def execute(self:Self, action:Execute, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
try:
|
|
action.callback(shared)
|
|
action.done = True
|
|
except Exception as exception:
|
|
action.exception = exception
|
|
|
|
@abstractmethod
|
|
def open(self:Self, action:Open, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
|
|
|
|
@abstractmethod
|
|
def close(self:Self, action:Close, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
|
|
|
|
@abstractmethod
|
|
def switch(self:Self, action:Switch, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
|
|
|
|
def wait(self:Self, action:Wait, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
if action.is_fixed:
|
|
self.anp.wait(action.time)
|
|
else:
|
|
if action.is_range:
|
|
self.anp.wait(Common.random_number(*action.time[0]))
|
|
else:
|
|
self.anp.wait(Common.random(*action.time))
|
|
action.done = True
|
|
|
|
def do(self:Self, action:Do, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
|
|
subaction:Action
|
|
i:int
|
|
|
|
action.done = True
|
|
|
|
for i, subaction in enumerate(action.actions):
|
|
if not self.anp.working():
|
|
action.done = False
|
|
return
|
|
|
|
if isinstance(subaction, Action):
|
|
subaction.done = False
|
|
subaction.exception = None
|
|
if subaction.__class__ in self.actions:
|
|
try:
|
|
|
|
ok:bool
|
|
|
|
self.actions[subaction.__class__](subaction, shared, last_item)
|
|
ok = subaction.done and subaction.exception is None
|
|
|
|
if not ok and not subaction.ignore_fail:
|
|
if subaction.exception is None:
|
|
subaction.print_errors and self.anp.print("warning", "anp_browser_action_not_done", {
|
|
"key" : self.key,
|
|
"action": subaction.__class__.__name__,
|
|
"i" : i
|
|
})
|
|
else:
|
|
self.anp.exception(subaction.exception, "anp_browser_action_exception", {
|
|
"key" : self.key,
|
|
"action": subaction.__class__.__name__,
|
|
"i" : i
|
|
})
|
|
action.done = False
|
|
break
|
|
|
|
if subaction.wait:
|
|
self.wait(Wait(self.human_time, True))
|
|
|
|
ok and subaction.print_ok and self.anp.print("info", "anp_browser_action_done", {
|
|
"key" : self.key,
|
|
"action": subaction.__class__.__name__,
|
|
"i" : i
|
|
})
|
|
|
|
except Exception as exception:
|
|
subaction.exception = exception
|
|
subaction.print_errors and self.anp.exception(exception, "anp_browser_action_exception", {
|
|
"key" : self.key,
|
|
"action": subaction.__class__.__name__,
|
|
"i" : i
|
|
})
|
|
if not subaction.ignore_fail:
|
|
action.done = False
|
|
break
|
|
|
|
else:
|
|
subaction.print_errors and self.anp.print("warning", "anp_browser_action_not_supported", {
|
|
"key" : self.key,
|
|
"action": subaction.__class__.__name__,
|
|
"i" : i
|
|
})
|
|
if not subaction.ignore_fail:
|
|
action.done = False
|
|
break
|
|
else:
|
|
self.anp.print("warning", "anp_browser_action_not_action", {
|
|
"key" : self.key,
|
|
"i" : i
|
|
})
|
|
action.done = False
|
|
break
|
|
|
|
|
|
@abstractmethod
|
|
def go(self:Self, action:Go, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
|
|
|
|
@abstractmethod
|
|
def get_selector(self:Self, selector:Selector, from_item:Optional[Any] = None, force_multiple:bool = False) -> Any|list[Any]|None:pass
|
|
|
|
@staticmethod
|
|
def get_value(action:Get, value:str) -> str:
|
|
if action.pattern is None:
|
|
return value
|
|
return action.pattern.sub(action.matches, value)
|
|
|
|
@abstractmethod
|
|
def get(self:Self, action:Get, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass
|
|
|
|
def foreach(self:Self, action:ForEach, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
|
|
item:Any|None
|
|
done:bool = True
|
|
is_selector:bool = isinstance(action.items, Selector)
|
|
|
|
for item in (
|
|
self.get_selector(action.items, last_item, True) if is_selector else
|
|
action.items):
|
|
|
|
if not self.anp.working():
|
|
done = False
|
|
break
|
|
|
|
if action.callback is not None:
|
|
try:
|
|
item = action.callback(item)
|
|
except Exception as exception:
|
|
action.exception = exception
|
|
done = False
|
|
break
|
|
|
|
subactions:Do = Do(*action.actions)
|
|
|
|
self.do(subactions, shared, item if is_selector else last_item)
|
|
if not subactions.done or subactions.exception is not None:
|
|
action.exception = subactions.exception
|
|
done = False
|
|
break
|
|
|
|
action.done = done
|
|
|
|
def _and(self:Self, action:And, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
|
|
|
|
condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition
|
|
|
|
action.ok = True
|
|
|
|
for condition in action.conditions:
|
|
if isinstance(condition, Condition):
|
|
self.do(condition, shared, last_item)
|
|
if not condition.ok:
|
|
action.ok = False
|
|
break
|
|
elif isinstance(condition, Selector):
|
|
if not self.get_selector(condition, last_item):
|
|
action.ok = False
|
|
break
|
|
elif callable(condition):
|
|
try:
|
|
if not condition(shared):
|
|
action.ok = False
|
|
break
|
|
except Exception as exception:
|
|
action.exception = exception
|
|
action.ok = False
|
|
break
|
|
|
|
def _or(self:Self, action:Or, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
|
|
|
|
condition:Callable[[dict[str, Any|None]], bool]|Selector|Condition
|
|
|
|
action.ok = False
|
|
|
|
for condition in action.conditions:
|
|
if isinstance(condition, Condition):
|
|
self.do(condition, shared, last_item)
|
|
if condition.ok:
|
|
action.ok = True
|
|
break
|
|
elif isinstance(condition, Selector):
|
|
if self.get_selector(condition, last_item):
|
|
action.ok = True
|
|
break
|
|
elif callable(condition):
|
|
try:
|
|
if condition(shared):
|
|
action.ok = True
|
|
break
|
|
except Exception as exception:
|
|
action.exception = exception
|
|
action.ok = False
|
|
break
|
|
|
|
def _not(self:Self, action:Not, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> bool:
|
|
if isinstance(action.condition, Condition):
|
|
self.do(action.condition, shared, last_item)
|
|
action.ok = not action.condition.ok
|
|
elif isinstance(action.condition, Selector):
|
|
action.ok = not self.get_selector(action.condition, last_item)
|
|
elif callable(action.condition):
|
|
try:
|
|
action.ok = not action.condition(shared)
|
|
except Exception as exception:
|
|
action.exception = exception
|
|
action.ok = False
|
|
|
|
def _if(self:Self, action:If, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
|
|
if not isinstance(action.condition, Condition):
|
|
action.condition = Condition((action.condition,))
|
|
|
|
self.do(action.condition, shared, last_item)
|
|
|
|
if action.condition.done and action.condition.exception is None:
|
|
|
|
subactions:Do = Do(*((action.if_actions if action.condition.ok else action.else_actions) or []))
|
|
|
|
self.do(subactions, shared, last_item)
|
|
action.done = subactions.done and subactions.exception is None
|
|
action.exception = subactions.exception
|
|
|
|
else:
|
|
action.done = action.condition.done
|
|
action.exception = action.condition.exception
|
|
|
|
def _while(self:Self, action:While, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
|
|
if not isinstance(action.condition, Condition):
|
|
action.condition = Condition((action.condition,))
|
|
|
|
while True:
|
|
if not self.anp.working():
|
|
action.done = False
|
|
break
|
|
|
|
self.do(action.condition, shared, last_item)
|
|
|
|
if not action.condition.done or action.condition.exception is not None:
|
|
action.done = action.condition.done
|
|
action.exception = action.condition.exception
|
|
break
|
|
|
|
if action.condition.ok:
|
|
|
|
subactions:Do = Do(*action.actions)
|
|
|
|
self.do(subactions, shared, last_item)
|
|
if not subactions.done or subactions.exception is not None:
|
|
action.exception = subactions.exception
|
|
action.done = False
|
|
break
|
|
|
|
else:
|
|
action.done = True
|
|
break
|
|
|
|
def do_while(self:Self, action:DoWhile, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:
|
|
|
|
if not isinstance(action.condition, Condition):
|
|
action.condition = Condition((action.condition,))
|
|
|
|
while True:
|
|
if not self.anp.working():
|
|
action.done = False
|
|
break
|
|
|
|
subactions:Do = Do(*action.actions)
|
|
|
|
self.do(subactions, shared, last_item)
|
|
if not subactions.done or subactions.exception is not None:
|
|
action.exception = subactions.exception
|
|
action.done = False
|
|
break
|
|
|
|
self.do(action.condition, shared, last_item)
|
|
|
|
if not action.condition.done or action.condition.exception is not None:
|
|
action.done = action.condition.done
|
|
action.exception = action.condition.exception
|
|
break
|
|
|
|
if not action.condition.ok:
|
|
action.done = True
|
|
break
|
|
|
|
@abstractmethod
|
|
def download(self:Self, action:Download, shared:dict[str, Any|None] = {}, last_item:Optional[Any] = None) -> None:pass |