CustusX  15.3.4-beta
An IGT application
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
cxSessionStorageServiceImpl.cpp
Go to the documentation of this file.
1 /*=========================================================================
2 This file is part of CustusX, an Image Guided Therapy Application.
3 
4 Copyright (c) 2008-2014, SINTEF Department of Medical Technology
5 All rights reserved.
6 
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9 
10 1. Redistributions of source code must retain the above copyright notice,
11  this list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its contributors
18  may be used to endorse or promote products derived from this software
19  without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 =========================================================================*/
32 
34 #include "cxSettings.h"
35 #include "cxReporter.h"
36 #include <QTextStream>
37 #include "cxTime.h"
38 #include <QDir>
39 #include "cxConfig.h"
40 #include "cxFileHelpers.h"
41 #include "cxDataLocations.h"
42 #include <QApplication>
43 #include <QTimer>
44 #include "cxLogger.h"
45 
46 
47 namespace cx
48 {
49 
51 {
52  settings()->fillDefault("globalPatientDataFolder", QDir::homePath() + "/Patients");
53 
54  this->clearCache();
55  mActivePatientFolder = DataLocations::getNoPatientFolder();
56  connect(this, &SessionStorageServiceImpl::sessionChanged, this, &SessionStorageServiceImpl::onSessionChanged);
57 
58  QTimer::singleShot(100, this, SLOT(startupLoadPatient())); // make sure this is called after application state change
59 }
60 
62 {
63  this->clearCache();
64 }
65 
67 {
68  return false;
69 }
70 
71 
73 {
74  bool valid = this->isValidSessionFolder(dir);
75  bool exists = this->folderExists(dir);
76 
77  if (valid)
78  {
79  this->loadSession(dir);
80  }
81  else if (!exists)
82  {
83  this->initializeNewSession(dir);
84  }
85  else
86  {
87  reportError(QString("Failed to initialize session in existing folder with no valid session: %1").arg(dir));
88  }
89 }
90 
91 bool SessionStorageServiceImpl::isValidSessionFolder(QString dir) const
92 {
93  QString filename = QDir(dir).absoluteFilePath(this->getXmlFileName());
94  return QFileInfo::exists(filename);
95 }
96 
97 bool SessionStorageServiceImpl::folderExists(QString dir) const
98 {
99  return QFileInfo(dir).exists() && QFileInfo(dir).isDir();
100 }
101 
102 void SessionStorageServiceImpl::loadSession(QString dir)
103 {
104  if (this->isActivePatient(dir))
105  return;
106  this->loadPatientSilent(dir);
107  this->reportActivePatient();
108  this->writeRecentPatientData();
109 }
110 
111 void SessionStorageServiceImpl::initializeNewSession(QString dir)
112 {
113  this->clearPatientSilent();
114  dir = this->convertToValidFolderName(dir);
115  this->createPatientFolders(dir);
116  this->setActivePatient(dir);
117  this->save();
118  this->reportActivePatient();
119  this->writeRecentPatientData();
120 }
121 
122 QString SessionStorageServiceImpl::getXmlFileName() const
123 {
124  return "custusdoc.xml";
125 }
126 
128 {
129  if (!this->isValid())
130  return;
131 // if (mActivePatientFolder.isEmpty())
132 // return;
133 
134  //Gather all the information that needs to be saved
135  QDomDocument doc;
136  this->generateSaveDoc(doc);
137 
138  QDomElement element = doc.documentElement();
139  emit isSaving(element); // give all listeners a chance to add to the document
140 
141  QString filename = QDir(mActivePatientFolder).absoluteFilePath(this->getXmlFileName());
142  this->writeXmlFile(doc, filename);
143 
144  report("Saved patient " + mActivePatientFolder);
145 // this->writeRecentPatientData();
146 }
147 
148 void SessionStorageServiceImpl::writeXmlFile(QDomDocument doc, QString filename)
149 {
150  QFile file(filename);
151  if (file.open(QIODevice::WriteOnly | QIODevice::Truncate))
152  {
153  QTextStream stream(&file);
154  stream << doc.toString(4);
155  file.close();
156  }
157  else
158  {
159  reportError("Could not open " + file.fileName() + " Error: " + file.errorString());
160  }
161 }
162 
164 {
165  this->clearPatientSilent();
166  this->reportActivePatient();
167  this->writeRecentPatientData();
168 }
169 
171 {
172  return !mActivePatientFolder.isEmpty() &&
173  (mActivePatientFolder != DataLocations::getNoPatientFolder());
174 }
175 
177 {
178  return mActivePatientFolder;
179 }
180 
181 void SessionStorageServiceImpl::reportActivePatient()
182 {
183  report("Set Active Patient: " + mActivePatientFolder);
184 }
185 
186 void SessionStorageServiceImpl::setActivePatient(const QString& activePatientFolder)
187 {
188  if (activePatientFolder == mActivePatientFolder)
189  return;
190 
191  mActivePatientFolder = activePatientFolder;
192 
193  emit sessionChanged();
194 }
195 
196 void SessionStorageServiceImpl::onSessionChanged()
197 {
198  reporter()->setLoggingFolder(this->getSubFolder("Logs/"));
199 }
200 
201 void SessionStorageServiceImpl::clearPatientSilent()
202 {
203  this->setActivePatient(DataLocations::getNoPatientFolder());
204  emit cleared();
205 }
206 
207 bool SessionStorageServiceImpl::isActivePatient(QString patient) const
208 {
209  return (patient == mActivePatientFolder);
210 }
211 
212 void SessionStorageServiceImpl::loadPatientSilent(QString choosenDir)
213 {
214  if (this->isActivePatient(choosenDir))
215  return;
216  this->clearPatientSilent();
217  if (choosenDir == QString::null)
218  return; // On cancel
219 
220  QString filename = QDir(choosenDir).absoluteFilePath(this->getXmlFileName());
221  QDomDocument doc = this->readXmlFile(filename);
222 
223  mActivePatientFolder = choosenDir; // must set path before emitting isLoading()
224 
225  if (!doc.isNull())
226  {
227  //Read the xml
228  QDomElement element = doc.documentElement();
229  emit isLoading(element);
230  }
231 
232  emit sessionChanged();
233 }
234 
235 QDomDocument SessionStorageServiceImpl::readXmlFile(QString filename)
236 {
237  QDomDocument retval;
238  QFile file(filename);
239  if (!file.open(QIODevice::ReadOnly))
240  {
241  reportError("Could not open XML file :" + file.fileName() + ".");
242  return QDomDocument();
243  }
244 
245  QString emsg;
246  int eline, ecolumn;
247  // Read the file
248  if (!retval.setContent(&file, false, &emsg, &eline, &ecolumn))
249  {
250  reportError("Could not parse XML file :" + file.fileName() + " because: " + emsg + "");
251  return QDomDocument();
252  }
253 
254  return retval;
255 }
256 
260 void SessionStorageServiceImpl::writeRecentPatientData()
261 {
262  settings()->setValue("startup/lastPatient", mActivePatientFolder);
263  settings()->setValue("startup/lastPatientSaveTime", QDateTime::currentDateTime().toString(timestampSecondsFormat()));
264 }
265 
266 void SessionStorageServiceImpl::generateSaveDoc(QDomDocument& doc)
267 {
268  doc.appendChild(doc.createProcessingInstruction("xml version =", "'1.0'"));
269 
270  QDomElement patientNode = doc.createElement("patient");
271 
272  // note: all nodes must be below <patient>. XML requires only one root node per file.
273  QDomElement versionName = doc.createElement("version_name");
274  versionName.appendChild(doc.createTextNode(this->getVersionName()));
275  patientNode.appendChild(versionName);
276 
277  QDomElement activePatientNode = doc.createElement("active_patient");
278  activePatientNode.appendChild(doc.createTextNode(mActivePatientFolder.toStdString().c_str()));
279  patientNode.appendChild(activePatientNode);
280  doc.appendChild(patientNode);
281 }
282 
283 QString SessionStorageServiceImpl::convertToValidFolderName(QString dir) const
284 {
285  if (!dir.endsWith(".cx3"))
286  dir.append(".cx3");
287  return dir;
288 }
289 
290 void SessionStorageServiceImpl::createPatientFolders(QString dir)
291 {
292  QDir().mkpath(dir);
293  QDir().mkpath(dir+"/Images");
294  QDir().mkpath(dir+"/Logs");
295  QDir().mkpath(dir+"/US_Acq");
296 }
297 
298 QString SessionStorageServiceImpl::getVersionName()
299 {
300  return QString("%1").arg(CustusX_VERSION_STRING);
301 }
302 
303 void SessionStorageServiceImpl::clearCache()
304 {
305 // std::cout << "DataLocations::getCachePath() " << DataLocations::getCachePath() << std::endl;
306  // clear the global cache used by app
308 }
309 
313 QString SessionStorageServiceImpl::getCommandLineStartupPatient()
314 {
315  int doLoad = QApplication::arguments().indexOf("--load");
316  if (doLoad < 0)
317  return "";
318  if (doLoad + 1 >= QApplication::arguments().size())
319  return "";
320 
321  QString folder = QApplication::arguments()[doLoad + 1];
322 
323  return folder;
324 }
325 
328 void SessionStorageServiceImpl::startupLoadPatient()
329 {
330  QString folder = this->getCommandLineStartupPatient();
331 
332  if (!folder.isEmpty())
333  {
334  report(QString("Startup Load [%1] from command line").arg(folder));
335  }
336 
337  if (folder.isEmpty() && settings()->value("Automation/autoLoadRecentPatient").toBool())
338  {
339  folder = settings()->value("startup/lastPatient").toString();
340 
341  QDateTime lastSaveTime = QDateTime::fromString(settings()->value("startup/lastPatientSaveTime").toString(), timestampSecondsFormat());
342  double minsSinceLastSave = lastSaveTime.secsTo(QDateTime::currentDateTime())/60;
343  double autoLoadRecentPatientWithinHours = settings()->value("Automation/autoLoadRecentPatientWithinHours").toDouble();
344  int allowedMinsSinceLastSave = autoLoadRecentPatientWithinHours*60;
345  if (minsSinceLastSave > allowedMinsSinceLastSave) // if less than 8 hours, accept
346  {
347  report(
348  QString("Startup Load: Ignored recent patient because %1 hours since last save, limit is %2")
349  .arg(int(minsSinceLastSave/60))
350  .arg(int(allowedMinsSinceLastSave/60)));
351  folder = "";
352  }
353 
354  if (!folder.isEmpty())
355  report(QString("Startup Load [%1] as recent patient").arg(folder));
356  }
357 
358  if (folder.isEmpty())
359  return;
360 
361  this->load(folder);
362 }
363 
364 } // namespace cx
void reportError(QString msg)
Definition: cxLogger.cpp:92
void isLoading(QDomElement &root)
emitted while loading a session. Xml storage is available, getRootFolder() is set to loaded value...
ReporterPtr reporter()
Definition: cxReporter.cpp:59
bool removeNonemptyDirRecursively(const QString &dirName)
virtual void save()
Save all application data to XML file.
virtual void load(QString dir)
load session from dir, or create new session in this location if none exist
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Definition: cxSettings.cpp:99
QString timestampSecondsFormat()
Definition: cxTime.cpp:39
void sessionChanged()
emitted after change to a new session (new or loaded or cleared)
void setValue(const QString &key, const QVariant &value)
Definition: cxSettings.cpp:91
static QString getCachePath()
return path to a folder that is used during execution, will be cleared at start and stop...
QString getSubFolder(QString relative)
return and create a folder/path relative to root. Created if necessary.
Settings * settings()
Shortcut for accessing the settings instance.
Definition: cxSettings.cpp:42
void cleared()
emitted when session is cleared, before isLoading is called
void report(QString msg)
Definition: cxLogger.cpp:90
static QString getNoPatientFolder()
void fillDefault(QString name, T value)
Definition: cxSettings.h:85
SessionStorageServiceImpl(ctkPluginContext *context)
void isSaving(QDomElement &root)
xml storage is available