Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions extensions/appointment-no-show-task/CANVAS_MANIFEST.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"sdk_version": "0.1.4",
"plugin_version": "0.0.1",
"name": "appointment_no_show_task",
"description": "Automatically creates a task for the specified team when an appointment is marked as no-show",
"components": {
"protocols": [
{
"class": "appointment_no_show_task.protocols.no_show_creates_task:NoShowCreatesTask",
"description": "Creates a Task for specified team in response to an appointment being marked as no-show",
"data_access": {
"event": "",
"read": [],
"write": []
}
}
],
"commands": [],
"content": [],
"effects": [],
"views": []
},
"secrets": ["TEAM_NAME", "LABELS"],
"tags": {},
"references": [],
"license": "",
"diagram": false,
"readme": "./README.md"
}
109 changes: 109 additions & 0 deletions extensions/appointment-no-show-task/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Appointment No-Show Task Plugin

## Description

This plugin automatically creates a task when a user marks an appointment as "no show". The task reminds the team to reach out to the patient and reschedule the appointment.

## How It Works

1. When an appointment is marked as "no-show" (via `APPOINTMENT_NO_SHOWED` event)
2. The plugin automatically creates a task with:
- Title: "Reschedule no-show appointment for [Patient Name]"
- Assignment: Team specified in plugin secrets
- Labels: Labels specified in plugin secrets
- Patient context: Linked to the patient who no-showed


## Configuration

### Plugin Secrets Configuration

The plugin uses secrets to allow admins to configure behavior without modifying code.

#### TEAM_NAME (Required)

Specifies which team should receive no-show tasks.

**Configuration:**
1. In the Canvas admin interface, navigate to the plugin settings
2. Set the `TEAM_NAME` secret to the exact name of your team
- Example: `"Admin"`, `"Scheduling"`, `"Front Desk"`, etc.
3. Ensure the team name matches exactly (case-sensitive) with a team that exists in Canvas

**Note:** If the `TEAM_NAME` secret is not configured or the team doesn't exist, tasks will still be created but without team assignment.

#### LABELS (Optional)

Specifies comma-separated labels to apply to created tasks.

**Configuration:**
- Set the `LABELS` secret to a comma-separated list of labels
- Default value if not configured: `"no-show,reschedule"`
- Examples:
- `"no-show,reschedule"` - Default labels
- `"no-show,urgent,follow-up"` - Custom labels
- `"patient-no-show"` - Single label

**Example SECRETS.json:**
```json
{
"TEAM_NAME": "Admin",
"LABELS": "no-show,reschedule"
}
```
Comment on lines +47 to +53
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove


### Advanced Customization (Optional)

For further customization beyond what secrets provide, you can modify the protocol code in [no_show_creates_task.py](protocols/no_show_creates_task.py):

- **Task title**: Modify line 96 to change the title format
```python
task_title = f"Reschedule no-show appointment for {patient_name}"
```
- **Due date**: Add a `due` parameter to the `AddTask` call (requires `import arrow`)
```python
effect = AddTask(
patient_id=patient_id,
title=task_title,
team_id=team_id,
labels=labels,
due=arrow.now().shift(hours=24).datetime, # Due in 24 hours
)
```


## Important Note

The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it gets updated if you add, remove, or rename protocols.

## Troubleshooting

### Tasks not being created

1. Verify the plugin is installed and enabled in Canvas
2. Check the plugin logs for error messages (look for lines starting with `NoShowCreatesTask:`)
3. Ensure you're marking the appointment as "no-show" (not just cancelling it)

### Tasks created without team assignment

This happens when:
- The `TEAM_NAME` secret is not configured
- The team name in the secret doesn't match an existing team (case-sensitive)
- The team was deleted after configuration

**To fix:**
1. Check your plugin secrets configuration in Canvas admin
2. Verify the `TEAM_NAME` value matches an existing team exactly
3. Check the logs to see what team name the plugin is looking for

### Viewing plugin logs

Plugin logs will show detailed information about each no-show event:
```
NoShowCreatesTask: Event received - Type: 7
NoShowCreatesTask: Event name: APPOINTMENT_NO_SHOWED
NoShowCreatesTask: Extracted appointment ID: <uuid>
NoShowCreatesTask: Found appointment with status: 'no-show'
NoShowCreatesTask: Task will be assigned to team 'Admin' (ID: <team-id>)
NoShowCreatesTask: Creating task for patient <patient-id>
```
1 change: 1 addition & 0 deletions extensions/appointment-no-show-task/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Appointment No-Show Task Plugin for Canvas Medical."""
1 change: 1 addition & 0 deletions extensions/appointment-no-show-task/protocols/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Protocols for the Appointment No-Show Task Plugin."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Protocol to create a task for admin team when appointment is marked as no-show."""

from canvas_sdk.effects import Effect
from canvas_sdk.effects.task import AddTask
from canvas_sdk.events import EventType
from canvas_sdk.protocols import BaseProtocol
from canvas_sdk.v1.data.appointment import Appointment
from canvas_sdk.v1.data.team import Team


class NoShowCreatesTask(BaseProtocol):
"""
Create a Task for admin team when an appointment status changes to 'no-show'.

This protocol responds to APPOINTMENT_UPDATED events and creates a task
assigned to the admin team to reschedule the appointment.
"""

RESPONDS_TO = [
EventType.Name(EventType.APPOINTMENT_NO_SHOWED),
]

def compute(self) -> list[Effect]:
"""
Check if appointment was marked as no-show and create a task if so.

Returns:
list[Effect]: List containing an AddTask effect if status is 'no-show',
otherwise an empty list.
"""
# Get the appointment ID from the event target
# Target is a string UUID for APPOINTMENT_NO_SHOWED event
appointment_id = self.target

if not appointment_id:
return []

try:
# Fetch the appointment to get patient details
appointment = Appointment.objects.get(id=appointment_id)
except Appointment.DoesNotExist:
return []

# Since APPOINTMENT_NO_SHOWED event fired, we know it's a no-show
# No need to check status - the event itself confirms this

# Get patient information
patient_id = appointment.patient.id
patient_name = appointment.patient.first_name + " " + appointment.patient.last_name

# Get appointment details for task description
appointment_date = appointment.start_time.strftime("%Y-%m-%d %H:%M")

# Get the team from plugin secrets
team_id = None
team_name = self.secrets.get("TEAM_NAME")

if team_name:
try:
team = Team.objects.get(name=team_name)
team_id = str(team.id)
except Team.DoesNotExist:
pass

# Get labels from plugin secrets
labels_str = self.secrets.get("LABELS", "no-show,reschedule")
# Parse comma-separated labels and strip whitespace
labels = [label.strip() for label in labels_str.split(",") if label.strip()]

# Create the task
task_title = f"Reschedule no-show appointment for {patient_name}"

effect = AddTask(
patient_id=patient_id,
title=task_title,
team_id=team_id,
labels=labels,
)

return [effect.apply()]
1 change: 1 addition & 0 deletions extensions/appointment-no-show-task/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tests for the Appointment No-Show Task Plugin."""
Loading