Merge branch 'main' of github.com:ztjhz/DesktopEnv

This commit is contained in:
Siheng Zhao
2024-01-23 14:47:30 +08:00
26 changed files with 1254 additions and 111 deletions

View File

@@ -7,3 +7,4 @@ from .misc import get_rule, get_accessibility_tree
from .replay import get_replay
from .vlc import get_vlc_playing_info, get_vlc_config
from .vscode import get_vscode_config
from .impress import get_audio_in_slide

View File

@@ -3,17 +3,20 @@ from .docs import compare_font_names, compare_subscript_contains, has_page_numbe
from .docs import find_default_font, contains_page_break, compare_docx_files, compare_docx_tables, compare_line_spacing, \
compare_insert_equation
from .docs import is_first_line_centered, check_file_exists, compare_contains_image
from .general import exact_match, fuzzy_match, check_csv, check_accessibility_tree, check_list
from .docs import evaluate_colored_words_in_tables, check_highlighted_words, evaluate_strike_through_last_paragraph, \
evaluate_conversion, evaluate_spacing, check_italic_font_size_14, evaluate_alignment, get_unique_train_ids, \
check_no_duplicates
from .general import exact_match, fuzzy_match
from .general import check_csv, check_accessibility_tree, run_sqlite3, check_json
from .gimp import increase_saturation, decrease_brightness, check_file_exists, compare_triangle_positions
from .slides import check_presenter_console_disable, check_image_stretch_and_center, check_slide_numbers_color, compare_pptx_files, check_strikethrough, \
check_slide_orientation_Portrait, evaluate_presentation_fill_to_rgb_distance, check_left_panel
from .libreoffice import check_libre_locale
from .pdf import check_pdf_pages
#from .table import check_sheet_list, check_xlsx_freeze, check_xlsx_zoom, check_data_validations
from .table import compare_table
from .thunderbird import check_thunderbird_prefs, check_thunderbird_filter
from .vlc import is_vlc_playing, is_vlc_recordings_folder, is_vlc_fullscreen, compare_images, compare_audios, \
compare_videos
from .gimp import increase_saturation, decrease_brightness, check_file_exists, compare_triangle_positions
from .general import check_csv, check_accessibility_tree, check_list, run_sqlite3, check_json
from .thunderbird import check_thunderbird_prefs, check_thunderbird_filter
from .vscode import compare_text_file, compare_config, compare_answer, is_extension_installed
from .impress import check_image_stretch_and_center, check_slide_numbers_color, compare_pptx_files, check_strikethrough, \
check_for_audio, check_formula_shape
from .impress import check_slide_orientation_Portrait, contains_mp4_video
from .vscode import compare_text_file, compare_config, compare_answer, is_extension_installed, check_json_settings, check_json_keybindings

View File

@@ -5,6 +5,7 @@ from typing import List, Dict, Any
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import RGBColor
logger = logging.getLogger("desktopenv.metric.docs")
@@ -197,3 +198,162 @@ def compare_contains_image(docx_file1, docx_file2):
# config_path = "/home/[username]/.config/libreoffice/4/user/registrymodifications.xcu"
# print(find_default_font("Ani", config_path))
def evaluate_colored_words_in_tables(file_path):
document = Document(file_path)
for table in document.tables:
# Iterate through rows and cells in the table
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for run in paragraph.runs:
word = run.text
if word:
first_letter = word[0].lower()
if first_letter in 'aeiou' and run.font.color.rgb != RGBColor(255, 0, 0):
return 0 # Vowel-colored words should be red
elif first_letter not in 'aeiou' and run.font.color.rgb != RGBColor(0, 0, 255):
return 0 # Non-vowel-colored words should be blue
return 1 # All words in tables are correctly colored
def check_highlighted_words(file_path):
document = Document(file_path)
for paragraph in document.paragraphs:
for run in paragraph.runs:
if run.font.highlight_color is not None:
return 0 # Highlighted words found
return 1 # No highlighted words found
def evaluate_strike_through_last_paragraph(file_path):
document = Document(file_path)
# Get the last paragraph
last_paragraph = document.paragraphs[-1]
# Check if any run in the last paragraph has strike-through formatting
for run in last_paragraph.runs:
if not run.font.strike:
return 0 # At least one word does not have strike-through formatting
return 1 # All words in the last paragraph have strike-through formatting
def evaluate_conversion(file_path):
document = Document(file_path)
for table in document.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for run in paragraph.runs:
if run.text.isupper():
return 0 # Uppercase text should be converted to lowercase
for paragraph in document.paragraphs:
for run in paragraph.runs:
if run.text.isupper():
return 0 # Uppercase text should be converted to lowercase
return 1 # All uppercase text has been successfully converted
def evaluate_spacing(file_path):
document = Document(file_path)
# Check line spacing for introduction, body, and conclusion
introduction_spacing = document.paragraphs[0].paragraph_format.line_spacing
body_spacing = document.paragraphs[1].paragraph_format.line_spacing
conclusion_spacing = document.paragraphs[2].paragraph_format.line_spacing
if (introduction_spacing == 1.0 and body_spacing == 2.0 and conclusion_spacing == 1.5):
return 1
else:
return 0
def check_italic_font_size_14(path):
document = Document(path)
for paragraph in document.paragraphs:
for run in paragraph.runs:
if run.italic:
# Check if font size is 14
if run.font.size is None or run.font.size.pt != 14:
return 0
return 1
def evaluate_alignment(docx_path):
# Load the document
doc = Document(docx_path)
# Iterate through each paragraph in the document
for para in doc.paragraphs:
# Split the paragraph into individual sentences
sentences = para.text.split('.')
for sentence in sentences:
# Split the sentence into words
words = sentence.strip().split()
# Check if the sentence has at least three words
if len(words) < 3:
continue # Skip sentences with less than three words
# The first three words should be separated from the rest
first_part = ' '.join(words[:3])
second_part = ' '.join(words[3:])
# Check if the sentence structure matches the pattern: first part + large space/tab + second part
if not (first_part in sentence and second_part in sentence and sentence.find(first_part) < sentence.find(second_part)):
return 0 # The sentence does not meet the alignment criteria
return 1 # All sentences meet the alignment criteria
def get_unique_train_ids(initial_file): #fixed standard
doc = Document(initial_file)
train_ids = set()
processed_lines = 0
for para in doc.paragraphs:
line_parts = para.text.split(',')
if len(line_parts) == 4:
train_id = line_parts[1].strip()
if train_id not in train_ids:
train_ids.add(train_id)
processed_lines += 1
return train_ids, processed_lines
def check_no_duplicates(initial_file, processed_file):
# Open the document
train_ids_ini, ini_lines = get_unique_train_ids(initial_file)
doc_processed = Document(processed_file)
train_ids_pro = set()
processed_lines = 0 # Counter for valid lines processed
# processed
for para in doc_processed.paragraphs:
# Each line has the format: time_HH:MM:SS, train_id, station_id, platform_no
line_parts = para.text.split(',')
# Ensure the line has the correct format
if len(line_parts) == 4:
train_id = line_parts[1].strip()
# If train_id is already in the set, it's a duplicate
if train_id in train_ids_pro:
return 0 # Duplicate found
train_ids_pro.add(train_id)
processed_lines += 1 # Increment valid lines counter
if train_ids_pro != train_ids_ini or processed_lines != ini_lines:
return 0
# No duplicates found and at least one valid line was processed
return 1

