Skip to content

Commit

Permalink
chore: local development improvements (#102)
Browse files Browse the repository at this point in the history
Co-authored-by: José Magalhães <jose.magalhaes@canvasmedical.com>
  • Loading branch information
aduane and jamagalhaes authored Oct 31, 2024
1 parent 3b4d7b5 commit ca4458e
Show file tree
Hide file tree
Showing 72 changed files with 343 additions and 54 deletions.
3 changes: 3 additions & 0 deletions canvas_cli/apps/emit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from canvas_cli.apps.emit.emit import emit

__all__ = ("emit",)
67 changes: 67 additions & 0 deletions canvas_cli/apps/emit/emit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import json
import random
from pathlib import Path
from typing import Annotated

import grpc
import typer

from canvas_generated.messages.events_pb2 import Event as PluginRunnerEvent
from canvas_generated.messages.events_pb2 import EventType as PluginRunnerEventType
from canvas_generated.services.plugin_runner_pb2_grpc import PluginRunnerStub


def emit(
event_fixture: str,
plugin_runner_port: Annotated[
str, typer.Option(help="Port of your locally running plugin runner")
] = "50051",
) -> None:
"""
Send an event fixture to your locally running plugin-runner process, and print any resultant effects.
Valid fixture files are newline-delimited JSON, with each containing the keys `EventType`, `target`, and `context`. Some fixture files are included in the canvas-plugins repo.
"""
# If an event fixture file exists at the specified path, use it.
# Otherwise, see if it represents an event that we have a Canvas-provided
# fixture for and use that.
event_fixture_path = Path(event_fixture)

if not event_fixture_path.exists():
candidate_built_in_fixture_path = (
Path(__file__).resolve().parent / "event_fixtures" / f"{event_fixture}.ndjson"
)
if candidate_built_in_fixture_path.exists():
event_fixture_path = candidate_built_in_fixture_path
else:
print(f"ERROR: No file found at location {event_fixture}.")
print(f"ERROR: No built-in fixture file found named {event_fixture}.ndjson.")
return

# Grab a random event from the fixture file ndjson
lines = event_fixture_path.read_text().splitlines()
myline = random.choice(lines)
event_data = json.loads(myline)
event = PluginRunnerEvent(
type=PluginRunnerEventType.Value(event_data["EventType"]),
target=event_data["target"],
context=event_data["context"],
)
with grpc.insecure_channel(f"localhost:{plugin_runner_port}") as channel:
stub = PluginRunnerStub(channel)
responses = stub.HandleEvent(event)

at_least_one_effect = False
try:
for response in responses:
for effect in response.effects:
at_least_one_effect = True
print(effect)

if not at_least_one_effect:
print("SUCCESS: No effects returned.")
except grpc.RpcError as e:
if e.code() == grpc.StatusCode.UNAVAILABLE:
print(
f"ERROR: Couldn't make a connection to a plugin runner process at localhost:{plugin_runner_port}. Is it running?"
)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
3 changes: 3 additions & 0 deletions canvas_cli/apps/run_plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from canvas_cli.apps.run_plugins.run_plugins import run_plugin, run_plugins

__all__ = ("run_plugins", "run_plugin")
16 changes: 16 additions & 0 deletions canvas_cli/apps/run_plugins/run_plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from plugin_runner.plugin_runner import run_server


def run_plugin(plugin_directory: str) -> None:
"""
Run the specified plugin for local development.
"""
return run_plugins([plugin_directory])


def run_plugins(plugin_directories: list[str]) -> None:
"""
Run the specified plugins for local development.
"""
run_server(plugin_directories)
return
46 changes: 8 additions & 38 deletions canvas_cli/main.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import importlib.metadata
import json
import random
from pathlib import Path
from typing import Annotated, Optional
from typing import Optional

import grpc
import typer

from canvas_cli.apps import plugin
from canvas_cli.apps.emit import emit
from canvas_cli.apps.logs import logs as logs_command
from canvas_cli.apps.run_plugins import run_plugin, run_plugins
from canvas_cli.utils.context import context
from canvas_generated.messages.events_pb2 import Event as PluginRunnerEvent
from canvas_generated.messages.events_pb2 import EventType as PluginRunnerEventType
from canvas_generated.services.plugin_runner_pb2_grpc import PluginRunnerStub

APP_NAME = "canvas_cli"

Expand All @@ -28,6 +24,11 @@
app.command(short_help="List all plugins from a Canvas instance")(plugin.list)
app.command(short_help="Validate the Canvas Manifest json file")(plugin.validate_manifest)
app.command(short_help="Listen and print log streams from a Canvas instance")(logs_command)
app.command(
short_help="Send an event fixture to your locally running plugin-runner process, and print any resultant effects."
)(emit)
app.command(short_help="Run the specified plugins for local development.")(run_plugins)
app.command(short_help="Run the specified plugin for local development.")(run_plugin)

# Our current version
__version__ = importlib.metadata.version("canvas")
Expand Down Expand Up @@ -59,37 +60,6 @@ def get_or_create_config_file() -> Path:
return config_path


@app.command()
def emit(
event_fixture: str,
plugin_runner_port: Annotated[
str, typer.Option(help="Port of your locally running plugin runner")
] = "50051",
) -> None:
"""
Grab an event from a fixture file and send it your locally running plugin-runner process.
Any resultant effects will be printed.
Valid fixture files are newline-delimited JSON, with each containing the keys `EventType`, `target`, and `context`. Some fixture files are included in the canvas-plugins repo.
"""
# Grab a random event from the fixture file ndjson
lines = Path(event_fixture).read_text().splitlines()
myline = random.choice(lines)
event_data = json.loads(myline)
event = PluginRunnerEvent(
type=PluginRunnerEventType.Value(event_data["EventType"]),
target=event_data["target"],
context=event_data["context"],
)
with grpc.insecure_channel(f"localhost:{plugin_runner_port}") as channel:
stub = PluginRunnerStub(channel)
responses = stub.HandleEvent(event)

for response in responses:
for effect in response.effects:
print(effect)


@app.callback()
def main(
version: Optional[bool] = typer.Option(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{% for _ in cookiecutter.__project_slug %}={% endfor %}
{{ cookiecutter.__project_slug }}
{% for _ in cookiecutter.__project_slug %}={% endfor %}

Expand Down
4 changes: 2 additions & 2 deletions canvas_generated/messages/events_pb2.py

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions canvas_generated/messages/events_pb2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class EventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
PROTOCOL_OVERRIDE_DELETED: _ClassVar[EventType]
TASK_COMMENT_UPDATED: _ClassVar[EventType]
TASK_COMMENT_DELETED: _ClassVar[EventType]
DEVICE_CREATED: _ClassVar[EventType]
DEVICE_UPDATED: _ClassVar[EventType]
OBSERVATION_CREATED: _ClassVar[EventType]
OBSERVATION_UPDATED: _ClassVar[EventType]
PRE_COMMAND_ORIGINATE: _ClassVar[EventType]
POST_COMMAND_ORIGINATE: _ClassVar[EventType]
PRE_COMMAND_UPDATE: _ClassVar[EventType]
Expand Down Expand Up @@ -628,6 +632,10 @@ PROTOCOL_OVERRIDE_UPDATED: EventType
PROTOCOL_OVERRIDE_DELETED: EventType
TASK_COMMENT_UPDATED: EventType
TASK_COMMENT_DELETED: EventType
DEVICE_CREATED: EventType
DEVICE_UPDATED: EventType
OBSERVATION_CREATED: EventType
OBSERVATION_UPDATED: EventType
PRE_COMMAND_ORIGINATE: EventType
POST_COMMAND_ORIGINATE: EventType
PRE_COMMAND_UPDATE: EventType
Expand Down
27 changes: 27 additions & 0 deletions canvas_sdk/v1/data/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django.db import models

from canvas_sdk.v1.data.patient import Patient
from canvas_sdk.v1.data.user import CanvasUser


class Command(models.Model):
"""Command."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_commands_command_001"

id = models.UUIDField()
dbid = models.BigIntegerField(primary_key=True)
created = models.DateTimeField()
modified = models.DateTimeField()
originator = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
state = models.CharField()
patient = models.ForeignKey(Patient, on_delete=models.DO_NOTHING)
note_id = models.BigIntegerField()
schema_key = models.TextField()
data = models.JSONField()
origination_source = models.CharField()
44 changes: 44 additions & 0 deletions canvas_sdk/v1/data/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from django.db import models

from canvas_sdk.v1.data import Patient
from canvas_sdk.v1.data.user import CanvasUser


class Device(models.Model):
"""Device."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_device_001"

id = models.UUIDField()
dbid = models.BigIntegerField(primary_key=True)
created = models.DateTimeField()
modified = models.DateTimeField()
originator = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
patient = models.ForeignKey(Patient, on_delete=models.DO_NOTHING, related_name="devices")
note_id = models.BigIntegerField()
deleted = models.BooleanField()
labeled_contains_NRL = models.BooleanField()
assigning_authority = models.CharField()
scoping_entity = models.CharField()
udi = models.CharField()
di = models.CharField()
issuing_agency = models.CharField()
lot_number = models.CharField()
brand_name = models.CharField()
mri_safety_status = models.CharField()
version_model_number = models.CharField()
company_name = models.CharField()
gmdnPTName = models.TextField()
status = models.CharField()
expiration_date = models.DateField()
expiration_date_original = models.CharField()
serial_number = models.CharField()
manufacturing_date_original = models.CharField()
manufacturing_date = models.DateField()
manufacturer = models.CharField()
procedure_id = models.BigIntegerField()
117 changes: 117 additions & 0 deletions canvas_sdk/v1/data/observation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from django.db import models

from canvas_sdk.v1.data.base import CommittableModelManager, ValueSetLookupQuerySet
from canvas_sdk.v1.data.patient import Patient
from canvas_sdk.v1.data.user import CanvasUser


class ObservationQuerySet(ValueSetLookupQuerySet):
"""ObservationQuerySet."""

pass


class Observation(models.Model):
"""Observation."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_observation_001"

objects = CommittableModelManager.from_queryset(ObservationQuerySet)()

id = models.UUIDField()
dbid = models.BigIntegerField(primary_key=True)
created = models.DateTimeField()
modified = models.DateTimeField()
originator = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
committer = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
entered_in_error = models.ForeignKey(CanvasUser, on_delete=models.DO_NOTHING)
deleted = models.BooleanField()
patient = models.ForeignKey(Patient, on_delete=models.DO_NOTHING, related_name="observations")
is_member_of = models.ForeignKey(
"self", on_delete=models.DO_NOTHING, null=True, related_name="members"
)
category = models.CharField()
units = models.TextField()
value = models.TextField()
note_id = models.BigIntegerField()
name = models.TextField()
effective_datetime = models.DateTimeField()


class ObservationCoding(models.Model):
"""ObservationCoding."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_observationcoding_001"

dbid = models.BigIntegerField(primary_key=True)
system = models.CharField()
version = models.CharField()
code = models.CharField()
display = models.CharField()
user_selected = models.BooleanField()
observation = models.ForeignKey(
Observation, on_delete=models.DO_NOTHING, related_name="codings"
)


class ObservationComponent(models.Model):
"""ObservationComponent."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_observationcomponent_001"

dbid = models.BigIntegerField(primary_key=True)
created = models.DateTimeField()
modified = models.DateTimeField()
observation = models.ForeignKey(
Observation, on_delete=models.DO_NOTHING, related_name="components"
)
value_quantity = models.TextField()
value_quantity_unit = models.TextField()
name = models.TextField()


class ObservationComponentCoding(models.Model):
"""ObservationComponentCoding."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_observationcomponentcoding_001"

dbid = models.BigIntegerField(primary_key=True)
system = models.CharField()
version = models.CharField()
code = models.CharField()
display = models.CharField()
user_selected = models.BooleanField()
observation_component = models.ForeignKey(
ObservationComponent, on_delete=models.DO_NOTHING, related_name="codings"
)


class ObservationValueCoding(models.Model):
"""ObservationValueCoding."""

class Meta:
managed = False
app_label = "canvas_sdk"
db_table = "canvas_sdk_data_api_observationvaluecoding_001"

dbid = models.BigIntegerField(primary_key=True)
system = models.CharField()
version = models.CharField()
code = models.CharField()
display = models.CharField()
user_selected = models.BooleanField()
observation = models.ForeignKey(
Observation, on_delete=models.DO_NOTHING, related_name="value_codings"
)
Loading

0 comments on commit ca4458e

Please sign in to comment.