From d35bafeec39b6688a8dd07c51ea304e348b10fa2 Mon Sep 17 00:00:00 2001
From: deva <deva>
Date: Fri, 7 Aug 2009 11:27:18 +0000
Subject: Added new testdb, that can be activated through the cli (-d testdb)
 or through the config file (database_backend = testdb) .

---
 server/src/Makefile.am            |  20 +++
 server/src/configuration.cc       |   1 +
 server/src/configuration.h        |   1 +
 server/src/configurationparser.cc |   6 +
 server/src/database.cc            |   5 +
 server/src/pracrod.cc             |  13 +-
 server/src/pracrodaotest.cc       | 336 ++++++++++++++++++++++++++++++++++++++
 server/src/pracrodaotest.h        |  93 +++++++++++
 server/src/server.cc              |   2 +-
 9 files changed, 475 insertions(+), 2 deletions(-)
 create mode 100644 server/src/pracrodaotest.cc
 create mode 100644 server/src/pracrodaotest.h

(limited to 'server')

diff --git a/server/src/Makefile.am b/server/src/Makefile.am
index 3bc3069..399d7fa 100644
--- a/server/src/Makefile.am
+++ b/server/src/Makefile.am
@@ -25,6 +25,7 @@ pracrod_SOURCES = \
 	macroparser.cc \
 	pracrodao.cc \
 	pracrodaopgsql.cc \
+	pracrodaotest.cc \
 	resumeparser.cc \
 	saxparser.cc \
 	server.cc \
@@ -59,6 +60,7 @@ macrotool_SOURCES = \
 	macrotool_util.cc \
 	pracrodao.cc \
 	pracrodaopgsql.cc \
+	pracrodaotest.cc \
 	saxparser.cc \
 	templateparser.cc \
 	versionstr.cc
@@ -88,6 +90,7 @@ EXTRA_DIST = \
 	macrotool_util.h \
 	pracrodao.h \
 	pracrodaopgsql.h \
+	pracrodaotest.h \
 	resumeparser.h \
 	saxparser.h \
 	server.h \
@@ -106,6 +109,8 @@ EXTRA_DIST = \
 ################
 
 TESTFILES = \
+	test_pracrodaotest \
+	test_widgetgenerator \
 	test_configurationparser \
 	test_exception \
 	test_templateheaderparser \
@@ -143,6 +148,21 @@ test: $(TESTFILES)
 test_clean:
 	rm -f $(TESTFILES) $(TESTLOGS)
 
+TEST_PRACRODAOTEST_FILES = \
+	pracrodaotest.cc \
+	pracrodao.cc \
+	$(BASICFILES)
+test_pracrodaotest: $(TEST_PRACRODAOTEST_FILES)
+	@../../tools/test $(TEST_PRACRODAOTEST_FILES) $(BASICFLAGS)
+
+TEST_WIDGETGENERATOR_FILES = \
+	widgetgenerator.cc \
+	xml_encode_decode.cc \
+	luaquerymapper.cc \
+	$(BASICFILES)
+test_widgetgenerator: $(TEST_WIDGETGENERATOR_FILES)
+	@../../tools/test $(TEST_WIDGETGENERATOR_FILES) $(BASICFLAGS) $(LUA_LIBS)
+
 TEST_CONFIGURATIONPARSER_FILES = \
 	configurationparser.cc \
 	configuration.cc \
diff --git a/server/src/configuration.cc b/server/src/configuration.cc
index c921da6..c1dcab1 100644
--- a/server/src/configuration.cc
+++ b/server/src/configuration.cc
@@ -41,6 +41,7 @@ time_t Conf::pentominos_max_ttl = 7 * 60 * 60 * 24;
 std::string Conf::pentominos_addr = "localhost";
 port_t Conf::pentominos_port = 11108;
 
+std::string Conf::database_backend = "pgsql";
 std::string Conf::database_addr = "localhost";
 std::string Conf::database_user = "pracro";
 std::string Conf::database_passwd = "pracro";
