### Install Note Management Plugin Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/README.md Use this command to install the plugin, replacing YOUR_CLIENT_ID with the client ID obtained from your OAuth application setup. ```bash canvas install note_management_app/note_management_app --secret client_id= ``` -------------------------------- ### Install the Plugin Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_state_transition_buttons/note_state_transition_buttons/README.md This command installs the note state transition buttons plugin to your Canvas instance. ```bash canvas install /path/to/note_state_transition_buttons ``` -------------------------------- ### Install Staff Signature Plugin Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/staff_signature_plugin/staff_signature_plugin/README.md Use this command to install the example staff signature plugin. Ensure you are in the root directory of your Canvas plugins. ```sh canvas install ./example-plugins/staff_signature_plugin ``` -------------------------------- ### Install Plugin with Canvas CLI Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md Install a plugin into a Canvas instance. Specify the path to the plugin and optionally the target Canvas host. ```console $ canvas install [OPTIONS] PLUGIN_NAME ``` -------------------------------- ### Install Plugin to Canvas Instance Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/send_all_prescriptions/send_all_prescriptions/README.md Installs the plugin to your Canvas instance using the Canvas CLI. Replace '/path/to/send_all_prescriptions' with the actual path to your plugin. ```bash canvas install /path/to/send_all_prescriptions ``` -------------------------------- ### GET Request to Hello World Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/api_samples/api_samples/README.md Use this sample to make a GET request to a simple endpoint that returns 'Hello World'. Ensure your API key is correctly configured. ```bash curl --request GET \ --url https://xpc-dev.canvasmedical.com/plugin-io/api/api_samples/hello-world \ --header 'authorization: test123' ``` -------------------------------- ### Install Canvas CLI Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md Install the Canvas CLI using pip. This command makes the `canvas` executable available in your environment. ```bash pip install canvas ``` -------------------------------- ### Search Notes Example Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/charting_api_examples/charting_api_examples/README.md This example shows how to search for notes using various filters like limit, offset, patient ID, note type, and date ranges. It requires API key authentication. ```bash curl --request GET \ --url 'https://training.canvasmedical.com/plugin-io/api/charting_api_examples/notes/?limit=2&offset=4' \ --header 'Authorization: ' ``` -------------------------------- ### Search Notes Example Response Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/charting_api_examples/charting_api_examples/README.md This is an example response for searching notes, showing pagination details and a list of notes with their associated information. It includes 'next_page' for further retrieval. ```json { "next_page": "https://training.canvasmedical.com/plugin-io/api/charting_api_examples/notes/?limit=2&offset=6", "count": 2, "notes": [ { "id": "10ff2047-6301-4ab4-81cd-b500e7df8ef7", "patient_id": "5350cd20de8a470aa570a852859ac87e", "provider_id": "5843991a8c934118ab4f424c839b340f", "datetime_of_service": "2025-02-21 23:31:45.627894+00:00", "note_type": { "id": "c5df4f03-58e4-442b-ad6c-0d3dadc6b726", "name": "Office visit", "coding": { "display": "Office Visit", "code": "308335008", "system": "http://snomed.info/sct" } } }, { "id": "4dba128f-96cc-4dd0-814b-a064bfdcde7e", "patient_id": "5350cd20de8a470aa570a852859ac87e", "provider_id": "336159560091471cb6b0e149d9054697", "datetime_of_service": "2025-02-21 23:31:45.928071+00:00", "note_type": { "id": "c5df4f03-58e4-442b-ad6c-0d3dadc6b726", "name": "Office visit", "coding": { "display": "Office Visit", "code": "308335008", "system": "http://snomed.info/sct" } } } ] } ``` -------------------------------- ### List Plugins with Canvas CLI Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md List all installed plugins for a Canvas instance. You can specify the target Canvas host. ```console $ canvas list [OPTIONS] ``` -------------------------------- ### Example Log Output Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/appointment_coverage_label/appointment_coverage_label/README.md Illustrates typical log messages generated by the plugin during its operation, including INFO, WARNING, and ERROR levels. ```text INFO: Handling APPOINTMENT_CREATED for patient abc123 INFO: Patient abc123 has no coverage. Checking appointments for labeling. INFO: Found 2 appointments to label for patient abc123. INFO: Creating AddAppointmentLabel effect for appointment xyz789 ``` -------------------------------- ### Example Plugin Log Output Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/coverage_metadata_sync/coverage_metadata_sync/README.md Illustrative log messages demonstrating normal workflow events, skipped operations, and error handling within the plugin. ```text INFO: Reacting to 'APPOINTMENT_LABEL_ADDED'. Updating patient abc123 metadata 'coverage_status' to 'Missing'. INFO: Ignoring event for label 'URGENT' because it is not the monitored label ('MISSING_COVERAGE'). ERROR: Failed to create PatientMetadata effect for patient xyz789: [error details] ``` -------------------------------- ### Read Note Example Response Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/charting_api_examples/charting_api_examples/README.md This is an example response structure for reading a specific note by its ID. It includes details about the note, patient, provider, and service date. ```json { "note": { "id": "1490b8db-00a9-47d9-9170-ec142460b586", "patient_id": "5350cd20de8a470aa570a852859ac87e", "provider_id": "6b33e69474234f299a56d480b03476d3", "datetime_of_service": "2025-10-02 23:30:00+00:00", "state": "NEW", "note_type": { "id": "c5df4f03-58e4-442b-ad6c-0d3dadc6b726", "name": "Office visit", "coding": { "display": "Office Visit", "code": "308335008", "system": "http://snomed.info/sct" } } } } ``` -------------------------------- ### Run Pharmacy Search Demo Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/pharmacy_search_demo/README.md Use this GET request to run the pharmacy search demo plugin. It requires an authenticated staff session and returns a JSON report of test case results. ```bash GET /plugin-io/api/pharmacy_search_demo/run ``` -------------------------------- ### Add Multiple Commands to Note Request Body Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/charting_api_examples/charting_api_examples/README.md This example shows the request body for adding multiple commands to a note. In this specific case, the body is null, indicating no additional commands are being sent. ```json null ``` -------------------------------- ### Structured Output Schema Example Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/llm/llm_manip/README.md Define Pydantic models inheriting from `BaseModelLlmJson` to specify the structure for LLM responses. This example defines a schema for counting animals. ```python class Result(BaseModelLlmJson): count_dogs: int = Field(description="the number of dogs") count_cats: int = Field(description="the number of cats") count_total: int = Field(description="the number of animals") ``` -------------------------------- ### Define a Custom Appointment Factory Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Provides an example of creating a custom DjangoModelFactory for an Appointment model, including required relationships and fuzzy fields. ```python import factory from factory.fuzzy import FuzzyDate, FuzzyChoice import datetime from canvas_sdk.v1.data.appointment import Appointment as AppointmentData from canvas_sdk.test_utils.factories import PatientFactory, StaffFactory, PracticeLocationFactory class AppointmentFactory(factory.django.DjangoModelFactory): """Factory for creating test Appointments.""" class Meta: model = AppointmentData # Required relationships patient = factory.SubFactory(PatientFactory) provider = factory.SubFactory(StaffFactory) location = factory.SubFactory(PracticeLocationFactory) # Date/time fields with reasonable defaults start_time = factory.LazyFunction( lambda: datetime.datetime.now() + datetime.timedelta(days=7) ) # Use FuzzyChoice for fields with limited options duration_minutes = FuzzyChoice([15, 30, 45, 60]) # Simple string fields comment = factory.Faker("sentence") description = factory.Faker("sentence") ``` -------------------------------- ### Get Email Events Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/sendgrid_email/sendgrid_email/README.md Get events for a specific email using its message ID. ```APIDOC ## GET /email_events/ ### Description Get events for a specific email using its message ID. ### Method GET ### Endpoint /email_events/{message_id} ### Parameters #### Path Parameters - **message_id** (string) - Required - The unique identifier of the email. ### Response #### Success Response (200) - **events** (array of objects) - List of events associated with the email. - **event_type** (string) - Type of the event (e.g., 'delivered', 'opened', 'clicked'). - **timestamp** (string) - Timestamp of the event (ISO 8601 format). - **details** (object) - Additional details about the event. #### Response Example ```json { "events": [ { "event_type": "delivered", "timestamp": "2023-10-27T10:35:00Z", "details": {} }, { "event_type": "opened", "timestamp": "2023-10-27T11:00:00Z", "details": {"ip_address": "192.168.1.1"} } ] } ``` ``` -------------------------------- ### Initialize New Plugin with Canvas CLI Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md Use the `init` command to create a new plugin. This command has no additional options. ```console $ canvas init [OPTIONS] ``` -------------------------------- ### Use Custom Appointment Factory Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Demonstrates how to use the custom AppointmentFactory to create appointments, both with default and custom values. ```python def test_with_custom_appointment_factory(): """Example test using the custom AppointmentFactory.""" # Basic creation appointment = AppointmentFactory.create() assert appointment.id is not None assert appointment.patient is not None assert appointment.provider is not None # With custom values specific_patient = PatientFactory.create(first_name="Alice") appointment = AppointmentFactory.create( patient=specific_patient, duration_minutes=60 ) assert appointment.patient.first_name == "Alice" assert appointment.duration_minutes == 60 ``` -------------------------------- ### Testing Protocols with Factories Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Demonstrates how to use factories to set up test data for a protocol. It involves creating a patient and a note using factories, then creating a mock event to pass to the protocol for testing. ```python from canvas_sdk.test_utils.factories import PatientFactory, NoteFactory def test_my_protocol(): # Create test data patient = PatientFactory.create( first_name="Test", birth_date=datetime.date(1980, 1, 1) ) note = NoteFactory.create(patient=patient) # Create a mock event mock_event = Mock() mock_event.context = {"patient": {"id": patient.id}} # Test your protocol protocol = MyProtocol(event=mock_event) effects = protocol.compute() assert len(effects) > 0 ``` -------------------------------- ### Get Single Note Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Retrieves a specific note by its ID. ```APIDOC ## GET /plugin-io/api/custom_data_room_booking/notes/{note_id} ### Description Retrieves a single note using its unique identifier. ### Method GET ### Endpoint /plugin-io/api/custom_data_room_booking/notes/{note_id} ### Parameters #### Path Parameters - **note_id** (integer) - Required - The ID of the note to retrieve. ``` -------------------------------- ### Get Note Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Retrieves a specific note by its ID. Requires an Authorization header. ```APIDOC ## Get Note ### Description Retrieves a specific note by its ID. Requires an Authorization header. ### Method GET ### Endpoint /plugin-io/api/custom_data_room_booking/notes/{note_id} ### Parameters #### Path Parameters - **note_id** (integer) - Required - The ID of the note to retrieve. ### Request Example ```bash curl -s -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ "http://localhost:8000/plugin-io/api/custom_data_room_booking/notes/227" ``` ``` -------------------------------- ### Full Test Sequence: List Staff and Create Rooms Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Commands to list available staff and create rooms as part of an end-to-end test sequence. Includes piping output to json_tool for formatting. ```bash # List available staff curl -s -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ "http://localhost:8000/plugin-io/api/custom_data_room_booking/staff" | python3 -m json.tool # Create rooms curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/rooms" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" -d '{"name": "Room A"}' curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/rooms" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" -d '{"name": "Room B"}' ``` -------------------------------- ### Retrieve Results Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/extend_ai_pdf/pdf_manip/README.md Gets the extracted data from a completed document processing run. ```APIDOC ## GET /result/ ### Description Get extraction results. ### Method GET ### Endpoint /result/ ``` -------------------------------- ### Get Room Schedule Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Retrieves the schedule for a specific room. Requires an Authorization header. ```APIDOC ## Get Room Schedule ### Description Retrieves the schedule for a specific room. Requires an Authorization header. ### Method GET ### Endpoint /plugin-io/api/custom_data_room_booking/rooms/{room_id}/schedule ### Parameters #### Path Parameters - **room_id** (integer) - Required - The ID of the room to get the schedule for. ### Request Example ```bash curl -s "http://localhost:8000/plugin-io/api/custom_data_room_booking/rooms/1/schedule" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" ``` ``` -------------------------------- ### Good Practice: Using Factories for Test Data Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Demonstrates the recommended approach of using factories to create test data, contrasting it with manual object creation which requires specifying many fields. ```python # Good: Using factories def test_patient_lookup(): patient = PatientFactory.create(first_name="John") result = lookup_patient(patient.id) assert result.first_name == "John" # Avoid: Manual object creation def test_patient_lookup_manual(): patient = Patient.objects.create( first_name="John", last_name="Doe", birth_date=datetime.date(1980, 1, 1), # ... many more required fields ) ``` -------------------------------- ### Get Recent Notes Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/README.md Retrieves a list of the 10 most recent notes, including patient information and their current status. ```APIDOC ## GET /notes/recent ### Description Fetches the 10 most recent notes, including patient details and status. ### Method GET ### Endpoint /notes/recent ``` -------------------------------- ### Configure Canvas CLI Credentials Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md Create a credentials file to store client IDs and secrets for Canvas instances. Define a default instance by setting `is_default=true`. ```ini [my-canvas-instance] client_id=myclientid client_secret=myclientsecret [my-dev-canvas-instance] client_id=devclientid client_secret=devclientsecret is_default=true [localhost] client_id=localclientid client_secret=localclientsecret ``` -------------------------------- ### Get Note API Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Use this endpoint to retrieve a specific note by its ID. Requires the note ID and an Authorization header. ```bash curl -s -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ "http://localhost:8000/plugin-io/api/custom_data_room_booking/notes/227" ``` -------------------------------- ### Hello World Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/api_samples/api_samples/README.md This endpoint returns a simple 'Hello World' message. It requires an authorization header. ```APIDOC ## GET /hello-world ### Description Returns a "Hello World" message. ### Method GET ### Endpoint https://xpc-dev.canvasmedical.com/plugin-io/api/api_samples/hello-world ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```bash curl --request GET \ --url https://xpc-dev.canvasmedical.com/plugin-io/api/api_samples/hello-world \ --header 'authorization: test123' ``` ### Response #### Success Response (200) - **message** (string) - A greeting message. #### Response Example ```json { "message": "Hello World" } ``` ``` -------------------------------- ### Get URL with Provider - JavaScript Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/llm/llm_manip/templates/llm_form.html Appends the selected LLM provider to a base URL. Ensure the 'llmProvider' element exists in the DOM. ```javascript const animalsCountURL = "{{animalsCountURL | safe}}"; const chatURL = "{{chatURL | safe}}"; const fileURL = "{{fileURL | safe}}"; // Get URL with provider appended function getUrlWithProvider(baseUrl) { const provider = document.getElementById('llmProvider').value; return `${baseUrl}/${provider}`; } ``` -------------------------------- ### Launch Note Management App - Python Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Handles the application open event by launching the note management application in a new window using a relative URL. ```python from canvas_sdk.effects import Effect from canvas_sdk.effects.launch_modal import LaunchModalEffect from canvas_sdk.handlers.application import Application class NoteManagementApplication(Application): """External note management application with OAuth integration.""" def on_open(self) -> Effect: """Handle the application open event. Launches the note management application in a new window. """ # Build the URL to the API endpoint that serves the HTML # Using relative path - Canvas will resolve to the correct instance app_url = "/plugin-io/api/note_sign_api/app" return LaunchModalEffect( url=app_url, target=LaunchModalEffect.TargetType.NEW_WINDOW, ).apply() ``` -------------------------------- ### Get Room Schedule API Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Use this endpoint to retrieve the schedule for a specific room. Requires the room ID and an Authorization header. ```bash curl -s "http://localhost:8000/plugin-io/api/custom_data_room_booking/rooms/1/schedule" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" ``` -------------------------------- ### Serve Application Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Serves the HTML application for the note management plugin. ```APIDOC ## Serve Application ### Description Serves the main HTML application for the note management plugin. ### Method GET ### Endpoint `/app` ``` -------------------------------- ### Plugin File Structure Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/simple_note_button_plugin/simple_note_button_plugin/README.md This is the standard file structure for the simple note button plugin. ```tree simple_note_button_plugin/ ├── handlers/ ├── CANVAS_MANIFEST.json └── README.md ``` -------------------------------- ### Get Paginated Notes Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note-timeline-restrictions/note_timeline_restrictions/README.md Retrieves a paginated list of notes with their restriction status. Supports filtering by patient name, note state, and restriction status. ```APIDOC ## GET /notes ### Description Paginated note list with restriction status. Supports `patient_search`, `state`, `locked`, `page`, `page_size` query params. ### Method GET ### Endpoint /plugin-io/api/note_timeline_restrictions/notes ### Query Parameters - **patient_search** (string) - Optional - Search notes by patient name. - **state** (string) - Optional - Filter by note state. - **locked** (boolean) - Optional - Filter by restriction status (true for restricted, false for unrestricted). - **page** (integer) - Optional - The page number for pagination. - **page_size** (integer) - Optional - The number of items per page. ``` -------------------------------- ### Initial Load of Processors and Files Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/extend_ai_pdf/pdf_manip/templates/pdf_form.html Calls functions to load processors and stored files when the page initially loads. This ensures that the necessary data is available to the user upon entry. ```javascript // Load processors and stored files on page load loadProcessors(); loadStoredFiles(); ``` -------------------------------- ### Basic API Authentication Check Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md This Python snippet demonstrates a basic authentication check by verifying the presence of an authenticated Canvas user. It is a simplified example for demonstration purposes. ```python from canvas_sdk.handlers.simple_api import Credentials def authenticate(self, credentials: Credentials) -> bool: return self.event.actor.instance is not None ``` -------------------------------- ### Using build() vs create() Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Explains the difference between `PatientFactory.create()`, which persists the object to the database, and `PatientFactory.build()`, which creates an object in memory only, suitable for unit tests where database interaction is not required. ```python # Creates object in database patient = PatientFactory.create() # Creates object in memory only (faster for unit tests) patient = PatientFactory.build() ``` -------------------------------- ### Clone the Repository Source: https://github.com/canvas-medical/canvas-plugins/blob/main/CONTRIBUTING.md Clone your fork of the repository to your local machine. ```bash git clone https://github.com/your-username/canvas-plugins.git ``` -------------------------------- ### Post-Generation Hooks with @factory.post_generation Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Use the @factory.post_generation decorator to execute custom logic after an object has been created by the factory. This is useful for adding related objects or performing complex setup. ```python class TaskFactory(factory.django.DjangoModelFactory): class Meta: model = Task title = factory.Faker("sentence") patient = factory.SubFactory(PatientFactory) @factory.post_generation def add_labels(self, create, extracted, **kwargs): """Add labels to the task after creation.""" if not create: return if extracted: # Use provided labels for label in extracted: self.labels.add(label) else: # Add default labels self.labels.add("test-label") # Usage: task = TaskFactory.create(add_labels=["urgent", "review"]) ``` -------------------------------- ### Configure Inbound Webhook API Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/twilio_sms_mms/twilio_sms_mms/README.md Configure the webhook for handling inbound messages for a specific phone number. Specify the URL and the HTTP method (GET or POST) for the webhook. ```json { "url": "https://example.com/webhook", "method": "POST" } ``` -------------------------------- ### Project Structure Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/twilio_sms_mms/twilio_sms_mms/README.md The project is organized into specific directories for constants, handlers, static assets, and templates. ```tree twilio_sms_mms/ ├── __init__.py ├── CANVAS_MANIFEST.json ├── README.md ├── constants/ │ ├── __init__.py │ └── constants.py ├── handlers/ │ ├── __init__.py │ ├── sms_form_app.py │ └── sms_manip.py ├── static/ │ └── twilio_sms_mms.png └── templates/ └── sms_form.html ``` -------------------------------- ### Generate Protobuf Python Files Source: https://github.com/canvas-medical/canvas-plugins/blob/main/CLAUDE.md Regenerate Python files from `.proto` definitions using the `bin/generate-protobufs` script. ```sh bin/generate-protobufs ``` -------------------------------- ### Serve Note Management App HTML - Python Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Serves the note management application's HTML by rendering a template with the Canvas instance URL. Authentication is bypassed as OAuth handles it within the app. ```python from http import HTTPStatus from canvas_sdk.effects import Effect from canvas_sdk.effects.note import Note from canvas_sdk.effects.simple_api import HTMLResponse, JSONResponse, Response from canvas_sdk.handlers.simple_api import Credentials, SimpleAPI, api from canvas_sdk.templates import render_to_string from canvas_sdk.v1.data import Note as NoteModel class AppApi(SimpleAPI): """API handler for serving the note management application.""" PREFIX = "" def authenticate(self, credentials: Credentials) -> bool: """Allow access without authentication. The OAuth flow will handle authentication within the app. """ return True @api.get("/app") def note_management_app(self) -> list[Response | Effect]: """Serve the note management application HTML.""" # Get the Canvas instance URL from the request Host header host = self.request.headers.get("Host", "localhost:8000") # Determine protocol based on host if "localhost" in host or "127.0.0.1" in host: canvas_instance = f"http://{host}" else: canvas_instance = f"https://{host}" # Render the HTML template with context context = {"canvas_instance": canvas_instance} return [ HTMLResponse( render_to_string("templates/note_management_app.html", context), status_code=HTTPStatus.OK, ) ] ``` -------------------------------- ### OAuth 2.0 Token Exchange Response Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Example response upon successful token exchange, containing the access token, refresh token, token type, expiration time, and granted scopes. ```json { "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...", "refresh_token": "6KHq3fjkSWQ1vaBbF6WHG9...", "token_type": "Bearer", "expires_in": 36000, "scope": "offline_access" } ``` -------------------------------- ### Full Test Sequence: Make Staff Bookable Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Commands to make staff members bookable as part of an end-to-end test sequence. ```bash # Make staff bookable curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/staff/4150cd20de8a470aa570a852859ac87e/bookable" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/staff/def9038ce8d446bdac9c10af8a9f45cf/bookable" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" ``` -------------------------------- ### View Processor Definition Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/extend_ai_pdf/pdf_manip/templates/pdf_form.html Fetches and displays the definition of a selected AI processor. It retrieves the processor ID from a select element, makes a GET request, and displays the JSON response in a text area. Includes status updates and error handling. ```javascript document.getElementById('viewProcessorBtn').addEventListener('click', async function() { const processorId = document.getElementById('processorId').value; const resultText = document.getElementById('resultText'); const statusText = document.getElementById('statusText'); if (!processorId) { alert('Please select a processor first'); return; } try { statusText.textContent = 'Loading processor definition...'; statusText.className = 'processing'; const response = await fetch(`${processorsURL}/${processorId}`); if (!response.ok) { throw new Error(`Failed to fetch processor: ${response.status}`); } const data = await response.json(); resultText.value = JSON.stringify(data, null, 2); statusText.textContent = 'Processor definition loaded'; statusText.className = 'success'; } catch (error) { console.error('Error fetching processor:', error); statusText.textContent = 'Error: ' + error.message; statusText.className = 'error'; resultText.value = JSON.stringify({"result": "error"}, null, 2); } }); ``` -------------------------------- ### Initialize UI and Authentication Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/templates/note_management_app.html Sets up event listeners for DOMContentLoaded to handle authentication callbacks and update the user interface. This function is called when the page finishes loading. ```javascript window.addEventListener('DOMContentLoaded', () => { handleAuthCallback(); updateUI(); }); ``` -------------------------------- ### Scenario 1: New Patient Without Insurance Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/coverage_metadata_sync/coverage_metadata_sync/README.md Illustrates the event flow for a new patient without insurance, showing how the 'MISSING_COVERAGE' label addition leads to the 'coverage_status' metadata being set to 'Missing'. ```text Initial State: - Patient John Doe, no insurance - Appointment created for 2025-11-15 Event Flow: 1. Appointment created → **appointment_coverage_label** adds "MISSING_COVERAGE" label 2. Label added → **coverage_metadata_sync** sets metadata `coverage_status = "Missing"` Final State: - Appointment has "MISSING_COVERAGE" label - Patient metadata shows `coverage_status: "Missing"` - Reports can now identify patients needing coverage ``` -------------------------------- ### AWS S3 Form JavaScript Functions Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/aws_s3/aws_manip/templates/aws_form.html Core JavaScript functions for managing AWS S3 items. Includes functions for tab switching, status messages, loading items, getting selected item keys, fetching content, copying presigned URLs, and deleting items. These functions interact with predefined URLs for S3 operations. ```javascript const listItemsURL = "{{listItemsURL | safe}}"; const getItemURL = "{{getItemURL | safe}}"; const presignedUrlURL = "{{presignedUrlURL | safe}}"; const uploadItemURL = "{{uploadItemURL | safe}}"; const deleteItemURL = "{{deleteItemURL | safe}}"; // Switch between tabs function switchTab(tabName) { document.querySelectorAll('.tab-content').forEach(content => { content.classList.remove('active'); }); document.querySelectorAll('.tab').forEach(tab => { tab.classList.remove('active'); }); document.getElementById(tabName).classList.add('active'); event.target.classList.add('active'); } // Show status message function showStatus(elementId, message, type) { const statusEl = document.getElementById(elementId); statusEl.textContent = message; statusEl.className = 'status ' + type; statusEl.style.display = 'block'; if (type === 'success') { setTimeout(() => { statusEl.style.display = 'none'; }, 3000); } } // Load items into listbox async function loadItems() { const itemsList = document.getElementById('itemsList'); itemsList.innerHTML = ''; try { showStatus('listgetStatus', 'Loading items...', 'info'); const response = await fetch(listItemsURL); if (!response.ok) { throw new Error(`Failed to load items: ${response.status}`); } const items = await response.json(); items.forEach(item => { const option = document.createElement('option'); option.value = item; option.textContent = item; itemsList.appendChild(option); }); showStatus('listgetStatus', `Loaded ${items.length} items`, 'success'); } catch (error) { console.error('Error loading items:', error); showStatus('listgetStatus', 'Error loading items: ' + error.message, 'error'); } } // Get selected item key function getSelectedKey() { const itemsList = document.getElementById('itemsList'); if (itemsList.selectedIndex === -1) { return null; } return itemsList.options[itemsList.selectedIndex].value; } // Refresh button handler document.getElementById('refreshBtn').addEventListener('click', loadItems); // Get content button handler document.getElementById('getContentBtn').addEventListener('click', async function() { const key = getSelectedKey(); if (!key) { showStatus('listgetStatus', 'Please select an item first', 'error'); return; } const contentArea = document.getElementById('contentArea'); const btn = this; try { btn.disabled = true; showStatus('listgetStatus', 'Fetching content...', 'info'); const response = await fetch(`${getItemURL}/${encodeURIComponent(key)}`); if (!response.ok) { throw new Error(`Failed to get content: ${response.status}`); } const data = await response.text(); contentArea.value = data; showStatus('listgetStatus', 'Content loaded', 'success'); } catch (error) { console.error('Error getting content:', error); showStatus('listgetStatus', 'Error getting content: ' + error.message, 'error'); contentArea.value = ''; } finally { btn.disabled = false; } }); // Copy presigned URL button handler document.getElementById('copyUrlBtn').addEventListener('click', async function() { const key = getSelectedKey(); if (!key) { showStatus('listgetStatus', 'Please select an item first', 'error'); return; } const btn = this; try { btn.disabled = true; showStatus('listgetStatus', 'Getting presigned URL...', 'info'); const response = await fetch(`${presignedUrlURL}/${encodeURIComponent(key)}`); if (!response.ok) { throw new Error(`Failed to get presigned URL: ${response.status}`); } const url = await response.text(); await navigator.clipboard.writeText(url); showStatus('listgetStatus', 'Presigned URL copied to clipboard', 'success'); } catch (error) { console.error('Error copying presigned URL:', error); showStatus('listgetStatus', 'Error: ' + error.message, 'error'); } finally { btn.disabled = false; } }); // Delete item button handler document.getElementById('deleteItemBtn').addEventListener('click', async function() { const key = getSelectedKey(); if (!key) { showStatus('listgetStatus', 'Please select an item first', 'error'); return; } if (!confirm(`Are you sure you want to delete "${key}"?`)) { return; } const btn = this; try { btn.disabled = true; showStatus('listgetStatus', 'Deleting...', 'info'); const response = await fetch(`${deleteItemURL}/${encodeURIComponent(key)}`, { method: 'DELETE' }); if (!response.ok) { throw new Error(`Failed to delete: ${response.status}`); } // Remove the item from the list const itemsList = document.getElementById('itemsList'); const selectedOption = itemsList.options[itemsList.selectedIndex]; if (selectedOption) { selectedOption.remove(); } // Clear the content area document.getElementById('contentArea').value = ''; showStatus('listgetStatus', 'Item deleted successfully', 'success'); } catch (error) { console.error('Error deleting item:', error); showStatus('listgetStatus', 'Error: ' + error.message, 'error'); } finally { btn.disabled = false; } }); // Save form submission handler document.getElementById('saveForm').addEventListener('submit', async function(e) { e.preventDefault(); const itemName = document.g ``` -------------------------------- ### Create a Note Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/charting_api_examples/charting_api_examples/README.md Creates a new note. ```APIDOC ## POST /plugin-io/api/charting_api_examples/notes/ ### Description Creates a new note. ### Method POST ### Endpoint /plugin-io/api/charting_api_examples/notes/ ``` -------------------------------- ### Canvas CLI Usage Source: https://github.com/canvas-medical/canvas-plugins/blob/main/README.md The main entry point for the Canvas CLI. Use this to access various commands for plugin management and other operations. ```console $ canvas [OPTIONS] COMMAND [ARGS]... ``` -------------------------------- ### Initiate OAuth Flow Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/templates/note_management_app.html Constructs the authorization URL with PKCE parameters and redirects the user to the Canvas instance for authentication. Stores the code verifier in sessionStorage for the callback. ```javascript async function initiateOAuth() { const codeVerifier = generateRandomString(128); const codeChallenge = await generateCodeChallenge(codeVerifier); // Store code verifier for later use sessionStorage.setItem('code_verifier', codeVerifier); const authUrl = `${CANVAS_INSTANCE}/auth/authorize/?` + `response_type=code` + `&client_id=${encodeURIComponent(CLIENT_ID)}` + `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` + `&scope=${encodeURIComponent(SCOPES)}` + `&code_challenge=${codeChallenge}` + `&code_challenge_method=S256` + `&launch=${LAUNCH_CONTEXT}`; window.location.href = authUrl; } ``` -------------------------------- ### Note Management Application Handler Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Handles the application open event by launching the note management application in a new window. ```APIDOC ## NoteManagementApplication.on_open ### Description Handle the application open event. Launches the note management application in a new window. ### Method Signature `on_open() -> Effect` ### Returns - `Effect`: An effect to launch the modal window for the application. ``` -------------------------------- ### Note Management App Directory Structure Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Illustrates the typical directory structure for the note management app plugin, including handlers, templates, and manifest files. ```shell note_sign_api/ ├── handlers/ │ ├── __init__.py │ ├── api.py # API endpoints for note operations │ ├── application.py # Application handler ├── templates/ │ └── note_management_app.html # HTML application ├── CANVAS_MANIFEST.json └── README.md ``` -------------------------------- ### Create a Patient Record Source: https://github.com/canvas-medical/canvas-plugins/blob/main/canvas_sdk/test_utils/factories/FACTORY_GUIDE.md Demonstrates basic usage of PatientFactory to create a patient with random data and access its fields. ```python from canvas_sdk.test_utils.factories import PatientFactory, StaffFactory, NoteFactory def test_example(): # Create a patient with random data patient = PatientFactory.create() # Access generated fields assert patient.first_name is not None assert patient.last_name is not None assert patient.birth_date is not None # Patient automatically has a user account assert patient.user is not None ``` -------------------------------- ### File Analysis Request (Multipart Form) Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/llm/llm_manip/README.md Use a multipart form POST request to the `/file` endpoint. Include the file to analyze and a question about its content in the `input` field. ```text Send a multipart form POST to `/file` with: - `file`: The file to analyze (image or PDF) - `input`: The question about the file ``` -------------------------------- ### Run Pharmacy Search Demo Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/pharmacy_search_demo/README.md This endpoint runs the pharmacy search demo plugin. It requires a logged-in staff session and returns a JSON report of test case statuses. ```APIDOC ## GET /plugin-io/api/pharmacy_search_demo/run ### Description Executes the pharmacy search demo plugin to test new search filter parameters. Returns a JSON report of all test cases with their pass/fail status. ### Method GET ### Endpoint /plugin-io/api/pharmacy_search_demo/run ### Authentication Requires a logged-in staff session (StaffSessionAuthMixin). ### Response #### Success Response (200) Returns a JSON object containing the results of the test cases. ``` -------------------------------- ### Create Room Booking Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Use this endpoint to create a new room booking. Ensure you provide valid room, staff, and patient IDs, along with the scheduled time. ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 1, "staff_id": "4150cd20de8a470aa570a852859ac87e", "patient_id": "405ecf7a18cc48f989d46af2f112e6fc", "scheduled_at": "2026-03-15T10:00:00"}' ``` ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 1, "staff_id": "2a6cfdb145c8469b9d935fe91f6b0172", "patient_id": "405ecf7a18cc48f989d46af2f112e6fc", "scheduled_at": "2026-03-15T11:00:00"}' ``` ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 1, "staff_id": "4150cd20de8a470aa570a852859ac87e", "patient_id": "d51a4d0ce83540efba4e96461685b3ec", "scheduled_at": "2026-03-15T14:00:00"}' ``` ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 3, "staff_id": "4150cd20de8a470aa570a852859ac87e", "patient_id": "405ecf7a18cc48f989d46af2f112e6fc", "scheduled_at": "2026-03-15T09:00:00"}' ``` ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 3, "staff_id": "def9038ce8d446bdac9c10af8a9f45cf", "patient_id": "d51a4d0ce83540efba4e96461685b3ec", "scheduled_at": "2026-03-15T10:00:00"}' ``` ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 4, "staff_id": "4150cd20de8a470aa570a852859ac87e", "patient_id": "998279c9c268436bb4f13c6e7004bda4", "scheduled_at": "2026-03-15T11:00:00"}' ``` -------------------------------- ### Initiate OAuth Flow Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/note_management_app/note_management_app/INTEGRATION_GUIDE.md Initiates the OAuth 2.0 authorization code flow by generating PKCE values, storing the code verifier, and redirecting the user to the Canvas authorization server. Ensure `CLIENT_ID`, `REDIRECT_URI`, `SCOPES`, `CANVAS_INSTANCE`, and `LAUNCH_CONTEXT` are defined. ```javascript async function initiateOAuth() { // Generate PKCE values const codeVerifier = generateRandomString(128); const codeChallenge = await generateCodeChallenge(codeVerifier); // Store verifier for token exchange sessionStorage.setItem('code_verifier', codeVerifier); // Build authorization URL const authUrl = `${CANVAS_INSTANCE}/auth/authorize/?` + `response_type=code` + `&client_id=${encodeURIComponent(CLIENT_ID)}` + `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` + `&scope=${encodeURIComponent(SCOPES)}` + `&code_challenge=${codeChallenge}` + `&code_challenge_method=S256` + `&launch=${LAUNCH_CONTEXT}`; // Redirect to Canvas authorization window.location.href = authUrl; } ``` -------------------------------- ### Create Booking API Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Use this endpoint to create a new booking. Requires an Authorization header and a JSON payload with room ID, staff ID, patient ID, and scheduled time. ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/bookings" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" \ -H "Content-Type: application/json" \ -d '{"room_id": 1, "staff_id": "4150cd20de8a470aa570a852859ac87e", "patient_id": "405ecf7a18cc48f989d46af2f112e6fc", "scheduled_at": "2026-03-15T10:00:00"}' ``` -------------------------------- ### Scenario 3: Multiple Appointments Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/appointment_coverage_label/appointment_coverage_label/README.md Illustrates how the plugin handles multiple appointments for a patient with no coverage. It ensures all existing and newly created appointments are consistently flagged. ```text Initial State: - Patient has no coverage - First appointment created on 2025-11-01 Plugin Action (First Appointment): - Adds "MISSING_COVERAGE" label to first appointment Later: - Second appointment created on 2025-11-10 for same patient Plugin Action (Second Appointment): - Detects no coverage still - Adds "MISSING_COVERAGE" label to BOTH appointments Result: - All appointments consistently flagged until coverage is added ``` -------------------------------- ### File Input Preview Handler Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/aws_s3/aws_manip/templates/aws_form.html Handles the 'change' event on a file input to display a preview of the selected file, including its name and size. ```javascript document.getElementById('fileInput').addEventListener('change', function(e) { const filePreview = document.getElementById('filePreview'); const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = function(e) { filePreview.innerHTML = ` Preview
${file.name} (${(file.size / 1024).toFixed(2)} KB)
`; }; reader.readAsDataURL(file); } else { filePreview.innerHTML = ''; } }); ``` -------------------------------- ### Plugin Code Structure Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/appointment_coverage_label/appointment_coverage_label/README.md Overview of the directory structure for the appointment coverage label plugin, highlighting key files and directories. ```text appointment_coverage_label/ ├── CANVAS_MANIFEST.json # Plugin configuration ├── README.md # This file ├── __init__.py # Package initializer └── handlers/ ├── __init__.py # Handler package initializer └── appointment_labels.py # Main handler implementation ``` -------------------------------- ### Make Staff Bookable API Endpoint Source: https://github.com/canvas-medical/canvas-plugins/blob/main/example-plugins/custom_data_room_booking/README.md Use this endpoint to make a staff member bookable. Requires the staff ID and an Authorization header. ```bash curl -s -X POST "http://localhost:8000/plugin-io/api/custom_data_room_booking/staff/4150cd20de8a470aa570a852859ac87e/bookable" \ -H "Authorization: f2464a67e6fa9839579189a8c1c781e9" ```