View File

@@ -1,5 +1,5 @@
import logging
import operator
#import operator
from numbers import Number
from typing import Any, Union, cast, Callable
from typing import Dict, List, Tuple
@@ -14,7 +14,8 @@ from openpyxl.worksheet.worksheet import Worksheet
#from openpyxl.worksheet.cell_range import MultiCellRange
from openpyxl.worksheet.datavalidation import DataValidation
from .utils import load_charts, load_sparklines, _match_value_to_rule
from .utils import load_charts, load_sparklines, load_rows_or_cols, load_xlsx_styles
from .utils import _match_value_to_rule
logger = logging.getLogger("desktopenv.metric.table")
@@ -160,18 +161,19 @@ def compare_table(result: str, expected: str, **options) -> float:
logger.debug("Assertion: %s[chart] == %s[chart] - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Compare Charts #
elif r["type"] == "number_format":
# Compare Number Formats {{{ #
elif r["type"] == "style":
# Compare Style (Also Conditional Formatiing) {{{ #
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
# sheet_idx1: as sheet_idx0
# props: list of str indicating concerned styles
sheet1: Worksheet = _load_sheet(*parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke))
sheet2: Worksheet = _load_sheet(*parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke))
number_formats1: List[str] = [c.number_format.lower() for col in sheet1.iter_cols() for c in col if c.data_type=="n"]
number_formats2: List[str] = [c.number_format.lower() for col in sheet2.iter_cols() for c in col if c.data_type=="n"]
metric: bool = number_formats1 == number_formats2
logger.debug("Assertion: %s.nf == %s.nf - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Compare Number Formats #
styles1: Dict[str, List[Any]] = load_xlsx_styles(*parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke), **r)
styles2: Dict[str, List[Any]] = load_xlsx_styles(*parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke), **r)
#number_formats1: List[str] = [c.number_format.lower() for col in sheet1.iter_cols() for c in col if c.value is not None and c.data_type=="n"]
#number_formats2: List[str] = [c.number_format.lower() for col in sheet2.iter_cols() for c in col if c.value is not None and c.data_type=="n"]
metric: bool = styles1 == styles2
logger.debug("Assertion: %s.style == %s.style - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Compare Style (Also Conditional Formatiing) #
elif r["type"] == "freeze":
# Compare Freezing {{{ #
@@ -203,7 +205,7 @@ def compare_table(result: str, expected: str, **options) -> float:
elif r["type"] == "data_validation":
# Check Data Validation {{{ #
# sheet_idx: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
# dv_props: list of dict like {attribute: "method": str, "ref": anythin}
# dv_props: list of dict like {attribute: {"method": str, "ref": anything}}
# available attributes:
# * ranges
# * type
@@ -224,14 +226,14 @@ def compare_table(result: str, expected: str, **options) -> float:
sheet: Worksheet = _load_sheet(*parse_idx(r["sheet_idx"], xlworkbookr, xlworkbooke))
data_validators: List[DataValidation] = sheet.data_validations.dataValidation
total_metric = True
total_metric = len(data_validators)>=len(r["dv_props"])
for dat_vldt in data_validators:
metric = False
for r in r["dv_props"]:
for prpt in r["dv_props"]:
metric = metric or all( _match_value_to_rule( getattr(dat_vldt, attrbt)
, mr
)\
for attrbt, mr in r.items()
for attrbt, mr in prpt.items()
)
if metric:
break
@@ -243,6 +245,44 @@ def compare_table(result: str, expected: str, **options) -> float:
metric: bool = total_metric
# }}} Check Data Validation #
elif r["type"] == "row_props":
# Check Row Properties {{{ #
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
# sheet_idx1: as sheet_idx0
# props: list of str, see utils.load_rows_or_cols
rows1: Dict[str, Any] = load_rows_or_cols( *parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke)
, obj="row"
, **r
)
rows2: Dict[str, Any] = load_rows_or_cols( *parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke)
, obj="row"
, **r
)
logger.debug("Rows1: %s", repr(rows1))
logger.debug("Rows2: %s", repr(rows2))
metric: bool = rows1 == rows2
logger.debug("Assertion: %s[rows] == %s[rows] - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Check Row Properties #
elif r["type"] == "col_props":
# Check Row Properties {{{ #
# sheet_idx0: 0 == "RI0" == "RNSheet1" | "EI0" == "ENSheet1"
# sheet_idx1: as sheet_idx0
# props: list of str, see utils.load_rows_or_cols
cols1: Dict[str, Any] = load_rows_or_cols( *parse_idx(r["sheet_idx0"], xlworkbookr, xlworkbooke)
, obj="column"
, **r
)
cols2: Dict[str, Any] = load_rows_or_cols( *parse_idx(r["sheet_idx1"], xlworkbookr, xlworkbooke)
, obj="column"
, **r
)
metric: bool = cols1 == cols2
logger.debug("Assertion: %s[cols] == %s[cols] - %s", r["sheet_idx0"], r["sheet_idx1"], metric)
# }}} Check Row Properties #
else:
raise NotImplementedError("Unimplemented sheet check: {:}".format(r["type"]))
@@ -254,15 +294,48 @@ def compare_table(result: str, expected: str, **options) -> float:
# }}} function compare_table #
if __name__ == '__main__':
path1 = "../../任务数据/LibreOffice Calc/Freeze_row_column.xlsx"
path2 = "../../任务数据/LibreOffice Calc/Freeze_row_column_gold.xlsx"
import datetime
import sys
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
datetime_str: str = datetime.datetime.now().strftime("%Y%m%d@%H%M%S")
file_handler = logging.FileHandler(os.path.join("logs", "normal-{:}.log".format(datetime_str)))
debug_handler = logging.FileHandler(os.path.join("logs", "debug-{:}.log".format(datetime_str)))
stdout_handler = logging.StreamHandler(sys.stdout)
sdebug_handler = logging.FileHandler(os.path.join("logs", "sdebug-{:}.log".format(datetime_str)))
file_handler.setLevel(logging.INFO)
debug_handler.setLevel(logging.DEBUG)
stdout_handler.setLevel(logging.INFO)
sdebug_handler.setLevel(logging.DEBUG)
formatter = logging.Formatter(fmt="\x1b[1;33m[%(asctime)s \x1b[31m%(levelname)s \x1b[32m%(module)s/%(lineno)d-%(processName)s\x1b[1;33m] \x1b[0m%(message)s")
file_handler.setFormatter(formatter)
debug_handler.setFormatter(formatter)
stdout_handler.setFormatter(formatter)
sdebug_handler.setFormatter(formatter)
stdout_handler.addFilter(logging.Filter("desktopenv"))
sdebug_handler.addFilter(logging.Filter("desktopenv"))
logger.addHandler(file_handler)
logger.addHandler(debug_handler)
logger.addHandler(stdout_handler)
logger.addHandler(sdebug_handler)
path1 = "../../任务数据/LibreOffice Calc/Calendar_Highlight_Weekend_Days.xlsx"
path2 = "../../任务数据/LibreOffice Calc/Calendar_Highlight_Weekend_Days_gold.xlsx"
rules = [ { "type": "sheet_data"
, "sheet_idx0": 0
, "sheet_idx1": "EI0"
}
, { "type": "freeze"
, { "type": "style"
, "sheet_idx0": 0
, "sheet_idx1": "EI0"
, "props": ["bgcolor"]
}
]
print( compare_table( path1, path2
@@ -274,17 +347,31 @@ if __name__ == '__main__':
)
)
#path = "../../任务数据/LibreOffice Calc/Order_Id_Mark_Pass_Fail_gold.xlsx"
#print( check_data_validations( path, [ { "ranges": { "method": "spreadsheet_range"
#, "ref": ["D2:D29", "D2:D1048576"]
#}
#, "type": { "method": "eq"
#, "ref": "list"
#}
#, "formula1": { "method": "str_set_eq"
#, "ref": ["Pass", "Fail", "Held"]
#}
#}
#]
#)
#)
# Row Properties
#path1 = "../../任务数据/LibreOffice Calc/Date_Budget_Variance_HideNA.xlsx"
#path2 = "../../任务数据/LibreOffice Calc/Date_Budget_Variance_HideNA_gold.xlsx"
#workbook: Workbook = openpyxl.load_workbook(filename=path1)
#worksheet: Worksheet = workbook.active
#for r_no, dms in worksheet.column_dimensions.items():
#print(r_no, type(r_no), type(dms), dms.hidden)
# Conditional Formats
#import formulas
#path1 = "../../任务数据/LibreOffice Calc/Calendar_Highlight_Weekend_Days.xlsx"
#path2 = "../../任务数据/LibreOffice Calc/Calendar_Highlight_Weekend_Days_gold.xlsx"
#path3 = "../../任务数据/LibreOffice Calc/Calendar_Highlight_Weekend_Days_gold_test.xlsx"
#workbook: Workbook = openpyxl.load_workbook(filename=path2)
#worksheet: Worksheet = workbook.active
#print(worksheet.conditional_formatting)
#for itm in worksheet.conditional_formatting:
#print(itm.cells)
#for r in itm.rules:
#print( r.type, r.formula, r.dxf.font.color.rgb
#, r.dxf.fill.fgColor.rgb, r.dxf.fill.bgColor.rgb
#)
#condition = formulas.Parser().ast("=" + r.formula[0])[1].compile()
##print(r.type, r.operator, r.dxfId, r.dxf)
#for r in itm.cells:
#for c in r.cells:
#value = worksheet.cell(row=c[0], column=c[1]).value
#print(value, condition(str(value)))

View File

@@ -1,6 +1,6 @@
import logging
import zipfile
from typing import Any, TypeVar, Union, Iterable, Optional
from typing import Any, TypeVar, Union, Iterable, Optional, Callable
from typing import Dict, List, Set, Match
from urllib.parse import urlparse, urlunparse
import re
@@ -17,6 +17,12 @@ from openpyxl import Workbook
from openpyxl.chart._chart import ChartBase
from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.worksheet.cell_range import MultiCellRange
from openpyxl.worksheet.dimensions import DimensionHolder
from openpyxl.formatting.formatting import ConditionalFormattingList
#from openpyxl.utils import get_column_letter
from openpyxl.cell.cell import Cell
from openpyxl.styles.differential import DifferentialStyle
import formulas
V = TypeVar("Value")
@@ -31,9 +37,8 @@ _xlsx_ns_imapping = dict(map(lambda itm: (itm[1], itm[0]), _xlsx_namespaces))
_sheet_name_selector = lxml.cssselect.CSSSelector("oo|sheets>oo|sheet", namespaces=_xlsx_ns_mapping)
_sparklines_selector = lxml.cssselect.CSSSelector("x14|sparkline", namespaces=_xlsx_ns_mapping)
def load_sparklines(xlsx_file: str, sheet_name: str) -> Dict[str, str]:
# function load_sparklines {{{ #
"""
This function modifies data_frame in-place
Args:
xlsx_file (str): path to xlsx
sheet_name (str): sheet name
@@ -64,6 +69,7 @@ def load_sparklines(xlsx_file: str, sheet_name: str) -> Dict[str, str]:
)
sparklines_dict[sparkline["x14:sparkline"]["xm:sqref"]] = sparkline["x14:sparkline"]["xm:f"]
return sparklines_dict
# }}} function load_sparklines #
# Available Chart Properties:
@@ -75,6 +81,7 @@ def load_sparklines(xlsx_file: str, sheet_name: str) -> Dict[str, str]:
# direction: "bar" (hori) | "col" (vert)
# xtitle, ytitle, ztitle: str
def load_charts(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[str, Any]:
# function load_charts {{{ #
"""
Args:
xlsx_file (Workbook): concerned excel book
@@ -83,7 +90,12 @@ def load_charts(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[str, An
giving the concerned chart properties
Returns:
Dict[str, Any]: information of charts
Dict[str, Any]: information of charts, dict like
{
<str representing data source>: {
<str as property>: anything
}
}
"""
# workbook: Workbook = openpyxl.load_workbook(filename=xlsx_file)
@@ -140,7 +152,132 @@ def load_charts(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[str, An
info["ztitle"] = ch.z_axis.title.tx.rich.p[0].r[0].t
chart_set[series] = info
return chart_set
# }}} function load_charts #
# Supported Styles:
# number_format
# font_name - str
# font_family - float
# font_color - in aRGB, e.g., FF000000 is black
# font_bold - bool
# font_italic - bool
# fill_type - "patternFill" | "gradientFill"
# bgcolor - in aRGB, e.g., FFFF0000 is red
# fgcolor - in aRGB, e.g., FF00FFFF is yellow
def _read_cell_style(style_name: str, cell: Cell, diff_style: Optional[DifferentialStyle] = None) -> Any:
if style_name=="number_format":
return (cell.number_format if diff_style is None else diff_style.numFmt.formatCode)\
if cell.value is not None and cell.data_type=="n" else None
elif style_name=="font_name":
return (diff_style or cell).font.name if cell.value is not None else None
elif style_name=="font_family":
return (diff_style or cell).font.family if cell.value is not None else None
elif style_name=="font_color":
return (diff_style or cell).font.color.rgb if cell.value is not None else None
elif style_name=="font_bold":
return (diff_style or cell).font.bold if cell.value is not None else None
elif style_name=="font_italic":
return (diff_style or cell).font.italic if cell.value is not None else None
elif style_name=="fill_type":
return (diff_style or cell).fill.tagname
elif style_name=="bgcolor":
return (diff_style or cell).fill.bgColor.rgb
elif style_name=="fgcolor":
return (diff_style or cell).fill.fgColor.rgb
else:
raise NotImplementedError("Unsupported Style: {:}".format(style_name))
def load_xlsx_styles(xlsx_file: Workbook, sheet_name: str, **options) -> Dict[str, List[Any]]:
# function load_xlsx_styles {{{ #
"""
Args:
xlsx_file (Workbook): concerned excel book
sheet_name (str): sheet name
options (Dict[str, List[str]): dick like {"props": list of str} giving
the concerned styles
Returns:
Dict[str, List[Any]]: dict like
{
<str as cell coordinates>: list of anything indicating concerned
property values
}
"""
worksheet: Worksheet = xlsx_file[sheet_name]
style_dict: Dict[str, List[Any]] = {}
concerned_styles: List[str] = options.get("props", [])
# Handles Cell Styles
for col in worksheet.iter_cols():
for c in col:
style_list: List[Any] = []
for st in concerned_styles:
style_list.append(_read_cell_style(st, c))
style_dict[c.coordinate] = style_list
# Handles Conditional Formatting
conditional_formattings: ConditionalFormattingList = worksheet.conditional_formatting
formula_parser = formulas.Parser()
for fmt in conditional_formattings:
for r in fmt.rules:
active_cells: List[Cell] = []
if r.type == "expression":
condition: Callable[[str], bool] = formula_parser.ast("=" + r.formula[0])[1].compile()
for rge in fmt.cells:
for c in rge.cells:
cell: Cell = worksheet.cell(row=c[0], column=c[1])
if condition(str(cell.value)):
active_cells.append(cell)
else:
raise NotImplementedError("Not Implemented Condition Type: {:}".format(r.type))
for c in active_cells:
style_dict[c.coordinate] = [_read_cell_style(st, c, r.dxf) for st in concerned_styles]
return style_dict
# }}} function load_xlsx_styles #
# Available Row Properties:
# hidden
# collapsed
# height
#
# Available Column Properties:
# width
# auto_size
# hidden
# collapsed
# min
# max
def load_rows_or_cols(xlsx_file: Workbook, sheet_name: str, **options)\
-> Dict[Union[int, str], Dict[str, Any]]:
# function load_rows_or_cols {{{ #
"""
Args:
xlsx_file (Workbook): concerned excel book
sheet_name (str): sheet name
options (Dict[str, List[str]]): dict like
{"obj": "row" | "column", "props": list of str} giving the concerned
row/column properties
Returns:
Dict[Union[int, str], Dict[str, Any]]: row/column information
"""
worksheet: Worksheet = xlsx_file[sheet_name]
objs: DimensionHolder = getattr(worksheet, "{:}_dimensions".format(options["obj"]))
obj_set: Dict[int, Any] = {}
obj_props: Set[str] = set(options.get("props", []))
for obj_no, obj_dms in objs.items():
info_dict: Dict[str, Any] = {}
for prop in obj_props:
info_dict[prop] = getattr(obj_dms, prop)
obj_set[obj_no] = info_dict
return obj_set
# }}} function load_rows_or_cols #
def _match_record(pattern: Dict[str, Any], item: Dict[str, Any]) -> bool:
return all(k in item and item[k] == val for k, val in pattern.items())

View File

@@ -1,5 +1,5 @@
from typing import Dict
import json
import json, copy
def check_json_keybindings(actual: str, expected: str, **options) -> float:
"""
@@ -10,36 +10,58 @@ def check_json_keybindings(actual: str, expected: str, **options) -> float:
Return:
float: the score
"""
def direct_load_json(fp):
try:
with open(fp, 'r') as f:
data = json.load(f)
return data
except:
return None
def skip_first_line_load_json(fp):
try:
with open(fp, 'r') as f:
f.readline()
data = json.load(f)
return data
except:
return None
with open(actual) as f:
data = json.load(f)
for func in [direct_load_json, skip_first_line_load_json]:
data = func(actual)
if data is not None and type(data) == list:
break
else:
return 0.0
expected = expected['expect']
if expected in data:
return 1.0
else:
return 0.0
def check_json_settings(actual: str, expected: str, **options) -> float:
"""
Args:
actual (str): path to result text file
expected (str): expected dict{}
expected (dict): expected dict{}, containing key "expect"
Return:
float: the score
"""
with open(actual) as f:
with open(actual, 'r') as f:
data = json.load(f)
expect = set(expected.items())
json = set(data.items())
if expect.issubset(json):
expect = expected['expect']
data_copy = copy.deepcopy(data)
data_copy.update(expect)
if data == data_copy:
return 1.0
else:
return 0.0
def compare_text_file(actual: str, expected: str, **options) -> float:
"""
Args:

View File

@@ -14,7 +14,7 @@ import pyautogui
import requests
from PIL import Image
from Xlib import display, X
from flask import Flask, request, jsonify, send_file, abort #, send_from_directory
from flask import Flask, request, jsonify, send_file, abort # , send_from_directory
from lxml.etree import _Element
from pyatspi import Accessible, StateType
from pyatspi import Action as ATAction
@@ -33,6 +33,7 @@ logger = app.logger
recording_process = None # fixme: this is a temporary solution for recording, need to be changed to support multiple-process
recording_path = "/tmp/recording.mp4"
@app.route('/setup/execute', methods=['POST'])
@app.route('/execute', methods=['POST'])
def execute_command():
@@ -147,7 +148,7 @@ def get_terminal_output():
terminals: List[_Element] = desktop_xml.xpath(xpath, namespaces=_accessibility_ns_map)
output = terminals[0].text.rstrip() if len(terminals) == 1 else None
else: # windows and macos platform is not implemented currently
#raise NotImplementedError
# raise NotImplementedError
return "Currently not implemented for platform {:}.".format(platform.platform()), 500
return jsonify({"output": output, "status": "success"})
except:
@@ -172,11 +173,10 @@ def _create_atspi_node(node: Accessible) -> _Element:
states: List[StateType] = node.getState().get_states()
for st in states:
state_name: str = StateType._enum_lookup[st]
attribute_dict["{{{:}}}{:}" \
.format(_accessibility_ns_map["st"]
, state_name.split("_", maxsplit=1)[1].lower()
)
] = "true"
if len(state_name.split("_", maxsplit=1)[1].lower()) == 0:
continue
attribute_dict[
"{{{:}}}{:}".format(_accessibility_ns_map["st"], state_name.split("_", maxsplit=1)[1].lower())] = "true"
# }}} States #
# Attributes {{{ #
@@ -185,11 +185,9 @@ def _create_atspi_node(node: Accessible) -> _Element:
attribute_name: str
attribute_value: str
attribute_name, attribute_value = attrbt.split(":", maxsplit=1)
attribute_dict["{{{:}}}{:}" \
.format(_accessibility_ns_map["attr"]
, attribute_name
)
] = attribute_value
if len(attribute_name) == 0:
continue
attribute_dict["{{{:}}}{:}".format(_accessibility_ns_map["attr"], attribute_name)] = attribute_value
# }}} Attributes #
# Component {{{ #
@@ -220,11 +218,9 @@ def _create_atspi_node(node: Accessible) -> _Element:
attribute_name: str
attribute_value: str
attribute_name, attribute_value = attrbt.split(":", maxsplit=1)
attribute_dict["{{{:}}}{:}" \
.format(_accessibility_ns_map["docattr"]
, attribute_name
)
] = attribute_value
if len(attribute_name) == 0:
continue
attribute_dict["{{{:}}}{:}".format(_accessibility_ns_map["docattr"], attribute_name)] = attribute_value
# }}} Document #
# Text {{{ #
@@ -277,12 +273,18 @@ def _create_atspi_node(node: Accessible) -> _Element:
, action_name
)
] = action.getKeyBinding(i)
# }}} Action #
# }}} Action #
xml_node = lxml.etree.Element(node.getRoleName().replace(" ", "-")
, attrib=attribute_dict
, nsmap=_accessibility_ns_map
)
if node.getRoleName().strip() == "":
node_role_name = "unknown"
else:
node_role_name = node.getRoleName().replace(" ", "-")
xml_node = lxml.etree.Element(
node_role_name,
attrib=attribute_dict,
nsmap=_accessibility_ns_map
)
if "text" in locals() and len(text) > 0:
xml_node.text = text
for ch in node:
@@ -465,7 +467,7 @@ def get_directory_tree():
def get_file():
# Retrieve filename from the POST request
if 'file_path' in request.form:
file_path = request.form['file_path']
file_path = os.path.expanduser(request.form['file_path'])
else:
return jsonify({"error": "file_path is required"}), 400
@@ -481,7 +483,7 @@ def get_file():
def upload_file():
# Retrieve filename from the POST request
if 'file_path' in request.form and 'file_data' in request.files:
file_path = request.form['file_path']
file_path = os.path.expanduser(request.form['file_path'])
file = request.files["file_data"]
file.save(file_path)
return "File Uploaded"
@@ -507,7 +509,7 @@ def change_wallpaper():
if not path:
return "Path not supplied!", 400
path = Path(path)
path = Path(os.path.expanduser(path))
if not path.exists():
return f"File not found: {path}", 404
@@ -538,7 +540,7 @@ def download_file():
if not url or not path:
return "Path or URL not supplied!", 400
path = Path(path)
path = Path(os.path.expanduser(path))
path.parent.mkdir(parents=True, exist_ok=True)
max_retries = 3
@@ -567,7 +569,7 @@ def open_file():
if not path:
return "Path not supplied!", 400
path = Path(path)
path = Path(os.path.expanduser(path))
if not path.exists():
return f"File not found: {path}", 404
@@ -589,7 +591,7 @@ def activate_window():
window_name = data.get('window_name', None)
if not window_name:
return "window_name required", 400
strict: bool = data.get("strict", False) # compare case-sensitively and match the whole string
strict: bool = data.get("strict", False) # compare case-sensitively and match the whole string
by_class_name: bool = data.get("by_class", False)
os_name = platform.system()
@@ -601,11 +603,11 @@ def activate_window():
windows: List[gw.Window] = gw.getWindowsWithTitle(window_name)
window: Optional[gw.Window] = None
if len(windows)==0:
if len(windows) == 0:
return "Window {:} not found (empty results)".format(window_name), 404
elif strict:
for wnd in windows:
if wnd.title==wnd:
if wnd.title == wnd:
window = wnd
if window is None:
return "Window {:} not found (strict mode).".format(window_name), 404
@@ -621,11 +623,11 @@ def activate_window():
windows = gw.getWindowsWithTitle(window_name)
window: Optional[gw.Window] = None
if len(windows)==0:
if len(windows) == 0:
return "Window {:} not found (empty results)".format(window_name), 404
elif strict:
for wnd in windows:
if wnd.title==wnd:
if wnd.title == wnd:
window = wnd
if window is None:
return "Window {:} not found (strict mode).".format(window_name), 404
@@ -638,26 +640,27 @@ def activate_window():
elif os_name == 'Linux':
# Attempt to activate VS Code window using wmctrl
subprocess.run( [ "wmctrl"
, "-{:}{:}a".format( "x" if by_class_name else ""
, "F" if strict else ""
)
, window_name
subprocess.run(["wmctrl"
, "-{:}{:}a".format("x" if by_class_name else ""
, "F" if strict else ""
)
, window_name
]
)
)
else:
return f"Operating system {os_name} not supported.", 400
return "Window activated successfully", 200
@app.route("/setup/close_window", methods=["POST"])
def close_window():
data = request.json
if "window_name" not in data:
return "window_name required", 400
window_name: str = data["window_name"]
strict: bool = data.get("strict", False) # compare case-sensitively and match the whole string
strict: bool = data.get("strict", False) # compare case-sensitively and match the whole string
by_class_name: bool = data.get("by_class", False)
os_name: str = platform.system()
@@ -669,11 +672,11 @@ def close_window():
windows: List[gw.Window] = gw.getWindowsWithTitle(window_name)
window: Optional[gw.Window] = None
if len(windows)==0:
if len(windows) == 0:
return "Window {:} not found (empty results)".format(window_name), 404
elif strict:
for wnd in windows:
if wnd.title==wnd:
if wnd.title == wnd:
window = wnd
if window is None:
return "Window {:} not found (strict mode).".format(window_name), 404
@@ -681,14 +684,14 @@ def close_window():
window = windows[0]
window.close()
elif os_name == "Linux":
subprocess.run( [ "wmctrl"
, "-{:}{:}c".format( "x" if by_class_name else ""
, "F" if strict else ""
)
, window_name
subprocess.run(["wmctrl"
, "-{:}{:}c".format("x" if by_class_name else ""
, "F" if strict else ""
)
, window_name
]
)
elif os_name=="Darwin":
)
elif os_name == "Darwin":
import pygetwindow as gw
return "Currently not supported on macOS.", 500
else:
@@ -696,6 +699,7 @@ def close_window():
return "Window closed successfully.", 200
@app.route('/start_recording', methods=['POST'])
def start_recording():
global recording_process
@@ -722,7 +726,7 @@ def end_recording():
recording_process.terminate()
recording_process.wait()
#return_code = recording_process.returncode
# return_code = recording_process.returncode
output, error = recording_process.communicate()
recording_process = None

View File

@@ -0,0 +1,76 @@
{
"id": "01b269ae-2111-4a07-81fd-3fcd711993b0",
"snapshot": "libreoffice_calc",
"instruction": "Fill all the blank cells with the value in the cell above it",
"source": "https://www.youtube.com/shorts/VrUzPTIwQ04",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1FuOZ-5YoKgLLwl_oZd4R3D8pZACf_ukS&export=download&authuser=0&confirm=t&uuid=2051e7a6-5930-4cef-8d77-20ebf66ec6e6&at=APZUnTX1fXqlxy6rluq-Kw-LUhS5:1705919461032",
"path": "/home/user/Student_Level_Fill_Blank.xlsx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "/home/user/Student_Level_Fill_Blank.xlsx"
}
}
],
"trajectory": "trajectories/01b269ae-2111-4a07-81fd-3fcd711993b0",
"related_apps": [
"libreoffice calc"
],
"evaluator": {
"postconfig": [
{
"type": "activate_window",
"parameters": {
"window_name": "Student_Level_Fill_Blank.xlsx - LibreOffice Calc",
"strict": true
}
},
{
"type": "sleep",
"parameters": {
"seconds": 0.5
}
},
{
"type": "execute",
"parameters": {
"command": [
"python",
"-c",
"import pyautogui; pyautogui.press([\"ctrl\", \"s\"]);"
]
}
}
],
"func": "compare_table",
"expected": {
"type": "cloud_file",
"path": "https://drive.usercontent.google.com/download?id=1HTle3vgdZSjJIK_wjXyjtWwbiYJeguwv&export=download&authuser=0&confirm=t&uuid=c5d0868b-bed2-48fb-949b-8a9f3f61e8cf&at=APZUnTVqS9CTZFJ1rPqCGQPDCv3p:1705919542916",
"dest": "Student_Level_Fill_Blank_gold.xlsx"
},
"result": {
"type": "vm_file",
"path": "/home/user/Student_Level_Fill_Blank.xlsx",
"dest": "Student_Level_Fill_Blank.xlsx"
},
"options": {
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
}
]
}
}
}