diff --git a/server/src/configuration.h b/server/src/configuration.h
index 56a2f0e..3d9f7f1 100644
--- a/server/src/configuration.h
+++ b/server/src/configuration.h
@@ -48,6 +48,7 @@ namespace Conf {
   extern std::string pentominos_addr;
   extern port_t pentominos_port;
 
+  extern std::string database_backend;
   extern std::string database_addr;
   extern std::string database_user;
   extern std::string database_passwd;
diff --git a/server/src/configurationparser.cc b/server/src/configurationparser.cc
index a3e7532..8247a45 100644
--- a/server/src/configurationparser.cc
+++ b/server/src/configurationparser.cc
@@ -104,6 +104,12 @@ void ConfigurationParser::reload()
   } catch( ... ) {
   }
 
+  try {
+    std::string a = lookup("database_backend");
+    Conf::database_backend = a;
+  } catch( ... ) {
+  }
+
   try {
     std::string a = lookup("database_addr");
     Conf::database_addr = a;
diff --git a/server/src/database.cc b/server/src/database.cc
index 5a03126..9a4905b 100644
--- a/server/src/database.cc
+++ b/server/src/database.cc
@@ -32,6 +32,7 @@
 
 #include "debug.h"
 #include "pracrodaopgsql.h"
+#include "pracrodaotest.h"
 
 Database::Database(std::string _backend, std::string _host, std::string _port, std::string _user, std::string _passwd, std::string _dbname)
 {
@@ -42,6 +43,10 @@ Database::Database(std::string _backend, std::string _host, std::string _port, s
     dao = new PracroDAOPgsql(_host, _port, _user, _passwd, _dbname);
   }
 #endif/*WITHOUT_DB*/
+  if(_backend == "testdb") {
+    Data data;
+    dao = new PracroDAOTest(data, true);
+  }
 }
 
 Database::~Database()
diff --git a/server/src/pracrod.cc b/server/src/pracrod.cc
index a929f63..75f58a6 100644
--- a/server/src/pracrod.cc
+++ b/server/src/pracrod.cc
@@ -88,6 +88,7 @@ static const char usage_str[] =
 "  -v, --version       Print version information and exit.\n"
 "  -h, --help          Print this message and exit.\n"
 "  -D, --debug ddd     Enable debug messages on 'ddd'; see documentation for details\n"
+"  -d  --database db   Use db as the database backend. Can be one of pgsql or testdb (default pgsql).\n"
 ;
 
 ConfigurationParser *configparser = NULL;
@@ -145,6 +146,7 @@ int main(int argc, char *argv[])
   bool foreground = false;
   char *xml_basedir = NULL;
   char *debugstr = NULL;
+  std::string database;
 
   pracro_debug_init();
 
@@ -160,15 +162,20 @@ int main(int argc, char *argv[])
       {"version", no_argument, 0, 'v'},
       {"xml-basedir", required_argument, 0, 'x'},
       {"debug", required_argument, 0, 'D'},
+      {"database", required_argument, 0, 'd'},
       {0, 0, 0, 0}
     };
     
