AnP/Python/AnP/Abstracts/BrowserAbstract.py
2026-07-02 07:46:49 +02:00

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