View File

@@ -0,0 +1,76 @@
{
"id": "4e6fcf72-daf3-439f-a232-c434ce416af6",
"snapshot": "libreoffice_calc",
"instruction": "Please calculate the ages of the employees according to their birthday.",
"source": "https://www.youtube.com/shorts/0uxJccNCKcE",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1WIyJbssCCscQ96be2hF9N7tXPz23JoBT&export=download&authuser=0&confirm=t&uuid=503cdbf3-2fe3-4019-bfd1-5d1faab8d049&at=APZUnTV-XLlF8KEx7zMjtX2kYSuM:1705909207212",
"path": "/home/user/Employee_Age_By_Birthday.xlsx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "/home/user/Employee_Age_By_Birthday.xlsx"
}
}
],
"trajectory": "trajectories/4e6fcf72-daf3-439f-a232-c434ce416af6",
"related_apps": [
"libreoffice calc"
],
"evaluator": {
"postconfig": [
{
"type": "activate_window",
"parameters": {
"window_name": "Employee_Age_By_Birthday.xlsx - LibreOffice Calc",
"strict": true
}
},
{
"type": "sleep",
"parameters": {
"seconds": 0.5
}
},
{
"type": "execute",
"parameters": {
"command": [
"python",
"-c",
"import pyautogui; pyautogui.press([\"ctrl\", \"s\"]);"
]
}
}
],
"func": "compare_table",
"expected": {
"type": "cloud_file",
"path": "https://drive.usercontent.google.com/download?id=1sRI72UGfHiVBRFuV4uwhr173u3Sf46Y6&export=download&authuser=0&confirm=t&uuid=90da5e2b-39c0-449d-b753-09dfed73b509&at=APZUnTVFInccKo2QB9JNnIidFfG3:1705909465173",
"dest": "Employee_Age_By_Birthday_gold.xlsx"
},
"result": {
"type": "vm_file",
"path": "/home/user/Employee_Age_By_Birthday.xlsx",
"dest": "Employee_Age_By_Birthday.xlsx"
},
"options": {
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
}
]
}
}
}

