> ## Documentation Index
> Fetch the complete documentation index at: https://docs.encord.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Issues & Comments in Projects

You can programmatically create issues/comments on data units using Encord's SDK.

## Create Issues

You can use the SDK to create a file issue, frame issue, or a pinned (coordinate) issue.

<CodeGroup>
  ```python Template theme={"dark"}
  task.issues.add_file_issue("<text-about-issue>", ["<issue-tag-1>", "<issue-tag-2"])
  task.issues.add_frame_issue(<frame-number>, "<text-about-issue>", ["<issue-tag-1>", "<issue-tag-2"])
  task.issues.add_coordinate_issue(<frame-number>, <coordinate-x>, <coordinate-y>, "<text-about-issue>", ["<issue-tag-1>", "<issue-tag-2"])
  ```

  ```python Example theme={"dark"}
  task.issues.add_file_issue("File issue from annotate", ["Label too large", "incorrect object"])
  task.issues.add_frame_issue(3, "Frame issue from annotate", ["label too large", "incorrect object"])
  task.issues.add_coordinate_issue(7, 0.5 ,0.5, "Pinned/Coordinate issue from annotate", [])
  ```
</CodeGroup>

<Warning>
  Issue tags are optional. However, if issue tags are specified while adding an issue using the SDK, the issue tags **MUST** exist in the Project.
</Warning>

## Supported Modalities

Not all modalities support all issue types.

<table>
  <thead>
    <tr>
      <th>Modality</th>
      <th>File</th>
      <th>Frame</th>
      <th>Pinned/Coordinate</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Images</td>
      <td>✅</td>
      <td>❌</td>
      <td>✅</td>
    </tr>

    <tr>
      <td>Videos</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>

    <tr>
      <td>Audio files</td>
      <td>✅</td>
      <td>✅</td>
      <td>❌</td>
    </tr>

    <tr>
      <td>Text files</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>

    <tr>
      <td>HTML files</td>
      <td>✅</td>
      <td>❌</td>
      <td>❌</td>
    </tr>

    <tr>
      <td>PDFs</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>

    <tr>
      <td>DICOM</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>

    <tr>
      <td>NifTi</td>
      <td>✅</td>
      <td>✅</td>
      <td>✅</td>
    </tr>
  </tbody>
</table>

## Using Issues SDK

The following code provides an example of how you are likely going to use the SDK with Issues/Comments.

**ANNOTATE stage**

1. Issues/comments are added to the specified data units.
2. A label is added to the data unit.
3. The task is submitted for review.

**REVIEW stage**

1. Issues/comments are added to the specified data units.
2. The task is rejected.

