Load forms from a module

@adunsulag Thanks for the forms module loader. I was hoping you could help flesh out the implementation. I brought in the code changes from

In my bootstrap file.

namespace Juggernaut\Module\WoundCare;

use OpenEMR\Core\Kernel;
use OpenEMR\Events\Encounter\EncounterMenuEvent;
use OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

class Bootstrap
{
/**
 * @var EventDispatcherInterface The object responsible for sending and subscribing to events through the OpenEMR system
 */
private $eventDispatcher;
private ?Kernel $kernel;

public function __construct(EventDispatcher $dispatcher, ?Kernel $kernel = null)
{
    if (empty($kernel)) {
        $kernel = new Kernel();
    }

    $this->eventDispatcher = $dispatcher;
    $this->kernel = $kernel;
}

public function subscribeToEvents(): void
{
    $this->registerMenuItems();
    $this->registerFormFilters();
}

public function registerMenuItems(): void
{
    /**
     * @var EventDispatcherInterface $eventDispatcher
     * @var array $module
     * @global                       $eventDispatcher @see ModulesApplication::loadCustomModule
     * @global                       $module @see ModulesApplication::loadCustomModule
     */
    $this->eventDispatcher->addListener(EncounterMenuEvent::MENU_RENDER, [$this, 'addHealthScribeEncItem']);
}

public function registerFormFilters(): void
{
    $this->eventDispatcher->addListener(LoadEncounterFormFilterEvent::EVENT_NAME, [$this, 'addWoundCareAssistant']);
}

public function addHealthScribeEncItem(EncounterMenuEvent $event): EncounterMenuEvent
{
    $menu = $event->getMenuData();
    $menu['WC AI Agent'] = [
        'children' => [
            [
                'state' => 1,
                'directory' => 'oe-module-wound-care',
                'id' => 41,
                'unpackaged' => 1,
                'date' => '2023-03-01 00:00:00',
                'priority' => 13,
                'aco_spec' => 'encounters|coding',
                'LBF' => '',
                'displayText' => 'Assistant',
            ]
        ],
    ];
    $event->setMenuData($menu);
    return $event;
}
public function addWoundCareAssistant(LoadEncounterFormFilterEvent $event): LoadEncounterFormFilterEvent
{
    $event->setFormName('oe-module-wound-care');
    $event->setPageName('new.php');
    return $event;
}
}

When I click on the assistant button. I get a blank page. I took a step back and dumped the Loader.

   // AI GENERATED CODE: HEADER END
    $filteredEvent = $GLOBALS['kernel']->getEventDispatcher()->dispatch($event, LoadEncounterFormFilterEvent::EVENT_NAME);
    echo "<pre>";
    var_dump($filteredEvent); die;

This is the result of the dump

 object(OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent)#588 (4) {
["formName":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  string(20) "oe-module-wound-care"
["dir":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  string(62) "/var/www/html/openemr702/interface/forms/oe-module-wound-care/"
["pageName":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  string(7) "new.php"
["pid":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  uninitialized(?int)
  ["encounter":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  uninitialized(?int)
  ["isLBF":"OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent":private]=>
  bool(false)
}

This is looking in the wrong place “/var/www/html/openemr702/interface/forms/oe-module-wound-care/”

Please let me know where I am going in the wrong direction. What is $page for? This seems incomplete as it does don’t account for the module directory even though it is kinda does in the LoadEncounterFormFilterEvent. Why is called a filter and not a selector or just drop the word filter. Filter is confusing to me. What am I filter? Filter means trying to get rid of something, to me.

This is what I have in my module file for handling the event.

 public function respondToLoadEncounterFormFilterEvent(\OpenEMR\Events\Encounter\LoadEncounterFormFilterEvent $event)
    {
$customForms = ['my-test-form'];
        if (in_array($event->getFormName(), $customForms)) {

            $dir = $this->modulePath . 'public/forms/' . $event->getFormName() . '/';
            $event->setDir($dir);
        }
}

You need to override the directory and tell the filter event where your form is located. My modulePath is set to

$this->modulePath = $GLOBALS['fileroot'] .  "/interface/modules/custom_modules/oe-my-custom-module/";

Action events expect you do respond and do something.
Filter events are for changing / modifying a value that will be consumed by the calling code. You can change/remove stuff, or leave it alone. IE the form include path is passing through a filter. In this case the intended behavior is to filter the file path of what should be included.

FormLocator makes a call go the LoadEncounterFormFilterEvent->getFormIncludePath() method.


    public function getFormIncludePath()
    {
        return $this->dir . $this->pageName;
    }

$pageName is to give you context of the php page that the form is loading on. IE load_form.php, forms.php, encounter_ajax.php, custom_report.php, portal_custom_report.php.

Hope that helps.

1 Like

Does this stay the module directory?

public function addHealthScribeEncItem(EncounterMenuEvent $event): EncounterMenuEvent
{
    $menu = $event->getMenuData();
    $menu['WC AI Agent'] = [
        'children' => [
            [
                'state' => 1,
                'directory' => 'oe-module-wound-care',
                'id' => 41,
                'unpackaged' => 1,
                'date' => '2023-03-01 00:00:00',
                'priority' => 13,
                'aco_spec' => 'encounters|coding',
                'LBF' => '',
                'displayText' => 'Assistant',
            ]
        ],
    ];
    $event->setMenuData($menu);
    return $event;
}

The way I built it assumes you’ll add forms as a table entry into the form registry.

Haven’t tested just inserting the entry in via the EncounterMenuEvent. You could give it a try, but not sure if that will work or not.

1 Like

So, I took out the menu load because it did not work. It is a different path. I inserted this into the registry

INSERT INTO `registry` (`name`, `state`, `directory`, `id`, `sql_run`, `unpackaged`, `date`, `priority`, `category`, `nickname`, `patient_encounter`, `therapy_group_encounter`, `aco_spec`, `form_foreign_id`) VALUES
('Wound Care AI Assistant',
 1,
 'assistant',
 10,
 1,
 1,
 CURDATE(),
 0,
 'Clinical',
 'Jarvis',
 1,
 0,
 'encounters|notes',
 NULL
);

I put this in the boostrap:

public function addWoundCareAssistant(LoadEncounterFormFilterEvent $event): void
{
    $customForms = [
        'assistant',
    ];

    if (in_array($event->getFormName(), $customForms)) {
        $event->setFormName('view.php');
        $dir = $this->modulePath . "/assistant/" . $event->getFormName();

        $event->setDir($dir);
    }
}

I dumped the $dir and this is what is in it

 "/var/www/html/openemr702/interface/modules/custom_modules/oe-module-wound-care/assistant/view.php"

When I click on Jarvis, I see nothing.

Debugging: Should I have put new.php in here instead of view?

Looks like I am putting in improperly formatted data.

Here is where the compare is preformed/

$finalPath is incorrect. This is where the failure occurs. It has view.phpnew.php as the final destination.

$dir is a directory path, not a file path. Since you are setting form name to be ‘view.php’ you end up setting our $dir to be a file path.

If you actually want to include new.php when a request to view.php is called, then you should use setPageName() not setFormName().

I finally got it to work.

1 Like