Clone and prepare for development
Clone the repository and navigate into the annotation-checker directory. Now you will need to install all packages that do not come with python.
If you want to use
Pipenv, make sure you are in the repository folder and run:$ pipenv shell $ pipenv install -e .
If you want to use your own virtual environment:
$ python -m pip install -r requirements.txt $ python -m pip install -e .
This will install the module together with all other packages needed.
If you need some help with virtual environments, have a look at: https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/
Creating your own Check
This is a tutorial on how to create your own check using an example check that checks annotation for capital letters.
Go to
src/acheck/checksand create a new module for your check. In this example we will call it:capital_letters.pyOpen
capital_letters.pyand create a new class that inheritsImport the abstract
Checkclass from theacheck/checking/check_interface.pymodule and create a new class with the name of the check, which inherits from theCheckinterface.Now you need to implement the abstract method
runof theCheckclass and your code should look like this:
1from pathlib import Path
2from typing import List
3from acheck.checking.check_interface import Check
4from acheck.checking.error import Error
5
6class CapitalLettersCheck(Check):
7 def run(self, annotation: Path, domain: Path, problem: Path, line_limit: int = -1) -> List[Error]:
8 pass
Now we can create a function that checks the annotation for capital letters, and returns a list of
Errorobjects. It should contain at least all parameters of the run method, plus acheck_idandlogs:
1class CapitalLettersCheck(Check):
2
3 @staticmethod
4 def _check_capital_letters(self, annotation ,domain, problem, check_id, logs, line_limit):
5 pass
6 def run(self, annotation: Path, domain: Path, problem: Path, line_limit: int = -1) -> List[Error]:
7 pass
We need to create a new
ErrorType. So navigate into theacheck/checking/error.pymodule and create the newErrorType.IllegalUppercasetype at the end ofErrorType(Enum):
1class ErrorType(Enum):
2"""All different error types that a check can display"""
3
4IllegalFile = auto()
5"""There is an error when opening or reading the file"""
6IllegalCSVFile = auto()
7"""There is an error when opening or reading a csv file"""
8WrongSpelling = auto()
9"""There a spelling mistake"""
10IllegalCharacter = auto()
11"""There are symbols in the annotation that are not allowed"""
12IllegalTimestampNoNumber = auto()
13"""The time slice of an annotation is not a number"""
14IllegalTimestampNotAscending = auto()
15"""The time stamps of the actions are equal and or not ascending"""
16IllegalExpressionStructure = auto()
17"""The structure of the expressions does not correspond to the predefined structure of an annotation expression"""
18UnknownAction = auto()
19"""An action is not defined in the domain"""
20UnknownObject = auto()
21"""Another object is not known in the domain"""
22IllegalSignature = auto()
23"""The signature of an action is not correct or marked as correct"""
24PlanValidationError = auto()
25"""An error occurred when validating the plan resulting from the annotation."""
26IllegalDomainDescription = auto()
27"""The PDDL description is not correct"""
28IllegalProblemDescription = auto()
29
30"""There are uppercase letters in the annotation"""
31IllegalUppercase = auto()
Now implement your logic. You can use functions from
acheck/utils/annotationhelper.py, that help you iterate through the annotation file:
1"""Helper functions parse_annotation and read_annotation"""
2times, divs, expressions = ah.parse_annotation(annotation,line_limit)
3lines = ah.read_annotation(annotation, line_limit)
4
5"""For an example.csv that looks like:
6
70,putsock-left_sock-left_foot
820,putsock-right_sock-right_foot
9
10The returning values of parse_annotation() will look like this:
11times = ["0","20"]
12divs = ["-","-"]
13expressions = ["left_sock-left_foot","right_sock-right_foot"]
14
15The returning values of read_annotation() will look like this:
16lines = [" 0,putsock-left_sock-left_foot"," 20,putsock-right_sock-right_foot"]
17
18"""
Now we can implement the logic, that checks for capital letters:
1from pathlib import Path
2from typing import List
3from acheck.checking.check_interface import Check
4from acheck.checking.error import Error, ErrorType, Sequence, Fix, FixCode, ErrorLevel
5import acheck.utils.annotationhelper as ah
6
7class CapitalLettersCheck(Check):
8
9 @staticmethod
10 def _check_capital_letters(self, annotation, domain, problem, check_id, logs, line_limit):
11 # Create an empty list, that will be returned at the end, containing all errors that were found.
12 errors = []
13
14 # Use helper function to get a list of all annotation lines
15 lines = ah.read_annotation(annotation, line_limit)
16
17 # Iterate through all lines
18 for index, line in enumerate(lines):
19 # Checking if there are any uppercase letters
20 if line != line.lower():
21 # We create an Error object and append it to the list
22 errors.append(
23 Error(file_name = annotation, # Simply pass the value
24 error_type = ErrorType.IllegalUppercase, # Newly created ErrorType.IllegalUppercase
25 check_id = check_id, # Simply pass the value
26 line_number = index + 1, # Specify the line number
27 incorrect_sequence = Sequence(start_index=0, char_sequence=line), # Specify the incorrect char Sequence. In this case we want to mark the whole line. So we can replace it with the correct one later.
28 fixes=[Fix(correct_string=line.lower(), fix_code=FixCode.ReplaceSequence)], # Specify the auto fix behavior. In this case it will replace the incorrect sequence with the correct string.
29 error_level=ErrorLevel.Error, # Specify the error level
30 )
31 )
32 # Return the list at the end
33 return errors
34
35 def run(self, annotation: Path, domain: Path, problem: Path, line_limit: int = -1) -> List[Error]:
36 pass
If you want to give some information to the user, you can just append message strings to the
logslist, and they will be shown later in the tool.As an important info, if there is any kind of
Exceptionduring the checking process, this check will be disabled automatically and the error message is shown in the tool. If you want to specify your ownExceptionsjust raise them with a custom message.Now it is time to set up the
run()method:
1def run(self, annotation: Path, domain: Path, problem: Path, line_limit: int = -1) -> List[Error]: 2 3 #Always empty the logs at the start 4 self.logs.clear() 5 6 # Returning the list of errors, that was created by the `_check_capital_letters` method. 7 return CapitalLettersCheck._check_capital_letters( 8 annotation = annotation, # Just pass the value 9 domain = domain, # Just pass the value 10 problem = problem, # Just pass the value 11 check_id = self.id, # Just pass the value. The id is generated automatically. 12 logs = self.logs, # Just pass the value 13 line_limit = line_limit # Just pass the value. The id is generated automatically. 14 )
Now we need to register the
Checkinsideacheck/checkers.py. Choose at which position you want the check to start and if you want it to be sequential or continuous:1. 2. 3. 4 5from acheck.checks.capital import CapitalLettersCheck 6 7def register_checks(tool_meta): 8 9. 10. 11. 12 13default_checks = [ 14 15 # For this example we just added the check at the beginning of the sequentially running checks 16 CapitalLettersCheck( 17 group=CheckGroup.Default, # Pass the default group. For async_checks it would be CheckGroup.Async 18 tool_meta=tool_meta # Just pass the value. For more information have look in the API Listing under `ToolObjectsMeta` 19 ), 20 21 ReadFileCheck( 22 group=CheckGroup.PreStart, 23 tool_meta=tool_meta, 24 ), 25. 26. 27.
Now that everything has been registered correctly, the application can be started and the new check appears in the tool.