Merge branch 'main' of github.com:ztjhz/DesktopEnv
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"func": "check_data_validations",
|
||||
"func": "compare_table",
|
||||
"options": {
|
||||
"rules": [
|
||||
{
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"result": {
|
||||
"type": "vscode_config",
|
||||
"vscode_extension_command": "OpenProject",
|
||||
"path": "OpenProject.txt",
|
||||
"path": "/home/user/OpenProject.txt",
|
||||
"dest": "OpenProject.txt"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"result": {
|
||||
"type": "vscode_config",
|
||||
"vscode_extension_command": "GetColorTheme",
|
||||
"path": "GetColorTheme.txt",
|
||||
"path": "/home/user/GetColorTheme.txt",
|
||||
"dest": "GetColorTheme.txt"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -33,3 +33,4 @@ pymupdf
|
||||
chardet
|
||||
playwright
|
||||
backoff
|
||||
formulas
|
||||
|
||||
Reference in New Issue
Block a user