Merge branch 'main' into xiaochuanli/addChromeExtensions
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import csv
|
||||
import functools
|
||||
import json
|
||||
import yaml
|
||||
import operator
|
||||
import re
|
||||
import pdfplumber
|
||||
@@ -15,9 +16,13 @@ from lxml.cssselect import CSSSelector
|
||||
from lxml.etree import _Element
|
||||
from rapidfuzz import fuzz
|
||||
from docx import Document
|
||||
import difflib
|
||||
|
||||
from .utils import _match_record, _match_value_to_rule
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("desktopenv.metric.general")
|
||||
|
||||
def check_include_exclude(result: str, rules: Dict[str, List[str]]) -> float:
|
||||
if result is None:
|
||||
@@ -41,6 +46,24 @@ def exact_match(result, rules) -> float:
|
||||
else:
|
||||
return 0.
|
||||
|
||||
|
||||
def literal_match(result: Any, expected: Any, **options) -> float:
|
||||
literal_type = options.get('type', 'str')
|
||||
if literal_type == 'str':
|
||||
ignore_case = options.get('ignore_case', False)
|
||||
score = str(result) == str(expected) if not ignore_case else str(result).lower() == str(expected).lower()
|
||||
return float(score)
|
||||
elif literal_type == 'list':
|
||||
if type(result) not in [list, tuple] or type(expected) not in [list, tuple] or len(result) != len(expected):
|
||||
return .0
|
||||
ignore_case = options.get('ignore_case', False)
|
||||
result = [str(s) for s in result] if not ignore_case else [str(s).lower() for s in result]
|
||||
expected = [str(s) for s in expected] if not ignore_case else [str(s).lower() for s in expected]
|
||||
return float(result == expected)
|
||||
else:
|
||||
raise NotImplementedError(f"Type {type} not supported")
|
||||
|
||||
|
||||
def is_in_list(result, rules) -> float:
|
||||
expect = rules["expected"]
|
||||
if expect in result:
|
||||
@@ -48,6 +71,15 @@ def is_in_list(result, rules) -> float:
|
||||
else:
|
||||
return 0.
|
||||
|
||||
def diff_text_file(result: str, expect: str) -> float:
|
||||
if result is None:
|
||||
return 0.
|
||||
|
||||
with open(result) as f:
|
||||
result_lines: List[str] = f.read().splitlines()
|
||||
with open(expect) as f:
|
||||
expected_lines: List[str] = f.read().splitlines()
|
||||
return difflib.SequenceMatcher(a=result_lines, b=expected_lines).ratio()
|
||||
|
||||
def fuzzy_match(result, rules) -> float:
|
||||
expect = rules["expected"]
|
||||
@@ -62,7 +94,7 @@ def fuzzy_place_math(result_file_path, rules) -> float:
|
||||
words_list = []
|
||||
for para in doc.paragraphs:
|
||||
words_list.extend(para.text.split())
|
||||
# 打印出提取的单词列表
|
||||
# Print out the list of extracted words
|
||||
print(words_list)
|
||||
for word in words_list:
|
||||
if not any(ans in word for ans in expect):
|
||||
@@ -140,11 +172,11 @@ _accessibility_ns_map = {"st": "uri:deskat:state.at-spi.gnome.org"
|
||||
}
|
||||
|
||||
|
||||
def check_accessibility_tree(result: str, rules: Dict[str, Any]) -> float:
|
||||
def check_accessibility_tree(result: str, rules: List[Dict[str, Any]]) -> float:
|
||||
"""
|
||||
Args:
|
||||
result (str): XML of GNOME Accessibility Tree
|
||||
rules (Dict[str, Any]): dict like
|
||||
rules (List[Dict[str, Any]]): list of dict like
|
||||
{
|
||||
"selectors": list of str as CSS selectors, will be connected by ", "
|
||||
to form a composite selector. Only one from `selectors` and
|
||||
@@ -162,30 +194,33 @@ def check_accessibility_tree(result: str, rules: Dict[str, Any]) -> float:
|
||||
"""
|
||||
|
||||
at: _Element = lxml.etree.fromstring(result)
|
||||
if "xpath" in rules:
|
||||
elements: List[_Element] = at.xpath(rules["xpath"], namespaces=_accessibility_ns_map)
|
||||
elif "selectors" in rules:
|
||||
selector = CSSSelector(", ".join(rules["selectors"]), namespaces=_accessibility_ns_map)
|
||||
elements: List[_Element] = selector(at)
|
||||
else:
|
||||
raise ValueError("At least one of xpath and selectors is required")
|
||||
total_match_score = 1.
|
||||
for r in rules:
|
||||
if "xpath" in r:
|
||||
elements: List[_Element] = at.xpath(r["xpath"], namespaces=_accessibility_ns_map)
|
||||
elif "selectors" in r:
|
||||
selector = CSSSelector(", ".join(r["selectors"]), namespaces=_accessibility_ns_map)
|
||||
elements: List[_Element] = selector(at)
|
||||
else:
|
||||
raise ValueError("At least one of xpath and selectors is required")
|
||||
|
||||
if len(elements) == 0:
|
||||
print("no elements")
|
||||
return 0.
|
||||
if len(elements) == 0:
|
||||
logger.info("No elements: %s", r["xpath"] if "xpath" in r else r["selectors"])
|
||||
return 0.
|
||||
|
||||
if "text" in rules:
|
||||
match_func: Callable[[str], Number] = functools.partial(operator.eq if rules["exact"] \
|
||||
else (lambda a, b: fuzz.ratio(a, b) / 100.)
|
||||
, rules["text"]
|
||||
)
|
||||
match_score: Number = 0
|
||||
for elm in elements:
|
||||
match_score = max(match_score, match_func(elm.text or None))
|
||||
else:
|
||||
match_score = 1.
|
||||
if "text" in r:
|
||||
match_func: Callable[[str], Number] = functools.partial( operator.eq if r["exact"] \
|
||||
else (lambda a, b: fuzz.ratio(a, b) / 100.)
|
||||
, r["text"]
|
||||
)
|
||||
match_score: Number = 0
|
||||
for elm in elements:
|
||||
match_score = max(match_score, match_func(elm.text or None))
|
||||
else:
|
||||
match_score = 1.
|
||||
total_match_score *= match_score
|
||||
|
||||
return float(match_score)
|
||||
return float(total_match_score)
|
||||
|
||||
|
||||
# def check_existence(result: str, *args) -> float:
|
||||
@@ -197,7 +232,7 @@ def run_sqlite3(result: str, rules: Dict[str, Any]) -> float:
|
||||
return float(cursor.fetchone()[0] or 0)
|
||||
|
||||
|
||||
def check_json(result: str, rules: Dict[str, List[Dict[str, Union[List[str], str]]]]) -> float:
|
||||
def check_json(result: str, rules: Dict[str, List[Dict[str, Union[List[str], str]]]], is_yaml: bool = False) -> float:
|
||||
"""
|
||||
Args:
|
||||
result (str): path to json file
|
||||
@@ -212,6 +247,7 @@ def check_json(result: str, rules: Dict[str, List[Dict[str, Union[List[str], str
|
||||
],
|
||||
"unexpect": <the same as `expect`
|
||||
}
|
||||
is_yaml (bool): yaml rather than json
|
||||
|
||||
Returns:
|
||||
float
|
||||
@@ -220,7 +256,10 @@ def check_json(result: str, rules: Dict[str, List[Dict[str, Union[List[str], str
|
||||
if result is None:
|
||||
return 0.
|
||||
with open(result) as f:
|
||||
result: Dict[str, Any] = json.load(f)
|
||||
if is_yaml:
|
||||
result: Dict[str, Any] = yaml.load(f, Loader=yaml.Loader)
|
||||
else:
|
||||
result: Dict[str, Any] = json.load(f)
|
||||
|
||||
expect_rules = rules.get("expect", {})
|
||||
unexpect_rules = rules.get("unexpect", {})
|
||||
@@ -229,14 +268,21 @@ def check_json(result: str, rules: Dict[str, List[Dict[str, Union[List[str], str
|
||||
for r in expect_rules:
|
||||
value = result
|
||||
for k in r["key"]:
|
||||
value = value[k]
|
||||
try:
|
||||
value = value[k]
|
||||
except KeyError:
|
||||
return 0.
|
||||
metric = metric and _match_value_to_rule(value, r)
|
||||
for r in unexpect_rules:
|
||||
value = result
|
||||
for k in r["key"]:
|
||||
value = value[k]
|
||||
try:
|
||||
value = value[k]
|
||||
except KeyError:
|
||||
value = None
|
||||
break
|
||||
metric = metric and not _match_value_to_rule(value, r)
|
||||
return metric
|
||||
return float(metric)
|
||||
|
||||
|
||||
def check_direct_json_object(result, rules)->float:
|
||||
@@ -257,6 +303,7 @@ def check_direct_json_object(result, rules)->float:
|
||||
print(rules["expected"])
|
||||
if result is None:
|
||||
return 0.
|
||||
|
||||
expect_in_result = rules.get("expect_in_result", False)
|
||||
if not expect_in_result:
|
||||
expected_json = rules["expected"]
|
||||
@@ -374,8 +421,6 @@ def compare_python_pure_text(py_file_path, gold_file_path):
|
||||
content1 = file1.read()
|
||||
with open(gold_file_path, 'r') as file2:
|
||||
content2 = file2.read()
|
||||
# 移除文件内容中的所有空白字符
|
||||
content1_no_whitespace = remove_whitespace(content1)
|
||||
content2_no_whitespace = remove_whitespace(content2)
|
||||
# 比较处理后的文件内容
|
||||
return content1_no_whitespace == content2_no_whitespace
|
||||
Reference in New Issue
Block a user