View File

@@ -0,0 +1,82 @@
{
"id": "6054afcb-5bab-4702-90a0-b259b5d3217c",
"snapshot": "libreoffice_calc",
"instruction": "Some data are missed by now and are filled by 'N/A' temporarily. Please hide them in the table for now. Do not delete them and filter is no needed.",
"source": "https://www.youtube.com/shorts/JTbZ8sRxkdU",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1e1Ccsr_CQau9-boF92GxzZ0RtEHPtfdX&export=download&authuser=0&confirm=t&uuid=a1d4518d-e085-4bfa-ae6f-2514ed48efba&at=APZUnTU_ng4YNBQO7u6Dsuj21Gmq:1705911243359",
"path": "/home/user/Date_Budget_Variance_HideNA.xlsx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "/home/user/Date_Budget_Variance_HideNA.xlsx"
}
}
],
"trajectory": "trajectories/6054afcb-5bab-4702-90a0-b259b5d3217c",
"related_apps": [
"libreoffice calc"
],
"evaluator": {
"postconfig": [
{
"type": "activate_window",
"parameters": {
"window_name": "Date_Budget_Variance_HideNA.xlsx - LibreOffice Calc",
"strict": true
}
},
{
"type": "sleep",
"parameters": {
"seconds": 0.5
}
},
{
"type": "execute",
"parameters": {
"command": [
"python",
"-c",
"import pyautogui; pyautogui.press([\"ctrl\", \"s\"]);"
]
}
}
],
"func": "compare_table",
"expected": {
"type": "cloud_file",
"path": "https://drive.usercontent.google.com/download?id=1ReZexJAvbAAUng0JD3lEHN70J0WcS0_i&export=download&authuser=0&confirm=t&uuid=a11148b1-93e8-4634-a413-26e0e433c2c9&at=APZUnTV6KulVQf6LpHl4IVNqE5hA:1705914637572",
"dest": "Date_Budget_Variance_HideNA_gold.xlsx"
},
"result": {
"type": "vm_file",
"path": "/home/user/Date_Budget_Variance_HideNA.xlsx",
"dest": "Date_Budget_Variance_HideNA.xlsx"
},
"options": {
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
},
{
"type": "row_props",
"sheet_idx0": 0,
"sheet_idx1": "EI0",
"props": ["hidden"]
}
]
}
}
}