-    c = getopt_long (argc, argv, "D:hvfc:u:g:x:", long_options, &option_index);
+    c = getopt_long (argc, argv, "D:hvfc:u:g:x:d:", long_options, &option_index);
     
     if (c == -1)
       break;
 
     switch(c) {
+    case 'd':
+      database = optarg;
+      break;
+
     case 'c':
       configfile = strdup(optarg);
       break;
@@ -217,6 +224,10 @@ int main(int argc, char *argv[])
   if(configfile) configparser = new ConfigurationParser(configfile);
   else configparser = new ConfigurationParser(ETC"/pracrod.conf");
 
+  if(database != "") {
+    Conf::database_backend = database;
+  }
+
   if(!user) {
     user = strdup(Conf::server_user.c_str());
   }
diff --git a/server/src/pracrodaotest.cc b/server/src/pracrodaotest.cc
new file mode 100644
index 0000000..c084e5b
--- /dev/null
+++ b/server/src/pracrodaotest.cc
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
+/***************************************************************************
+ *            pracrodaotest.cc
+ *
+ *  Fri Aug  7 10:25:07 CEST 2009
+ *  Copyright 2009 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 "pracrodaotest.h"
+
+#include <stdlib.h>
+#include <sstream>
+
+#include "debug.h"
+
+PracroDAOTest::PracroDAOTest(Data &data, bool ignore_fieldnames)
+  : PracroDAO("", "", "", "", "")
+{
+  this->data = data;
+  this->ignore_fieldnames = ignore_fieldnames;
+  PRACRO_DEBUG(db, "New test (memory only) database\n");
+}
+
+PracroDAOTest::~PracroDAOTest()
+{
+  PRACRO_DEBUG(db, "Delete test (memory only) database\n");
+}
+
+void PracroDAOTest::commitTransaction(std::string user, std::string patientid,
+                                      Macro &_macro, Fields &fields, time_t now)
+{
+  PRACRO_DEBUG(db, "(%s, %s, %s, <%u fields>, %ld)\n",
+               user.c_str(),
+               patientid.c_str(),
+               _macro.attributes["name"].c_str(),
+               fields.size(),
+               now);
+  if(fields.size() == 0) return;
+
+  std::string version = _macro.attributes["version"];
+  std::string macro = _macro.attributes["name"];
+  std::stringstream timestamp; timestamp << now;
+
+  dbrow_t t;
+  t["uid"] = data.trseq.nextval();
+  t["patientid"] = patientid;
+  t["macro"] = macro;
+  t["version"] = version;
+  t["timestamp"] = timestamp.str();
+  t["user"] = user;
+  data.transactions.push_back(t);
+
+  // Iterate fields...
+  Fields::iterator fi = fields.begin();
+  while(fi != fields.end()) {
+    
+    if(ignore_fieldnames == false) {
+      // Search for it in fieldnames table
+      dbtable_t::iterator ti = data.fieldnames.begin();
+      while(ti != data.fieldnames.end()) {
+        
+        // If found, insert the field values into the fields table.
+        if(fi->first == (*ti)["name"]) {
+          
+          dbrow_t f;
+          f["transaction"] = data.trseq.currval();
+          f["name"] = fi->first;
+          f["value"] = fi->second;
+          data.fields.push_back(f);
+          
+        }
+        
+        ti++;
+      }
+    } else {
+      dbrow_t f;
+      f["transaction"] = data.trseq.currval();
+      f["name"] = fi->first;
+      f["value"] = fi->second;
+      data.fields.push_back(f);
+    }
+    
+    fi++;
+  }
+}
+
+Values PracroDAOTest::getLatestValues(std::string patientid, Macro *macro,
+                                      Fieldnames &fieldnames, time_t oldest)
+{
+  PRACRO_DEBUG(db, "(%s, %s, <%u fieldnames>, %ld)\n",
+               patientid.c_str(),
+               macro ? macro->attributes["name"].c_str() : "(null)", fieldnames.size(),
+               oldest);
+  Values values;
+
+  // TODO: Take Macro* into account. If supplied (not NULL) the macro name, and
+  //  optionally version number should match the transaction.
+
+  Fieldnames::iterator fi = fieldnames.begin();
+  while(fi != fieldnames.end()) {
+    std::string fieldname = *fi;
+
+    // Find matching transactions
+    dbtable_t::iterator ti = data.transactions.begin();
+    while(ti != data.transactions.end()) {
+      std::map<std::string, std::string> &transaction = *ti;
+      time_t timestamp = atol(transaction["timestamp"].c_str());
+      if(transaction["patientid"] == patientid && timestamp >= oldest) {
+        std::string tid = transaction["uid"];
+
+        // Find transaction values
+        dbtable_t::iterator vi = data.fields.begin();
+        while(vi != data.fields.end()) {
+          std::map<std::string, std::string> &field = *vi;
+          
+          // Upon match, insert it into values
+          if(field["transaction"] == tid && field["name"] == fieldname) {
+            if(values.find(fieldname) == values.end() || values[fieldname].timestamp < timestamp) {
+              values[fieldname].timestamp = timestamp;
+              values[fieldname].value = field["value"];
+              values[fieldname].source = "testdb";
+            }
+          }
+          vi++;
+        }
+    
+      }
+      ti++;
+    }
+    
+    fi++;
+  }
+
+  return values;
+}
+
+
+unsigned PracroDAOTest::nrOfCommits(std::string patientid, std::string macroname, time_t oldest)
+{
+  unsigned num = 0;
+
+  // Find and count matching transactions
+  dbtable_t::iterator ti = data.transactions.begin();
+  while(ti != data.transactions.end()) {
+    std::map<std::string, std::string> &transaction = *ti;
+    time_t timestamp = atol(transaction["timestamp"].c_str());
+    if(transaction["patientid"] == patientid &&
+       transaction["macro"] == macroname &&
+       timestamp >= oldest) {
+      num++;
+    }
+    ti++;
+  }
+
+  return num;
+}
+
+void PracroDAOTest::addFieldname(std::string name, std::string description)
+{
+  // TODO
+  
+  /*
+  std::stringstream timestamp; timestamp << time(NULL);
+  std::string ts;
+  try {
+    pqxx::work W(*conn);
+    ts = "INSERT INTO fieldnames (name, description, \"timestamp\") VALUES ("
+      " '" + W.esc(name) + "', "
+      " '" + W.esc(description) + "', "
+      " '" + W.esc(timestamp.str()) + "' "
+      ")"
+      ;
+    PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str());
+    pqxx::result R = W.exec(ts);
+    W.commit();
+  } catch (std::exception &e) {
+    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str());
+  }
+  */
+}
+
+void PracroDAOTest::delFieldname(std::string name)
+{
+  // TODO
+
+  /*
+  std::string ts;
+  try {
+    pqxx::work W(*conn);
+    ts = "DELETE FROM fieldnames WHERE name="
+      "'" + W.esc(name) + "' ";
+    PRACRO_DEBUG(sql, "Query: %s\n", ts.c_str());
+    pqxx::result R = W.exec(ts);
+    W.commit();
+  } catch (std::exception &e) {
+    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), ts.c_str());
+  }
+  */
+}
+
+std::vector<Fieldname> PracroDAOTest::getFieldnames()
+{
+  // TODO
+
+  std::vector<Fieldname> fieldnames;
+  /*
+  std::string query;
+  try {
+    pqxx::work W(*conn);
+    query = "SELECT * FROM fieldnames";
+    PRACRO_DEBUG(sql, "Query: %s\n", query.c_str());
+    pqxx::result R = W.exec(query);
+    pqxx::result::const_iterator ri = R.begin();
+    while(ri != R.end()) {
+      Fieldname f;
+      f.name = (*ri)[0].c_str();
+      f.description = (*ri)[1].c_str();
+      f.timestamp = atol((*ri)[2].c_str());
+      fieldnames.push_back(f);
+      ri++;
+    }
+  } catch (std::exception &e) {
+    PRACRO_ERR_LOG(db, "Query failed: %s: %s\n", e.what(), query.c_str());
+  }
+  */
+  return fieldnames;
+}
+
+
+
+
+#ifdef TEST_PRACRODAOTEST
+
+#define PATIENTID "1234567890"
+
+int main()
+{
+  time_t now = time(NULL);
+
+  Data data;
+
+  // Add some fieldnames
+  dbrow_t f;
+  f["name"] = "field1"; data.fieldnames.push_back(f);
+  f["name"] = "field2"; data.fieldnames.push_back(f);
+  f["name"] = "field3"; data.fieldnames.push_back(f);
+
+  PracroDAOTest db(data);
+
+  // Make a commit
+  Macro macro;
+  macro.attributes["version"] = "1.0";
+  macro.attributes["name"] = "testmacro";
+
+  Fields fields;
+  fields["field1"] = "testval1";
+  fields["field2"] = "testval2";
+  fields["field3"] = "testval3";
+  fields["field4"] = "testval4";
+  db.commitTransaction("testuser", PATIENTID, macro, fields, now);
+
+  // Retrieve the data again
+  unsigned num = db.nrOfCommits(PATIENTID, "testmacro", now);
+  if(num != 1) return 1;
+
+  Fieldnames fieldnames;
+  fieldnames.push_back("field1");
+  fieldnames.push_back("field2");
+  fieldnames.push_back("field3");
+  fieldnames.push_back("field4");
+  Values values = db.getLatestValues(PATIENTID, NULL, fieldnames, now);
+
+  Values::iterator i = values.begin();
+  while(i != values.end()) {
+    printf("%s => %s\n", i->first.c_str(), i->second.value.c_str());
+    i++;
+  }
+  if(values["field1"].value != "testval1") return 1;
+  if(values["field2"].value != "testval2") return 1;
+  if(values["field3"].value != "testval3") return 1;
+
+  // This value was not committed, since it wasn't in the fieldnames table.
+  if(values.find("field4") != values.end()) return 1;
+
+  // Make another commit (one second later)
+  fields["field1"] = "testval1-2";
+  fields["field2"] = "testval2-2";
+  fields["field3"] = "testval3-2";
+  fields["field4"] = "testval4-2";
+  db.commitTransaction("testuser", PATIENTID, macro, fields, now+1);
+
+  // Retrieve the data again
+  num = db.nrOfCommits(PATIENTID, "testmacro", now);
+  if(num != 2) return 1;
+
+  fieldnames.push_back("field1");
+  fieldnames.push_back("field2");
+  fieldnames.push_back("field3");
+  fieldnames.push_back("field4");
+  values = db.getLatestValues(PATIENTID, NULL, fieldnames, now);
+
+  i = values.begin();
+  while(i != values.end()) {
+    printf("%s => %s\n", i->first.c_str(), i->second.value.c_str());
+    i++;
+  }
+  if(values["field1"].value != "testval1-2") return 1;
+  if(values["field2"].value != "testval2-2") return 1;
+  if(values["field3"].value != "testval3-2") return 1;
+
+  // This value was not committed, since it wasn't in the fieldnames table.
+  if(values.find("field4") != values.end()) return 1;
+
+  return 0;
+}
+
+#endif/*TEST_PRACRODAOTEST*/
diff --git a/server/src/pracrodaotest.h b/server/src/pracrodaotest.h
new file mode 100644
index 0000000..8dd4655
--- /dev/null
+++ b/server/src/pracrodaotest.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: */
+/***************************************************************************
+ *            pracrodaotest.h
+ *
+ *  Fri Aug  7 10:25:07 CEST 2009
+ *  Copyright 2009 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_PRACRODAOTEST_H__
+#define __PRACRO_PRACRODAOTEST_H__
+
+#include <string>
+#include <vector>
+#include <map>
+
+#include <config.h>
+
+#include "pracrodao.h"
+
+typedef std::map<std::string, std::string> dbrow_t; 
+typedef std::vector< dbrow_t > dbtable_t;
+
+class dbcounter_t {
+public:
+  dbcounter_t()
+  {
+    counter = 0;
+  }
+
+  std::string nextval()
+  {
+    counter++;
+    return currval();
+  }
+
+  std::string currval()
+  {
+    char buf[32];
+    sprintf(buf, "%d", counter);
+    return buf;
+  }
+
+private:
+  size_t counter;
+};
+
+class Data {
+public:
+  dbcounter_t trseq;
+  dbtable_t transactions;
+  dbtable_t fieldnames;
+  dbtable_t fields;
+};
+
+class PracroDAOTest : public PracroDAO
+{
+public:
+  PracroDAOTest(Data &data, bool ignore_fieldnames = false);
+  ~PracroDAOTest();
+
+  void commitTransaction(std::string user, std::string patientid, Macro &macro, Fields &fields, time_t now);
+  Values getLatestValues(std::string patientid, Macro *macro, Fieldnames &fieldnames, time_t oldest);
+  unsigned nrOfCommits(std::string patientid, std::string macroname, time_t oldest);
+
+  void addFieldname(std::string name, std::string description);
+  void delFieldname(std::string name);
+  std::vector<Fieldname> getFieldnames();
+
+private: 
+  Data data;
+  bool ignore_fieldnames;
+};
+
+#endif/*__PRACRO_PRACRODAOTEST_H__*/
diff --git a/server/src/server.cc b/server/src/server.cc
index d0e9524..a47eb28 100644
--- a/server/src/server.cc
+++ b/server/src/server.cc
@@ -299,7 +299,7 @@ static void handleConnection(TCPSocket *socket)
   pentominos_socket.connect(Conf::pentominos_addr, Conf::pentominos_port);
 #endif/*WITHOUT_PENTOMINOS*/
 
-  Database db("pgsql", Conf::database_addr, "", Conf::database_user, Conf::database_passwd, "");
+  Database db(Conf::database_backend, Conf::database_addr, "", Conf::database_user, Conf::database_passwd, "");
 
   JournalWriter journalwriter(Conf::journal_commit_addr.c_str(), Conf::journal_commit_port);
 
-- 
cgit v1.2.3