```python Issues/Comments Annotate and Review Example theme={"dark"}
# Import dependencies
from encord import EncordUserClient
from encord.workflow import AnnotationStage, ReviewStage
from encord.objects import ChecklistAttribute, Object, ObjectInstance, Option, RadioAttribute, TextAttribute
from encord.objects.coordinates import BoundingBoxCoordinates

# User input
SSH_PATH = "/Users/chris-encord/ssh-private-key.txt"
PROJECT_ID = "ef4c2685-512a-4af9-9ac1-443f766c6c80"

# Authentication
user_client: EncordUserClient = EncordUserClient.create_with_ssh_private_key(
    ssh_private_key_path=SSH_PATH,
    # For US platform users use "https://api.us.encord.com"
    domain="https://api.encord.com",
)

project = user_client.get_project(PROJECT_ID)
workflow = project.workflow


ANNOTATE_ISSUES = [
    {
        "data_title": "cherries-010.jpg",
        "file_issue": {"text": "Annotate file issue 1", "tags": ["incorrect object", "label too large"]},
        "coordinate_issue": {"frame": 0, "x": 0.5, "y": 0.5, "text": "Annotate coordinate issue 1", "tags": ["incorrect object"]},
    },
    {
        "data_title": "cherries-vid-001.mp4",
        "file_issue": {"text": "Annotate file issue 1", "tags": ["incorrect object", "label too large"]},
        "frame_issue": {"frame": 0, "text": "Annotate frame issue 1", "tags": ["incorrect object"]},
        "coordinate_issue": {"frame": 0, "x": 0.5, "y": 0.5, "text": "Annotate coordinate issue 1", "tags": ["incorrect object", "label too large"]},
    },
]

REVIEW_ISSUES = [
    {
        "data_title": "cherries-sequence",
        "file_issue": {"text": "Review file issue 1", "tags": ["incorrect object"]},
        "frame_issue": {"frame": 3, "text": "Review frame issue 1", "tags": ["label too large"]},
        "coordinate_issue": {"frame": 0, "x": 0.3, "y": 0.3, "text": "Review coordinate issue 1", "tags": ["incorrect object", "label too large"]},
    },
    {
        "data_title": "cherries-ig",
        "file_issue": {"text": "Review file issue 1", "tags": ["incorrect object", "label too large"]},
        "frame_issue": {"frame": 2, "text": "Review frame issue 1", "tags": ["incorrect object", "label too large"]},
        "coordinate_issue": {"frame": 0, "x": 0.3, "y": 0.3, "text": "Review coordinate issue 1", "tags": ["incorrect object", "label too large"]},
    },
    {
        "data_title": "cherries-004.jpg",
        "file_issue": {"text": "Review file issue 1", "tags": ["incorrect object", "label too large"]},
        "coordinate_issue": {"frame": 0, "x": 0.3, "y": 0.3, "text": "Review coordinate issue 1", "tags": ["incorrect object", "label too large"]},
    },
]

# Annotate stage
annotate_stage = workflow.get_stage(name='Annotate 1', type_=AnnotationStage)
for issue in ANNOTATE_ISSUES:
    data_title = issue["data_title"]
    task = next((t for t in annotate_stage.get_tasks() if t.data_title == data_title), None)
    if not task:
        print(f"[Annotate] Task not found for: {data_title}")
        continue

    print(f"Processing Annotate Task: {data_title}")
    task.assign('chris-encord@acme.com')

    task.issues.add_file_issue(issue["file_issue"]["text"], issue["file_issue"]["tags"])
    if "frame_issue" in issue:
        fi = issue["frame_issue"]
        task.issues.add_frame_issue(fi["frame"], fi["text"], fi["tags"])
    ci = issue["coordinate_issue"]
    task.issues.add_coordinate_issue(ci["frame"], ci["x"], ci["y"], ci["text"], ci["tags"])

    label_row = project.list_label_rows_v2(data_hashes=[task.data_hash])[0]
    label_row.initialise_labels()
    box_object = label_row.ontology_structure.get_child_by_title("BoundingBox", type_=Object)
    box_instance = box_object.create_instance()
    box_instance.set_for_frames(
        coordinates=BoundingBoxCoordinates(height=0.1, width=0.1, top_left_x=0.5, top_left_y=0.5),
    )
    label_row.add_object_instance(box_instance)
    label_row.save()

    task.submit()

# Review stage
review_stage = workflow.get_stage(name='Review 1', type_=ReviewStage)
for issue in REVIEW_ISSUES:
    data_title = issue["data_title"]
    task = next((t for t in review_stage.get_tasks() if t.data_title == data_title), None)
    if not task:
        print(f"[Review] Task not found for: {data_title}")
        continue

    print(f"Processing Review Task: {data_title}")
    task.issues.add_file_issue(issue["file_issue"]["text"], issue["file_issue"]["tags"])
    if "frame_issue" in issue:
        fi = issue["frame_issue"]
        task.issues.add_frame_issue(fi["frame"], fi["text"], fi["tags"])
    ci = issue["coordinate_issue"]
    task.issues.add_coordinate_issue(ci["frame"], ci["x"], ci["y"], ci["text"], ci["tags"])

    for label_review in task.get_label_reviews():
        label_review.reject(comment='Rejected due to issue', issue_tags=ci["tags"])  # Or use any tag set you prefer

    task.reject()
```

## Delete Issues

Use `task.issues.delete()` to delete Issues from your Tasks. You can filter by a number of criteria.

<Warning>
  Your **Service Account** MUST be added to the Project as an **Admin** to delete Issues.
</Warning>