View File

@@ -0,0 +1,82 @@
{
"id": "8b1ce5f2-59d2-4dcc-b0b0-666a714b9a14",
"snapshot": "libreoffice_calc",
"instruction": "Given a partial calendar, please highlight all the weekends (Satureday & Sunday) by setting the cell background as red (#ff0000).",
"source": "https://www.youtube.com/shorts/Hbcwu6IQ1ns",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1_gyig5Vs3VOuvkjRoLt2ZpXBIyCZfUmV&export=download&authuser=0&confirm=t&uuid=ed113cdd-4279-454b-a66d-07447e31c818&at=APZUnTVztf5DcbF0DjLJitkpUUxt:1705920417565",
"path": "/home/user/Calendar_Highlight_Weekend_Days.xlsx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "/home/user/Calendar_Highlight_Weekend_Days.xlsx"
}
}
],
"trajectory": "trajectories/8b1ce5f2-59d2-4dcc-b0b0-666a714b9a14",
"related_apps": [
"libreoffice calc"
],
"evaluator": {
"postconfig": [
{
"type": "activate_window",
"parameters": {
"window_name": "Calendar_Highlight_Weekend_Days.xlsx - LibreOffice Calc",
"strict": true
}
},
{
"type": "sleep",
"parameters": {
"seconds": 0.5
}
},
{
"type": "execute",
"parameters": {
"command": [
"python",
"-c",
"import pyautogui; pyautogui.press([\"ctrl\", \"s\"]);"
]
}
}
],
"func": "compare_table",
"expected": {
"type": "cloud_file",
"path": "https://drive.usercontent.google.com/download?id=1URKsHvPdWDvB-qwsIZ-SqHAmiXaosXKW&export=download&authuser=0&confirm=t&uuid=849064c9-7402-48c5-87f6-e5c290e4bd24&at=APZUnTXarmqM0cO4I0z-Lv7MElzX:1705920495794",
"dest": "Calendar_Highlight_Weekend_Days_gold.xlsx"
},
"result": {
"type": "vm_file",
"path": "/home/user/Calendar_Highlight_Weekend_Days.xlsx",
"dest": "Calendar_Highlight_Weekend_Days.xlsx"
},
"options": {
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
},
{
"type": "style",
"sheet_idx0": 0,
"sheet_idx1": "EI0",
"props": "bgcolor"
}
]
}
}
}

