Porel, short for Atemporel (Timeless in French), is the code name for a comprehensive nutrition order-entry application for hospitalized infants. This page is a Progress Report on its development as of May 2021. It can be found online at https://porel.info/porel5/docs/. The most recent iteration of the Porel codebase can be accessed in this folder ../core/. This and all previous iterations are also stored offline on the hard drives of two personal computers and an external backup drive. One of the first things we should do is initiate version control in a code repository such as Git. The source code consists of nine files:
logic.js — The Algorithm
porel.html — The Spreadsheet
porel.css — The Stylesheet
tiny.js — TinyJS Framework
facts01.js — The Dictionaries
facts02.js — The Dictionaries
epic.js — The Epic AGL API
cpoe.js — The Local OE Logic
builds.js — The Build Library
See also Progress, Programming, Testing, and Documentation.
logic.js)Porel is a nutrition spreadsheet, the purpose of which is to recalculate hundreds of values each time one of the values is changed. This task is performed by the Algorithm coded in the logic.js module. Embedded within this module is DNP(), a factory function which returns a composite data object of all the variables to be recalculated and redisplayed. An instance of this object is called a 'dnp', the acronym for Daily Nutrition Plan. The logic module has four public methods:
See also Programming Paradigms.
porel.html, porel.css)Porel is a nutrition spreadsheet, so the user interface looks like a spreadsheet. Written in pure HTML with external CSS, the layout uses <table> elements because: (1) it is a table with rows and columns, and (2) Epic AGL (IE11) does not fully support CSS Grid. The HTML has no embedded JavaScript, no event-handlers assigned in element tags. Specialized elements are declared using the "class" attribute or one of the exclusive "data-" attributes, and the event-handler properties of these elements are set by the TinyJS framework. You can try the Spreadsheet UI here.
tiny.js)TinyJS is a SPA micro-framework written exclusively for Porel. It is the glue that connects the Spreadsheet to the Algorithm. The following "data-" attributes and classes are supported. Together these constitute the declarative Tiny Markup Language (TiML).

Two of these features deserve special mention in this overview. (1) The data-model attribute creates a bidirectional connection between a cell in the spreadsheet and a variable in the dnp data object. This is the Model-View-ViewModel (MVVM) pattern. (2) The data-step attribute creates a special numeric input cell. It may not be essential, but this unique component has proven highly popular. The powerful TinyJS micro-framework consists of less than a thousand lines of well-factored code.
facts01.js, facts02.js)Dictionaries are JSON data objects that contain facts (information) needed by the Algorithm for calculations and the UI. These include lists of nutritional products and databases of nutrients, fluids, electrolytes, and so on. It should be noted that the main formula dictionary with macro- and micro-nutrient data is derived from the PENS master product database.
epic.js)
As powerful as the Porel calculation tool is, no one would use it clinically if they had to transcribe its results into another system for order entry. What makes Porel viable is the capability of (1) opening it within a patient record in the EHR, and (2) posting nutrition orders back to the EHR to be signed by the provider. The JS module that provides the Epic-specific, but facility-neutral code is epic.js. The public methods for this module are:
cpoe.js)Each facility has a unique set of rules for choosing which orders are to be used in which circumstances. The logic behind these rules must also be coded in JS, but we want to maintain this code separate from the facility-neutral code. It is defined in its own module, cpoe.js, which has one public method, cpoe.placeOrders(dnp, last). This method generates the required orders (using epic.generateOrder()), sets up the order panel, and then posts it to Epic.
builds.js)This is a JSON dictionary which is an abstraction of the Epic orders. The epic.generateOrder() method uses this dictionary to match the order questions with the appropriate data elements and perform necessary translations. Obviously, this dictionary is specific to each facility.
The Algorithm has been more 95% complete for over a year. Most of the code has not changed in several years. What remains to be done is related to: (1) notifications, (2) stock IV orders, (3) pediatric vs infant details, and (4) recent changes in formula orders. Although the present code is well-tested and sound, testing and retesting is a high priority during the next phase.
The Spreadsheet is over 90% complete. What remains is related to: (1) notifications, (2) stock IV orders, and (3) oral supplements. Although the UI has been well-tested by clinicians, one of our goals is to pay closer attention to the user experience.
The TinyJS framework is at least 98% complete and quite stable. The core of this module has changed little since I ported it over to Porel, and the new additions are well-tested.
The Dictionaries need work. The formats are solid, but content is lacking. The most important is products. I am currently waiting for an updated copy of the PENS Master Product database. Translating the appropriate products into the JSON format will require effort. The other dictionaries are in various stages of completion, perhaps as high as 80% or more.
The Active Guidelines API may need tweaking, but it is complete and fully functional. It has been tested well enough to prove the concept to our satisfaction.
The Local OE Logic is still a work in progress. The sub-module on formula orders is complete but needs testing. Sub-modules for other orders (including breast feeding and TPN) are next for development.
The Builds Library has likewise been completed for the formula orders, but remains on the development list for other Epic orders.
JS files in Porel are either code modules with well-defined public APIs or dictionaries. The dictionaries are JSON structures but not JSON strings. They are written as executable JS code for faster loading. Nonetheless, the JSON paradigm plays a central role in Porel, not only for dictionaries of factual information, but also at the core of the Algorithm.