<CodeGroup>
  ```python Delete all Issues in a Stage theme={"dark"}
  # Import dependencies
  from encord import EncordUserClient
  from encord.workflow import AnnotationStage

  SSH_PATH = "/Users/chris-encord/ssh-private-key.txt"  # Replace with the file path to your SSH private key
  PROJECT_ID = "00000000-0000-0000-0000-000000000000"  # Replace with the unique Project ID

  user_client = EncordUserClient.create_with_ssh_private_key(
      ssh_private_key_path=SSH_PATH,
      # For US platform users use domain="https://api.us.encord.com"
      domain="https://api.encord.com",
  )

  project = user_client.get_project(PROJECT_ID)
  annotate_stage = project.workflow.get_stage(name="Annotate 1", type_=AnnotationStage)

  for task in annotate_stage.get_tasks():
      issues_to_delete = list(task.issues.list())
      task.issues.delete(issues_to_delete)
  ```

  ```python Delete Issues on specific Tasks theme={"dark"}
  # Import dependencies
  from encord import EncordUserClient
  from encord.workflow import AnnotationStage

  SSH_PATH = "/Users/chris-encord/ssh-private-key.txt"  # Replace with the file path to your SSH private key
  PROJECT_ID = "00000000-0000-0000-0000-000000000000"  # Replace with the unique Project ID
  DATA_TITLE = "frame_001.jpg" # Replace with the name of the data unit you want to filter by

  user_client = EncordUserClient.create_with_ssh_private_key(
      ssh_private_key_path=SSH_PATH,
      domain="https://api.encord.com",
  )

  project = user_client.get_project(PROJECT_ID)
  annotate_stage = project.workflow.get_stage(name="Annotate 1", type_=AnnotationStage)

  for task in annotate_stage.get_tasks(data_title=DATA_TITLE):
      issues_to_delete = list(task.issues.list())
      task.issues.delete(issues_to_delete)
  ```

  ```python Delete Issues filter by Issue Tag theme={"dark"}
  # Import dependencies
  from encord import EncordUserClient
  from encord.workflow import AnnotationStage

  SSH_PATH = "/Users/chris-encord/ssh-private-key.txt"  # Replace with the file path to your SSH private key
  PROJECT_ID = "00000000-0000-0000-0000-000000000000"  # Replace with the unique Project ID
  TAG_NAME = "needs-review" # Replace with the Issue Tag you want to filter by

  user_client = EncordUserClient.create_with_ssh_private_key(
      ssh_private_key_path=SSH_PATH,
      # For US platform users use domain="https://api.us.encord.com"
      domain="https://api.encord.com",
  )

  project = user_client.get_project(PROJECT_ID)
  annotate_stage = project.workflow.get_stage(name="Annotate 1", type_=AnnotationStage)

  for task in annotate_stage.get_tasks():
      issues_to_delete = []
      for issue in task.issues.list():
          if any(tag.name == TAG_NAME for tag in issue.tags):
              issues_to_delete.append(issue)

      task.issues.delete(issues_to_delete)
  ```

  ```python Delete Issue by Issue Author theme={"dark"}
  # Import dependencies
  from encord import EncordUserClient
  from encord.workflow import AnnotationStage

  SSH_PATH = "/Users/chris-encord/ssh-private-key.txt"  # Replace with the file path to your SSH private key
  PROJECT_ID = "00000000-0000-0000-0000-000000000000"  # Replace with the unique Project ID
  AUTHOR_EMAIL = "annotator-001@example.com" # Replace with the author of the Issue

  user_client = EncordUserClient.create_with_ssh_private_key(
      ssh_private_key_path=SSH_PATH,
      domain="https://api.encord.com",
  )

  project = user_client.get_project(PROJECT_ID)
  annotate_stage = project.workflow.get_stage(name="Annotate 1", type_=AnnotationStage)

  for task in annotate_stage.get_tasks():
      issues_to_delete = []
      for issue in task.issues.list():
          if issue.comments and issue.comments[0].author_email == AUTHOR_EMAIL:
              issues_to_delete.append(issue)

      task.issues.delete(issues_to_delete)
  ```
</CodeGroup>