View File

@@ -0,0 +1,76 @@
{
"id": "abed40dc-063f-4598-8ba5-9fe749c0615d",
"snapshot": "libreoffice_calc",
"instruction": "Check the names in column \"Names with duplicates\" and put the unique ones in column \"Unique Names\". Keep the original order.",
"source": "https://help.libreoffice.org/7.6/ro/text/scalc/guide/remove_duplicates.html?&DbPAR=SHARED&System=UNIX",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1A3O37a2M_tkmXHUn6G8kYu73cUMRUZnt&export=download&authuser=0&confirm=t&uuid=9a44147f-15e4-426c-9235-74fdda7439dc&at=APZUnTU4MAD7rODyryb9r0YolrrN:1705918712764",
"path": "/home/user/Names_Duplicate_Unique.xlsx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "/home/user/Names_Duplicate_Unique.xlsx"
}
}
],
"trajectory": "trajectories/abed40dc-063f-4598-8ba5-9fe749c0615d",
"related_apps": [
"libreoffice calc"
],
"evaluator": {
"postconfig": [
{
"type": "activate_window",
"parameters": {
"window_name": "Names_Duplicate_Unique.xlsx - LibreOffice Calc",
"strict": true
}
},
{
"type": "sleep",
"parameters": {
"seconds": 0.5
}
},
{
"type": "execute",
"parameters": {
"command": [
"python",
"-c",
"import pyautogui; pyautogui.press([\"ctrl\", \"s\"]);"
]
}
}
],
"func": "compare_table",
"expected": {
"type": "cloud_file",
"path": "https://drive.usercontent.google.com/download?id=1GYG97VdmPG9mlhSBjMlMpjsuDsEDWXNB&export=download&authuser=0&confirm=t&uuid=6dd49f77-6a87-4f99-9027-0c74bad23d6d&at=APZUnTWzHV6JFiTPuo2ICUSEZqq8:1705918802025",
"dest": "Names_Duplicate_Unique_gold.xlsx"
},
"result": {
"type": "vm_file",
"path": "/home/user/Names_Duplicate_Unique.xlsx",
"dest": "Names_Duplicate_Unique.xlsx"
},
"options": {
"rules": [
{
"type": "sheet_data",
"sheet_idx0": 0,
"sheet_idx1": "EI0"
}
]
}
}
}

