diff options
82 files changed, 2725 insertions, 584 deletions
diff --git a/client/client.pro b/client/client.pro index e002942..5e16942 100644 --- a/client/client.pro +++ b/client/client.pro @@ -16,15 +16,15 @@ debug { DEFINES+=USE_DEBUG } -DEFINES+=VERSION=\\\"2.2.1\\\" +DEFINES+=VERSION=\\\"2.2.2\\\" win32 { - LIBPATH += lua/lib + QMAKE_LIBDIR += lua/lib INCLUDEPATH += lua/include LIBS += -llua51 DEFINES += HOST_WIN32 # debug { - CONFIG += console + CONFIG += # } } diff --git a/client/collapser.cc b/client/collapser.cc index 2057a0e..abbe295 100644 --- a/client/collapser.cc +++ b/client/collapser.cc @@ -32,7 +32,7 @@ #include "debug.h" -#define ANIM_TIME 100 // ms +#define ANIM_TIME 200 // ms #define ANIM_INTERVAL 50 // ms Collapser::Collapser(QWidget *current, QScrollArea *scroll) diff --git a/client/docgen/doc.h b/client/docgen/doc.h index ae693f5..5678476 100644 --- a/client/docgen/doc.h +++ b/client/docgen/doc.h @@ -69,6 +69,9 @@ public: QString title; QString name; QString tag; + bool serverside; + bool clientside; + QString classname; QString extends; bool container; QString description; diff --git a/client/docgen/docgen.cc b/client/docgen/docgen.cc index e4af0a3..4eb2e08 100644 --- a/client/docgen/docgen.cc +++ b/client/docgen/docgen.cc @@ -32,6 +32,7 @@ #include <QDir> #include <QMap> #include <QDate> +#include <QFileInfo> #include <stdio.h> @@ -39,7 +40,9 @@ #include "parse.h" #include "generate.h" -#define INPUT "../widgets" +#define WIDGETS_DIR "../widgets" +#define SERVER_DIR "../../server/src" +#define CLIENT_DIR ".." #define OUTPUT "html" QString output; @@ -94,16 +97,46 @@ void writeIndex(QMap<QString, Doc> &docs) out += "<h1>Pracro "VERSION" Documentation</h1>\n"; out += "<h2>Overview</h2>\n"; + out += "<h3>Clientside</h3>\n"; + out += "<h4>Widgets</h4>\n"; out += "<ul>\n"; QMap<QString, Doc>::iterator i = docs.begin(); while(i != docs.end()) { Doc &doc = *i; - out += "<li><a href=\""+doc.name+".html\">["+doc.name+"]</a> - "+ - doc.title+"</li>\n"; + if(doc.tag != "") { + out += "<li><a href=\""+doc.name+".html\">["+doc.name+"]</a> - "+ + doc.title+"</li>\n"; + } + i++; + } + out += "</ul>\n"; + out += "<h4>Classes</h4>\n"; + out += "<ul>\n"; + i = docs.begin(); + while(i != docs.end()) { + Doc &doc = *i; + if(doc.classname != "" && doc.clientside) { + out += "<li><a href=\""+doc.name+".html\">["+doc.name+"]</a> - "+ + doc.title+"</li>\n"; + } i++; } out += "</ul>\n"; + out += "<h3>Serverside</h3>\n"; + out += "<ul>\n"; + i = docs.begin(); + while(i != docs.end()) { + Doc &doc = *i; + if(doc.classname != "" && doc.serverside) { + out += "<li><a href=\""+doc.name+".html\">["+doc.name+"]</a> - "+ + doc.title+"</li>\n"; + } + i++; + } + out += "</ul>\n"; + + QFile::remove(output + "/index.html"); QFile ofile(output + "/index.html"); ofile.open(QIODevice::ReadWrite | QIODevice::Text); @@ -133,15 +166,47 @@ int main(int argc, char *argv[]) QFile::remove(output + "/style.css"); QFile::copy("style.css", output + "/style.css"); - QDir dir(INPUT); - QStringList filter; filter << "*.h"; - dir.setNameFilters(filter); + QStringList files; + + // Widgets + { + QDir dir(WIDGETS_DIR); + QStringList filter; filter << "*.h"; + dir.setNameFilters(filter); + if(!dir.exists()) return 1; + QFileInfoList inflst = dir.entryInfoList(QDir::Files); + foreach(QFileInfo inf, inflst) { + files.append(inf.absoluteFilePath()); + } + } + + // Client + { + QDir dir(CLIENT_DIR); + QStringList filter; filter << "*.h"; + dir.setNameFilters(filter); + if(!dir.exists()) return 1; + QFileInfoList inflst = dir.entryInfoList(QDir::Files); + foreach(QFileInfo inf, inflst) { + files.append(inf.absoluteFilePath()); + } + } + + // Server + { + QDir dir(SERVER_DIR); + QStringList filter; filter << "*.h"; + dir.setNameFilters(filter); + if(!dir.exists()) return 1; + QFileInfoList inflst = dir.entryInfoList(QDir::Files); + foreach(QFileInfo inf, inflst) { + files.append(inf.absoluteFilePath()); + } + } - if(!dir.exists()) return 1; - QStringList l = dir.entryList(QDir::Files); - foreach(QString file, l) { - Doc doc = parse(QString(INPUT) + "/" + file); - if(doc.title != "" || doc.tag != "") + foreach(QString file, files) { + Doc doc = parse(file); + if(doc.title != "" || doc.tag != "" || doc.classname != "") docs[doc.name] = doc; } diff --git a/client/docgen/generate.cc b/client/docgen/generate.cc index b521f1f..f418b24 100644 --- a/client/docgen/generate.cc +++ b/client/docgen/generate.cc @@ -219,6 +219,18 @@ QString generate(Doc &doc, QMap<QString, QVector<Method> > &meths, highlight("<" + doc.tag + " />", xml) + "</pre></div>\n"; } + if(doc.serverside) + out += " <div class=\"availablility\">Available serverside.</div>\n"; + + if(doc.clientside) + out += " <div class=\"availablility\">Available clientside.</div>\n"; + + + if(doc.classname != "" ){ + out += " <div class=\"classname\">Class<pre class=\"code\">" + + doc.classname + "</pre></div>\n"; + } + if(doc.container) { out += " <div class=\"container\">Widget is a container.</div>\n"; } @@ -248,21 +260,23 @@ QString generate(Doc &doc, QMap<QString, QVector<Method> > &meths, out += generateExamples(doc.examples, xml); - out += " <h2>Attributes</h2>\n"; - out += " <div class=\"attributes\">\n"; - if(doc.attributes.size()) { - out += generateAttributes(doc.attributes); - } + if(doc.tag != "") { + out += " <h2>Attributes</h2>\n"; + out += " <div class=\"attributes\">\n"; + if(doc.attributes.size()) { + out += generateAttributes(doc.attributes); + } - QMap<QString, QVector<Attribute> >::iterator i = atts.begin(); - while(i != atts.end()) { - out += " <h3>Attributes inherited from <a href=\"" + i.key() + - ".html\">"+i.key()+"</a>:</h3>\n"; - out += generateAttributes(i.value()); - i++; + QMap<QString, QVector<Attribute> >::iterator i = atts.begin(); + while(i != atts.end()) { + out += " <h3>Attributes inherited from <a href=\"" + i.key() + + ".html\">"+i.key()+"</a>:</h3>\n"; + out += generateAttributes(i.value()); + i++; + } + out += " </div>\n"; } - out += " </div>\n"; - + out += generateMethodOverview(doc, meths); out += " <h2>Methods</h2>\n"; diff --git a/client/docgen/parse.cc b/client/docgen/parse.cc index 64c2aa7..0354f57 100644 --- a/client/docgen/parse.cc +++ b/client/docgen/parse.cc @@ -89,6 +89,8 @@ Doc parse(QString filename) { Doc doc; doc.container = false; + doc.serverside = false; + doc.clientside = false; QFileInfo fi(filename); QString name = fi.fileName(); @@ -134,10 +136,26 @@ Doc parse(QString filename) continue; } + if(state == none && line.left(6) == "@class") { + doc.classname = line.right(line.length() - line.indexOf(" ") - 1); + doc.name = doc.classname; + continue; + } + if(state == none && line.left(8) == "@extends") { doc.extends = line.right(line.length() - line.indexOf(" ") - 1); continue; } + + if(state == none && line.left(11) == "@serverside") { + doc.serverside = true; + continue; + } + + if(state == none && line.left(11) == "@clientside") { + doc.clientside = true; + continue; + } if(state == none && line.left(11) == "@screenshot") { Screenshot s; diff --git a/client/docgen/style.css b/client/docgen/style.css index e938648..17f087e 100644 --- a/client/docgen/style.css +++ b/client/docgen/style.css @@ -26,6 +26,11 @@ a:hover { } .doc .tagname { + font-size: 1.5em; + text-align: center; +} +.doc .classname { + font-size: 1.5em; text-align: center; } .doc .extends {} diff --git a/client/luadb.cc b/client/luadb.cc index 317831f..a848f36 100644 --- a/client/luadb.cc +++ b/client/luadb.cc @@ -77,7 +77,7 @@ typedef struct db_userdata { DB *db; } db_userdata; -static int db_value(lua_State *L) +int db_value(lua_State *L) { db_userdata *dbu; @@ -91,7 +91,7 @@ static int db_value(lua_State *L) return 1; } -static int db_exec(lua_State *L) +int db_exec(lua_State *L) { db_userdata *dbu; @@ -105,7 +105,7 @@ static int db_exec(lua_State *L) return 0; } -static int db_next(lua_State *L) +int db_next(lua_State *L) { db_userdata *dbu; @@ -119,7 +119,7 @@ static int db_next(lua_State *L) return 1; } -static int db_new(lua_State *L) +int db_new(lua_State *L) { const char *driver = luaL_checkstring(L, 1); const char *host = luaL_checkstring(L, 2); diff --git a/client/luadb.h b/client/luadb.h index a87baf5..91c8777 100644 --- a/client/luadb.h +++ b/client/luadb.h @@ -30,6 +30,59 @@ #include <lua.hpp> +/*** + * Database Class + * @class DB + * @clientside + * The DB class connects and handles communication with the an SQL server using + * the QDatabase API. + */ + +/*** + * @method object DB.new(string driver, string host, string database, string user, string password, int port) + * Create a new DB connection object. + * @param driver A string containing the QT SQL driver name. QPSQL: postgresql + * driver. + * @param host The hostname to connect to. + * @param database The database to open. + * @param user The username to use for authentication. + * @param password The password to use for authentication. + * @param port The port to connect to. + * @return The newly created communication object. + * @example Create a new database object: + * px = DB.new('QPSQL', 'localhost', 'mydb', 'user01', 'verysecret', '5432') + */ +int db_new(lua_State *L); + +/*** + * @method nil exec(string query) + * Execute an SQL query. + * @param query A string containing the SQL query to execute. + */ +int db_exec(lua_State *L); + +/*** + * @method string value(int index) + * Get current value from the result of the last query. + * @param index The column index number to use for the value retrieval. + * @return A string containing the value in the result cell. + */ +int db_value(lua_State *L); + +/*** + * @method boolean next() + * Iterate to next row in result set. + * @return true if another row is availble, false if the end of the list has + * been reached. + * @example Iterate through a result list and print the value in column 0 in each row: + * while db:next() + * do + * local item = db:value(0) + * print(item) + * end + */ +int db_next(lua_State *L); + void register_db(lua_State *L); #endif/*__PRACRO_LUADB_H__*/ diff --git a/client/luapraxisd.cc b/client/luapraxisd.cc index 11d619e..00ed961 100644 --- a/client/luapraxisd.cc +++ b/client/luapraxisd.cc @@ -72,7 +72,9 @@ static int px_getcave(lua_State *L) int top = lua_gettop(L); for(int i = 0; i < cavelist.size(); i++) { - lua_pushstring(L, QString::fromUtf8(cavelist[i].toStdString().c_str()).toStdString().c_str()); + QString c = cavelist[i]; + DEBUG(cavelist, "CAVE '%s'\n", c.toStdString().c_str()); + lua_pushstring(L, c.toStdString().c_str()); lua_rawseti(L, top, i); } @@ -85,7 +87,7 @@ static int px_cavelist(lua_State *L) pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); luaL_argcheck(L, pxu, 1, "Praxisd expected"); - QVector<cave_t> cavelist = pxu->px->diverse_get_cave(""); + QVector<cave_t> cavelist = pxu->px->diverse_get_cave("C"); if(pxu->px->hasError()) { lua_pushstring(L, pxu->px->errorString().toStdString().c_str()); lua_error(L); @@ -96,7 +98,9 @@ static int px_cavelist(lua_State *L) int top = lua_gettop(L); for(size_t i = 0; i < (size_t)cavelist.size(); i++) { - lua_pushstring(L, QString::fromUtf8(cavelist[i].cave.toStdString().c_str()).toStdString().c_str()); + QString c = cavelist[i].cave; + DEBUG(cavelist, "CAVE '%s'\n", c.toStdString().c_str()); + lua_pushstring(L, c.toStdString().c_str()); lua_rawseti(L, top, i); } diff --git a/client/mainwindow.cc b/client/mainwindow.cc index 9e62e5d..756f759 100644 --- a/client/mainwindow.cc +++ b/client/mainwindow.cc @@ -172,7 +172,7 @@ void MainWindow::closeCommit() return; } netcom.commit(); - isStored = true; + // isStored = true; closing = true; } @@ -186,7 +186,7 @@ void MainWindow::closeNoCommit() return; } netcom.nocommit(); - isStored = true; + // isStored = true; closing = true; } @@ -210,7 +210,7 @@ void MainWindow::closeDiscard() if(res == MessageBox::Yes) { netcom.discard(); - isStored = true; + // isStored = true; closing = true; } } @@ -233,7 +233,7 @@ void MainWindow::closeEvent(QCloseEvent *event) == MessageBox::Yes) { if(!isStored) { netcom.discard(); - isStored = true; + // isStored = true; closing = true; event->ignore(); return; @@ -343,6 +343,9 @@ void MainWindow::updateMacros(QDomNodeList &nodes) break; } } + + QVBoxLayout *l = (QVBoxLayout*)central->layout(); + l->addStretch(1); } } @@ -391,8 +394,6 @@ void MainWindow::setTemplate(QString name) void MainWindow::handle(QDomDocument &doc) { - if(closing) close(); - DEBUG(mainwindow, "Handle %s\n", doc.toString().toStdString().c_str()); @@ -403,6 +404,7 @@ void MainWindow::handle(QDomDocument &doc) if(element.tagName() == "error") { showError("Pracro Server Error", element.text()); + closing = false; continue; } @@ -433,4 +435,9 @@ void MainWindow::handle(QDomDocument &doc) } } + + if(closing) { + isStored = true; + close(); + } } diff --git a/client/pcpviewer/pcpviewer.cc b/client/pcpviewer/pcpviewer.cc index 5853e0a..ea59edb 100644 --- a/client/pcpviewer/pcpviewer.cc +++ b/client/pcpviewer/pcpviewer.cc @@ -42,6 +42,9 @@ PCPViewer::PCPViewer(QString patientid) : praxisd("sarge", 10000) { + setWindowTitle(tr("Pracro Patient View")); + setWindowIcon(QIcon(":/icons/icon.png")); + this->patientid = patientid; setLayout(new QVBoxLayout()); diff --git a/client/pracro_dk.ts b/client/pracro_dk.ts index 9c7fd40..2582280 100644 --- a/client/pracro_dk.ts +++ b/client/pracro_dk.ts @@ -2,6 +2,19 @@ <!DOCTYPE TS> <TS version="2.0" language="da_DK"> <context> + <name>AboutBox</name> + <message> + <location filename="aboutbox.cc" line="40"/> + <source>About Pracro</source> + <translation>Om Pracro</translation> + </message> + <message> + <location filename="aboutbox.cc" line="72"/> + <source><h1>Pracro Client</h1><h2>v.</source> + <translation><h1>Pracro Client</h1><h2>v.</translation> + </message> +</context> +<context> <name>DBWidget</name> <message> <source>Write something in the searchfield</source> @@ -53,43 +66,43 @@ et CPR nummer pÃ¥ 10 cifre.</translation> <context> <name>MacroWindow</name> <message> - <location filename="macrowindow.cc" line="177"/> + <location filename="macrowindow.cc" line="181"/> <source>Error</source> <translation>Der er sket en fejl</translation> </message> <message> - <location filename="macrowindow.cc" line="178"/> + <location filename="macrowindow.cc" line="182"/> <source>The macro </source> <translation>Makroen </translation> </message> <message> - <location filename="macrowindow.cc" line="179"/> + <location filename="macrowindow.cc" line="183"/> <source> was not filled out correctly, please try again. </source> <translation> er ikke.udfyldt korrekt. Prøv igen.</translation> </message> <message> - <location filename="macrowindow.cc" line="213"/> + <location filename="macrowindow.cc" line="219"/> <source>Save the macro changes?</source> <translation>Gem makro ændringer?</translation> </message> <message> - <location filename="macrowindow.cc" line="214"/> + <location filename="macrowindow.cc" line="220"/> <source>you have choosen to close the macro </source> <translation>Du har valgt at lukke makroen </translation> </message> <message> - <location filename="macrowindow.cc" line="216"/> + <location filename="macrowindow.cc" line="222"/> <source>do you want to save before closing?</source> <translation>ønsker du at gemme dine ændringer først?</translation> </message> <message> - <location filename="macrowindow.cc" line="241"/> + <location filename="macrowindow.cc" line="247"/> <source>Close first</source> <translation>Luk først</translation> </message> <message> - <location filename="macrowindow.cc" line="242"/> + <location filename="macrowindow.cc" line="248"/> <source>Close other one first.</source> <translation>Luk den Ã¥bne makro først.</translation> </message> @@ -97,26 +110,26 @@ et CPR nummer pÃ¥ 10 cifre.</translation> <context> <name>MainWindow</name> <message> - <location filename="mainwindow.cc" line="112"/> + <location filename="mainwindow.cc" line="124"/> <source>Close and commit</source> <translation>Gem i journal</translation> </message> <message> - <location filename="mainwindow.cc" line="115"/> + <location filename="mainwindow.cc" line="127"/> <source>Close no commit</source> <translation>Gem som kladde</translation> </message> <message> - <location filename="mainwindow.cc" line="150"/> - <location filename="mainwindow.cc" line="162"/> - <location filename="mainwindow.cc" line="174"/> + <location filename="mainwindow.cc" line="168"/> + <location filename="mainwindow.cc" line="180"/> + <location filename="mainwindow.cc" line="192"/> <source>Close first.</source> <translation>Luk først.</translation> </message> <message> - <location filename="mainwindow.cc" line="151"/> - <location filename="mainwindow.cc" line="163"/> - <location filename="mainwindow.cc" line="175"/> + <location filename="mainwindow.cc" line="169"/> + <location filename="mainwindow.cc" line="181"/> + <location filename="mainwindow.cc" line="193"/> <source>Close open macros first.</source> <translation>Luk Ã¥bne makroer først.</translation> </message> @@ -133,14 +146,14 @@ et CPR nummer pÃ¥ 10 cifre.</translation> <translation type="obsolete">Denne session bliver husket pÃ¥ denne specifikke maskine. For at genÃ¥bne pÃ¥ et senere tidspunkt, skal du blot genÃ¥bne pÃ¥ samme patient.</translation> </message> <message> - <location filename="mainwindow.cc" line="181"/> - <location filename="mainwindow.cc" line="203"/> + <location filename="mainwindow.cc" line="199"/> + <location filename="mainwindow.cc" line="221"/> <source>Discard</source> <translation>Kassér</translation> </message> <message> - <location filename="mainwindow.cc" line="182"/> - <location filename="mainwindow.cc" line="204"/> + <location filename="mainwindow.cc" line="200"/> + <location filename="mainwindow.cc" line="222"/> <source>This session will <strong>NOT</strong> be stored in the journal.<br/>Are you sure you want to continue?</source> <translation>Dette vil slette alle indtastede data. Denne session vil <strong>IKKE</strong> blive gemt i journalen hvis du fortsætter. Fortsætter du kan dataene <strong>IKKE</strong> gendannes.<br/>Er du sikker pÃ¥ du vil fortsætte?</translation> </message> @@ -158,6 +171,25 @@ et CPR nummer pÃ¥ 10 cifre.</translation> <oldsource>Remove form list</oldsource> <translation>Fjern fra listen</translation> </message> + <message> + <location filename="widgets/multilist.cc" line="133"/> + <source>Inner widget changed.</source> + <translation>Data er ændret</translation> + </message> + <message> + <location filename="widgets/multilist.cc" line="134"/> + <source>The inner widget changed, and you didn't add it to the list. +Do you want to continue and discard the change?</source> + <translation>Der er indtastet data, som ikke er tilføjet til listen. Ønsker du at fortsætte og glemme disse data?</translation> + </message> +</context> +<context> + <name>PCPViewer</name> + <message> + <location filename="pcpviewer/pcpviewer.cc" line="45"/> + <source>Pracro Patient View</source> + <translation>Pracro Patient Oversigt</translation> + </message> </context> <context> <name>QObject</name> @@ -252,9 +284,17 @@ et CPR nummer pÃ¥ 10 cifre.</translation> <translation>Ignorér</translation> </message> <message> - <location filename="macro.cc" line="121"/> + <location filename="macro.cc" line="122"/> <source>Depends on: </source> <translation>Afhænger af: </translation> </message> </context> +<context> + <name>Template</name> + <message> + <location filename="template.cc" line="54"/> + <source>Open</source> + <translation>Ã…bn</translation> + </message> +</context> </TS> diff --git a/client/praxisd.cc b/client/praxisd.cc index 161b471..2fbaddc 100644 --- a/client/praxisd.cc +++ b/client/praxisd.cc @@ -221,7 +221,7 @@ void Praxisd::replyFinished(QNetworkReply *reply) break; } } else { - emit networkError(reply->errorString()); + emit networkError(reply->errorString() + " - " + reply->readAll()); } replytypes.erase(replytypes.find(reply)); } diff --git a/client/widgets/altcombobox.cc b/client/widgets/altcombobox.cc index 9956fff..082601a 100644 --- a/client/widgets/altcombobox.cc +++ b/client/widgets/altcombobox.cc @@ -107,6 +107,12 @@ AltComboBox::~AltComboBox() { } +bool AltComboBox::setKeyboardFocus() +{ + combobox->setFocus(); + return true; +} + QComboBox *AltComboBox::qcombobox() { return combobox; diff --git a/client/widgets/altcombobox.h b/client/widgets/altcombobox.h index e6a21d7..73dee10 100644 --- a/client/widgets/altcombobox.h +++ b/client/widgets/altcombobox.h @@ -43,6 +43,7 @@ /*** * ComboBox Widget with Alternate Value * @tag altcombobox + * @clientside * @extends combobox * @screenshot Example: altitem selected. * <altcombobox name="ex" layout="vbox" value="altitem" type="select"> @@ -94,6 +95,8 @@ public: QComboBox *qcombobox(); + bool setKeyboardFocus(); + public slots: void comboChanged(); void onChildChange(); diff --git a/client/widgets/button.h b/client/widgets/button.h index 0bd7db9..d9053a6 100644 --- a/client/widgets/button.h +++ b/client/widgets/button.h @@ -34,6 +34,7 @@ /*** * PushButton Widget * @tag button + * @clientside * @screenshot Example button * <button caption="Click me"/> * @screenshot Example cancel button diff --git a/client/widgets/checkbox.h b/client/widgets/checkbox.h index ce31a62..5fef7cb 100644 --- a/client/widgets/checkbox.h +++ b/client/widgets/checkbox.h @@ -38,6 +38,7 @@ /*** * CheckBox Widget * @tag checkbox + * @clientside * @screenshot An example checkbox * <checkbox name="example" truevalue="true" falsevalue="false" value="true" * caption="A simple checkbox"/> diff --git a/client/widgets/checkgroupbox.h b/client/widgets/checkgroupbox.h index 5dfa7fb..96056a4 100644 --- a/client/widgets/checkgroupbox.h +++ b/client/widgets/checkgroupbox.h @@ -41,6 +41,7 @@ /*** * Checkable GroupBox Widget * @tag checkgroupbox + * @clientside * @extends checkbox * @screenshot Example 1: type="framed" * <checkgroupbox name="example" caption="Example" type="framed" layout="vbox" diff --git a/client/widgets/combobox.cc b/client/widgets/combobox.cc index eed2d10..935f620 100644 --- a/client/widgets/combobox.cc +++ b/client/widgets/combobox.cc @@ -93,6 +93,8 @@ ComboBox::ComboBox(QDomNode &node, MacroWindow *macrowindow) // Make empty default selection. combobox->setCurrentIndex(-1); + combobox->installEventFilter(this); + QDomElement elem = node.toElement(); combotype = SELECT; @@ -213,15 +215,26 @@ void ComboBox::changed() emit eventOnChange(); } +#include <QKeyEvent> bool ComboBox::eventFilter(QObject *obj, QEvent *event) { if(ignoreChangeEvents == true) return false; + if(combotype == SELECT) { if(event->type() == QEvent::MouseButtonRelease) { if(enabled()) combobox->showPopup(); } } + if(event->type() == QEvent::KeyPress) { + QKeyEvent *ke = (QKeyEvent*)event; + // printf("KEY: %d\n", ke->key()); + // if(ke->key() == Qt::Key_Up || ke->key() == Qt::Key_Down) { + if(ke->key() == Qt::Key_Space) { + if(enabled()) combobox->showPopup(); + } + } + return QObject::eventFilter(obj, event); } diff --git a/client/widgets/combobox.h b/client/widgets/combobox.h index bf61b1a..448a0ff 100644 --- a/client/widgets/combobox.h +++ b/client/widgets/combobox.h @@ -40,6 +40,7 @@ /*** * ComboBox Widget * @tag combobox + * @clientside * @screenshot Example * <combobox name="ex" layout="vbox" value="item" type="select"> * <item caption="Item" value="item"/> diff --git a/client/widgets/datetime.h b/client/widgets/datetime.h index 84df018..a36d87b 100644 --- a/client/widgets/datetime.h +++ b/client/widgets/datetime.h @@ -35,6 +35,7 @@ /*** * Date and Time Widget * @tag datetime + * @clientside * @screenshot Example with fuzziness="1" and value="1234567890" * <datetime name="x" fuzziness="1" value="1234567890"/> * @screenshot Example with fuzziness="7" and value="1234567890" diff --git a/client/widgets/groupbox.h b/client/widgets/groupbox.h index 58e52bb..9198a5a 100644 --- a/client/widgets/groupbox.h +++ b/client/widgets/groupbox.h @@ -37,6 +37,7 @@ /*** * Layout or Frame Widget * @tag frame + * @clientside * @screenshot Example with caption. * <frame caption="Caption" layout="vbox"> * <label caption="Label"/> diff --git a/client/widgets/label.h b/client/widgets/label.h index b303d0d..eae97b8 100644 --- a/client/widgets/label.h +++ b/client/widgets/label.h @@ -34,6 +34,7 @@ /*** * Label Widget * @tag label + * @clientside * @screenshot Example * <label width="300" caption="A nice label with a caption"/> * @extends widget diff --git a/client/widgets/lineedit.h b/client/widgets/lineedit.h index 08230a6..d482c15 100644 --- a/client/widgets/lineedit.h +++ b/client/widgets/lineedit.h @@ -39,6 +39,7 @@ /*** * Line Edit Widget * @tag lineedit + * @clientside * @screenshot Example * <lineedit name="myname" value="some text"/> * @extends widget diff --git a/client/widgets/listbox.h b/client/widgets/listbox.h index 82c5dfc..f9d967a 100644 --- a/client/widgets/listbox.h +++ b/client/widgets/listbox.h @@ -38,6 +38,7 @@ /*** * ListBox Widget + * @clientside * @tag listbox * @screenshot Example * <listbox name="x" value="item1"> diff --git a/client/widgets/metawidget.h b/client/widgets/metawidget.h index c6bf5d0..c6a7224 100644 --- a/client/widgets/metawidget.h +++ b/client/widgets/metawidget.h @@ -36,6 +36,7 @@ /*** * Meta Widget + * @clientside * @extends widget * @tag metawidget * @screenshot An example of a metawidget with two inner widgets. The metawidget is not in itself visible. diff --git a/client/widgets/multilist.h b/client/widgets/multilist.h index f7da8fa..a7c5332 100644 --- a/client/widgets/multilist.h +++ b/client/widgets/multilist.h @@ -37,6 +37,7 @@ /*** * Multi List Widget * @tag multilist + * @clientside * @container * @screenshot Example of multilist containing a metawidget. * <multilist name="x" layout="vbox" innerwidget="in" value="Abc: Def diff --git a/client/widgets/radiobuttons.h b/client/widgets/radiobuttons.h index 4b9ce67..e09d4e8 100644 --- a/client/widgets/radiobuttons.h +++ b/client/widgets/radiobuttons.h @@ -35,6 +35,7 @@ /*** * Radio Button Group Widget * @tag radiobuttons + * @clientside * @extends widget * @screenshot Example with four radiobuttons * <radiobuttons name="x" value="item2"> diff --git a/client/widgets/textedit.h b/client/widgets/textedit.h index 542dd6f..fbbb040 100644 --- a/client/widgets/textedit.h +++ b/client/widgets/textedit.h @@ -35,6 +35,7 @@ /*** * Multiline Text Edit Widget * @tag textedit + * @clientside * @screenshot Example of a textedit. * <textedit name="x" value="This is a multiline textfield. * It may contain all text in the universe, it will simply add a scrollbar."/> diff --git a/client/widgets/widget.h b/client/widgets/widget.h index 6199aa4..f2bf278 100644 --- a/client/widgets/widget.h +++ b/client/widgets/widget.h @@ -44,6 +44,7 @@ /*** * Virtual Base Widget * This tag is purely virtual. It is inherited by all other widgets. + * @clientside * @att name The name of the widget. This is also the name used by the scripts. * @att value The initial value of the widget. It is overwritten if there is a * map with a recent value or if the database contains a recent value. diff --git a/client/widgets/window.h b/client/widgets/window.h index ea3af0a..8fb5c59 100644 --- a/client/widgets/window.h +++ b/client/widgets/window.h @@ -34,6 +34,7 @@ * Widgets Outer Container * @tag widgets * @container + * @clientside * This is the outer tag of the macro widgets. * @att caption This is the name that will be shown in the macro. * @att layout this is the layout that is used by the macro. It can be one of diff --git a/server/configure.in b/server/configure.in index 63e2e24..e65ac33 100644 --- a/server/configure.in +++ b/server/configure.in @@ -2,7 +2,7 @@ AC_INIT(src/pracrod.cc) -VERSION="2.2.0" +VERSION="2.2.2" AM_INIT_AUTOMAKE( pracrod, $VERSION ) dnl ====================== @@ -95,7 +95,9 @@ else dnl ====================== dnl Check for libpqxx dnl ====================== - PKG_CHECK_MODULES(PQXX, libpqxx >= 2.6.8) + PKG_CHECK_MODULES(PQXX, libpqxx >= 4.0, + [AC_DEFINE_UNQUOTED([USE_NEW_PQXX], [1], [Use new pqx api])], + [PKG_CHECK_MODULES(PQXX, libpqxx >= 2.6.8)] ) fi dnl ====================== diff --git a/server/src/Makefile.am b/server/src/Makefile.am index 167f4e4..c852eb0 100644 --- a/server/src/Makefile.am +++ b/server/src/Makefile.am @@ -36,6 +36,8 @@ pracrod_SOURCES = \ journal.cc \ journal_uploadserver.cc \ log.cc \ + luascript.cc \ + luaoncommit.cc \ luapraxisd.cc \ luaquerymapper.cc \ luaresume.cc \ @@ -51,11 +53,11 @@ pracrod_SOURCES = \ queryhandlerpentominos.cc \ queryhandlerpracro.cc \ queryparser.cc \ - resumeparser.cc \ saxparser.cc \ semaphore.cc \ server.cc \ session.cc \ + sessionheaderparser.cc \ sessionparser.cc \ sessionserialiser.cc \ templatelist.cc \ @@ -97,6 +99,8 @@ EXTRA_DIST = \ journal.h \ journal_uploadserver.h \ log.h \ + luascript.h \ + luaoncommit.h \ luapraxisd.h \ luaquerymapper.h \ luaresume.h \ @@ -114,11 +118,11 @@ EXTRA_DIST = \ queryhandlerpracro.h \ queryparser.h \ queryresult.h \ - resumeparser.h \ saxparser.h \ semaphore.h \ server.h \ session.h \ + sessionheaderparser.h \ sessionparser.h \ sessionserialiser.h \ template.h \ diff --git a/server/src/admin_connection.cc b/server/src/admin_connection.cc index fac52a1..76c88ea 100644 --- a/server/src/admin_connection.cc +++ b/server/src/admin_connection.cc @@ -35,6 +35,7 @@ #include "debug.h" #include "configuration.h" +#include "sessionserialiser.h" static std::string admin_sessionunlock(Environment &env, std::string id) { @@ -59,18 +60,28 @@ static std::string admin_listactivesessions(Environment &env) { std::string str; - std::vector<std::string> act = env.sessions.activeSessions(); - std::vector<std::string>::iterator i = act.begin(); + std::vector<Sessions::SessionInfo> act = env.sessions.activeSessions(); + std::vector<Sessions::SessionInfo>::iterator i = act.begin(); while(i != act.end()) { - // NOTE: Returned session is returned in locked state! - Session *s = NULL; - SessionAutounlock l(&s); - s = env.sessions.lockedSession(*i); - str += "Session " + *i + ": "+s->templ+" on "+s->patientid+" "+ - std::string(s->idle()?"[idle]":"[active]")+"\n"; + str += "Session " + i->id + ": "+i->templ+" on "+i->patientid+" "+ + std::string(i->idle?"[idle]":"[active]")+"\n"; i++; } + SessionSerialiser ser(&env, Conf::session_path); + std::map<std::string, SessionHeaderParser::Header> files = ser.sessionFiles(); + + std::map<std::string, SessionHeaderParser::Header>::iterator j = files.begin(); + while(j != files.end()) { + std::string file = j->first; + SessionHeaderParser::Header header = j->second; + + str += "Session " + header.id + ": " + header.templ + " on " + + header.patientid + " [session file: " + file + "]\n"; + + j++; + } + return str; } @@ -90,70 +101,68 @@ AdminConnection::AdminConnection(Environment &e, headers_t a, std::string u) AdminConnection::~AdminConnection() {} -bool AdminConnection::handle(const char *data, size_t size) +bool AdminConnection::data(const char *, size_t) { return true; } + +bool AdminConnection::handle() { status = 200; // OK - if(data == NULL && size == 0) { - DEBUG(admin, "URI: %s\n", uri.c_str()); - - if(uri == "/") { - reply = admin_header(uri) + - "Command list:\n" - "<strong>/sessionunlock?id=<em>[ID]</em></strong> unlock session with [ID] as its session id.\n" - "<strong>/listactivesessions</strong> lists all active sessions on the server.\n" - "<strong>/flushsessions</strong> flushes all active sessions to disc.\n" - "<strong>/export?template=<em>[TEMPLATE]</em></strong> export template with name [TEMPLATE] to a\n csf file (comma seperated file, that can be opened in OOCalc or Excel).\n" - + admin_rc("footer"); - return true; - } - - if(uri == "/sessionunlock" && args.find("id") != args.end()) { - reply = admin_header(uri) + admin_sessionunlock(env, args["id"]) - + admin_rc("footer"); - return true; - } - - if(uri == "/listactivesessions") { - reply = admin_header(uri) + admin_listactivesessions(env) - + admin_rc("footer"); - return true; - } - - if(uri == "/flushsessions") { - reply = admin_header(uri) + admin_flush(env) + admin_rc("footer"); - return true; - } - - if(uri == "/export" && args.find("template") != args.end()) { - time_t from = 0; - if(args.find("from") != args.end()) from = atoi(args["from"].c_str()); - - time_t to = time(NULL); - if(args.find("to") != args.end()) to = atoi(args["to"].c_str()); - bool ok; - std::string res = admin_export(env, args["template"], &ok, from, to); - if(!ok) reply = admin_header(uri) + res + admin_rc("footer"); - else { - reply = res; - hdrs["Content-Type"] = "text/csv; charset=UTF-8"; - hdrs["Content-Disposition"] = "attachment; filename=\""+args["template"]+".csv\""; - } - return true; - } - - if(uri == "/favicon.ico") { - hdrs["Content-Type"] = "image/ico"; - reply = admin_rc("favicon"); - return true; - } + DEBUG(admin, "URI: %s\n", uri.c_str()); - reply = admin_header(uri) + - "'" + uri + "' not recognised as a valid command." + if(uri == "/") { + reply = admin_header(uri) + + "Command list:\n" + "<strong>/sessionunlock?id=<em>[ID]</em></strong> unlock session with [ID] as its session id.\n" + "<strong>/listactivesessions</strong> lists all active sessions on the server.\n" + "<strong>/flushsessions</strong> flushes all active sessions to disc.\n" + "<strong>/export?template=<em>[TEMPLATE]</em></strong> export template with name [TEMPLATE] to a\n csf file (comma seperated file, that can be opened in OOCalc or Excel).\n" + + admin_rc("footer"); + return true; + } + + if(uri == "/sessionunlock" && args.find("id") != args.end()) { + reply = admin_header(uri) + admin_sessionunlock(env, args["id"]) + + admin_rc("footer"); + return true; + } + + if(uri == "/listactivesessions") { + reply = admin_header(uri) + admin_listactivesessions(env) + admin_rc("footer"); return true; } + + if(uri == "/flushsessions") { + reply = admin_header(uri) + admin_flush(env) + admin_rc("footer"); + return true; + } + + if(uri == "/export" && args.find("template") != args.end()) { + time_t from = 0; + if(args.find("from") != args.end()) from = atoi(args["from"].c_str()); + + time_t to = time(NULL); + if(args.find("to") != args.end()) to = atoi(args["to"].c_str()); + bool ok; + std::string res = admin_export(env, args["template"], &ok, from, to); + if(!ok) reply = admin_header(uri) + res + admin_rc("footer"); + else { + reply = res; + hdrs["Content-Type"] = "text/csv; charset=UTF-8"; + hdrs["Content-Disposition"] = "attachment; filename=\""+args["template"]+".csv\""; + } + return true; + } + if(uri == "/favicon.ico") { + hdrs["Content-Type"] = "image/ico"; + reply = admin_rc("favicon"); + return true; + } + + reply = admin_header(uri) + + "'" + uri + "' not recognised as a valid command." + + admin_rc("footer"); return true; } diff --git a/server/src/admin_connection.h b/server/src/admin_connection.h index 4a413a6..591c128 100644 --- a/server/src/admin_connection.h +++ b/server/src/admin_connection.h @@ -38,7 +38,8 @@ public: AdminConnection(Environment &e, headers_t args, std::string uri); ~AdminConnection(); - bool handle(const char *data, size_t size); + bool data(const char *data, size_t size); + bool handle(); void getReply(Httpd::Reply &reply); diff --git a/server/src/admin_export.cc b/server/src/admin_export.cc index 0aa290c..3bdfcf6 100644 --- a/server/src/admin_export.cc +++ b/server/src/admin_export.cc @@ -41,6 +41,13 @@ #define SEP "\t" +#ifdef USE_NEW_PQXX +#include <pqxx/tuple.hxx> +typedef pqxx::tuple tuple_t; +#else +typedef pqxx::result::tuple tuple_t; +#endif + static std::string escape(std::string &str) { std::string out = "\""; @@ -91,7 +98,7 @@ public: " WHERE extract='true';"); pqxx::result::const_iterator ri = result.begin(); for(unsigned int r = 0; r < result.size(); r++) { - pqxx::result::tuple tuple = result.at(r); + tuple_t tuple = result.at(r); std::string name = tuple.at(0).c_str(); std::string caption = tuple.at(1).c_str(); addcell(name, caption); @@ -166,7 +173,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok, " WHERE extract='true';"); pqxx::result::const_iterator ri = result.begin(); for(unsigned int r = 0; r < result.size(); r++) { - pqxx::result::tuple tuple = result.at(r); + tuple_t tuple = result.at(r); std::string name = tuple.at(0).c_str(); filter.insert(name); //printf("filter: '%s'\n", name.c_str()); @@ -209,7 +216,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok, pqxx::result result = work.exec(q); pqxx::result::const_iterator ri = result.begin(); for(unsigned int r = 0; r < result.size(); r++) { - pqxx::result::tuple tuple = result.at(r); + tuple_t tuple = result.at(r); std::string patientid = tuple.at(0).c_str(); std::string templ = tuple.at(1).c_str(); std::string version = tuple.at(2).c_str(); @@ -239,7 +246,7 @@ static std::string do_export(Environment &env, std::string templ, bool *ok, " ORDER BY t.timestamp;"); pqxx::result::const_iterator ri = result.begin(); for(unsigned int r = 0; r < result.size(); r++) { - pqxx::result::tuple tuple = result.at(r); + tuple_t tuple = result.at(r); std::string name = tuple.at(0).c_str(); std::string value = tuple.at(1).c_str(); @@ -269,9 +276,9 @@ std::string admin_export(Environment &env, std::string templ, bool *ok, } #ifdef TEST_ADMIN_EXPORT -//deps: environment.cc connectionpool.cc mutex.cc semaphore.cc configuration.cc entitylist.cc artefact.cc database.cc pracrodao.cc pracrodaotest.cc pracrodaopgsql.cc templatelist.cc macrolist.cc debug.cc macroheaderparser.cc exception.cc log.cc saxparser.cc templateheaderparser.cc versionstr.cc inotify.cc session.cc sessionserialiser.cc journal.cc journal_uploadserver.cc journal_commit.cc xml_encode_decode.cc sessionparser.cc fieldnamescanner.cc util.cc macroparser.cc templateparser.cc -//cflags: -I.. -DWITHOUT_ARTEFACT $(PQXX_CFLAGS) $(EXPAT_CFLAGS) -//libs: $(PQXX_LIBS) $(EXPAT_LIBS) +//deps: environment.cc connectionpool.cc mutex.cc semaphore.cc configuration.cc entitylist.cc artefact.cc database.cc pracrodao.cc pracrodaotest.cc pracrodaopgsql.cc templatelist.cc macrolist.cc debug.cc macroheaderparser.cc exception.cc log.cc saxparser.cc templateheaderparser.cc versionstr.cc inotify.cc session.cc sessionserialiser.cc journal.cc journal_uploadserver.cc journal_commit.cc xml_encode_decode.cc sessionparser.cc fieldnamescanner.cc util.cc macroparser.cc templateparser.cc courselist.cc luascript.cc sessionheaderparser.cc courseparser.cc luautil.cc luapraxisd.cc praxisd.cc +//cflags: -I.. -DWITHOUT_ARTEFACT $(PQXX_CFLAGS) $(EXPAT_CFLAGS) $(PTHREAD_CFLAGS) $(LUA_CFLAGS) $(CURL_CFLAGS) +//libs: $(PQXX_LIBS) $(EXPAT_LIBS) $(PTHREAD_LIBS) $(LUA_LIBS) $(CURL_LIBS) #include "test.h" TEST_BEGIN; diff --git a/server/src/client_connection.cc b/server/src/client_connection.cc index 075dc46..c3de7b0 100644 --- a/server/src/client_connection.cc +++ b/server/src/client_connection.cc @@ -85,7 +85,7 @@ ClientConnection::ClientConnection(Environment &e, headers_t headers, did_commit = false; #endif - parser_complete = false; + parser_complete = true; } ClientConnection::~ClientConnection() @@ -108,6 +108,7 @@ void ClientConnection::nocommit(Session *session) } void ClientConnection::commit(Session *session) + throw(LUAScript::Exception, Journal::Exception) { if(docommit) { if(session->isReadonly()) { // Commit of an empty session discards it. @@ -117,7 +118,13 @@ void ClientConnection::commit(Session *session) DEBUG(connection, "Commit (%s)\n", session->id().c_str()); std::string sid = session->id(); - session->commit(); + try { + session->commit(); + } catch(LUAScript::Exception &e) { + throw e; + } catch(Journal::Exception &e) { + throw e; + } env.sessions.deleteSession(sid); sessionid = ""; docommit = false; @@ -139,8 +146,19 @@ void ClientConnection::discard(Session *session) } } -bool ClientConnection::handle(const char *data, size_t size) +bool ClientConnection::data(const char *data, size_t size) { + DEBUG(connection, "data(%p %d)\n", data, size); + + parser_complete = parser.parse(data, size); + + return true; +} + +bool ClientConnection::handle() +{ + DEBUG(connection, "handle\n"); + if(patientid == "") { response = error_box(xml_encode("Missing patientid.")); parser_complete = true; @@ -189,33 +207,28 @@ bool ClientConnection::handle(const char *data, size_t size) sessionid = session->id(); - try { + // Force session discard on empty template name. + if(templ == "") dodiscard = true; - if((!data || !size) && (docommit || donocommit || dodiscard)) { - parser_complete = true; - commit(session); - nocommit(session); - discard(session); - return true; + try { + if(parser_complete) { + response = handleTransaction(request, transaction, env, *session); } - // Force session discard on empty template name. - if(templ == "") dodiscard = true; - - if(size == 0 || parser.parse(data, size)) { - parser_complete = true; - - { - //SessionAutolock lock(session); - response = handleTransaction(request, transaction, env, *session); - } - + try { commit(session); - nocommit(session); - discard(session); - + } catch(LUAScript::Exception &e) { + response = error_box(xml_encode(e.msg)); + return true; + } catch(Journal::Exception &e) { + response = error_box(xml_encode(e.msg)); return true; } + nocommit(session); + discard(session); + + return true; + } catch(...) { ERR(connection, "Failed to parse data!\n"); response = error_box(xml_encode("XML Parse error.")); diff --git a/server/src/client_connection.h b/server/src/client_connection.h index e64dcad..0bfa2e4 100644 --- a/server/src/client_connection.h +++ b/server/src/client_connection.h @@ -36,6 +36,8 @@ #include "transaction.h" #include "transactionparser.h" +#include "luascript.h" + class Session; class ClientConnection : public Connection { @@ -49,12 +51,13 @@ public: headers_t args, std::string uri); ~ClientConnection(); - bool handle(const char *data, size_t size); + bool data(const char *data, size_t size); + bool handle(); void getReply(Httpd::Reply &reply); private: - void commit(Session *session); + void commit(Session *session) throw(LUAScript::Exception, Journal::Exception); void nocommit(Session *session); void discard(Session *session); diff --git a/server/src/connection.h b/server/src/connection.h index b7d17f4..098c735 100644 --- a/server/src/connection.h +++ b/server/src/connection.h @@ -36,7 +36,8 @@ class Connection { public: virtual ~Connection() {} - virtual bool handle(const char *data, size_t size) = 0; + virtual bool data(const char *data, size_t size) = 0; + virtual bool handle() = 0; virtual void getReply(Httpd::Reply &reply) = 0; }; diff --git a/server/src/daemon.cc b/server/src/daemon.cc index 938170c..e8e08ee 100644 --- a/server/src/daemon.cc +++ b/server/src/daemon.cc @@ -134,6 +134,10 @@ int Daemon::run(const char *user, const char* group, bool detach, } else { fprintf(fp, "%lu", (unsigned long int)pid); fclose(fp); + /* + fprintf(stderr, "Wrote pid %lu to file \"%s\"", + (unsigned long int)pid, pidfile.c_str()); + */ } } diff --git a/server/src/debug.cc b/server/src/debug.cc index fa83a80..e42baae 100644 --- a/server/src/debug.cc +++ b/server/src/debug.cc @@ -34,6 +34,17 @@ #include "log.h" +#include <pthread.h> + +#include "mutex.h" + +static Mutex mutex; + +static unsigned int gettid() +{ + return (unsigned int)pthread_self(); +} + static FILE *logfp = stderr; #define NELEM(x) (sizeof(x)/sizeof((x)[0])) @@ -69,11 +80,12 @@ int __debug(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, ...) { + MutexAutolock m(mutex); int ret = 0; if(__debug_enabled(cl, ch)) { if((unsigned)cl < NELEM(debug_class_str)) - ret += fprintf(logfp, "%s:%s:%s:%d ", - debug_class_str[(unsigned)cl], ch, func, line); + ret += fprintf(logfp, "%u %s:%s:%s:%d ", + gettid(), debug_class_str[(unsigned)cl], ch, func, line); if(fmt) { va_list va; va_start(va, fmt); @@ -91,11 +103,12 @@ int __debug_va(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, va_list va) { + MutexAutolock m(mutex); int ret = 0; if(__debug_enabled(cl, ch)) { if((unsigned)cl < NELEM(debug_class_str)) - ret += fprintf(logfp, "%s:%s:%s:%d ", - debug_class_str[(unsigned)cl], ch, func, line); + ret += fprintf(logfp, "%u %s:%s:%s:%d ", + gettid(), debug_class_str[(unsigned)cl], ch, func, line); if(fmt) ret += vfprintf(logfp, fmt, va); } @@ -110,6 +123,8 @@ int __debug_va(const char *func, const int line, int __log(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, ...) { + MutexAutolock m(mutex); + std::string logmsg; char str[8]; @@ -120,7 +135,8 @@ int __log(const char *func, const int line, const enum __debug_class cl, if((unsigned)cl < NELEM(debug_class_str)) if((unsigned)cl < NELEM(debug_class_str)) #ifdef WITH_DEBUG - ret = fprintf(logfp, "%s:%s:%s:%d ", debug_class_str[(unsigned)cl], ch, func, line); + ret = fprintf(logfp, "%u %s:%s:%s:%d ", + gettid(), debug_class_str[(unsigned)cl], ch, func, line); #endif sprintf(str, "%d", line); logmsg = std::string(debug_class_str[(unsigned)cl]) + ":" + ch + ":" + func + ":" + str; @@ -149,6 +165,8 @@ int __log(const char *func, const int line, const enum __debug_class cl, int __log_va(const char *func, const int line, const enum __debug_class cl, const char *ch, const char *fmt, va_list va) { + MutexAutolock m(mutex); + std::string logmsg; char str[8]; #ifdef WITH_DEBUG @@ -157,7 +175,8 @@ int __log_va(const char *func, const int line, const enum __debug_class cl, if(__debug_enabled(cl, ch)) { if((unsigned)cl < NELEM(debug_class_str)) #ifdef WITH_DEBUG - ret = fprintf(logfp, "%s:%s:%s:%d ", debug_class_str[(unsigned)cl], ch, func, line); + ret = fprintf(logfp, "%u %s:%s:%s:%d ", + gettid(), debug_class_str[(unsigned)cl], ch, func, line); #endif sprintf(str, "%d", line); logmsg = std::string(debug_class_str[(unsigned)cl]) + ":" + ch + ":" + func + ":" + str; @@ -181,9 +200,25 @@ int __log_va(const char *func, const int line, const enum __debug_class cl, void debug_init(FILE *fp) { + mutex.name = "debug"; + MutexAutolock m(mutex); logfp = fp; } +void debug_reinit(const char *logfile) +{ + MutexAutolock m(mutex); + + if(logfp != stderr) { + fclose(logfp); + logfp = fopen(logfile, "a"); + if(!logfp) { + fprintf(stderr, "Could not write to logfile: '%s'\n", logfile); + logfp = stderr; + } + } +} + /* * fmt := [set[,set]*]* @@ -193,6 +228,7 @@ void debug_init(FILE *fp) */ void debug_parse(const char *fmt) { + MutexAutolock m(mutex); char *s; char *next; char *opt; diff --git a/server/src/debug.h b/server/src/debug.h index a5f199d..838d83f 100644 --- a/server/src/debug.h +++ b/server/src/debug.h @@ -53,6 +53,7 @@ #endif/*HAVE_CONFIG*/ void debug_init(FILE *fp); +void debug_reinit(const char *file); void debug_parse(const char *fmt); enum __debug_class diff --git a/server/src/httpd.cc b/server/src/httpd.cc index 1c0575a..9349697 100644 --- a/server/src/httpd.cc +++ b/server/src/httpd.cc @@ -38,9 +38,15 @@ #include <string.h> #include <microhttpd.h> +#include <time.h> #include "debug.h" +// for inet_ntop +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + typedef struct { void *ptr; bool firstrun; @@ -68,10 +74,22 @@ static int request_handler(void *cls, unsigned int *data_size, void **con_cls) { + time_t now = time(NULL); + DEBUG(httpd, "Request time: %s", ctime(&now)); + + void *so; + so = MHD_get_connection_info(con, MHD_CONNECTION_INFO_CLIENT_ADDRESS)->client_addr; + + char peeraddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &((struct sockaddr_in*)so)->sin_addr, peeraddr, sizeof(peeraddr)); + DEBUG(httpd, "peer: %s\n", peeraddr); + DEBUG(httpd, "request_handler: cls(%p) con(%p) url(%s) method(%s)" " version(%s) *con_cls(%p)\n", cls, con, url, method, version, *con_cls); - DEBUG(httpd, "request_handler: *data_size(%u) data:[%s]\n", *data_size, data); + std::string datastr; datastr.append(data, *data_size); + DEBUG(httpd, "request_handler: *data_size(%u) data:[%s]\n", + *data_size, datastr.c_str()); int ret = MHD_YES; diff --git a/server/src/journal.cc b/server/src/journal.cc index fc4203c..04b1459 100644 --- a/server/src/journal.cc +++ b/server/src/journal.cc @@ -32,7 +32,8 @@ Journal::Journal() {} void Journal::addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ) + std::string resume, Template *templ, + LUAOnCommit *oncommit) { size_t index = 0; std::vector< Macro >::iterator i = templ->macros.begin(); @@ -70,16 +71,17 @@ void Journal::addEntry(Transaction &transaction, Commit &commit, } #endif - addEntry(resume, commit.macro, transaction.user, index); + addEntry(resume, commit.macro, transaction.user, index, oncommit); } void Journal::addEntry(std::string resume, std::string macro, - std::string user, int index) + std::string user, int index, LUAOnCommit *oncommit) { DEBUG(journal, "Add: %p %s %s - %s\n", this, macro.c_str(), user.c_str(), resume.c_str()); ResumeEntry re; + re.oncommit = oncommit; re.resume = resume; re.macro = macro; re.user = user; @@ -148,6 +150,22 @@ std::string Journal::patientID() return _patientid; } +void Journal::runOnCommitScripts() throw(LUAScript::Exception) +{ + std::map< int, ResumeEntry >::iterator i = entrylist.begin(); + while(i != entrylist.end()) { + if(i->second.oncommit != NULL) { + try { + i->second.oncommit->run(); + } catch(LUAScript::Exception &e) { + throw e; + } + } + i++; + } +} + + #ifdef TEST_JOURNAL //deps: debug.cc log.cc journal_uploadserver.cc journal_commit.cc //cflags: -I.. diff --git a/server/src/journal.h b/server/src/journal.h index 573f252..d098756 100644 --- a/server/src/journal.h +++ b/server/src/journal.h @@ -33,22 +33,31 @@ #include "transaction.h" #include "template.h" +#include "luaoncommit.h" class SessionSerialiser; class Journal { friend class SessionSerialiser; public: + class Exception { + public: + Exception(std::string m) : msg(m) {} + std::string msg; + }; + Journal(); virtual ~Journal() {} void addEntry(Transaction &transaction, Commit &commit, - std::string resume, Template *templ); + std::string resume, Template *templ, LUAOnCommit *oncommit); void addEntry(std::string resume, std::string macro, - std::string user, int index); + std::string user, int index, LUAOnCommit *oncommit); + + void runOnCommitScripts() throw(LUAScript::Exception); - virtual void commit() = 0; + virtual void commit() throw(Exception) = 0; std::string getEntry(std::string macro); void removeEntry(std::string macro); @@ -65,6 +74,7 @@ protected: std::string resume; std::string macro; std::string user; + LUAOnCommit *oncommit; bool dirty; }; diff --git a/server/src/journal_commit.cc b/server/src/journal_commit.cc index 0c1a93d..d765ebb 100644 --- a/server/src/journal_commit.cc +++ b/server/src/journal_commit.cc @@ -132,6 +132,7 @@ int journal_commit(const char *cpr, const char *user, // send body if(sock != -1 && write(sock, resume.c_str(), resume.size()) != (ssize_t)resume.size()) { ERR_LOG(journal, "write did not write all the bytes in the buffer.\n"); + return -1; } DEBUG(journal, "%s\n", buf); diff --git a/server/src/journal_uploadserver.cc b/server/src/journal_uploadserver.cc index a1299ec..eac6cd5 100644 --- a/server/src/journal_uploadserver.cc +++ b/server/src/journal_uploadserver.cc @@ -163,7 +163,10 @@ JournalUploadServer::JournalUploadServer(std::string host, } void JournalUploadServer::commit() + throw(Journal::Exception) { + int ret = 0; + #ifdef USE_MULTIPLE_USERS std::string resume; std::string olduser; @@ -177,9 +180,12 @@ void JournalUploadServer::commit() } if(i->second.user != olduser && olduser != "" && resume != "") { - journal_commit(patientID().c_str(), olduser.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), olduser.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); + + if(ret == -1) throw Journal::Exception("Journal Commit error."); + // FIXME - UGLY HACK: Avoid upload server spooling in the wrong order. usleep(200000); resume = ""; @@ -195,9 +201,9 @@ void JournalUploadServer::commit() if(resume == "") return; - journal_commit(patientID().c_str(), olduser.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), olduser.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); #else std::string resume; std::string user; @@ -222,10 +228,12 @@ void JournalUploadServer::commit() if(resume == "") return; // Connect to praxisuploadserver and commit all resumes in one bulk. - journal_commit(patientID().c_str(), user.c_str(), - host.c_str(), port, - resume.c_str(), resume.size()); + ret = journal_commit(patientID().c_str(), user.c_str(), + host.c_str(), port, + resume.c_str(), resume.size()); #endif/*USE_MULTIPLE_USERS*/ + + if(ret == -1) throw Journal::Exception("Journal Commit error."); } diff --git a/server/src/journal_uploadserver.h b/server/src/journal_uploadserver.h index 2393709..3e2fab3 100644 --- a/server/src/journal_uploadserver.h +++ b/server/src/journal_uploadserver.h @@ -33,7 +33,7 @@ class JournalUploadServer : public Journal { public: JournalUploadServer(std::string host, unsigned short int port); - void commit(); + void commit() throw(Journal::Exception); private: std::string host; diff --git a/server/src/luaoncommit.cc b/server/src/luaoncommit.cc new file mode 100644 index 0000000..8e96066 --- /dev/null +++ b/server/src/luaoncommit.cc @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luaoncommit.cc + * + * Thu Jan 12 08:38:02 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "luaoncommit.h" + +#include <lua.hpp> +#include <lauxlib.h> + +#include "luautil.h" +#include "luapraxisd.h" + +#include "debug.h" + +#include <stdio.h> + +LUAOnCommit::LUAOnCommit(Transaction &t, Commit &c) : LUAScript() +{ + setEnv(LUAScript::ENV_PATIENTID, t.patientid); + setEnv(LUAScript::ENV_TEMPLATE, c.templ); + setEnv(LUAScript::ENV_MACRO, c.macro); + setEnv(LUAScript::ENV_USER, t.user); + + std::map<std::string, std::string>::iterator i = c.fields.begin(); + while(i != c.fields.end()) { + addValue(i->first, i->second); + i++; + } +} + +#ifdef TEST_LUAONCOMMIT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUAONCOMMIT*/ diff --git a/server/src/resumeparser.h b/server/src/luaoncommit.h index 381e7c6..8db75f8 100644 --- a/server/src/resumeparser.h +++ b/server/src/luaoncommit.h @@ -1,9 +1,10 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ /*************************************************************************** - * resumeparser.h + * luaoncommit.h * - * Mon Oct 1 11:17:35 CEST 2007 - * Copyright 2007 Bent Bisballe Nyeng + * Thu Jan 12 08:38:02 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ @@ -24,13 +25,21 @@ * along with Pracro; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ -#ifndef __PRACRO_RESUMEPARSER_H__ -#define __PRACRO_RESUMEPARSER_H__ +#ifndef __PRACRO_LUAONCOMMIT_H__ +#define __PRACRO_LUAONCOMMIT_H__ + +#include "luascript.h" -#include <string> #include "transaction.h" -#include "template.h" +#include <string> + +class LUAOnCommit : public LUAScript { +public: + LUAOnCommit() {} + LUAOnCommit(Transaction &transaction, Commit &commit); + virtual ~LUAOnCommit() {} -std::string resume_parser(Macro ¯o, Commit &commit); + const char *name() { return "lua on commit"; } +}; -#endif/*__PRACRO_RESUMEPARSER_H__*/ +#endif/*__PRACRO_LUAONCOMMIT_H__*/ diff --git a/server/src/luapraxisd.cc b/server/src/luapraxisd.cc index 04a83db..da34d32 100644 --- a/server/src/luapraxisd.cc +++ b/server/src/luapraxisd.cc @@ -37,11 +37,14 @@ #define luaL_checkbool(L, i) \ (lua_isboolean(L,i) ? lua_toboolean(L,i) : luaL_checkint(L,i)) +#define error(L, m) \ + ERR(luapraxisd, "%s\n", m); lua_pushstring(L, m); lua_error(L); + typedef struct px_userdata { Praxisd *px; } px_userdata; -static int px_addcave(lua_State *L) +int px_addcave(lua_State *L) { px_userdata *pxu; pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); @@ -49,64 +52,377 @@ static int px_addcave(lua_State *L) const char *cpr = luaL_checkstring(L, 2); const char *cave = luaL_checkstring(L, 3); + const char *txt = luaL_checkstring(L, 4); std::string sogeord; std::string sogetxt; - std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(""); - std::vector<Praxisd::cave_t>::iterator i = cavelist.begin(); - while(i != cavelist.end()) { - Praxisd::cave_t &c = *i; - if(strcasecmp(cave, c.cave.c_str()) == 0) { - pxu->px->add_sogeord(cpr, c.sogenr, ""); - return 0; + try { + std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(""); + std::vector<Praxisd::cave_t>::iterator i = cavelist.begin(); + while(i != cavelist.end()) { + Praxisd::cave_t &c = *i; + if(strcasecmp(cave, c.cave.c_str()) == 0) { + pxu->px->add_sogeord(cpr, c.sogenr, txt); + return 0; + } + i++; } - i++; + + std::string text = cave; + text += " "; + text += txt; + pxu->px->add_sogeord(cpr, "CA0003", text.c_str()); // CA0003 == 'ANDET' + } catch(const char *msg) { + error(L, msg); } - pxu->px->add_sogeord(cpr, "CA0003", cave); // CA0003 == 'ANDET' + return 0; +} + +int px_addbehandling(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); + + const char *cpr = luaL_checkstring(L, 2); + const char *behandling = luaL_checkstring(L, 3); + const char *text = luaL_checkstring(L, 4); + + std::string sogeord; + std::string sogetxt; + + try { + std::vector<Praxisd::behandling_t> bhlst = + pxu->px->diverse_get_behandling(""); + std::vector<Praxisd::behandling_t>::iterator i = bhlst.begin(); + while(i != bhlst.end()) { + Praxisd::behandling_t &b = *i; + if(strcasecmp(behandling, b.kode.c_str()) == 0) { + pxu->px->add_sogeord(cpr, b.sogenr, text); + return 0; + } + i++; + } + } catch(const char *msg) { + error(L, msg); + } return 0; } -static int px_getcave(lua_State *L) +int px_adddiagnose(lua_State *L) { px_userdata *pxu; pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); luaL_argcheck(L, pxu, 1, "Praxisd expected"); const char *cpr = luaL_checkstring(L, 2); + const char *diagnose = luaL_checkstring(L, 3); + const char *text = luaL_checkstring(L, 4); - std::vector<Praxisd::cave_t> cavelist; + std::string sogeord; + std::string sogetxt; + + try { + std::vector<Praxisd::diagnose_t> dialst = + pxu->px->diverse_get_diagnose(""); + std::vector<Praxisd::diagnose_t>::iterator i = dialst.begin(); + while(i != dialst.end()) { + Praxisd::diagnose_t &d = *i; + if(strcasecmp(diagnose, d.kode.c_str()) == 0) { + pxu->px->add_sogeord(cpr, d.sogenr, text); + return 0; + } + i++; + } + } catch(const char *msg) { + error(L, msg); + } + + return 0; +} + +class cavedata_t : public Praxisd::cave_t { +public: + std::string sogetxt; + std::string sogedato; +}; +int px_getcave(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); + + const char *cpr = luaL_checkstring(L, 2); + + std::vector<cavedata_t> cavelist; + + try { + Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); + std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); + while(i != pat.sogeord.end()) { + Praxisd::sogeord_t &s = *i; + if(s.sogenr[0] == 'C') { + std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); + std::vector<Praxisd::cave_t> cl = pxu->px->diverse_get_cave(csogenr); + if(cl.size() == 1) { + cavedata_t cdata; + cdata.cave = cl[0].cave; + cdata.bemaerkning1 = cl[0].bemaerkning1; + cdata.bemaerkning2 = cl[0].bemaerkning2; + cdata.bemaerkning3 = cl[0].bemaerkning3; + cdata.sogetxt = s.sogetxt; + cdata.sogedato = s.sogedato; + cavelist.push_back(cdata); + } + } + + i++; + } + } catch(const char *msg) { + error(L, msg); + } + + int num = cavelist.size(); + int sz = 6; // 4 fields in cave_t + 1 sogetxt and 1 sogedato + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, cavelist[i - 1].cave.c_str()); + if(j == 2) lua_pushstring(L, cavelist[i - 1].bemaerkning1.c_str()); + if(j == 3) lua_pushstring(L, cavelist[i - 1].bemaerkning2.c_str()); + if(j == 4) lua_pushstring(L, cavelist[i - 1].bemaerkning3.c_str()); + if(j == 5) lua_pushstring(L, cavelist[i - 1].sogetxt.c_str()); + if(j == 6) lua_pushstring(L, cavelist[i - 1].sogedato.c_str()); + lua_rawseti(L, top, j); + } + + lua_rawseti(L, toptop, i); + } + + return 1; +} + +class behandlingdata_t : public Praxisd::behandling_t { +public: + std::string sogetxt; + std::string sogedato; +}; +int px_getbehandling(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); + + const char *cpr = luaL_checkstring(L, 2); + + std::vector<behandlingdata_t> behandlinglist; + + try { + Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); + std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); + while(i != pat.sogeord.end()) { + Praxisd::sogeord_t &s = *i; + if(s.sogenr[0] == 'B') { + std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); + std::vector<Praxisd::behandling_t> cl = pxu->px->diverse_get_behandling(csogenr); + if(cl.size() == 1) { + behandlingdata_t cdata; + cdata.kode = cl[0].kode; + cdata.behandling = cl[0].behandling; + cdata.bemaerkning = cl[0].bemaerkning; + cdata.udregning = cl[0].udregning; + cdata.sogetxt = s.sogetxt; + cdata.sogedato = s.sogedato; + behandlinglist.push_back(cdata); + } + } + + i++; + } + } catch(const char *msg) { + error(L, msg); + } + + int num = behandlinglist.size(); + int sz = 6; // 4 fields in behandling_t + 1 sogetxt and 1 sogedato + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, behandlinglist[i - 1].kode.c_str()); + if(j == 2) lua_pushstring(L, behandlinglist[i - 1].behandling.c_str()); + if(j == 3) lua_pushstring(L, behandlinglist[i - 1].bemaerkning.c_str()); + if(j == 4) lua_pushstring(L, behandlinglist[i - 1].udregning.c_str()); + if(j == 5) lua_pushstring(L, behandlinglist[i - 1].sogetxt.c_str()); + if(j == 6) lua_pushstring(L, behandlinglist[i - 1].sogedato.c_str()); + lua_rawseti(L, top, j); + } + + lua_rawseti(L, toptop, i); + } + + return 1; +} + +class diagnosedata_t : public Praxisd::diagnose_t { +public: + std::string sogetxt; + std::string sogedato; +}; +int px_getdiagnose(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); - Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); - std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); - while(i != pat.sogeord.end()) { - Praxisd::sogeord_t &s = *i; - if(s.sogenr[0] == 'C') { - std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); - std::vector<Praxisd::cave_t> cl = pxu->px->diverse_get_cave(csogenr); - if(cl.size() == 1) { - if(cl[0].cave == "ANDET") cl[0].cave = s.sogetxt; - cavelist.push_back(cl[0]); + const char *cpr = luaL_checkstring(L, 2); + + std::vector<diagnosedata_t> diagnoselist; + + try { + Praxisd::patient_t pat = pxu->px->patient_get_by_cpr(cpr); + std::vector<Praxisd::sogeord_t>::iterator i = pat.sogeord.begin(); + while(i != pat.sogeord.end()) { + Praxisd::sogeord_t &s = *i; + if(s.sogenr[0] == 'D') { + std::string csogenr = s.sogenr.substr(1, s.sogenr.length() - 1); + std::vector<Praxisd::diagnose_t> cl = pxu->px->diverse_get_diagnose(csogenr); + if(cl.size() == 1) { + diagnosedata_t cdata; + cdata.kode = cl[0].kode; + cdata.diagnose = cl[0].diagnose; + cdata.bemaerkning = cl[0].bemaerkning; + cdata.sogetxt = s.sogetxt; + cdata.sogedato = s.sogedato; + diagnoselist.push_back(cdata); + } } + + i++; + } + } catch(const char *msg) { + error(L, msg); + } + + int num = diagnoselist.size(); + int sz = 5; // 3 fields in diagnose_t + 1 sogetxt and 1 sogedato + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, diagnoselist[i - 1].kode.c_str()); + if(j == 2) lua_pushstring(L, diagnoselist[i - 1].diagnose.c_str()); + if(j == 3) lua_pushstring(L, diagnoselist[i - 1].bemaerkning.c_str()); + if(j == 4) lua_pushstring(L, diagnoselist[i - 1].sogetxt.c_str()); + if(j == 5) lua_pushstring(L, diagnoselist[i - 1].sogedato.c_str()); + lua_rawseti(L, top, j); + } + + lua_rawseti(L, toptop, i); + } + + return 1; +} + +int px_cavelist(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); + + const char *sogenr = luaL_checkstring(L, 2); + + std::vector<Praxisd::cave_t> cavelist; + try { + cavelist = pxu->px->diverse_get_cave(sogenr); + } catch(const char *msg) { + error(L, msg); + } + + + int num = cavelist.size(); + int sz = 4; // 4 fields in cave_t + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, cavelist[i - 1].cave.c_str()); + if(j == 2) lua_pushstring(L, cavelist[i - 1].bemaerkning1.c_str()); + if(j == 3) lua_pushstring(L, cavelist[i - 1].bemaerkning2.c_str()); + if(j == 4) lua_pushstring(L, cavelist[i - 1].bemaerkning3.c_str()); + lua_rawseti(L, top, j); } - i++; + lua_rawseti(L, toptop, i); + } + + return 1; +} + +int px_behandlinglist(lua_State *L) +{ + px_userdata *pxu; + pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); + luaL_argcheck(L, pxu, 1, "Praxisd expected"); + + const char *sogenr = luaL_checkstring(L, 2); + + std::vector<Praxisd::behandling_t> behandlinglist; + try { + behandlinglist = pxu->px->diverse_get_behandling(sogenr); + } catch(const char *msg) { + error(L, msg); } - lua_createtable(L, 0, cavelist.size()); - int top = lua_gettop(L); + int num = behandlinglist.size(); + int sz = 4; // 4 fields in behandling_t + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, behandlinglist[i - 1].kode.c_str()); + if(j == 2) lua_pushstring(L, behandlinglist[i - 1].behandling.c_str()); + if(j == 3) lua_pushstring(L, behandlinglist[i - 1].bemaerkning.c_str()); + if(j == 4) lua_pushstring(L, behandlinglist[i - 1].udregning.c_str()); + lua_rawseti(L, top, j); + } - for(size_t i = 0; i < cavelist.size(); i++) { - lua_pushstring(L, cavelist[i].cave.c_str()); - lua_rawseti(L, top, i); + lua_rawseti(L, toptop, i); } return 1; } -static int px_cavelist(lua_State *L) +int px_diagnoselist(lua_State *L) { px_userdata *pxu; pxu = (px_userdata *)luaL_checkudata(L, 1, "Praxisd"); @@ -114,20 +430,37 @@ static int px_cavelist(lua_State *L) const char *sogenr = luaL_checkstring(L, 2); - std::vector<Praxisd::cave_t> cavelist = pxu->px->diverse_get_cave(sogenr); + std::vector<Praxisd::diagnose_t> diagnoselist; + try { + diagnoselist = pxu->px->diverse_get_diagnose(sogenr); + } catch(const char *msg) { + error(L, msg); + } - lua_createtable(L, 0, cavelist.size()); - int top = lua_gettop(L); + int num = diagnoselist.size(); + int sz = 3; // 3 fields in diagnose_t + + lua_createtable(L, num, 0); + int toptop = lua_gettop(L); + + for(int i = 1; i <= num; i++) { + lua_createtable(L, 0, sz); + int top = lua_gettop(L); + + for(int j = 1; j <= sz; j++) { + if(j == 1) lua_pushstring(L, diagnoselist[i - 1].kode.c_str()); + if(j == 2) lua_pushstring(L, diagnoselist[i - 1].diagnose.c_str()); + if(j == 3) lua_pushstring(L, diagnoselist[i - 1].bemaerkning.c_str()); + lua_rawseti(L, top, j); + } - for(size_t i = 0; i < cavelist.size(); i++) { - lua_pushstring(L, cavelist[i].cave.c_str()); - lua_rawseti(L, top, i); + lua_rawseti(L, toptop, i); } return 1; } -static int px_new(lua_State *L) +int px_new(lua_State *L) { const char *host = luaL_checkstring(L, 1); int port = (int)luaL_checknumber(L, 2); @@ -138,12 +471,16 @@ static int px_new(lua_State *L) luaL_getmetatable(L, "Praxisd"); lua_setmetatable(L, -2); - pxu->px = new Praxisd(host, port); + try { + pxu->px = new Praxisd(host, port); + } catch(const char *msg) { + error(L, msg); + } return 1; } -static int px_gc(lua_State *L) +int px_gc(lua_State *L) { px_userdata *pxu; @@ -155,19 +492,6 @@ static int px_gc(lua_State *L) return 0; } -static const struct luaL_Reg px_meths[] = { - {"__gc" ,px_gc}, - {"cavelist", px_cavelist}, - {"getcave", px_getcave}, - {"addcave", px_addcave}, - {NULL, NULL} -}; - -static const struct luaL_reg px_funcs[] = { - {"new", px_new}, - {NULL, NULL} -}; - void register_praxisd(lua_State *L) { luaL_newmetatable(L, "Praxisd"); @@ -179,7 +503,7 @@ void register_praxisd(lua_State *L) } #ifdef TEST_LUAPRAXISD -//deps: praxisd.cc debug.cc saxparser.cc log.cc +//deps: praxisd.cc debug.cc saxparser.cc log.cc mutex.cc //cflags: -I.. $(LUA_CFLAGS) $(CURL_CFLAGS) $(EXPAT_CFLAGS) //libs: $(LUA_LIBS) $(CURL_LIBS) $(EXPAT_LIBS) #include "test.h" @@ -198,29 +522,36 @@ register_praxisd(L); ///// std::string program = "px = Praxisd.new('localhost', 10000)\n" - "cl = px:cavelist('')\n" - "for i=1,#cl do\n" - " print(cl[i])\n" - "end\n" + "--cl = px:cavelist('')\n" + "--for i=1,#cl do\n" + "-- print(cl[i])\n" + "--end\n" "\n" - "--pcl = px:addcave('1505050505', 'LUMIGAN')\n" + "pcl = px:addcave('1505050505', 'AZOPT', 'test')\n" "\n" - "pcl = px:addcave('1505050505', os.date('%H%M%S'))\n" + "--pcl = px:addcave('1505050505', os.date('%H%M%S'))\n" "\n" - "pcl = px:getcave('1505050505')\n" - "for i=1,#pcl do\n" - " print('p: ' .. pcl[i])\n" - "end\n" + "--pcl = px:getbehandling('1505050505')\n" + "--print('#pcl: ' .. #pcl)\n" + "--for i=1,#pcl do\n" + "-- pc = pcl[i]\n" + "-- print('#pc: ' .. #pc)\n" + "-- for j=1,#pc do\n" + "-- print(' pc: ' .. j .. ': ' .. pc[j])\n" + "-- end\n" + "--end\n" ; //int top = lua_gettop(L); if(luaL_loadbuffer(L, program.c_str(), program.size(), "luapraxisd test")) { ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); + printf("loadbuffer error: %s", lua_tostring(L, lua_gettop(L))); } if(lua_pcall(L, 0, LUA_MULTRET, 0)) { ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); + printf("pcall error: %s", lua_tostring(L, lua_gettop(L))); } /* if(top != lua_gettop(L) - 1) { diff --git a/server/src/luapraxisd.h b/server/src/luapraxisd.h index 2ab9ab5..82445e1 100644 --- a/server/src/luapraxisd.h +++ b/server/src/luapraxisd.h @@ -30,6 +30,221 @@ #include <lua.hpp> +/*** + * Praxisd Class + * @class Praxisd + * @serverside + * @clientside + * The praxisd class connects and handles communication with the praxisd + * daemon process. + */ + +/*** + * @method object Praxisd.new(string host, int port) + * Create a new Praxisd connection object. + * @param host The hostname to connect to. + * @param port The port number to connect to. + * @return The newly created communication object. + * @example Create a new praxisd object: + * px = Praxisd.new('localhost', 10000) + */ +int px_new(lua_State *L); + +/*** + * @method stringlist-list praxisd:cavelist() + * Get server cave list. + * To retrieve cavelist for a patient see @see praxisd:getcave(). + * @return The complete list of known (possible) cave from the server. + * The returned stringlist are indexed as follows: 1: cave - 2: bemaerkning1 - + * 3: bemaerkning2 - 4: bemaerkning3 + * @example Create a new praxisd object, get and print cavelist: + * px = Praxisd.new('localhost', 10000) + * lst = px:cavelist() + * for i=0,#lst do + * print('cave: ' .. lst[i][1]) + * print('bemaerkning1: ' .. lst[i][2]) + * print('bemaerkning2: ' .. lst[i][3]) + * print('bemaerkning3: ' .. lst[i][4]) + * end + */ +int px_cavelist(lua_State *L); + +/*** + * @method stringlist-list praxisd:behandlinglist() + * Get server behandling list. + * To retrieve behandlinglist for a patient see @see praxisd:getbehandling(). + * @return The complete list of known (possible) behandling from the server. + * The returned stringlist are indexed as follows: 1: kode - 2: behandling - + * 3: bemaerkning - 4: udregning. + * @example Create a new praxisd object, get and print behandlinglist: + * px = Praxisd.new('localhost', 10000) + * lst = px:behandlinglist() + * for i=0,#lst do + * print('kode: ' .. lst[i][1]) + * print('behandling: ' .. lst[i][2]) + * print('bemaerkning: ' .. lst[i][3]) + * print('udregning: ' .. lst[i][4]) + * end + */ +int px_behandlinglist(lua_State *L); + +/*** + * @method stringlist-list praxisd:diagnoselist() + * Get server diagnose list. + * To retrieve diagnoselist for a patient see @see praxisd:getdiagnose(). + * @return The complete list of known (possible) diagnose from the server. + * The returned stringlist are indexed as follows: 1: kode - 2: diagnose - + * 3: bemaerkning. + * @example Create a new praxisd object, get and print diagnoselist: + * px = Praxisd.new('localhost', 10000) + * lst = px:diagnoselist() + * for i=0,#lst do + * print('kode: ' .. lst[i][1]) + * print('diagnose: ' .. lst[i][2]) + * print('bemaerkning: ' .. lst[i][3]) + * end + */ +int px_diagnoselist(lua_State *L); + +/*** + * @method stringlist-list praxisd:getcave(string patientid) + * Get cave list from a patient. + * To retrieve cavelist from the server see @see praxisd:cavelist(). To add cave + * to a patient see @see praxisd:addcave(). + * @param patientid A string containing the patientid. + * @return The list cave registered with the patient. + * The returned stringlist are indexed as follows: 1: cave - 2: bemaerkning1 - + * 3: bemaerkning2 - 4: bemaerkning3 - 5: sogetxt - 6: sogedato. + * @example Create a new praxisd object, get and print cavelist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getcave('1234567890') + * for i=0,#lst do + * print('cave: ' .. lst[i][1]) + * print('bemaerkning1: ' .. lst[i][2]) + * print('bemaerkning2: ' .. lst[i][3]) + * print('bemaerkning3: ' .. lst[i][4]) + * print('sogetxt: ' .. lst[i][5]) + * print('sogedato: ' .. lst[i][6]) + * end + */ +int px_getcave(lua_State *L); + +/*** + * @method stringlist-list praxisd:getbehandling(string patientid) + * Get behandling list from a patient. + * To retrieve behandlinglist from the server see @see praxisd:behandlinglist(). To add behandling + * to a patient see @see praxisd:addbehandling(). + * @param patientid A string containing the patientid. + * @return The list behandling registered with the patient. + * The returned stringlist are indexed as follows: 1: kode - 2: behandling - + * 3: bemaerkning - 4: udregning - 5: sogetxt - 6: sogedato. + * @example Create a new praxisd object, get and print behandlinglist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getbehandling('1234567890') + * for i=0,#lst do + * print('kode: ' .. lst[i][1]) + * print('behandling: ' .. lst[i][2]) + * print('bemaerkning: ' .. lst[i][3]) + * print('udregning: ' .. lst[i][4]) + * print('sogetxt: ' .. lst[i][5]) + * print('sogedato: ' .. lst[i][6]) + * end + */ +int px_getbehandling(lua_State *L); + +/*** + * @method stringlist-list praxisd:getdiagnose(string patientid) + * Get diagnose list from a patient. + * To retrieve diagnoselist from the server see @see praxisd:diagnoselist(). To add diagnose + * to a patient see @see praxisd:adddiagnose(). + * @param patientid A string containing the patientid. + * @return The list diagnose registered with the patient. + * The returned stringlist are indexed as follows: 1: kode - 2: diagnose - + * 3: bemaerkning - 4: sogetxt - 5: sogedato. + * @example Create a new praxisd object, get and print diagnoselist: + * px = Praxisd.new('localhost', 10000) + * lst = px:getdiagnose('1234567890') + * for i=0,#lst do + * print('kode: ' .. lst[i][1]) + * print('diagnose: ' .. lst[i][2]) + * print('bemaerkning: ' .. lst[i][3]) + * print('sogetxt: ' .. lst[i][4]) + * print('sogedato: ' .. lst[i][5]) + * end + */ +int px_getdiagnose(lua_State *L); + +/*** + * @method nil praxisd:addcave(string patientid, string cave, string text) + * Add a cave entry to a patient. To retrieve list of cave from a patient + * see @see praxisd:getcave(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The cave string. + * @param text A text to store with the cave entry. NOTE: This is not shown in + * PCPraxis! The string mat be up to 6 characters long. If longer it will be + * trunkated. + * @example Create a new praxisd object and add a cave entry: + * px = Praxisd.new('localhost', 10000) + * px:addcave('1234567890', 'AZOPT', '') + */ +int px_addcave(lua_State *L); + +/*** + * @method nil praxisd:addbehandling(string patientid, string behandling, string text) + * Add a behandling to a patient. To retrieve list of behandling from a patient + * see @see praxisd:getbehandling(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The behandling code. + * @param text A text to store with the behandling code. The string mat be up to 6 + * characters long. If longer it will be trunkated. + * @example Create a new praxisd object and add a behandling: + * px = Praxisd.new('localhost', 10000) + * px:addbehandling('1234567890', 'B0001', 'o.sin') + */ +int px_addbehandling(lua_State *L); + +/*** + * @method nil praxisd:adddiagnose(string patientid, string diagnose, string text) + * Add a diagnose to a patient. To retrieve list of diagnose from a patient + * see @see praxisd:getdiagnose(). + * NOTE: This function is only available on the server. + * @param patientid A string containing the patientid. + * @param diagnose The diagnose code. + * @param text A text to store with the diagnose code. The string mat be up to 6 + * characters long. If longer it will be trunkated. + * @example Create a new praxisd object and add a diagnose: + * px = Praxisd.new('localhost', 10000) + * px:adddiagnose('1234567890', 'C0001', 'o.dxt') + */ +int px_adddiagnose(lua_State *L); + +/*** + * @method nil __gc() + * Garbage collector. Closes connection and frees all allocated memory. + */ +int px_gc(lua_State *L); + void register_praxisd(lua_State *L); +const struct luaL_Reg px_meths[] = { + {"__gc", px_gc}, + {"cavelist", px_cavelist}, + {"behandliglist", px_behandlinglist}, + {"diagnoselist", px_diagnoselist}, + {"getcave", px_getcave}, + {"getbehandling", px_getbehandling}, + {"getdiagnose", px_getdiagnose}, + {"addcave", px_addcave}, + {"addbehandling", px_addbehandling}, + {"adddiagnose", px_adddiagnose}, + {NULL, NULL} +}; + +const struct luaL_reg px_funcs[] = { + {"new", px_new}, + {NULL, NULL} +}; + #endif/*__PRACRO_LUAPRAXISD_H__*/ diff --git a/server/src/luaquerymapper.cc b/server/src/luaquerymapper.cc index 27dc21f..56ea1e9 100644 --- a/server/src/luaquerymapper.cc +++ b/server/src/luaquerymapper.cc @@ -109,9 +109,53 @@ LUAQueryMapper::~LUAQueryMapper() if(L) lua_close(L); } +static QueryResult splitgroups(QueryResult r) +{ + QueryResult result; + + result.timestamp = r.timestamp; + result.source = r.source; + + std::map< std::string, QueryResult >::iterator gi = r.groups.begin(); + while(gi != r.groups.end()) { + QueryResult child = splitgroups(gi->second); + result.groups[gi->first] = child; + gi++; + } + + std::map< std::string, std::string >::iterator vi = r.values.begin(); + while(vi != r.values.end()) { + std::string name = vi->first; + + // Also insert in table with name containing '.'. + if(name.find(".") != std::string::npos) result.values[name] = vi->second; + + QueryResult *ncurrent = &result; + while(name.find(".") != std::string::npos) { + DEBUG(splitgroups, "value name: %s\n", name.c_str()); + QueryResult grp; + grp.timestamp = ncurrent->timestamp; + grp.source = ncurrent->source; + std::string grpname = name.substr(0, name.find(".")); + ncurrent->groups[grpname] = grp; + ncurrent = &(ncurrent->groups[grpname]); + name = name.substr(name.find(".") + 1); + } + ncurrent->values[name] = vi->second; + + vi++; + } + + return result; +} + + void LUAQueryMapper::addQueryResult(QueryResult &result) throw(Exception) { - loadResult(L, result); + // Check for '.' in names and further split up into groups. + QueryResult splitted = splitgroups(result); + + loadResult(L, splitted); clean_top = lua_gettop(L); } diff --git a/server/src/luaresume.cc b/server/src/luaresume.cc index d3597ad..1db4fb3 100644 --- a/server/src/luaresume.cc +++ b/server/src/luaresume.cc @@ -26,122 +26,27 @@ */ #include "luaresume.h" +#include <lua.hpp> +#include <lauxlib.h> + #include "luautil.h" -#include "luapraxisd.h" #include "debug.h" #include <stdio.h> -#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" - -static int _value(lua_State *L) -{ - Pracro::checkParameters(L, - Pracro::T_STRING, - Pracro::T_END); - - std::string name = lua_tostring(L, lua_gettop(L)); - - lua_getglobal(L, GLOBAL_POINTER); - LUAResume *lua = (LUAResume*)lua_touserdata(L, lua_gettop(L)); - - if(!lua) { - lua_pushstring(L, "No LUA pointer!"); - lua_error(L); - return 1; - } - - std::string value = lua->value(name); - lua_pushstring(L, value.c_str()); - - return 1; -} - -LUAResume::LUAResume(Commit &c) - : commit(c) -{ - L = luaL_newstate(); - if(L == NULL) { - ERR(luaresume, "Could not create LUA state.\n"); - return; - } - - luaL_openlibs(L); - - lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance - lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. - - lua_register(L, "value", _value); - - register_praxisd(L); -} - -LUAResume::~LUAResume() -{ - lua_close(L); -} - -std::string LUAResume::value(std::string name) +LUAResume::LUAResume(Transaction &t, Commit &c) : LUAScript() { - if(commit.fields.find(name) == commit.fields.end()) { - ERR(luaresume, "LUAResume: No such field '%s'\n", name.c_str()); - return ""; + setEnv(LUAScript::ENV_PATIENTID, t.patientid); + setEnv(LUAScript::ENV_TEMPLATE, c.templ); + setEnv(LUAScript::ENV_MACRO, c.macro); + setEnv(LUAScript::ENV_USER, t.user); + + std::map<std::string, std::string>::iterator i = c.fields.begin(); + while(i != c.fields.end()) { + addValue(i->first, i->second); + i++; } - - return commit.fields[name]; -} - -std::string LUAResume::run(std::string program) -{ - if(L == NULL) { - ERR(luaresume, "LUA state not initialized!"); - return false; - } - - DEBUG(luaresume, "Running %s\n", program.c_str()); - - /* - lua_pushstring(L, value.toStdString().c_str()); - lua_setglobal(L, "value"); - - lua_pushstring(L, name.toStdString().c_str()); - lua_setglobal(L, "name"); - */ - - int top = lua_gettop(L); - - if(luaL_loadbuffer(L, program.c_str(), program.size(), - "lua resume generator")) { - ERR(luaresume, "loadbufer: %s\n", lua_tostring(L, lua_gettop(L))); - return false; - } - - // Run the loaded code - if(lua_pcall(L, 0, LUA_MULTRET, 0)) { - ERR(luaresume, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); - return false; - } - - if(top != lua_gettop(L) - 1) { - ERR(luaresume, "Program did not return a single value.\n"); - return false; - } - - if(lua_isstring(L, lua_gettop(L)) == false) { - ERR(luaresume, "Program did not return a string value.\n"); - return false; - } - - std::string res = lua_tostring(L, lua_gettop(L)); - lua_pop(L, 1); - - return res; -} - -void LUAResume::error(std::string message) -{ - ERR(luaresume, "LUA ERROR: %s\n", message.c_str()); } #ifdef TEST_LUARESUME diff --git a/server/src/luaresume.h b/server/src/luaresume.h index 1132f26..47a7652 100644 --- a/server/src/luaresume.h +++ b/server/src/luaresume.h @@ -27,26 +27,16 @@ #ifndef __PRACRO_LUARESUME_H__ #define __PRACRO_LUARESUME_H__ -#include <lua.hpp> -#include <lauxlib.h> +#include "luascript.h" #include "transaction.h" #include <string> -class LUAResume { +class LUAResume : public LUAScript { public: - LUAResume(Commit &commit); - ~LUAResume(); - - std::string run(std::string program); + LUAResume(Transaction &transaction, Commit &commit); - std::string value(std::string name); - - void error(std::string message); - -private: - lua_State *L; - Commit &commit; + const char *name() { return "lua resume generator"; } }; #endif/*__PRACRO_LUARESUME_H__*/ diff --git a/server/src/luascript.cc b/server/src/luascript.cc new file mode 100644 index 0000000..6ba52a9 --- /dev/null +++ b/server/src/luascript.cc @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luascript.cc + * + * Tue Jan 10 14:43:39 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "luascript.h" + +#include "configuration.h" +#include "debug.h" + +#include "luautil.h" +#include "luapraxisd.h" + +#define GLOBAL_POINTER "_pracroGlobalLUAObjectPointerThisShouldBeANameThatIsNotAccidentallyOverwritten" + +static int _debug(lua_State *L) +{ + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_END); + + std::string msg = lua_tostring(L, lua_gettop(L)); + + DEBUG(luascript, "%s\n", msg.c_str()); + + return 0; +} + +static int _value(lua_State *L) +{ + Pracro::checkParameters(L, + Pracro::T_STRING, + Pracro::T_END); + + std::string name = lua_tostring(L, lua_gettop(L)); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + if(lua->hasValue(name)) { + lua_pushstring(L, lua->value(name).c_str()); + } else { + lua_pushnil(L); + } + + return 1; +} + +static int _patientid(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_PATIENTID).c_str()); + + return 1; +} + +static int _template(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_TEMPLATE).c_str()); + + return 1; +} + +static int _macro(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_MACRO).c_str()); + + return 1; +} + +static int _user(lua_State *L) +{ + Pracro::checkParameters(L, Pracro::T_END); + + lua_getglobal(L, GLOBAL_POINTER); + LUAScript *lua = (LUAScript*)lua_touserdata(L, lua_gettop(L)); + + if(!lua) { + lua_pushstring(L, "No LUA pointer!"); + lua_error(L); + return 1; + } + + lua_pushstring(L, lua->env(LUAScript::ENV_USER).c_str()); + + return 1; +} + +LUAScript::LUAScript() +{ + L = NULL; +} + +void LUAScript::init() + throw(Exception) +{ + if(L) return; + + L = luaL_newstate(); + if(L == NULL) { + ERR(luascript, "Could not create LUA state.\n"); + throw Exception("Could not create LUA state."); + } + + luaL_openlibs(L); + + lua_pushlightuserdata(L, this); // Push the pointer to 'this' instance + lua_setglobal(L, GLOBAL_POINTER); // Assign it to a global lua var. + + lua_register(L, "value", _value); + lua_register(L, "patientid", _patientid); + lua_register(L, "template", _template); + lua_register(L, "macro", _macro); + lua_register(L, "user", _user); + lua_register(L, "debug", _debug); + + register_praxisd(L); +} + +void LUAScript::addFile(std::string src) +{ + std::string file = + Conf::xml_basedir + "/include/" + src; + FILE *fp = fopen(file.c_str(), "r"); + if(fp) { + char buf[64]; + size_t sz; + std::string inc; + while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { + inc.append(buf, sz); + } + fclose(fp); + addCode(inc, file); + } +} + +void LUAScript::addCode(std::string c, std::string name) +{ + scripts.push_back(std::make_pair(c, name)); +} + +void LUAScript::addValue(std::string name, const std::string &value) +{ + values[name] = value; +} + +void LUAScript::addScripts(std::vector< Script > &scripts) +{ + std::vector< Script >::iterator spi = scripts.begin(); + while(spi != scripts.end()) { + if(spi->attributes.find("src") != spi->attributes.end()) { + std::string src = spi->attributes["src"]; + addFile(src); + } else { + addCode(spi->code); + } + spi++; + } +} + +void LUAScript::run() + throw(Exception) +{ + try { + init(); + } catch(Exception &e) { + throw Exception(e.msg); + } + + if(L == NULL) { + ERR(luascript, "LUA state not initialized!"); + return; + } + + top = lua_gettop(L); + + std::vector<std::pair<std::string, std::string> >::iterator i = + scripts.begin(); + while(i != scripts.end()) { + std::string program = i->first; + std::string codename = name(); + if(i->second != "") codename += ": " + i->second; + + DEBUG(luascript, "Running %s: %s\n", codename.c_str(), program.c_str()); + + if(luaL_loadbuffer(L, program.c_str(), program.size(), codename.c_str())) { + ERR(luascript, "loadbuffer: %s\n", lua_tostring(L, lua_gettop(L))); + throw Exception(lua_tostring(L, lua_gettop(L))); + } + + // Run the loaded code + if(lua_pcall(L, 0, LUA_MULTRET, 0)) { + ERR(luascript, "pcall: %s\n" , lua_tostring(L, lua_gettop(L))); + throw Exception(lua_tostring(L, lua_gettop(L))); + } + + i++; + } +} + +std::string LUAScript::resultString() throw(Exception) +{ + if(top != lua_gettop(L) - 1) { + ERR(luascript, "Program did not return a single value.\n"); + throw Exception("Program did not return a single value."); + } + + if(lua_isstring(L, lua_gettop(L)) == false) { + ERR(luascript, "Program did not return a string value.\n"); + throw Exception("Program did not return a string value."); + } + + std::string res = lua_tostring(L, lua_gettop(L)); + lua_pop(L, 1); + + return res; +} + +bool LUAScript::hasValue(std::string name) +{ + return values.find(name) != values.end(); +} + +std::string LUAScript::value(std::string name) +{ + if(values.find(name) != values.end()) return values[name]; + return ""; +} + +std::string LUAScript::env(LUAScript::env_t id) +{ + if(_env.find(id) == _env.end()) return ""; + return _env[id]; +} + +void LUAScript::setEnv(LUAScript::env_t id, std::string value) +{ + _env[id] = value; +} + +#ifdef TEST_LUASCRIPT +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_LUASCRIPT*/ diff --git a/server/src/luascript.h b/server/src/luascript.h new file mode 100644 index 0000000..11fb77d --- /dev/null +++ b/server/src/luascript.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * luascript.h + * + * Tue Jan 10 14:43:39 CET 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_LUASCRIPT_H__ +#define __PRACRO_LUASCRIPT_H__ + +#include <lua.hpp> +#include <lauxlib.h> + +#include <string> +#include <map> +#include <vector> + +#include "template.h" + +class LUAScript { + friend class SessionSerialiser; + friend class SessionParser; +public: + typedef enum { + ENV_PATIENTID, + ENV_TEMPLATE, + ENV_MACRO, + ENV_USER + } env_t; + + class Exception { + public: + Exception(std::string m) : msg(m) {} + std::string msg; + }; + + LUAScript(); + virtual ~LUAScript() {} + + virtual const char *name() { return ""; } + + void init() throw(Exception); + + void addFile(std::string file); + void addCode(std::string code, std::string codename = ""); + void addScripts(std::vector< Script > &scripts); + + void addValue(std::string name, const std::string &value); + + void run() throw(Exception); + + bool hasValue(std::string name); + std::string value(std::string value); + + std::string env(env_t id); + void setEnv(env_t id, std::string value); + + std::string resultString() throw(Exception); + +protected: + lua_State *L; + + std::map<env_t, std::string> _env; + +private: + std::vector<std::pair<std::string, std::string> > scripts; + std::map<std::string, std::string> values; + + int top; +}; + + +#endif/*__PRACRO_LUASCRIPT_H__*/ diff --git a/server/src/macroparser.cc b/server/src/macroparser.cc index d1604b2..be781aa 100644 --- a/server/src/macroparser.cc +++ b/server/src/macroparser.cc @@ -114,7 +114,12 @@ void MacroParser::characterData(std::string &data) if(state == SCRIPT) { assert(current_script); // No script present! - current_script->attributes["code"].append(data); + current_script->code.append(data); + } + + if(state == COMMIT_SCRIPT) { + assert(current_commit_script); // No script present! + current_commit_script->code.append(data); } } @@ -147,6 +152,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr) return; } + // Enable oncommit parsing + if(name == "oncommit") { + if(state != MACRO) error("oncommit found outside macro."); + state = COMMIT_SCRIPTS; + + m->resume.attributes = attr; + + assert(m); // No macro is currently available, cannot create resume! + + return; + } + // Enable Query parsing if(name == "queries") { if(state != MACRO) error("queries found outside macro."); @@ -232,8 +249,18 @@ void MacroParser::startTag(std::string name, attributes_t &attr) current_resume_script = &(m->resume_scripts.back()); } break; + case COMMIT_SCRIPTS: + { + state = COMMIT_SCRIPT; + + Script s; + s.attributes = attr; + m->commit_scripts.push_back(s); + current_commit_script = &(m->commit_scripts.back()); + } + break; default: - error("<script> tag found outside <scripts> or <resume> tags."); + error("<script> tag found outside <scripts>, <commitscripts> or <resume> tags."); break; } return; @@ -290,6 +317,7 @@ void MacroParser::endTag(std::string name) state = UNDEFINED; } if(name == "resume") state = MACRO; + if(name == "oncommit") state = MACRO; if(name == "queries") state = MACRO; if(name == "query") state = QUERIES; if(name == "maps") state = MACRO; @@ -310,6 +338,11 @@ void MacroParser::endTag(std::string name) state = RESUME; break; + case COMMIT_SCRIPT: + current_commit_script = NULL; + state = COMMIT_SCRIPTS; + break; + default: // tag mismatch? break; diff --git a/server/src/macroparser.h b/server/src/macroparser.h index 71ef911..ab6fda5 100644 --- a/server/src/macroparser.h +++ b/server/src/macroparser.h @@ -42,7 +42,9 @@ class MacroParser : public SAXParser { MAP, WIDGETS, SCRIPTS, - SCRIPT + SCRIPT, + COMMIT_SCRIPTS, + COMMIT_SCRIPT } ParserState; public: @@ -76,6 +78,7 @@ private: Map *current_map; Script *current_script; Script *current_resume_script; + Script *current_commit_script; std::vector< Widget* > widgetstack; // Error callback function. diff --git a/server/src/mutex.cc b/server/src/mutex.cc index ec0d0e8..9805591 100644 --- a/server/src/mutex.cc +++ b/server/src/mutex.cc @@ -27,6 +27,11 @@ */ #include "mutex.h" +#include "debug.h" + +#define MUTEX_DBG(x) +//#define MUTEX_DBG(x) x + Mutex::Mutex() { pthread_mutex_init (&mutex, NULL); @@ -39,16 +44,23 @@ Mutex::~Mutex() bool Mutex::trylock() { - return pthread_mutex_trylock(&mutex) == 0; + bool ret = pthread_mutex_trylock(&mutex) == 0; + + if(ret) MUTEX_DBG(printf("trylock succeeded on %s\n", name.c_str())); + else MUTEX_DBG(printf("trylock failed on %s\n", name.c_str())); + + return ret; } void Mutex::lock() { pthread_mutex_lock(&mutex); + MUTEX_DBG(printf("lock on %s\n", name.c_str())); } void Mutex::unlock() { + MUTEX_DBG(printf("unlock on %s\n", name.c_str())); pthread_mutex_unlock(&mutex); } diff --git a/server/src/mutex.h b/server/src/mutex.h index cf052ad..87f18e1 100644 --- a/server/src/mutex.h +++ b/server/src/mutex.h @@ -30,6 +30,8 @@ #include <pthread.h> +#include <string> + class Mutex { public: Mutex(); @@ -39,6 +41,8 @@ public: void lock(); void unlock(); + std::string name; + private: pthread_mutex_t mutex; }; diff --git a/server/src/pracrod.cc b/server/src/pracrod.cc index 81acfbc..b68cf07 100644 --- a/server/src/pracrod.cc +++ b/server/src/pracrod.cc @@ -69,7 +69,7 @@ static const char version_str[] = ; static const char copyright_str[] = -"Copyright (C) 2006-2009 Bent Bisballe Nyeng - Aasimon.org.\n" +"Copyright (C) 2006-2012 Bent Bisballe Nyeng - Aasimon.org.\n" "This is free software. You may redistribute copies of it under the terms of\n" "the GNU General Public License <http://www.gnu.org/licenses/gpl.html>.\n" "There is NO WARRANTY, to the extent permitted by law.\n" @@ -116,18 +116,12 @@ void childwait(int) } static FILE *logfp = stderr; -static std::string logfile; +std::string logfile; +bool logfile_reload = false; void reload(int) { - if(logfp != stderr) { - fclose(logfp); - logfp = fopen(logfile.c_str(), "a"); - if(!logfp) { - fprintf(stderr, "Could not write to logfile: '%s'\n", optarg); - logfp = stderr; - } - } + logfile_reload = true; } class PracroDaemon : public Daemon { diff --git a/server/src/pracrodaopgsql.cc b/server/src/pracrodaopgsql.cc index f9a773f..db0a9aa 100644 --- a/server/src/pracrodaopgsql.cc +++ b/server/src/pracrodaopgsql.cc @@ -228,6 +228,7 @@ void PracroDAOPgsql::commitTransaction(std::string sessionid, } +//#define NEW Values PracroDAOPgsql::getLatestValues(std::string sessionid, std::string patientid, Macro *macro, @@ -261,6 +262,70 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid, } pqxx::work W(*conn); + +#ifdef NEW + + // Do not search for nothing... + if(fieldnames.size() == 0) return values; + + std::string names; + std::vector< std::string >::iterator fni = fieldnames.begin(); + while(fni != fieldnames.end()) { + if(names != "") names += " OR "; + names += "name='" + W.esc(*fni) + "'"; + fni++; + } + + std::string macros; + if(macro) { + macros += " AND macro='" + macro->name + "'"; + if(macro->version != "") + macros += " AND t.version='" + macro->version + "'"; + } + + uncom = uncom; + query = "SELECT uid FROM commits WHERE patientid='"+patientid+"' AND" + " \"timestamp\">="+soldest.str()+" AND" + " (status='committed' OR uid="+sessionid+");"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result commits = W.exec(query); + pqxx::result::const_iterator ci = commits.begin(); + while(ci != commits.end()) { + std::string cid = (*ci)[0].c_str(); + + query = "SELECT uid, \"timestamp\" FROM transactions WHERE cid="+cid+ + macros+";"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result transactions = W.exec(query); + pqxx::result::const_iterator ti = transactions.begin(); + while(ti != transactions.end()) { + std::string tid = (*ti)[0].c_str(); + time_t timestamp = atol((*ti)[1].c_str()); + + query = "SELECT name, value FROM fields WHERE" + " transaction="+tid+" AND ("+ names +");"; + DEBUG(sql, "Query: %s\n", query.c_str()); + pqxx::result fields = W.exec(query); + DEBUG(sql, "Results: %lu\n", fields.size()); + pqxx::result::const_iterator fi = fields.begin(); + while(fi != fields.end()) { + std::string name = (*fi)[0].c_str(); + if(values.find(name) == values.end() || + values[name].timestamp <= timestamp) { + Value v; + v.value = (*fi)[1].c_str(); + v.timestamp = timestamp; + values[name] = v; + } + fi++; + } + + ti++; + } + + ci++; + } +#else/*NEW*/ std::string namecond; if(fieldnames.size() > 0) { @@ -318,6 +383,7 @@ Values PracroDAOPgsql::getLatestValues(std::string sessionid, values[(*ri)[0].c_str()] = v; ri++; } +#endif/*NEW*/ } catch (std::exception &e) { ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str()); } diff --git a/server/src/praxisd.cc b/server/src/praxisd.cc index 3ccdf0d..c198051 100644 --- a/server/src/praxisd.cc +++ b/server/src/praxisd.cc @@ -29,6 +29,13 @@ #include "saxparser.h" +static const char* str2err(std::string msg) +{ + static char errbuf[1024]; + sprintf(errbuf, "%s", msg.c_str()); + return errbuf; +} + static std::string strtime(bool with_sec = true) { std::string ret; @@ -59,12 +66,13 @@ static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp) } Praxisd::Praxisd(std::string h, int port) + throw(const char*) { ch = curl_easy_init(); host = h; curl_easy_setopt(ch, CURLOPT_PORT, port); - curl_easy_setopt(ch, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(ch, CURLOPT_FAILONERROR, 0L/*1L*/); curl_easy_setopt(ch, CURLOPT_TIMEOUT, 150L); curl_easy_setopt(ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT, 15L); @@ -82,6 +90,7 @@ Praxisd::~Praxisd() // Get Journal By CPR std::string Praxisd::journal_get_by_cpr(std::string cpr) + throw(const char*) { std::string journal; @@ -93,16 +102,20 @@ std::string Praxisd::journal_get_by_cpr(std::string cpr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); } - time_t time; - errornum = curl_easy_getinfo(ch, CURLINFO_FILETIME, &time); + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(journal); + } return journal; } time_t Praxisd::journal_last_changed(std::string cpr) + throw(const char*) { std::string journal; @@ -114,17 +127,27 @@ time_t Praxisd::journal_last_changed(std::string cpr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); } time_t time; errornum = curl_easy_getinfo(ch, CURLINFO_FILETIME, &time); + if(errornum != CURLE_OK) { + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(journal); + } return time; } void Praxisd::journal_add(std::string cpr, std::string entry) + throw(const char*) { std::string xml; xml += "<praxisd version=\"1.0\">\n"; @@ -150,12 +173,19 @@ void Praxisd::journal_add(std::string cpr, std::string entry) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch: %d %s\n", errornum, reply.c_str()); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(reply); } } void Praxisd::add_sogeord(std::string cpr, std::string sogeord, std::string sogetxt) + throw(const char*) { std::string datestr = strtime(false); @@ -187,7 +217,13 @@ void Praxisd::add_sogeord(std::string cpr, std::string sogeord, CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch: %d %s\n", errornum, reply.c_str()); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(reply); } } @@ -259,6 +295,7 @@ private: }; Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr) + throw(const char*) { patient_t p; @@ -272,10 +309,14 @@ Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); } - // printf("Get Patient: %d %s\n", xml.length(), xml.c_str()); fflush(stdout); + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(xml); + } PatientParser parser(p); parser.parse(xml.data(), xml.length()); @@ -284,6 +325,7 @@ Praxisd::patient_t Praxisd::patient_get_by_cpr(std::string cpr) } std::string Praxisd::get_sogenr(std::string sogenr) + throw(const char*) { std::string xml; @@ -296,7 +338,13 @@ std::string Praxisd::get_sogenr(std::string sogenr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(xml); } return xml; @@ -331,6 +379,7 @@ private: }; std::vector<Praxisd::adresse_t> Praxisd::diverse_get_adresse(std::string sogenr) + throw(const char*) { std::vector<Praxisd::adresse_t> lst; std::string xml = get_sogenr("A"+sogenr); @@ -365,6 +414,7 @@ private: std::vector<Praxisd::behandling_t> Praxisd::diverse_get_behandling(std::string sogenr) + throw(const char*) { std::vector<Praxisd::behandling_t> lst; std::string xml = get_sogenr("B"+sogenr); @@ -398,6 +448,7 @@ private: }; std::vector<Praxisd::cave_t> Praxisd::diverse_get_cave(std::string sogenr) + throw(const char*) { std::vector<Praxisd::cave_t> lst; std::string xml = get_sogenr("C"+sogenr); @@ -431,6 +482,7 @@ private: std::vector<Praxisd::diagnose_t> Praxisd::diverse_get_diagnose(std::string sogenr) + throw(const char*) { std::vector<Praxisd::diagnose_t> lst; std::string xml = get_sogenr("D"+sogenr); @@ -464,6 +516,7 @@ private: }; std::vector<Praxisd::frase_t> Praxisd::diverse_get_frase(std::string sogenr) + throw(const char*) { std::vector<Praxisd::frase_t> lst; std::string xml = get_sogenr("F"+sogenr); @@ -495,6 +548,7 @@ private: }; std::vector<Praxisd::grafik_t> Praxisd::diverse_get_grafik(std::string sogenr) + throw(const char*) { std::vector<Praxisd::grafik_t> lst; std::string xml = get_sogenr("G"+sogenr); @@ -530,6 +584,7 @@ private: std::vector<Praxisd::indholdstof_t> Praxisd::diverse_get_indholdstof(std::string sogenr) + throw(const char*) { std::vector<Praxisd::indholdstof_t> lst; std::string xml = get_sogenr("I"+sogenr); @@ -563,6 +618,7 @@ private: std::vector<Praxisd::klage_t> Praxisd::diverse_get_klage(std::string sogenr) + throw(const char*) { std::vector<Praxisd::klage_t> lst; std::string xml = get_sogenr("K"+sogenr); @@ -595,6 +651,7 @@ private: std::vector<Praxisd::oversigt_t> Praxisd::diverse_get_oversigt(std::string sogenr) + throw(const char*) { std::vector<Praxisd::oversigt_t> lst; std::string xml = get_sogenr("O"+sogenr); @@ -632,6 +689,7 @@ private: std::vector<Praxisd::postnummer_t> Praxisd::diverse_get_postnummer(std::string sogenr) + throw(const char*) { std::vector<Praxisd::postnummer_t> lst; std::string xml = get_sogenr("P"+sogenr); @@ -666,6 +724,7 @@ private: std::vector<Praxisd::type_t> Praxisd::diverse_get_type(std::string sogenr) + throw(const char*) { std::vector<Praxisd::type_t> lst; std::string xml = get_sogenr("T"+sogenr); @@ -699,6 +758,7 @@ private: std::vector<Praxisd::undersoegelse_t> Praxisd::diverse_get_undersoegelse(std::string sogenr) + throw(const char*) { std::vector<Praxisd::undersoegelse_t> lst; std::string xml = get_sogenr("U"+sogenr); @@ -736,6 +796,7 @@ private: std::vector<Praxisd::ydelse_t> Praxisd::diverse_get_ydelse(std::string sogenr) + throw(const char*) { std::vector<Praxisd::ydelse_t> lst; std::string xml = get_sogenr("Y"+sogenr); @@ -755,7 +816,7 @@ public: if(name == "aftale") { Praxisd::aftale_t a; if(attr.find("date") != attr.end()) a.date = attr["date"]; - if(attr.find("calendar") != attr.end()) a.date = attr["calendar"]; + if(attr.find("calendar") != attr.end()) a.calendar = attr["calendar"]; div.push_back(a); } DODIVTAG(cpr); @@ -775,8 +836,9 @@ static std::string i2s(int i) { return buf; } std::vector<Praxisd::aftale_t> -Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month, - int day) +Praxisd::aftale_get_all_by_date_and_calendar(int cal, + int year, int month, int day) + throw(const char*) { std::vector<Praxisd::aftale_t> aft; @@ -792,7 +854,13 @@ Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month, CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(xml); } AftaleParser parser(aft); @@ -802,6 +870,7 @@ Praxisd::aftale_get_all_by_date_and_calendar(int cal, int year, int month, } std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr) + throw(const char*) { std::vector<Praxisd::aftale_t> aft; @@ -815,7 +884,13 @@ std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(xml); } AftaleParser parser(aft); @@ -825,6 +900,7 @@ std::vector<Praxisd::aftale_t> Praxisd::aftale_get_all_by_cpr(std::string cpr) } bool Praxisd::authenticate(std::string user, std::string pass) + throw(const char*) { std::string uri = host + "/praxisd/1.0/authenticate?user=" + user + "&pass=" + pass; @@ -835,11 +911,14 @@ bool Praxisd::authenticate(std::string user, std::string pass) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - return false; + throw curl_easy_strerror(errornum); } long code = 0; errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(errornum != CURLE_OK) { + throw curl_easy_strerror(errornum); + } if(code == 401) return false; if(code == 200) return true; @@ -876,6 +955,7 @@ private: }; std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr) + throw(const char*) { std::vector<Praxisd::dokmenu_t> dokmenu; std::string xml; @@ -888,7 +968,13 @@ std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr) CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(xml); } DokMenuParser parser(dokmenu); @@ -900,6 +986,7 @@ std::vector<Praxisd::dokmenu_t> Praxisd::dokmenu_get_all_by_cpr(std::string cpr) // Get Dokmenu by Name and CPR std::string Praxisd::dokmenu_get_by_cpr_and_name(std::string cpr, std::string name) + throw(const char*) { std::string data; @@ -912,7 +999,13 @@ std::string Praxisd::dokmenu_get_by_cpr_and_name(std::string cpr, CURLcode errornum = curl_easy_perform(ch); if(errornum != CURLE_OK) { - printf("Ouch %d\n", errornum); + throw curl_easy_strerror(errornum); + } + + long code = 0; + errornum = curl_easy_getinfo(ch, CURLINFO_RESPONSE_CODE, &code); + if(code != 200) { + throw str2err(data); } return data; diff --git a/server/src/praxisd.h b/server/src/praxisd.h index 84848b8..785cba1 100644 --- a/server/src/praxisd.h +++ b/server/src/praxisd.h @@ -37,13 +37,16 @@ class Praxisd { public: - Praxisd(std::string host, int port); + Praxisd(std::string host, int port) + throw(const char*); ~Praxisd(); - time_t journal_last_changed(std::string cpr); + time_t journal_last_changed(std::string cpr) + throw(const char*); // Get Journal By CPR - std::string journal_get_by_cpr(std::string cpr); + std::string journal_get_by_cpr(std::string cpr) + throw(const char*); // Get Patient By CPR typedef struct { @@ -89,7 +92,8 @@ public: std::string unknown251; std::string jtime; } patient_t; - patient_t patient_get_by_cpr(std::string cpr); + patient_t patient_get_by_cpr(std::string cpr) + throw(const char*); // Get Diverse From Sogenr typedef struct { @@ -102,7 +106,8 @@ public: std::string tlf; std::string fax; } adresse_t; - std::vector<adresse_t> diverse_get_adresse(std::string sogenr); + std::vector<adresse_t> diverse_get_adresse(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -111,7 +116,8 @@ public: std::string bemaerkning; std::string udregning; } behandling_t; - std::vector<behandling_t> diverse_get_behandling(std::string sogenr); + std::vector<behandling_t> diverse_get_behandling(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -120,7 +126,8 @@ public: std::string bemaerkning2; std::string bemaerkning3; } cave_t; - std::vector<cave_t> diverse_get_cave(std::string sogenr); + std::vector<cave_t> diverse_get_cave(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -128,7 +135,8 @@ public: std::string diagnose; std::string bemaerkning; } diagnose_t; - std::vector<diagnose_t> diverse_get_diagnose(std::string sogenr); + std::vector<diagnose_t> diverse_get_diagnose(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -137,14 +145,16 @@ public: std::string frase3; std::string frase4; } frase_t; - std::vector<frase_t> diverse_get_frase(std::string sogenr); + std::vector<frase_t> diverse_get_frase(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; std::string navn; std::string bemaerkning; } grafik_t; - std::vector<grafik_t> diverse_get_grafik(std::string sogenr); + std::vector<grafik_t> diverse_get_grafik(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -154,7 +164,8 @@ public: std::string form3; std::string form4; } indholdstof_t; - std::vector<indholdstof_t> diverse_get_indholdstof(std::string sogenr); + std::vector<indholdstof_t> diverse_get_indholdstof(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -162,14 +173,16 @@ public: std::string klage; std::string bemaerkning; } klage_t; - std::vector<klage_t> diverse_get_klage(std::string sogenr); + std::vector<klage_t> diverse_get_klage(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; std::string navn; std::string bemaerkning; } oversigt_t; - std::vector<oversigt_t> diverse_get_oversigt(std::string sogenr); + std::vector<oversigt_t> diverse_get_oversigt(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -181,7 +194,8 @@ public: std::string regionnavn; std::string kommunenavn; } postnummer_t; - std::vector<postnummer_t> diverse_get_postnummer(std::string sogenr); + std::vector<postnummer_t> diverse_get_postnummer(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -191,7 +205,8 @@ public: std::string bemaerkning3; std::string recept; } type_t; - std::vector<type_t> diverse_get_type(std::string sogenr); + std::vector<type_t> diverse_get_type(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -200,7 +215,8 @@ public: std::string bemaerkning; std::string udregning; } undersoegelse_t; - std::vector<undersoegelse_t> diverse_get_undersoegelse(std::string sogenr); + std::vector<undersoegelse_t> diverse_get_undersoegelse(std::string sogenr) + throw(const char*); typedef struct { std::string sogenr; @@ -213,7 +229,8 @@ public: std::string journal2; std::string moms; } ydelse_t; - std::vector<ydelse_t> diverse_get_ydelse(std::string sogenr); + std::vector<ydelse_t> diverse_get_ydelse(std::string sogenr) + throw(const char*); // Get Aftale All by Date and Calendar @@ -227,17 +244,21 @@ public: std::string cres; } aftale_t; std::vector<aftale_t> aftale_get_all_by_date_and_calendar(int cal, int year, - int month, int day); + int month, int day) + throw(const char*); // Get Aftale All by CPR - std::vector<aftale_t> aftale_get_all_by_cpr(std::string cpr); + std::vector<aftale_t> aftale_get_all_by_cpr(std::string cpr) + throw(const char*); // Authenticate - bool authenticate(std::string user, std::string pass); + bool authenticate(std::string user, std::string pass) + throw(const char*); #if 0 // Get Name by UserID - std::string user_get_name_by_id(std::string user); + std::string user_get_name_by_id(std::string user) + throw(const char*); #endif // Get All Dokmenu by CPR @@ -248,19 +269,23 @@ public: size_t filesize; std::string date; } dokmenu_t; - std::vector<dokmenu_t> dokmenu_get_all_by_cpr(std::string cpr); + std::vector<dokmenu_t> dokmenu_get_all_by_cpr(std::string cpr) + throw(const char*); // Get Dokmenu by Name and CPR - std::string dokmenu_get_by_cpr_and_name(std::string cpr, std::string name); + std::string dokmenu_get_by_cpr_and_name(std::string cpr, std::string name) + throw(const char*); // POST: // Add To Journal - void journal_add(std::string cpr, std::string entry); + void journal_add(std::string cpr, std::string entry) + throw(const char*); // Update Patient // Add Sogeord to Patient - void add_sogeord(std::string cpr, std::string sogeord, std::string sogetxt); + void add_sogeord(std::string cpr, std::string sogeord, std::string sogetxt) + throw(const char*); // Update Aftale // Add Aftale @@ -269,7 +294,8 @@ public: // Delete File from Dokmenu private: - std::string get_sogenr(std::string sogenr); + std::string get_sogenr(std::string sogenr) + throw(const char*); CURL *ch; std::string host; }; diff --git a/server/src/resumeparser.cc b/server/src/resumeparser.cc deleted file mode 100644 index 1c5335a..0000000 --- a/server/src/resumeparser.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/*************************************************************************** - * resumeparser.cc - * - * Mon Oct 1 11:17:35 CEST 2007 - * Copyright 2007 Bent Bisballe Nyeng - * deva@aasimon.org - ****************************************************************************/ - -/* - * This file is part of Pracro. - * - * Pracro is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Pracro is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Pracro; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ -#include "resumeparser.h" - -#include <string.h> - -#include "luaresume.h" -#include "configuration.h" - -std::string resume_parser(Macro ¯o, Commit &commit) -{ - LUAResume luaresume(commit); - - std::string code; - - std::vector< Script >::iterator spi = macro.resume_scripts.begin(); - while(spi != macro.resume_scripts.end()) { - if(spi->attributes.find("src") != spi->attributes.end()) { - std::string src = spi->attributes["src"]; - std::string file = - Conf::xml_basedir + "/include/" + src; - FILE *fp = fopen(file.c_str(), "r"); - if(fp) { - char buf[64]; - size_t sz; - std::string inc; - while((sz = fread(buf, 1, sizeof(buf), fp)) != 0) { - inc.append(buf, sz); - } - fclose(fp); - code += "\n-- BEGIN INCLUDE: '" + src + "'\n"; - code += inc; - code += "\n-- END INCLUDE: '" + src + "'\n"; - } - } else { - code += spi->code; - } - spi++; - } - - return luaresume.run(code); -} - -#ifdef TEST_RESUMEPARSER -//deps: luaresume.cc configuration.cc debug.cc log.cc luautil.cc -//cflags: -I.. $(LUA_CFLAGS) -//libs: $(LUA_LIBS) -#include <test.h> - -TEST_BEGIN; - -// TODO: Put some testcode here (see test.h for usable macros). -TEST_TRUE(false, "No tests yet!"); - -TEST_END; - -#endif/*TEST_RESUMEPARSER*/ diff --git a/server/src/saxparser.cc b/server/src/saxparser.cc index 95efffe..14f204c 100644 --- a/server/src/saxparser.cc +++ b/server/src/saxparser.cc @@ -130,6 +130,8 @@ bool SAXParser::parse(const char *data, size_t size) xml.append(data, size); DEBUG(sax, "parse %d bytes [%s]\n", size, xml.c_str()); + if(data == NULL || size == 0) return done; + bufferbytes = size; totalbytes += bufferbytes; diff --git a/server/src/server.cc b/server/src/server.cc index e4d6474..838da32 100644 --- a/server/src/server.cc +++ b/server/src/server.cc @@ -37,6 +37,9 @@ #include "client_connection.h" #include "admin_connection.h" +extern std::string logfile; +extern volatile bool logfile_reload; + class PracroHttpd : public Httpd { public: PracroHttpd() {} @@ -57,6 +60,12 @@ public: headers_t &getargs, headers_t &headers) { + if(logfile_reload) { + DEBUG(pracrod, "Reopen log file %s\n", logfile.c_str()); + debug_reinit(logfile.c_str()); + logfile_reload = false; + } + Connection *connection = NULL; if(headers.find("User-Agent") != headers.end() && @@ -73,7 +82,7 @@ public: { if(ptr) { Connection *connection = (Connection *)ptr; - connection->handle(data, data_size); + connection->data(data, data_size); } return true; } @@ -84,7 +93,7 @@ public: Connection *connection = (Connection *)ptr; // Flush and do commit/discards - connection->handle(NULL, 0); + if(!connection->handle()) return false; connection->getReply(reply); } diff --git a/server/src/session.cc b/server/src/session.cc index fcd138a..ea4d11c 100644 --- a/server/src/session.cc +++ b/server/src/session.cc @@ -55,6 +55,8 @@ Session::Session(Environment *e, patientid = pid; templ = t; + mutex.name = "session-" + sid; + DEBUG(session, "[%p] new Session(sessionid: '%s', patientid: '%s'," " template: '%s')\n", this, sid.c_str(), pid.c_str(), t.c_str()); @@ -82,10 +84,20 @@ std::string Session::id() void Session::lock() { mutex.lock(); + DEBUG(session, "lock() %p (%s)\n", this, sessionid.c_str()); +} + +bool Session::trylock() +{ + bool r = mutex.trylock(); + DEBUG(session, "trylock() %p (%s) == %s\n", + this, sessionid.c_str(), r?"true":"false"); + return r; } void Session::unlock() { + DEBUG(session, "unlock() %p (%s)\n", this, sessionid.c_str()); mutex.unlock(); } @@ -121,12 +133,21 @@ void Session::setIdle(bool idle) } } -void Session::commit() +void Session::commit() throw(LUAScript::Exception, Journal::Exception) { DEBUG(session, "[%p] commit(sessionid: '%s')\n", this, sessionid.c_str()); if(_journal != NULL) { - _journal->commit(); + try { + _journal->runOnCommitScripts(); + } catch(LUAScript::Exception &e) { + throw e; + } + try { + _journal->commit(); + } catch(Journal::Exception &e) { + throw e; + } delete _journal; _journal = NULL; } @@ -290,28 +311,76 @@ size_t Sessions::size() void Sessions::store() { + Session *session = NULL; + std::string sessionid; + do { + bool waitcont = false; + session = NULL; + { + MutexAutolock lock(mutex); + std::map<std::string, Session*>::iterator head = sessions.begin(); + if(head != sessions.end()) { + session = head->second; + sessionid = head->first; + if(session->trylock()) { + sessions.erase(sessionid); + } else { + waitcont = true; + } + } + } + + if(waitcont) { + usleep(200000); // sleep 200ms + continue; + } + + if(session != NULL) { + SessionSerialiser ser(env, Conf::session_path); + ser.save(session); + delete session; + } + } while(session != NULL); + + /* MutexAutolock lock(mutex); std::map<std::string, Session*>::iterator i = sessions.begin(); while(i != sessions.end()) { SessionSerialiser ser(env, Conf::session_path); - ser.save(i->second); - delete i->second; - sessions.erase(i); + Session *s = i->second; + s->lock(); + ser.save(s); + delete s; i++; } sessions.clear(); + */ } -std::vector<std::string> Sessions::activeSessions() +std::vector<Sessions::SessionInfo> Sessions::activeSessions() { MutexAutolock lock(mutex); - std::vector<std::string> act; + std::vector<SessionInfo> act; std::map<std::string, Session*>::iterator i = sessions.begin(); while(i != sessions.end()) { - act.push_back(i->first); + Session *s = i->second; + SessionInfo si; + si.id = i->first; + si.templ = "LOCKED"; + + if(s->trylock()) { + // si.user = "simpson"; + // si.course = s->course; + si.patientid = s->patientid; + si.templ = s->templ; + si.idle = s->idle(); + // si.ondisc = false; + s->unlock(); + } + act.push_back(si); i++; } @@ -326,6 +395,7 @@ SessionAutounlock::SessionAutounlock(Session **s) SessionAutounlock::~SessionAutounlock() { + DEBUG(session, "SessionAutounlock(%p)\n", *session); if(*session) (*session)->unlock(); } diff --git a/server/src/session.h b/server/src/session.h index 4d1ed12..1cb3041 100644 --- a/server/src/session.h +++ b/server/src/session.h @@ -37,6 +37,9 @@ #include "transaction.h" #include "template.h" +#include "luascript.h" +#include "journal.h" + class Environment; class Journal; @@ -50,9 +53,10 @@ public: std::string id(); void lock(); + bool trylock(); void unlock(); - void commit(); + void commit() throw(LUAScript::Exception, Journal::Exception); void nocommit(); void discard(); @@ -128,7 +132,17 @@ public: // // Admin methods // - std::vector<std::string> activeSessions(); + class SessionInfo { + public: + std::string id; + std::string patientid; + std::string user; + std::string course; + std::string templ; + bool idle; + bool ondisc; + }; + std::vector<SessionInfo> activeSessions(); private: std::map<std::string, Session *> sessions; diff --git a/server/src/sessionheaderparser.cc b/server/src/sessionheaderparser.cc new file mode 100644 index 0000000..c9a8e67 --- /dev/null +++ b/server/src/sessionheaderparser.cc @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionheaderparser.cc + * + * Thu Aug 9 09:06:32 CEST 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "sessionheaderparser.h" + +/* +<session timestamp="1234567890" + status="readonly"" + id="12345" + template="amd_forunders" + patientid="0000000000"> + ... +</session> +*/ + +#include <stdio.h> + +// For assert +#include <assert.h> + +// For open and friends +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +// For vprintf and friends +#include <stdarg.h> + +#include <errno.h> +#include <string.h> + +#include "debug.h" +#include "configuration.h" +#include "exception.h" + +void SessionHeaderParser::error(const char* fmt, ...) +{ + ERR_LOG(session, "Error in SessionHeaderParser: "); + + { + va_list argp; + va_start(argp, fmt); + ERR_LOG_VA(session, fmt, argp); + va_end(argp); + + fprintf(stderr, "\n"); + } + + { + char *p; + va_list argp; + va_start(argp, fmt); + if(vasprintf(&p, fmt, argp) != -1) { + throw Exception("Error in SessionHeaderParser: " + std::string(p)); + free(p); + } + va_end(argp); + } + +} + +SessionHeaderParser::SessionHeaderParser(std::string sessionfile) +{ + done = false; + + file = sessionfile; + + DEBUG(session, "Using session file: %s\n", sessionfile.c_str()); + + fd = open(sessionfile.c_str(), O_RDONLY); + if(fd == -1) error("Could not open file %s", sessionfile.c_str()); +} + +SessionHeaderParser::~SessionHeaderParser() +{ + if(fd != -1) close(fd); +} + +void SessionHeaderParser::startTag(std::string name, attributes_t &attr) +{ + if(done) return; + + if(name == "session") { + done = true; + if(attr.find("patientid") != attr.end()) { + header.patientid = attr["patientid"]; + } + + if(attr.find("template") != attr.end()) { + header.templ = attr["template"]; + } + + if(attr.find("id") != attr.end()) { + header.id = attr["id"]; + } + } else { + throw Exception("Missing root tag 'session' - found '" + name + "'"); + } +} + +int SessionHeaderParser::readData(char *data, size_t size) +{ + if(done) return 0; // If done is true we already found what we were looking + // for and can dismiss the rest of the document. + + if(fd == -1) { + ERR_LOG(session, "Invalid file descriptor.\n"); + return 0; + } + ssize_t r = read(fd, data, size); + if(r == -1) { + ERR_LOG(session, "Could not read...%s\n", strerror(errno)); + return 0; + } + return r; +} + +void SessionHeaderParser::parseError(const char *buf, size_t len, std::string error, int lineno) +{ + if(done) return; // If done is true we already found what we were looking + // for and can dismiss the rest of the document. + + ERR_LOG(session, "SessionHeaderParser[%s] error at line %d: %s\n", + file.c_str(), lineno, error.c_str()); + ERR_LOG(session, "\tBuffer %u bytes: [", len); + if(fwrite(buf, len, 1, stderr) != len) {} + ERR_LOG(session, "]\n"); + + char *slineno; + if(asprintf(&slineno, " at line %d\n", lineno) != -1) { + throw Exception(error + slineno); + free(slineno); + } +} + +SessionHeaderParser::Header SessionHeaderParser::getHeader() +{ + return header; +} + +#ifdef TEST_SESSIONHEADERPARSER +//Additional dependency files +//deps: +//Required cflags (autoconf vars may be used) +//cflags: +//Required link options (autoconf vars may be used) +//libs: +#include "test.h" + +TEST_BEGIN; + +// TODO: Put some testcode here (see test.h for usable macros). +TEST_TRUE(false, "No tests yet!"); + +TEST_END; + +#endif/*TEST_SESSIONHEADERPARSER*/ diff --git a/server/src/sessionheaderparser.h b/server/src/sessionheaderparser.h new file mode 100644 index 0000000..5f2d3cf --- /dev/null +++ b/server/src/sessionheaderparser.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * sessionheaderparser.h + * + * Thu Aug 9 09:06:32 CEST 2012 + * Copyright 2012 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pracro. + * + * Pracro is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pracro is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pracro; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PRACRO_SESSIONHEADERPARSER_H__ +#define __PRACRO_SESSIONHEADERPARSER_H__ + +#include "saxparser.h" + +/** + * Partial session parser. + * This class is used to parse only the first tag of a session xml file. + * The parser will run about 10 times faster than the one parsing the entire + * file (see the SessionParser class) and can be used to find a + * patientid/template match. + * This class inherits the SAXParser baseclass. + * Use the parse() method to run the parser, and collect the result via the + * getPatientID() and getTemplate() methods. + * If the file does not contain a session, or the file is not a valid xml file, + * an Exception is thrown. + */ +class SessionHeaderParser : public SAXParser { +public: + class Header { + public: + std::string patientid; + std::string templ; + std::string id; + }; + + /** + * Constructor. + * @param sessionfile A std::string containing the name of the file to parse. + */ + SessionHeaderParser(std::string sessionfile); + + /** + * Destructor. + */ + ~SessionHeaderParser(); + + /** + * Overloaded parser callback method. + */ + void startTag(std::string name, attributes_t &attr); + + /** + * Overloaded parser callback method. + */ + void parseError(const char *buf, size_t len, std::string error, int lineno); + + /** + * Get a pointer to the parsed macro. + * NOTE: The allocated memory for the macro is owned by the parser, and will be + * freed upon parser deletion. + * @return A pointer to the macro or NULL on error. + */ + Header getHeader(); + +protected: + /** + * Overloaded parser callback method. + */ + int readData(char *data, size_t size); + +private: + int fd; + + bool done; + + Header header; + + std::string file; + // Error callback function. + void error(const char* fmt, ...); +}; + +#endif/*__PRACRO_SESSIONHEADERPARSER_H__*/ diff --git a/server/src/sessionparser.cc b/server/src/sessionparser.cc index 6b3653e..8913e3c 100644 --- a/server/src/sessionparser.cc +++ b/server/src/sessionparser.cc @@ -37,6 +37,9 @@ SessionParser::SessionParser() totalbytes = 0; inresume = false; indatabase = false; + invalue = false; + inscript = false; + inenv = false; } SessionParser::~SessionParser() @@ -49,6 +52,26 @@ void SessionParser::characterData(std::string &data) entries[entries.size()-1].resume += data; } + if(inscript) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + std::pair<std::string, std::string> &val = + oncommit->scripts[oncommit->scripts.size() - 1]; + val.first += data; + } + + if(invalue) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->values[valuename] += data; + } + + if(inenv) { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->_env[envid] += data; + } + if(indatabase) { database += data; } @@ -80,12 +103,51 @@ void SessionParser::startTag(std::string name, attributes_t &attr) e.index = atoi(attr["index"].c_str()); e.macro = attr["macro"]; e.user = attr["user"]; + e.oncommit = NULL; entries.push_back(e); } if(name == "resume") { inresume = true; } + + if(name == "oncommit") { + Entry &e = entries[entries.size() - 1]; + if(e.oncommit != NULL) { + ERR(sessionparser, "Multiple oncommit tags in journal!\n"); + return; + } + e.oncommit = new LUAOnCommit(); + } + if(name == "envs") { } + + if(name == "env") { + if(attr["id"] == "ENV_PATIENTID") envid = LUAScript::ENV_PATIENTID; + else if(attr["id"] == "ENV_TEMPLATE") envid = LUAScript::ENV_TEMPLATE; + else if(attr["id"] == "ENV_MACRO") envid = LUAScript::ENV_MACRO; + else if(attr["id"] == "ENV_USER") envid = LUAScript::ENV_USER; + else { + // Unknown env id + return; + } + inenv = true; + } + + if(name == "values") { } + + if(name == "value") { + valuename = attr["name"]; + invalue = true; + } + + if(name == "scripts") {} + + if(name == "script") { + Entry &e = entries[entries.size() - 1]; + LUAOnCommit *oncommit = e.oncommit; + oncommit->addCode("", attr["name"]); + inscript = true; + } } void SessionParser::endTag(std::string name) @@ -96,6 +158,15 @@ void SessionParser::endTag(std::string name) if(name == "database") { indatabase = false; } + if(name == "env") { + inenv = false; + } + if(name == "value") { + invalue = false; + } + if(name == "script") { + inscript = false; + } } void SessionParser::parseError(const char *buf, size_t len, diff --git a/server/src/sessionparser.h b/server/src/sessionparser.h index df32b06..8734fbd 100644 --- a/server/src/sessionparser.h +++ b/server/src/sessionparser.h @@ -30,6 +30,8 @@ #include "saxparser.h" +#include "luaoncommit.h" + #include <string> #include <vector> @@ -50,10 +52,11 @@ public: std::string userid; std::string database; std::string dbtype; - + class Entry { public: int index; + LUAOnCommit *oncommit; std::string macro; std::string resume; std::string user; @@ -63,7 +66,12 @@ public: private: bool inresume; + bool inscript; + bool invalue; + bool inenv; + std::string valuename; bool indatabase; + LUAScript::env_t envid; }; #endif/*__PRACRO_SESSIONPARSER_H__*/ diff --git a/server/src/sessionserialiser.cc b/server/src/sessionserialiser.cc index 36d0a0d..6288ddd 100644 --- a/server/src/sessionserialiser.cc +++ b/server/src/sessionserialiser.cc @@ -34,6 +34,7 @@ #include "journal.h" #include "sessionparser.h" +#include "sessionheaderparser.h" #include "database.h" #include "xml_encode_decode.h" @@ -88,7 +89,8 @@ Session *SessionSerialiser::loadStr(const std::string &xml) j->setPatientID(XDEC(parser.patientid)); std::vector<SessionParser::Entry>::iterator i = parser.entries.begin(); while(i != parser.entries.end()) { - j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index); + j->addEntry(XDEC(i->resume), XDEC(i->macro), XDEC(i->user), i->index, + i->oncommit); i++; } @@ -122,6 +124,50 @@ std::string SessionSerialiser::saveStr(Session *session) " macro=\"" + XENC(i->second.macro) + "\"" " user=\"" + XENC(i->second.user) + "\">\n"; xml += " <resume>" + XENC(i->second.resume) + "</resume>\n"; + LUAOnCommit *oncommit = i->second.oncommit; + if(oncommit != NULL) { + xml += " <oncommit>\n"; + + xml += " <envs>\n"; + std::map<LUAScript::env_t, std::string>::iterator ei = + oncommit->_env.begin(); + while(ei != oncommit->_env.end()) { + std::string id; + switch(ei->first) { + case LUAScript::ENV_PATIENTID: id = "ENV_PATIENTID"; break; + case LUAScript::ENV_TEMPLATE: id = "ENV_TEMPLATE"; break; + case LUAScript::ENV_MACRO: id = "ENV_MACRO"; break; + case LUAScript::ENV_USER: id = "ENV_USER"; break; + } + + xml += " <env id=\"" + XENC(id) + "\">"+ + XENC(ei->second) + "</env>\n"; + ei++; + } + xml += " </envs>\n"; + + xml += " <values>\n"; + std::map<std::string, std::string>::iterator vi = + oncommit->values.begin(); + while(vi != oncommit->values.end()) { + xml += " <value name=\"" + XENC(vi->first) + "\">"+ + XENC(vi->second) + "</value>\n"; + vi++; + } + xml += " </values>\n"; + + xml += " <scripts>\n"; + std::vector<std::pair<std::string, std::string> >::iterator si = + oncommit->scripts.begin(); + while(si != oncommit->scripts.end()) { + xml += " <script name=\"" + XENC(si->second) + "\">"+ + XENC(si->first) + "</script>\n"; + si++; + } + xml += " </scripts>\n"; + + xml += " </oncommit>\n"; + } xml += " </entry>\n"; i++; @@ -170,6 +216,11 @@ void SessionSerialiser::save(Session *session) // write xml to file FILE *fp = fopen(filename.c_str(), "w"); + if(!fp) { + ERR(sessionserialiser, "Could not write session to file %s\n", + filename.c_str()); + return; + } fwrite(xml.data(), xml.size(), 1, fp); fclose(fp); } @@ -210,25 +261,34 @@ Session *SessionSerialiser::findFromTupple(const std::string &patientid, DEBUG(sessionserialiser, "Is xml file\n"); - // Load session file - FILE *fp = fopen(filename.c_str(), "r"); - std::string xml; - while(!feof(fp)) { - char str[64]; - memset(str, 0, sizeof(str)); - fread(str, sizeof(str) - 1, 1, fp); - xml += str; + SessionHeaderParser::Header header; + + SessionHeaderParser p(filename); + try { + p.parse(); + header = p.getHeader(); + } catch( ... ) { + continue; } - fclose(fp); - Session *session = loadStr(xml); + if(header.patientid == patientid && header.templ == templ) { + // Load session file + FILE *fp = fopen(filename.c_str(), "r"); + std::string xml; + while(!feof(fp)) { + char str[64]; + memset(str, 0, sizeof(str)); + fread(str, sizeof(str) - 1, 1, fp); + xml += str; + } + fclose(fp); + + Session *session = loadStr(xml); - DEBUG(sessionserialiser, "PatientID %s - Template %s\n", - session->patientid.c_str(), - session->templ.c_str()); + DEBUG(sessionserialiser, "PatientID %s - Template %s\n", + session->patientid.c_str(), + session->templ.c_str()); - if(session->patientid == patientid && - session->templ == templ) { closedir(dir); unlink(filename.c_str()); return session; @@ -241,6 +301,46 @@ Session *SessionSerialiser::findFromTupple(const std::string &patientid, return NULL; } +std::map<std::string, SessionHeaderParser::Header> +SessionSerialiser::sessionFiles() +{ + std::map<std::string, SessionHeaderParser::Header> list; + + DIR *dir = opendir(path.c_str()); + if(!dir) { + ERR(sessionserialiser, "Could not open directory: %s - %s\n", + path.c_str(), strerror(errno)); + return list; + } + + struct dirent *dirent; + while( (dirent = readdir(dir)) != NULL ) { + + std::string filename = path+"/"+dirent->d_name; + + if(isxmlfile(filename)) { + + SessionHeaderParser::Header header; + + SessionHeaderParser p(filename); + try { + p.parse(); + header = p.getHeader(); + } catch( ... ) { + continue; + } + + list[filename] = header; + } + } + + closedir(dir); + + return list; + +} + + #ifdef TEST_SESSIONSERIALISER //deps: session.cc journal.cc debug.cc configuration.cc mutex.cc journal_commit.cc sessionparser.cc saxparser.cc xml_encode_decode.cc database.cc pracrodaopgsql.cc pracrodaotest.cc pracrodao.cc journal_uploadserver.cc log.cc environment.cc semaphore.cc artefact.cc macrolist.cc templatelist.cc entitylist.cc inotify.cc versionstr.cc exception.cc macroheaderparser.cc templateheaderparser.cc //cflags: -I.. $(PTHREAD_CFLAGS) $(EXPAT_CFLAGS) $(PQXX_CFLAGS) -DWITHOUT_ARTEFACT diff --git a/server/src/sessionserialiser.h b/server/src/sessionserialiser.h index a85fc63..2c4e7ea 100644 --- a/server/src/sessionserialiser.h +++ b/server/src/sessionserialiser.h @@ -29,6 +29,9 @@ #define __PRACRO_SESSIONSERIALISER_H__ #include <string> +#include <map> + +#include "sessionheaderparser.h" #include "session.h" @@ -47,6 +50,8 @@ public: Session *load(const std::string &sessionid); void save(Session *session); + std::map<std::string, SessionHeaderParser::Header> sessionFiles(); + private: std::string path; Environment *env; diff --git a/server/src/template.h b/server/src/template.h index a069cff..853db3d 100644 --- a/server/src/template.h +++ b/server/src/template.h @@ -67,6 +67,7 @@ public: maps_t maps; std::vector< Script > scripts; std::vector< Script > resume_scripts; + std::vector< Script > commit_scripts; Widget widgets; Resume resume; @@ -84,6 +85,8 @@ public: class Template { public: + std::vector< Script > scripts; + std::vector< Macro > macros; std::string name; diff --git a/server/src/templateparser.cc b/server/src/templateparser.cc index b9c65f5..8fc3eff 100644 --- a/server/src/templateparser.cc +++ b/server/src/templateparser.cc @@ -81,6 +81,10 @@ TemplateParser::~TemplateParser() void TemplateParser::characterData(std::string &data) { + if(state == SCRIPT) { + assert(current_script); // No script present! + current_script->code.append(data); + } } void TemplateParser::startTag(std::string name, attributes_t &attr) @@ -127,6 +131,39 @@ void TemplateParser::startTag(std::string name, attributes_t &attr) return; } + // Enable script parsing + if(name == "scripts") { + if(state != TEMPLATE) error("scripts found outside template."); + state = SCRIPTS; + + assert(t); // No template is currently available, cannot create maps! + + return; + } + + // Create script + if(name == "script") { + + assert(t); // No template is currently available, cannot create script! + + switch(state) { + case SCRIPTS: + { + state = SCRIPT; + + Script s; + s.attributes = attr; + t->scripts.push_back(s); + current_script = &(t->scripts.back()); + } + break; + default: + error("<script> tag found outside <scripts> tag."); + break; + } + return; + } + error("Unknown/illegal tag: %s", name.c_str()); } @@ -137,6 +174,18 @@ void TemplateParser::endTag(std::string name) current_macro = NULL; state = TEMPLATE; } + if(name == "scripts") state = TEMPLATE; + if(name == "script") { + switch(state) { + case SCRIPT: + current_script = NULL; + state = SCRIPTS; + break; + default: + // tag mismatch? + break; + } + } } int TemplateParser::readData(char *data, size_t size) diff --git a/server/src/templateparser.h b/server/src/templateparser.h index 5b8302f..89f4917 100644 --- a/server/src/templateparser.h +++ b/server/src/templateparser.h @@ -34,6 +34,8 @@ typedef enum { UNDEFINED, TEMPLATE, MACRO, + SCRIPTS, + SCRIPT } ParserState; class TemplateParser : public SAXParser { @@ -60,6 +62,7 @@ private: ParserState state; Template *t; Macro *current_macro; + Script *current_script; std::vector< Widget* > widgetstack; // Error callback function. diff --git a/server/src/transactionhandler.cc b/server/src/transactionhandler.cc index 5203ee2..6ab3d89 100644 --- a/server/src/transactionhandler.cc +++ b/server/src/transactionhandler.cc @@ -28,18 +28,27 @@ #include "transactionhandler.h" #include "macroparser.h" -#include "resumeparser.h" #include "templateparser.h" #include "templateheaderparser.h" #include "courseparser.h" #include "configuration.h" #include "luaquerymapper.h" +#include "luaresume.h" +#include "luaoncommit.h" #include "queryhandlerpentominos.h" #include "queryhandlerpracro.h" #include "xml_encode_decode.h" #include "widgetgenerator.h" #include "journal.h" +#include "exception.h" + +class NotFoundException : public Exception { +public: + NotFoundException(Request &r) + : Exception("Macro " + r.macro + " not found in template " + r.templ) {} +}; + static std::string error_box(std::string message) { std::string errorbox = @@ -52,34 +61,45 @@ static std::string error_box(std::string message) static std::string handleCommits(Transaction &transaction, Environment &env, Session &session) + throw(LUAScript::Exception) { std::string answer; - if(transaction.commits.size() > 0) { - - Commits::iterator i = transaction.commits.begin(); - while(i != transaction.commits.end()) { - Commit &commit = *i; - - MacroParser mp(env.macrolist.getLatestVersion(commit.macro)); - mp.parse(); - Macro *macro = mp.getMacro(); - - std::string resume = resume_parser(*macro, commit); + Commits::iterator i = transaction.commits.begin(); + while(i != transaction.commits.end()) { + Commit &commit = *i; + + MacroParser mp(env.macrolist.getLatestVersion(commit.macro)); + mp.parse(); + Macro *macro = mp.getMacro(); + + std::string resume; + try { + LUAResume luaresume(transaction, commit); + luaresume.addScripts(macro->resume_scripts); + luaresume.run(); + resume = luaresume.resultString(); commit.fields["journal.resume"] = resume; session.commitMacro(transaction, commit, *macro); + } catch(LUAScript::Exception &e) { + throw e; + } - if(resume != "") { - - TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); - tp.parse(); - Template *templ = tp.getTemplate(); - - session.journal()->addEntry(transaction, commit, resume, templ); - } + LUAOnCommit *oncommit = NULL; + if(macro->commit_scripts.size() != 0) { + oncommit = new LUAOnCommit(transaction, commit); + oncommit->addScripts(macro->commit_scripts); + } + + if(resume != "" || oncommit != NULL) { + TemplateParser tp(env.templatelist.getLatestVersion(commit.templ)); + tp.parse(); + Template *templ = tp.getTemplate(); - i++; + session.journal()->addEntry(transaction, commit, resume, templ, oncommit); } + + i++; } return answer; @@ -87,6 +107,7 @@ static std::string handleCommits(Transaction &transaction, Environment &env, static std::string handleRequest(Request &request, Environment &env, Session &session) + throw(NotFoundException, Exception) { std::string answer; @@ -244,7 +265,7 @@ static std::string handleRequest(Request &request, Environment &env, answer +="\n-- END INCLUDE: '"+spi->attributes["src"]+"'\n"; } } else { - answer += xml_encode(spi->attributes["code"]); + answer += xml_encode(spi->code); } answer += "</script>\n"; spi++; @@ -316,9 +337,9 @@ std::string handleTransaction(Request &request, try { answer += handleCommits(transaction, env, session); - } catch( std::exception &e ) { - ERR(server, "Commit error: %s\n", e.what()); - return error_box(xml_encode(e.what())); + } catch( LUAScript::Exception &e ) { + ERR(server, "Commit error: %s\n", e.msg.c_str()); + return error_box(xml_encode(e.msg)); } try { diff --git a/server/src/transactionhandler.h b/server/src/transactionhandler.h index 43ecf0b..914c9ad 100644 --- a/server/src/transactionhandler.h +++ b/server/src/transactionhandler.h @@ -28,18 +28,12 @@ #ifndef __PRACRO_TRANSACTIONHANDLER_H__ #define __PRACRO_TRANSACTIONHANDLER_H__ +#include <string> + #include "transaction.h" #include "session.h" #include "environment.h" -#include "exception.h" - -class NotFoundException : public Exception { -public: - NotFoundException(Request &r) - : Exception("Macro " + r.macro + " not found in template " + r.templ) {} -}; - std::string handleTransaction(Request &resuest, Transaction &transaction, Environment &env, |