The Daily Nutrition Plan (dnp) is a composite JSON structure created by the DNP() factory function embedded in the logic module. Nested composition is a form of encapsulation because leaf variables can be accessed only through branch properties. Inheritance is used where it is possible to reuse code but kept to a minimum to keep coupling loose. What this module does not do is package functions with their data objects. They are naked, so to speak. Private within the logic module, they are otherwise formally unattached. Their only attachments to the DNP and its components are informal in the arguments expected, but this is a telling link. It means that: (1) nested functions can parallel the nested data objects, and (2) the same functions can be used with different data objects (a form of polymorphism). View the 'class' diagram here.
Polymorphism is the concept of allowing different classes to use the same interface. Because Porel does not encapsulate functions in class definitions, true polymorphism is not possible. Instead, we use a naming convention where the function "fluid(d,wt)" is understood to mean Fluid.recalculate(d,wt), the function "infusion(d,wt)" is understood to mean Infusion.recalculate(d,wt), and so on. The 'recalculate() method' for each 'class' is named the same as its object factory except in lower case.
The public methods and private functions of the logic module are "pure functions" in the sense of accepting arguments and returning a value only. Methods and functions never act on (or change) state variables. There are no shared mutable states. Input arguments and return values may be (and often are) complex data objects, but given the same input, each function always returns the same output and produces no side-effects. Data objects are passed from one function to another. This makes testing much easier.
The Spreadsheet webpage is pure HTML. It has no embedded JS, no event-handlers assigned in element tags. The functionality of the application is coded in attributes of the HTML elements giving a declarative style of programming. After the page loads, event-handler properties of these elements are set by the TinyJS framework. Subsequent manipulation of the DOM is minimal.
It should be noted that the page layout makes extensive use of <table> elements. This practice is widely criticized in web development for good reason, so a serious effort was made to revise Porel around <div> elements and CSS Grid. It turns out that the AGL browser (IE11) does not support CSS Grid well. Furthermore, the Porel spreadsheet design is ideal for table elements.
We will not here review all of the exclusive "data-" attributes supported by TiML, but the "data-model" attribute creates what can best be understood as a form of MVVM pattern, where the Model is the data element in the dnp object, the View is the HTML element, and the ViewModel is TinyJS, which binds or automates the communication between the View and the Model.
Inline CSS is not prohibited in the Porel HTML, but it is discouraged. The external CSS file is a subset of the Bootstrap Reboot collection, except that it informally follows BEM conventions where feasible. It is responsive to the size of the AGL iframe viewport.
For a given set of inputs, each function has one expected outcome and only one. A unit test, then, for a function consists of (1) a given set of input values, and (2) the expected results following execution. The possible combinations of input values might be numerous, so a suite of unit tests should cover a fair sampling of the range of possibilities and explore their boundaries. During initial development, each function was tested in this way, individually and 'manually', but the goal is to have an automated system that tests all functions. Moreover, private functions are not accessible from outside the logic module. To test them individually at this point would require breaking encapsulation, perhaps putting the test framework inside the module, but this is clearly not where such a tool belongs. Separation of concerns requires the module tested to have no knowledge of the tests performed and the testing module to be independent, using only the public interface. This means the testing module must call the logic.recalculate(d) method.
Initial values. The input argument for recalculate(d) is a DNP object created by the factory function DNP() as returned by logic.makeDNP(). Theoretically, the returned object contains independent variables (properties), while dependent variables and their values are added by recalculate(), although this is not strictly the case. (Some dependent variables are added by the factory function to simplify the recalculation code.) Properties are declared, and most are initialized, even if with a default value of zero or an empty string.
Modified values. Independent variables are modified by the put(d, id, value) function. This obviously will not work for dependent variables. In a typical spreadsheet, one cannot modify a calculated cell (dependent variable). Porel has implemented a way to make a dependent variable into a pseudo-independent variable by writing a "watcher" function to back-calculate one of the true independent variables from the desired value. The logic.enter() method checks to see if there is a watcher function for the id variable. If so, the watcher is executed; if not, the generic put() function is executed. Dependent variables without a watcher are never attached to an enabled input control. After the user modifies the value of any variable in Porel, all dependent variables are recalculated.
Given. A unit test begins with a default DNP. One can test this object once, but thereafter certain values need to be changed before checking the recalculation again. The safest way would be for the testing program to use logic.enter(), but this method must be invoked once for each variable. We would prefer to define a set of independent values at the same time (without defining the entire DNP object). Starting with a default DNP, we want to "inject" a set of values into it before recalculating. This is easily done with a copyInto(d, s) function, where d is the destination and s is the source subset from the unit test 'given' object. Six lines long, it is a recursive function. Note: Any combination of independent variables may be changed with this function, but attempting to change a dependent variable (even one of the "pseudo-independent" variables) will produce no change.
Expected. Likewise, the output for recalculate() is a DNP object, easily represented by a JS dictionary, and similarly only a small subset of the resulting DNP needs to be compared to an expected outcome. This is done with the compare(t, s) function, where t is the target and s is the source. Notice that only the target is compared to its equivalent in the outcome. The remainder of the outcome is ignored. This, too, is a recursive function, complicated somewhat by the need to identify the 'path to failure' if the test does not pass. Numeric values are considered "passing" if they are within 0.1 of the target.
The porel.info website contains a wealth of history of this project, much of it now dated (or outdated), but some of it is timeless, pun intended. A few of the pages that may deserve study are listed below.