View File

@@ -52,7 +52,7 @@
}
}
],
"func": "check_data_validations",
"func": "compare_table",
"options": {
"rules": [
{

View File

@@ -0,0 +1,42 @@
{
"id": "0a0faba3-5580-44df-965d-f562a99b291c",
"snapshot": "libreoffice_writer",
"instruction": "I would like to make the first three words of the sentence left-aligned and the rest right-aligned. I basically want to have some empty space in the middle to add some photos. Assume that every sentence will have at least three words. Could you help me on alignment for me?",
"source": "https://stackoverflow.com/questions/64528055/how-to-make-part-of-my-sentence-left-aligned-and-rest-as-right-aligned",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1Wrjxsf184Go70TcRGM4Tohczh29Q9B_U&export=download",
"path": "Desktop/04 CHIN9505 EBook Purchasing info 2021 Jan.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "Desktop/04 CHIN9505 EBook Purchasing info 2021 Jan.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "evaluate_alignment",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1yyHGj8KUHDMsZmc1QeJ1KkvSEGy83jMR&export=download",
"dest": "04 CHIN9505 EBook Purchasing info 2021 Jan_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/04 CHIN9505 EBook Purchasing info 2021 Jan.docx",
"dest": "04 CHIN9505 EBook Purchasing info 2021 Jan.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "6a33f9b9-0a56-4844-9c3f-96ec3ffb3ba2",
"snapshot": "libreoffice_writer",
"instruction": "I have been editing my document and some words that needed to be rewritten are highlighted in yellow. As I fixed those words, I removed highlight. Now I want to make sure that there is no highlight word. Could you help me on finding if there is no highlighted words in the file?",
"source": "https://superuser.com/questions/762500/how-do-i-find-all-highlighted-text-in-libreoffice-writer",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1-ygC5pClvU1vxPQ5SGxl3teQAbxCVm8s&export=download",
"path": "Desktop/DG75-DrawGuide.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "Desktop/DG75-DrawGuide.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "check_highlighted_words",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1Z5WkW0YH5tWh-D2YuU5zR7QLNef-37ya&export=download",
"dest": "DG75-DrawGuide_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/DG75-DrawGuide.docx",
"dest": "DG75-DrawGuide.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "6f81754e-285d-4ce0-b59e-af7edb02d108",
"snapshot": "libreoffice_writer",
"instruction": "A certain railway company in Hong Kong uses a signaling system to keep track of trains in its railway system. Each line in the docx file represents a train calling at a station from 0600 to 1200 on 2022-09-22, and has the following format: time_HH:MM:SS, train_id, station_id, platform_no.. I want to remove duplicated train ids in order to know how many different trains are running from 0600 to 1200. Could you help me on this? I am doing it manually and it is very inefficient.",
"source": "https://superuser.com/questions/789473/remove-duplicate-lines-in-libreoffice-openoffice-writer",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1cK1AMt_qKVAPp6EndSFG8y8r7KOPsqC1&export=download",
"path": "Desktop/HK train record.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "Desktop/HK train record.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": ["get_unique_train_ids", "check_no_duplicates"],
"result": {
"type": "vm_file",
"path": "Desktop/HK train record.docx",
"dest": "HK train record.docx"
},
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1wZ5CKxCD3biB4mFFlrBInZO-bzo36vVG&export=download",
"dest": "HK train record_Gold.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "72b810ef-4156-4d09-8f08-a0cf57e7cefe",
"snapshot": "libreoffice_writer",
"instruction": "I am peer-reviewing my friend's course outline. I think the last paragraph is redundant so I want to add strike-through on words in the last paragraph. Can you do this for me?",
"source": "https://superuser.com/questions/657792/libreoffice-writer-how-to-apply-strikethrough-text-formatting?rq=1",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1Uqgr9Y_kjoMoDoUwt80hv1EtFaisyztU&export=download",
"path": "Desktop/GEOG2169_Course_Outline_2022-23.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "GEOG2169_Course_Outline_2022-23.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "evaluate_strike_through_last_paragraph",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1IpAnQRYo1whrnzIGyo8UldZf4Tli-yVT&export=download",
"dest": "GEOG2169_Course_Outline_2022-23_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/GEOG2169_Course_Outline_2022-23.docx",
"dest": "GEOG2169_Course_Outline_2022-23.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "8472fece-c7dd-4241-8d65-9b3cd1a0b568",
"snapshot": "libreoffice_writer",
"instruction": "I am writing a word list for a dyslexic kid. To ease things for him, I want to use red for words start with vowels and blue for those start with non-vowels. Can you do this for me? I'm doing it manually, and it is a pain.",
"source": "https://stackoverflow.com/questions/37259827/libreoffice-writer-how-to-set-different-colors-to-each-letter",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1QHk3fVFSlvYu2k013_7ahEkVQl_o1GTU&export=download",
"path": "Desktop/Dolch Sight Words Primer.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "Desktop/Dolch Sight Words Primer.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "evaluate_colored_words_in_tables",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1ksn444K17lFOdm5pELrQYvuZHkOsKq69&export=download",
"dest": "Dolch Sight Words Primer_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/Dolch Sight Words Primer.docx",
"dest": "Dolch Sight Words Primer.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "b21acd93-60fd-4127-8a43-2f5178f4a830",
"snapshot": "libreoffice_writer",
"instruction": "I have been praciticing professional writing lately. Now I am writing essay which requires one paragraph each for introduction, body and conclusion with single-space for introduction, double-space for body then one-and-a-half-space for conclusion. The font size of this essay is 12. Could you help me on this?",
"source": "https://superuser.com/questions/1097199/how-can-i-double-space-a-document-in-libreoffice?rq=1",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1akFeAURJiqnK9wGNlRgPoPuQ6vRmnUPe&export=download",
"path": "Desktop/CCHU9045 Course Outline 2019-20.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "CCHU9045 Course Outline 2019-20.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "evaluate_spacing",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=16LN7uYSSXk_xwgc4IZXnN2Z1nCmPJfLm&export=download",
"dest": "CCHU9045 Course Outline 2019-20_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/CCHU9045 Course Outline 2019-20.docx",
"dest": "CCHU9045 Course Outline 2019-20.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "d53ff5ee-3b1a-431e-b2be-30ed2673079b",
"snapshot": "libreoffice_writer",
"instruction": "I am currently engaged in text processing and require assistance in converting all uppercase text to lowercase within my document. This precision is critical for maintaining a uniform and polished presentation. Could you help me on this?",
"source": "https://ask.libreoffice.org/t/how-to-convert-all-uppercase-to-lowercase/53341",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1j6Gx6KCxA9Cp-TE1uZ5lKcTSKVRPW-CB&export=download",
"path": "Desktop/presentation instruction 2023 Feb.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "presentation instruction 2023 Feb.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "evaluate_conversion",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1bB1N2TWN0puZ6DwUFS_TDjvRWchaGP9T&export=download",
"dest": "presentation instruction 2023 Feb_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/presentation instruction 2023 Feb.docx",
"dest": "presentation instruction 2023 Feb.docx"
}
}
}

View File

@@ -0,0 +1,42 @@
{
"id": "e246f6d8-78d7-44ac-b668-fcf47946cb50",
"snapshot": "libreoffice_writer",
"instruction": "I found Italic font very hard to discern from the normal text for me, as it is also dark black with the same size. Current font size is 12 and I want to change the font size of italicized words to 14 to make it more discernible. Can you help me on this?",
"source": "https://ask.libreoffice.org/t/how-to-change-text-size-color-of-italic-font/77712",
"config": [
{
"type": "download",
"parameters": {
"files": [
{
"url": "https://drive.google.com/uc?id=1b8mPpEDlBrTLcOpf0ZcjdUV4vLAwxH1r&export=download",
"path": "Desktop/Y22-2119-assign4.docx"
}
]
}
},
{
"type": "open",
"parameters": {
"path": "Desktop/Y22-2119-assign4.docx"
}
}
],
"trajectory": "trajectories/",
"related_apps": [
"libreoffice_writer"
],
"evaluator": {
"func": "check_italic_font_size_14",
"expected": {
"type": "cloud_file",
"path": "https://drive.google.com/uc?id=1GTZ-DkMxpdYx38z_s0ab85Ejgxv3qfEp&export=download",
"dest": "Y22-2119-assign4.docx_Gold.docx"
},
"result": {
"type": "vm_file",
"path": "Desktop/Y22-2119-assign4.docx",
"dest": "Y22-2119-assign4.docx"
}
}
}

View File

@@ -10,7 +10,7 @@
"files": [
{
"url": "https://drive.usercontent.google.com/download?id=1I0tp77_4Qwuz_JW0Tluo_DJzScTARkOZ&export=download&authuser=0&confirm=t&uuid=3e090432-df3f-4f68-8c77-f0f06d399d08&at=APZUnTUFLQTdU4MtnLGUnOVAVcxW:1704282082382",
"path": "Desktop/vscode_replace_text.txt"
"path": "/home/user/Desktop/vscode_replace_text.txt"
}
]
}
@@ -18,7 +18,7 @@
{
"type": "launch",
"parameters": {
"command": ["code", "Desktop/vscode_replace_text.txt"]
"command": ["code", "/home/user/Desktop/vscode_replace_text.txt"]
}
},
{
@@ -41,7 +41,7 @@
},
"result": {
"type": "vm_file",
"path": "Desktop/vscode_replace_text.txt",
"path": "/home/user/Desktop/vscode_replace_text.txt",
"dest": "vscode_replace_text.txt"
}
}

