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.

  1. Go to src/acheck/checks and create a new module for your check. In this example we will call it: capital_letters.py

  2. Open capital_letters.py and create a new class that inherits

  3. Import the abstract Check class from the acheck/checking/check_interface.py module and create a new class with the name of the check, which inherits from the Check interface.

  4. Now you need to implement the abstract method run of the Check class 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
  1. Now we can create a function that checks the annotation for capital letters, and returns a list of Error objects. It should contain at least all parameters of the run method, plus a check_id and logs:

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
  1. We need to create a new ErrorType. So navigate into the acheck/checking/error.py module and create the new ErrorType.IllegalUppercase type at the end of ErrorType(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()
  1. 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"""
  1. 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
  1. If you want to give some information to the user, you can just append message strings to the logs list, and they will be shown later in the tool.

  2. As an important info, if there is any kind of Exception during the checking process, this check will be disabled automatically and the error message is shown in the tool. If you want to specify your own Exceptions just raise them with a custom message.

  3. 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  )
  1. Now we need to register the Check inside acheck/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.
    
  2. Now that everything has been registered correctly, the application can be started and the new check appears in the tool.