User Tools

Site Tools


pracro:devas_corner

Deva's corner

Session sharing

Client logon with (patientid, template) tupple.
Server makes lookup in active sessions for tupples matching the one supplied by the client.
If none matches, a new session is created and 'returned' to the client.
If a match exists, the client is queried if it want to take over the session.
The session state is to be stored with the session (open, closed_no_commit) so the client must actively notify the server when a session is closed without commit.
If a client is about to take over an active (not closed_no_committed) a warning should be issued, about possible race conditions happening.

New Clientside Scripting System

<?xml version='1.0' encoding='UTF-8'?>
<macro name="test_resume" version="1.0">
 
  <resume>
    <script src="regexp.lua"/>
    <script>
      -- inline code
      if(regexp('.+', ''))
      then
        return 'a string'
      else
        return 'another string'
      end
    </script>
  </resume>
 
  <scripts>
    <script>
      function foo()
        -- this: current widget (widget that triggered the event)
 
        this:setChecked(true)
 
        foo = widget('test2')
 
        if ( foo && foo:type() == 'lineedit' )
        then
          foo:setValue('bleh')
        end
      end
    </script>
  </scripts>
  <widgets caption="Test Resume"
          layout="vbox">
    <checkbox name="bar" caption="?" onChange="foo()"
              truevalue="ja" falsevalue="nej"/>
  </widgets>
</macro>

The Script Tag

The script tag can either consist of inline code, or be a reference to a serverside file, that will be inserted in-place by the server before it is sent to the client.

<scripts>
  <script src="foo.lua"/>
  <script>
    --example
  </script>
</scripts>

The Widget Events

Each widget has an event that is inspired by the current script attribute.
Currently the script is referred by its name, and called whenever the widget changes its value.
In the new system, the script attribute is removed and replaced by event attributes. The event attributes will themselves contain lua code and not simply a name. In order to call 'external' code, simply wrap it in a function and call it here.
The return values will no longer be used. Instead widget methods will be added to control the validation state of each widget, eg. setValid(true|false).
Since it is no longer required (due to redundancy) the regexp attribute will be removed.

Event Handlers

  • onChange: triggered whenever the value of the widget is changed, either programmatically or by the user.
  • more to come

The Widget Object

When in a lua function, invoked by an event handler, the calling object (the object invoking the event) will be made available through the value 'this'. Other object can be retrieved using the widget('name') function.

function foo()
   if (this:value() == 'bar')
   then
      this:setValue('dut')
   end
 
   bas = widget('mywdg')
   if (bas && bas:value() == 'bar' )
   then
      bas:setValue('dims')
   end
end

A small number of methods are common to all widget types. These include are:

  • setValue(string), value()
  • setValid(boolean), valid()
  • setVisible(boolean), visible()
  • setEnabled(boolean), enabled()
  • type()
  • name()

Others are specific to certain widget types, such as (and many more):

  • setChekced(boolean), checked() (checkbox)

New protocol

Branched in CVS using tag new_protocol.
Check out using cvs checkout -r new_protocol pracro.
The new protocol concept is based on a challenge response system, combined with server-push.

    Client                   Server
      |                        |
      |--------|request|------>|       Single client request
      |                        |
      |               ,--------|
      |         /data/         |
      |<-------'               |
      |                        |
                  .
                  .
                  .
      |                        |       Server push
      |<--------|data|---------|
      |                        |

A request can be either a macro query, a template query or a commit.
The response is always a list of macros (potentially empty) or an error.

Normal session

Initially the client requests a template which is simply a predefined ordered list of macros and headers. The list is returned to the client, all in collapsed/resume state.
The macros will all be created by the client in the order in which they arrive. And they will remain in their positions throughout the entire session (unless the user reorders them that is).
Some macros are static macros and will remain open at all times, and not be drawn with collapse buttons (FIXME defined in the macro files, not in the template). They will be pushed by the server upon content changes. (FIXME is this even possible? - think of 'off site' changes, eg. in Artefact. These are not necessarily pushed to the server)
When the 'open' is handled, a request for the given macros is sent to the server. This macros is returned in expanded/non-resume state. If the macro was already filled out, it is to be queried in edit mode (see below). The resulting macros from a template request will never have ids. They are to be treated as new never-before-committed macros.

Edit session

If a macro is to be opened in edit mode, the client simply needs to achieve its id, and send it in the query. The returned expanded macro will be prefilled with the values from the original commit. The id is to be stored and sent with data when the changes are to be committed. The server will then connect the old values with the new ones in a way similar to a CVS system. The old values are preserved and can be retrieved in some manner, but the latest ones are 'on top'. This includes the generated resume text. It is therefore the responsibility of the server to maintain a list of commit ids within a live session.
If a macro is not given an id, it means that the macro has not yet been committed. The id will be generated when first committed.