View File

@@ -71,7 +71,7 @@
"result": {
"type": "vscode_config",
"vscode_extension_command": "OpenProject",
"path": "OpenProject.txt",
"path": "/home/user/OpenProject.txt",
"dest": "OpenProject.txt"
}
}

View File

@@ -34,7 +34,7 @@
"result": {
"type": "vscode_config",
"vscode_extension_command": "GetColorTheme",
"path": "GetColorTheme.txt",
"path": "/home/user/GetColorTheme.txt",
"dest": "GetColorTheme.txt"
}
}

View File

@@ -1,7 +1,7 @@
{
"id": "ea98c5d7-3cf9-4f9b-8ad3-366b58e0fcae",
"snapshot": "vscode",
"instruction": "Please help me remove the shortcut \"cmd+f\" for Tree view Find (Explorer search) in VS Code Explorer view to avoid shortcut conflict.",
"instruction": "Please help me remove the shortcut \"ctrl+f\" for Tree view Find (Explorer search) in VS Code Explorer view to avoid shortcut conflict.",
"source": ["https://superuser.com/questions/1748097/vs-code-disable-tree-view-find-explorer-search",
"https://superuser.com/questions/1417361/how-to-disable-file-filtering-in-vs-code-sidebar-explorer?rq=1"
],
@@ -32,7 +32,7 @@
"rules": {
"expect":
{
"key": "cmd+f",
"key": "ctrl+f",
"command": "-list.find",
"when": "listFocus && listSupportsFind"
}

View File

@@ -33,3 +33,4 @@ pymupdf
chardet
playwright
backoff
formulas