Server event map

Input: Request, template.
Events: Read template file, read macros, process macros.
Output: Macros in resume mode. Macro ids

Input: Request, macro.
Events: Read macro, process macro.
Output: Macro in widgets mode. Macro id

Input: Request, macro resume.
Events: Read macro, process macro.
Output: Macro in resume mode. Macro id

Input: Commit, macro, macro id.
Events: Read macro, process macro, make commit
Output: Transaction id.

Input: Commit, session, id list (commit order)
Events: Order stored resume texts. Commit resume texts to journal. Remove session. Output: Empty

Class Hierarchy

                     MainWindow
                         |
                   OrganiserWidget
                         |                         NetCom
                    MacroWidget
                    /         \
        WidgetsWidget         ResumeWidget
        /     |     \
QWidgets   Widgets   Lua

Drafts

A draft is a piece of data produced, but not yet verified as authoritative data (ie. journal data). Drafts are to remain drafts until for up to 2 workdays - then they must be written to the journal. The actual journal file should not contain the draft data. The draft data should be stored elsewhere, and be viewable by a special viewer. This viewer should mark the drafts using some colour coding or font face. The history of draft data should not be stored in the journal, only the result of the draft history. This means that any debate or other correspondence building up the draft, is dismissed at the point in time where the draft is stored in the journal. Should an error be discovered after the point of elevation, an correctional entry must be made, explaining about the error, and what data should replace it. This error correction is stored in the journal, and will be kept there. All the data that are put in the journal is to be considered final/static data.

Committing paradox

When the journal is to be committed to the referring doctor or other authorities, the drafts are not to be included. But since the draft most of the time will become authoritative by time, they will almost never be authoritative at the point where they are to be committed, which is immediately after the procedure has finished. This requires the secretary who is in charge of the committing to finalize all draft data prior to committing, which in theory requires this person to be able to understand the contents of the drafts in order to verify that nothing erroneous is being finalized. This is not possible since the secretary is a secretary and not a doctor. One might even argue that it is not legal!
If the procedure were to require the doctor to finalize the drafts prior to committing, it will make the committing process almost impossible to handle, since a very large number of doctors all of a sudden would be required during the process.

PCP-DB interoperability

GOAL: Have PCP work the way it has always done, but store journal data in a structured fashion.
It might be achieved by using a Samba VSF module, that hooks the JOURNAL.TXT read/write calls. The read calls should initiate a generation of a file compatible with the PCP JOURNAL.TXT file, generated from the actual structural journal file (database, xml file or something of that flavor). When PCP writes the JOURNAL.TXT file a diff should be made, and the newly added data parsed stored in the real journal.

                      [samba server]
                       JOURNAL.TXT <--------VFS--------> [database/xml]
[PCP] <=={network}==>  JOURNAL.OTX - normal file
                       DOKMENU     <--------VFS?-------> [database/xml]

Database

The journal will be written in chunks. Each chunk with a header, including a timestamp and other structural data such as user, writer, edited-from etc. A chunk is in draft mode until it has been marked as final explicitly or it is more than two workdays old. This timespan is configurable. When JOURNAL.TXT is generated from the chunk, they are simply concatenated. Chunks marked as deleted (obsolete) are not included. Chunks in draft mode are made editable to pcp. If a new chunk is added in pcp, this will be detected and the chunk header parsed and stored in the db. If a draft chunk is edited, this will be detected and a new chunk will be generated with the new content. The old chunk will be marked as obsolete.

How does this play with pracro?

Not very well… Chunks put in the journal by pracro cannot be drafts, since it cannot be allowed that they are edited by pcp. This would lead to data divergence between the data stored in the journal, and the pracro database. Most changes made in pcp, are based on textual content rather than changing of numbers, but the latter may occur.
If pracro were to post each macro resume in its own chunk, and mark some chunk final upon creation (those containing numbers), would this solve matters?
In fact parcro might not even be the place to create textual lumbs of data such as conclusions etc. Maybe pracro should be re-thought to handle measurement-macros only.

Dynamic Templates

A template can be thought of as a startup state for the client view.
The client can add and delete macros from the view, however they can only be deleted as long as they have not yet been committed in this session.
Macros can be relocated when in resume mode, using drag'n'drop.
When adding a new macro to the view, all macros can be selected, including those already in the view.
Every macro in the view gets an ID.
When the session is being committed (typically when the view is closed) the macro ID order is sent to the server.
A macro in resume mode is marked with a distinct colour when committed.
A macro already committed, and reopened, is to be opened in edit mode, ie. it is to assume the old values from the commit.

pracro/devas_corner.txt · Last modified: 2011/01/14 14:49 by deva