]> Creatis software - creaImageIO.git/blob - src/creaImageIODicomDatabase.cpp
start!
[creaImageIO.git] / src / creaImageIODicomDatabase.cpp
1 #include <creaImageIODicomDatabase.h>
2
3 #include "CppSQLite3.h"
4
5 #include <sys/stat.h>
6
7 #include <creaImageIODicomDatabaseStructure.h>
8
9 //#include <creaImageIOUtilities.h>
10
11 //#include <icons/database.xpm>
12
13 #include <deque>
14
15 #include "wx/wx.h"
16 #include <wx/dir.h>
17 #include <wx/filename.h>
18
19
20 //#include <icons/close.xpm>
21
22 #include <creaWx.h>
23 #include <creaMessageManager.h>
24 using namespace crea;
25
26 #include <boost/filesystem.hpp>
27 #include <boost/algorithm/string/replace.hpp>
28
29 namespace creaImageIO
30 {
31
32
33   //=============================================================
34   DicomDatabase::DicomDatabase(const std::string& location, int flags)
35     : DicomNode(DicomNode::Database,0,0),
36       mFileName(location),
37       mFlags(flags)
38   {
39     mDicomDatabase = this;
40     DicomNodeTypeDescription::FieldDescriptionMapType& M = 
41       mDicomNodeTypeDescription[DicomNode::Database].GetFieldDescriptionMap();
42
43     boost::filesystem::path full_path(location);
44     mName = full_path.leaf();
45     Field::Description fname("Name",0,0,"Name",0);
46     M[fname.key] = fname;
47     UnsafeSetFieldValue(fname.key,mName);
48     Field::Description flocation("File name",0,0,"File name",0);
49     M[flocation.key] = flocation;
50     UnsafeSetFieldValue(flocation.key,location);
51
52     //    Field::Description ftype("Type",0,0,"Type",0);
53     //    M[ftype.key] = ftype;
54     //    UnsafeSetFieldValue(ftype.key,"Invalid location");
55
56     mDB = new CppSQLite3DB;
57     //    std::cout << "** SQLite Version: " << mDB->SQLiteVersion() << std::endl;
58
59
60   }
61   //=============================================================
62
63   //=============================================================
64   DicomDatabase::~DicomDatabase()
65   {
66     delete mDB;
67     /*
68       Already done in DicomNode now that DicomDatabase inherits from it
69     ChildrenListType::iterator i;
70     for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
71       {
72         delete *i;
73       }
74     */
75   }
76   //=============================================================
77   
78
79   //=============================================================
80   void DicomDatabase::BuildDefaultDicomNodeTypeDescriptions()
81   {
82     for (int i = DicomNode::Patient;
83          i <= DicomNode::Image; 
84          ++i)
85       {
86         mDicomNodeTypeDescription[i].BuildDefault(i);
87       }
88   }
89   //=============================================================
90
91   //=============================================================
92   void DicomDatabase::Print() const 
93   {
94     std::cout << "-> '"<<GetName()<< "' - '"
95               << GetFileName()<<"'"<<std::endl;
96     ChildrenListType::const_iterator i;
97     for (i=GetChildrenList().begin(); i!=GetChildrenList().end(); i++)
98       {
99         (*i)->Print();
100       }
101   }
102   //=============================================================
103   
104   //=====================================================================
105   bool DicomDatabase::LocationIsValid()
106   {
107     // TO DO 
108     return true;
109   }
110   //=====================================================================
111
112   char* format_sql(const std::string& s)
113   { 
114     return sqlite3_mprintf("%q",s.c_str());
115   }
116   //  sqlite3_exec(db, zSQL, 0, 0, 0);
117   //  sqlite3_free(zSQL);
118   //    char* CHAIN = format_sql(QUER);         \
119 //  sqlite3_free(CHAIN);                                \
120
121   //=====================================================================
122 #define QUERYDB(QUER,RES)                                               \
123     try                                                                 \
124       {                                                                 \
125         RES = mDB->execQuery(QUER.c_str());                             \
126       }                                                                 \
127     catch (CppSQLite3Exception& e)                                      \
128       {                                                                 \
129         std::cout << "SQLite query '"<<QUER<<"' : "<< e.errorCode() << ":" \
130                   << e.errorMessage()<<std::endl;                       \
131         creaError("SQLite query '"<<QUER<<"' : "<< e.errorCode() << ":" \
132                    << e.errorMessage() );                               \
133       }                                                                 \
134     
135   //=====================================================================
136   
137   //=====================================================================
138 #define UPDATEDB(UP)                                                    \
139   try                                                                   \
140     {                                                                   \
141       mDB->execDML(UP.c_str());                                         \
142     }                                                                   \
143   catch (CppSQLite3Exception& e)                                        \
144     {                                                                   \
145       std::cout << "SQLite update '"<<UP<<"' Error : "<< e.errorCode() << ":" \
146                 << e.errorMessage()<<std::endl;                         \
147       creaError("SQLite update '"<<UP<<"' Error : "<< e.errorCode() << ":" \
148                  << e.errorMessage() );                                 \
149     }                                                                   
150   //=====================================================================
151
152   //=====================================================================
153   bool DicomDatabase::Open()
154   {
155     //    std::cout << "***> DicomDatabase::Open('"<<GetFileName()<<"')"<<std::endl;
156     return OpenDB();
157   }
158
159   //=====================================================================
160   bool DicomDatabase::New()
161   {
162     //    std::cout << "***> DicomDatabase::New('"<<GetFileName()<<"')"<<std::endl;
163     return CreateDB();
164   }
165   //=====================================================================
166
167   //=====================================================================
168   bool DicomDatabase::OpenDB()
169   {
170     //    std::cout << "### Opening SQLite database '"<<GetFileName()<<std::endl;
171     // OPENING FILE
172     if (!boost::filesystem::exists(GetFileName())) 
173       {
174         return false;
175       }
176
177     try
178       {
179         mDB->open(GetFileName().c_str());
180       }
181     catch (CppSQLite3Exception& e)
182       {
183         std::cerr << "Opening '"<<GetFileName()<<"' : "
184                   << e.errorCode() << ":" 
185                   << e.errorMessage() << std::endl;
186         return false;
187       }
188     // TESTING STRUCTURE VALIDITY
189     if (!DBStructureIsValid())
190       {
191         std::cerr << "Opening '"<<GetFileName()<<"' : "
192                   << " invalid database structure" << std::endl;
193         return false;
194       }
195     // IMPORTING NODE TYPE DESCRIPTIONS
196     ImportDicomNodeTypeDescriptionsFromDB();
197     return true;
198   }
199   //=====================================================================
200
201   //=====================================================================
202   bool DicomDatabase::CreateDB()
203   {
204     //    std::cout << "### Creating SQLite database '"<<GetFileName()<<std::endl;
205
206     if (boost::filesystem::exists(GetFileName())) 
207       {
208         return false;
209       }
210     // OPENING
211     try
212       {
213         mDB->open(GetFileName().c_str());
214       }
215     catch (CppSQLite3Exception& e)
216       {
217         std::cerr << "Creating '"<<GetFileName()<<"' : "
218                    << e.errorCode() << ":" 
219                   << e.errorMessage() <<std::endl;
220         return false;
221       }
222     
223     // CREATING DEFAULT DB STRUCTURE
224     BuildDefaultDicomNodeTypeDescriptions();
225
226     // CREATING TABLES
227     try
228       {
229         std::string command;
230         // PATIENT
231 //      command = "create table PATIENT\n(\nID INTEGER PRIMARY KEY AUTOINCREMENT,\n";
232 // LG : AUTOINCREMENT unknown on sqlite3 for Win32 ??
233         command = "create table PATIENT\n(\nID INTEGER PRIMARY KEY,\n";
234         AppendSQLFieldsDefinition(DicomNode::Patient,command);
235         command += "\n)";
236         UPDATEDB(command);
237
238         // STUDY
239 //      command = "create table STUDY\n(\nID INTEGER PRIMARY KEY AUTOINCREMENT,\nPARENT_ID int not null,\n";    
240         command = "create table STUDY\n(\nID INTEGER PRIMARY KEY,\nPARENT_ID int not null,\n";  
241         AppendSQLFieldsDefinition(DicomNode::Study,command);
242         command +=",\n";
243         command +="constraint FK_PARENT foreign key (PARENT_ID) references PATIENT(ID) on delete restrict on update restrict\n)";
244         UPDATEDB(command);
245         
246         // SERIES
247 //      command = "create table SERIES\n(\nID INTEGER PRIMARY KEY AUTOINCREMENT,\nPARENT_ID int not null,\n";
248         command = "create table SERIES\n(\nID INTEGER PRIMARY KEY,\nPARENT_ID int not null,\n";
249         AppendSQLFieldsDefinition(DicomNode::Series,command);
250         command += ",\n";
251         command += "constraint FK_PARENT foreign key (PARENT_ID) references STUDY(ID) on delete restrict on update restrict\n)";
252         UPDATEDB(command);
253         
254         // IMAGE
255 //      command = "create table IMAGE\n(\nID INTEGER PRIMARY KEY AUTOINCREMENT,\nPARENT_ID int not null,\n";
256         command = "create table IMAGE\n(\nID INTEGER PRIMARY KEY,\nPARENT_ID int not null,\n";
257         AppendSQLFieldsDefinition(DicomNode::Image,command);
258         command += ",\n";
259         command += "constraint FK_PARENT foreign key (PARENT_ID) references SERIE (ID) on delete restrict on update restrict\n)";
260         UPDATEDB(command);
261
262         //constraint FK_DIRECTORY foreign key (DIRECTORY_ID)            \
263             //      references ROOTDIRECTORY (ID)                       \
264             //      on delete restrict on update restrict,              
265         
266         
267         // Create tables *_FIELDS
268         for (DicomNode::Type type=DicomNode::Patient; 
269              type<=DicomNode::Image; type++)
270           {
271             command = "create table ";
272             command += DicomDatabaseStructure::Table(type);
273             command += "_FIELDS\n(\n";
274             command += "Key text,\n";
275             command += "DicomGroup int,\n";
276             command += "DicomElement int,\n";       
277             command += "Name text,\n";      
278             command += "Flags int\n";       
279             command += "\n)";
280             UPDATEDB(command);
281           }
282         // Fill the tables *_FIELDS
283         ExportDicomNodeTypeDescriptionsToDB();
284
285       }
286     catch (std::exception)
287       {
288         return false;
289       }
290     // LG : TEST
291     if (DBStructureIsValid())
292       {
293         //      std::cout << "*** DONE ***"<<std::endl;
294         //      mDBLoaded = true;
295         SetChildrenLoaded(true);
296         return true;
297         
298       }
299     else 
300       {
301         //      std::cout << "*** AAAARRRRGGG ***"<<std::endl;
302         
303         return false;
304       }
305   }
306   //=====================================================================
307
308   //=====================================================================
309   void DicomDatabase::AppendSQLFieldsDefinition(DicomNode::Type c,
310                                              std::string& s)
311   {
312     std::vector<std::string*> keys;
313     DicomNodeTypeDescription::FieldDescriptionMapType::iterator i;
314     for (i  = GetDicomNodeTypeDescription(c).GetFieldDescriptionMap().begin();
315          i != GetDicomNodeTypeDescription(c).GetFieldDescriptionMap().end();
316          ++i)
317       {
318         if (i->second.flags==1) continue;
319         keys.push_back(&(i->second.key));
320       }
321     std::vector<std::string*>::iterator j;
322     for (j=keys.begin();j!=keys.end();)
323       {
324         s += **j + " text";
325         ++j;
326         if (j!=keys.end())
327           s += ",\n";
328       }
329
330   }
331   //=====================================================================
332   
333   //=====================================================================
334   void DicomDatabase::ExportDicomNodeTypeDescriptionsToDB()
335   {
336     //    std::cout<<"ExportDicomNodeTypeDescriptionsToDB()"<<std::endl;
337     for (DicomNode::Type type=DicomNode::Patient; 
338          type<=DicomNode::Image; 
339          type++)
340       {
341         DicomNodeTypeDescription::FieldDescriptionMapType::iterator i;
342         for (i = GetDicomNodeTypeDescription(type).GetFieldDescriptionMap().begin();
343              i!= GetDicomNodeTypeDescription(type).GetFieldDescriptionMap().end();
344              i++)
345           {
346             
347             std::stringstream insert;
348             insert << "INSERT INTO "
349                    << std::string(DicomDatabaseStructure::Table(type))
350                    << "_FIELDS (Key,DicomGroup,DicomElement,Name,Flags) "
351                    << "VALUES ('"
352                    << (*i).second.GetKey() << "',"
353                    << (*i).second.GetGroup() << ","
354                    << (*i).second.GetElement() << ",'"
355                    << (*i).second.GetName() << "',"
356                    << (*i).second.GetFlags() << ");";
357
358             //      std::cout << "** SQL = '"<<insert.str()<<"'"<<std::endl;
359             
360             UPDATEDB(insert.str());
361           }
362       }
363   }
364   //=====================================================================
365
366   //=====================================================================
367   void DicomDatabase::ImportDicomNodeTypeDescriptionsFromDB()
368   {
369     //    std::cout<<"ImportDicomNodeTypeDescriptionsFromDB()"<<std::endl;
370     for (DicomNode::Type type=DicomNode::Patient; 
371          type<=DicomNode::Image; 
372          type++)
373       {
374         std::string query = "SELECT * FROM ";
375         query += DicomDatabaseStructure::Table(type);
376         query += "_FIELDS";
377
378         //      std::cout << "** SQL = '"<<query<<"'"<<std::endl;
379         
380         CppSQLite3Query q;
381         QUERYDB(query,q);
382         
383
384         while (!q.eof())
385           {
386             Field::Description d(q.getStringField(0), // Key
387                                  q.getIntField(1), // Group
388                                  q.getIntField(2), // Element 
389                                  q.getStringField(3), // Name
390                                  q.getIntField(4) // Flags
391                                  );
392             GetDicomNodeTypeDescription(type).GetFieldDescriptionMap()[d.key] = d;
393             //      std::cout << d << std::endl;
394             q.nextRow();
395           }
396       }
397   }
398   //=====================================================================
399
400   //=====================================================================
401   bool DicomDatabase::Close()
402   {
403     //    std::cout << "***> DicomDatabase::Close"<<std::endl;
404
405     //    std::cout << "<*** DicomDatabase::Close"<<std::endl;
406           return true;
407   }
408   //=====================================================================
409   /*
410   //=====================================================================
411   bool DicomDatabase::LoadAll()
412   {
413     DBLoadChildren(0,true);
414     mDBLoaded = true;
415     return true;
416   }
417   //=====================================================================
418
419   //=====================================================================
420   bool DicomDatabase::LoadUpToSeries()
421   {
422     //return LoadAll();
423     DBLoadChildren(0,false);
424     //    mDBLoaded = true;
425     return true;
426   }
427   //=====================================================================
428   
429   //=====================================================================
430   bool DicomDatabase::LoadSeriesImages(DicomNode* series)
431   {
432     //    return false;
433     
434     if (!series->GetType()==DicomNode::Series) 
435       {
436         return false;
437         creaError("DicomDatabase::LoadSeriesImages : node is not a series !");
438       }
439     DBLoadChildren(series,true);
440     return true;
441   }
442   //=====================================================================
443 */
444
445   //=====================================================================
446   void DicomDatabase::Clear()
447   {
448   }
449   //=====================================================================
450   
451
452   //=====================================================================
453   int DicomDatabase::DBLoadChildren(DicomNode* parent, 
454                                      DicomNode::Type maxlevel)
455   {
456
457     //    std::cout << "DicomDatabase::DBLoadChildren("<<parent<<","<<maxlevel
458     //        << ")"<<std::endl;
459     int nbloaded = 0;
460     if (parent == this) { parent = 0; }
461     DicomNode* xparent = parent;
462     if ( xparent==0 ) xparent = this;
463     if ( xparent->ChildrenLoaded() ) 
464       {
465         //      std::cout << "--> Children already loaded"<<std::endl;
466         return nbloaded;
467       }
468     if ( xparent->GetType() == DicomNode::Image ) 
469       {
470         return nbloaded;
471       }
472     if ( xparent->GetType() >= maxlevel ) 
473       {
474         return nbloaded;
475       }
476     /*
477     if ( parent->ChildrenLoaded().size()>0 ) 
478       { 
479         DicomNode* n = this;
480         if (parent) n = parent;
481         // If parent already has loaded its children 
482         // simply recurse to its children 
483         DicomNode::ChildrenListType::iterator i;
484         for (i  = n->GetChildrenList().begin();
485              i != n->GetChildrenList().end();
486              i++)
487           {
488             DBLoadChildren(*i,load_images);
489           }
490         return;
491       }
492     */
493     msw[2].Pause();
494     msw[2].Resume();
495     DicomNode::Type type = xparent->GetType()+1;
496
497     // Query DB
498
499     std::string query = "SELECT * FROM ";
500     query += DicomDatabaseStructure::Table(type);
501     if (parent!=0)
502       {
503         query += " WHERE PARENT_ID='" + parent->GetFieldValue("ID") + "'";
504       }
505
506     //    std::cout << "** SQL = '"<<query<<"'"<<std::endl;
507
508     CppSQLite3Query q;
509     QUERYDB(query,q);
510
511     while (!q.eof())
512       {
513         nbloaded++;
514         DicomNode* n = new DicomNode(type,
515                            this,xparent);
516         for (int fld = 0; fld < q.numFields(); fld++)
517           {
518             n->SetFieldValue(q.fieldName(fld),q.getStringField(fld));       
519           }
520         // Index 
521         TypeId ti;
522         ti.type = type;
523         ti.id = n->GetFieldValue("ID");    
524         mTypeIdToDicomNodeMap[ti] = n;
525         // recurse 
526         if ( type < maxlevel ) 
527           {
528             msw[2].Pause();
529             nbloaded += DBLoadChildren(n,maxlevel);
530             msw[2].Resume();
531           }
532         // next entry in db
533         q.nextRow();
534       }
535
536     xparent->SetChildrenLoaded(true);
537     
538     msw[2].Pause();
539     return nbloaded;
540   }
541   //=====================================================================
542
543
544
545   //=====================================================================
546   bool DicomDatabase::DBStructureIsValid()
547   {
548     bool success = true;
549     for (int i = DicomDatabaseStructure::TableBegin();
550          i    != DicomDatabaseStructure::TableEnd();++i)
551       {
552         bool ok = mDB->tableExists(DicomDatabaseStructure::Table(i));
553         if (ok) 
554           {
555             //      std::cout << "** Table "<<DicomDatabaseStructure::Table(i)
556             //                <<" exists"<<std::endl;
557             // TO DO : TEST MANDATORY FIELDS EXIST
558           }
559         else 
560           {
561             //      std::cout << "** Table "<<DicomDatabaseStructure::Table(i)
562             //                <<" does *NOT* exist"<<std::endl;
563             success = false;
564           }
565       }
566     return success;
567   }  
568   //=====================================================================
569
570
571
572   //=====================================================================
573   bool DicomDatabase::DBInsert(DicomNode* alien_node,
574                                UpdateSummary& summary)
575   {
576     //    std::cout << "DicomDatabase::Insert('"<<alien_node->GetLabel()
577     //        <<"')"<<std::endl;
578     
579     //    if (!ChildrenLoaded()) DBLoadChildren(this,DicomNode::Database);
580     
581     // Find parent
582     DicomNode* parent;
583     std::string parent_id;
584     parent = DBGetOrCreateParent(alien_node,parent_id,summary);
585     
586     // Insert 
587     DBRecursiveGetOrCreateDicomNode(alien_node,parent,parent_id,summary);
588    return true;
589   }
590   //=====================================================================
591
592
593   //=====================================================================
594   DicomNode* DicomDatabase::DBGetOrCreateParent(DicomNode* alien_node,
595                                                 std::string& parent_id,
596                                                 UpdateSummary& summary)
597   {
598     //    std::cout << "DBGetOrCreateParent '" << alien_node->GetLabel()<<"'"
599     //        << std::endl;
600     // Load the patients if not already done
601     DBLoadChildren(this,DicomNode::Patient);
602
603     parent_id = "";
604     DicomNode* parent = this;
605
606     // The chain of ancestors
607     std::deque<DicomNode*> chain;
608     DicomNode* cur = alien_node->GetParent();
609     for (int type=DicomNode::Patient; 
610          type<alien_node->GetType();++type)
611       {
612         chain.push_front(cur);
613         cur = cur->GetParent();
614       }
615
616     // create the nodes if do not exist
617     std::deque<DicomNode*>::iterator i;
618     for (i=chain.begin();i!=chain.end();++i)
619       {
620         //      std::cout << " cur = '"<<(*i)->GetLabel()<<"'"<<std::endl;
621         //      std::string cur_id = DBGetDicomNodeId(*i,parent_id);
622         //      if (cur_id.size()==0)
623         //        {
624         // Node does not exist : create it
625         std::string cur_id;
626         parent = DBGetOrCreateDicomNode(*i, 
627                                         parent,
628                                         parent_id,
629                                         cur_id,
630                                         summary
631                                         );
632         DBLoadChildren(parent,parent->GetType()+1);
633         /*        }
634           else
635           {
636             // Node exists : get it and load its children
637             parent = GetDicomNodeFromTypeId((*i)->GetType(),cur_id);
638             DBLoadChildren(parent,parent->GetType()+1);
639           }
640         */
641         parent_id = cur_id;
642       }
643     return parent;
644   }
645   //=====================================================================
646
647
648
649   //=====================================================================
650   void DicomDatabase::DBRecursiveGetOrCreateDicomNode(DicomNode* alien_node, 
651                                                       DicomNode* parent, 
652                                                       const std::string& parent_id,
653                                                       UpdateSummary& summary)
654   {
655     //    std::cout << "DicomDatabase::RecursiveGetOrCreateDicomNode('"
656     //        <<alien_node->GetLabel()
657     //        <<"','"<<parent<<"','"<<parent_id<<"')"<<std::endl;
658     if (parent != 0) 
659       {
660         //      std::cout << " -- Parent = '"<<parent->GetLabel()<<"'"<<std::endl;
661       }   
662     std::string new_id;
663     DicomNode* new_node = DBGetOrCreateDicomNode(alien_node, 
664                                                  parent,
665                                                  parent_id,
666                                                  new_id,
667                                                  summary);
668     DicomNode::ChildrenListType::iterator i;
669     for (i  = alien_node->GetChildrenList().begin();
670          i != alien_node->GetChildrenList().end();
671          i++)
672       {
673         DBRecursiveGetOrCreateDicomNode((*i),new_node,new_id,summary);
674       }
675   }
676   //=====================================================================
677
678
679   //=====================================================================
680   DicomNode* DicomDatabase::DBGetOrCreateDicomNode(DicomNode* alien_node, 
681                                                    DicomNode* internal_parent,
682                                                    std::string parent_id,
683                                                    std::string& node_id,
684                                                    UpdateSummary& summary)
685   {
686     //   std::cout << "DBGetOrCreateDicomNode('"<<alien_node->GetLabel()<<"','"
687     //        << internal_parent << "','"<< parent_id<<"')"<<std::endl;
688     if (internal_parent != 0) 
689       {
690         //      std::cout << " -- Parent = '"<<internal_parent->GetLabel()<<"'"<<std::endl;
691       }
692     // DicomNode Exists ? return it 
693     // First try among children of internal parent 
694     DicomNode* node = GetChildrenLike(internal_parent,alien_node);
695     if (node>0)
696       {
697         node_id = node->UnsafeGetFieldValue("ID");
698         return node;
699       }
700     // Second : try in DB 
701     /*
702     node_id = DBGetDicomNodeId(alien_node,parent_id);
703     if (node_id.size()>0)
704       {
705         msw[4].Pause();
706         return GetDicomNodeFromTypeId(alien_node->GetType(),node_id);
707       }
708     */
709     // Does not exist : Create new one
710     node = new DicomNode(alien_node->GetType(),this,internal_parent);
711     node->SetChildrenLoaded(true);
712     // Copy fields values from alien
713     DicomNode::FieldValueMapType::iterator i,j;
714     for (i =  node->GetFieldValueMap().begin();
715          i != node->GetFieldValueMap().end();
716          i++)
717       {
718         j = alien_node->GetFieldValueMap().find(i->first);
719         if (j != alien_node->GetFieldValueMap().end() )
720           {
721             i->second = j->second;
722           }
723       }
724
725     msw[2].Resume();
726     if (node->GetType()!=DicomNode::Patient) 
727       node->SetFieldValue("PARENT_ID",parent_id);
728
729     // Insert in DB
730     std::string val;
731     BuildSQLFieldsValues(node,val);
732     std::string insert("INSERT INTO ");
733     insert += std::string(DicomDatabaseStructure::Table(node->GetType())) 
734       + " " + val + ";";
735     //    std::cout << "** SQL = '"<<insert<<"'"<<std::endl;
736     UPDATEDB(insert);
737     //    std::cout << "** SQL OK"<<std::endl;
738
739     // Store DB id of newly created node;
740     long lastrow = mDB->lastRowId();
741     std::stringstream ri;
742     ri << mDB->lastRowId();
743     node_id = ri.str();
744     //    std::cout << "LastRowId='"<<mDB->lastRowId()<<"' vs '"<<created_id<<"'"<<std::endl;
745
746     node->SetFieldValue("ID",node_id);
747     // Insert in TypeId map
748     TypeId ti;
749     ti.type = node->GetType();
750     ti.id = node_id;
751     mTypeIdToDicomNodeMap[ti] = node;
752     //    std::cout << "== Insert TypeId ("<<ti.type<<","<<ti.id<<")"<<std::endl; 
753     // 
754     msw[2].Pause();
755
756     if (node->GetType()==DicomNode::Patient) summary.added_patients++;
757     if (node->GetType()==DicomNode::Study) summary.added_studies++;
758     if (node->GetType()==DicomNode::Series) summary.added_series++;
759     if (node->GetType()==DicomNode::Image) summary.added_images++;
760
761     return node;
762   }
763   //=====================================================================
764
765   //=====================================================================
766   DicomNode* DicomDatabase::GetChildrenLike(DicomNode* parent,
767                                             DicomNode* alien_node)
768   {
769     DicomNode::ChildrenListType::iterator i;
770     for (i  = parent->GetChildrenList().begin();
771          i != parent->GetChildrenList().end();
772          i++)
773       {
774         DicomNode::Type type = alien_node->GetType();
775         bool ok = true;
776         for (int j=0;
777              j<DicomDatabaseStructure::NbQueryFields(type);
778              j++) 
779           {
780             if ( 
781                 alien_node->GetFieldValue(DicomDatabaseStructure::
782                                     QueryField(type,j).key )   !=
783                 (*i)->GetFieldValue(DicomDatabaseStructure::
784                                     QueryField(type,j).key ) )
785               {
786                 ok = false;
787                 break;
788               }
789           }
790         if (ok) 
791           {
792             return (*i);
793           }
794       }
795     return 0;    
796   }
797   //=====================================================================
798
799   //=====================================================================
800   std::string DicomDatabase::DBGetDicomNodeId(DicomNode* node, 
801                                               const std::string& parent_id)
802   {
803     //    std::cout << "DicomDatabase::DBGetDicomNodeId('"<<node->GetLabel()
804     //        <<"','"<<parent_id<<"')"
805     //        <<std::endl;
806     msw[2].Resume();
807     int type = node->GetType();
808
809     std::string table = DicomDatabaseStructure::Table(type);
810     std::string where = "WHERE ";
811     
812     if (type!=DicomNode::Patient)
813       {
814         where += "PARENT_ID='" + parent_id 
815           //node->GetFieldValue("PARENT_ID") 
816           + "' AND ";
817       }
818
819     for (int i=0;i<DicomDatabaseStructure::NbQueryFields(type);i++) 
820       {
821         where += DicomDatabaseStructure::QueryField(type,i).key + "='"
822           + node->GetFieldValue(DicomDatabaseStructure::QueryField(type,i).key) + "' ";
823         if (i<DicomDatabaseStructure::NbQueryFields(type)-1)
824           where += "AND ";
825     }
826
827     std::string query = "SELECT ID FROM " + table + " " + where + ";";                                                    
828     //    std::cout << "** SQL = '"<<query<<"'"<<std::endl;
829     CppSQLite3Query q;
830     QUERYDB(query,q);
831
832     if (!q.eof())
833       {
834         
835         //      std::cout << " - DicomNode exists " << std::endl;
836         std::string id = q.getStringField(0);
837         //      std::cout << " id = '"<<id<<"'"<<std::endl;
838         msw[2].Pause();
839         return id;
840       }
841     msw[2].Pause();
842     return "";
843   }
844   //=====================================================================
845
846
847
848   //=====================================================================
849   DicomNode* DicomDatabase::GetDicomNodeFromTypeId(DicomNode::Type type,
850                                        const std::string& id)
851   {
852     //    std::cout << "GetDicomNodeFromTypeId("<<type<<","<<id<<")"<<std::endl;
853
854     TypeId ti;
855     ti.type = type;
856     ti.id = id;
857     
858     TypeIdToDicomNodeMapType::iterator i = mTypeIdToDicomNodeMap.find(ti);
859     if (i == mTypeIdToDicomNodeMap.end())
860       {
861         /*
862         LoadAll();
863         i = mTypeIdToDicomNodeMap.find(ti);
864         if (i == mTypeIdToDicomNodeMap.end())
865           {
866         */
867             std::cout << "Internal error : mTypeIdToDicomNodeMap does not contain key"
868                       << std::endl;
869             creaError("Internal error : mTypeIdToDicomNodeMap does not contain key");
870             // }
871       }
872
873     //    std::cout << " ** DicomNode = "<<i->second<<std::endl;
874     return i->second;
875   }
876   //=====================================================================
877
878   //=====================================================================
879   bool DicomDatabase::Remove(DicomNode* node)
880   {
881     DBRecursiveRemoveDicomNode(node);
882     /*
883     if (node->GetType()==DicomNode::Patient)
884       {
885         //      std::cout << "IS PATIENT"<<std::endl;
886         ChildrenListType::iterator i = find(GetChildrenList().begin(),
887                                            GetChildrenList().end(),
888                                            node);
889         //      std::cout << "ERASE"<<std::endl;
890         GetChildrenList().erase(i);
891         //      std::cout << "ERASE OK"<<std::endl;
892       }
893     */
894     //    std::cout << "DELETE"<<std::endl;
895     if (node->GetParent())
896       {
897         node->GetParent()->RemoveChildrenFromList(node);
898       }
899     delete node;
900     //    std::cout << "DELETE OK"<<std::endl;
901     return true;
902   }
903   //========================================================================
904
905   //=====================================================================
906   void DicomDatabase::DBRecursiveRemoveDicomNode(DicomNode* node)
907   {
908     //    std::cout << "DicomDatabase::DBRecursiveRemoveDicomNode('"
909     //        <<node->GetLabel()<<"')"<<std::endl;
910
911     std::string query = "DELETE FROM ";
912     query += DicomDatabaseStructure::Table(node->GetType());
913     query += " WHERE ID='"+ node->GetFieldValue("ID") + "';";
914  
915     UPDATEDB(query);
916
917     DicomNode::ChildrenListType::iterator i;
918     for (i  = node->GetChildrenList().begin();
919          i != node->GetChildrenList().end();
920          i++)
921       {
922         DBRecursiveRemoveDicomNode((*i));
923       }
924   }
925   //=====================================================================
926   
927   //=====================================================================
928   int DicomDatabase::DBQueryNumberOfChildren(DicomNode* node)
929   {
930     std::string query = "SELECT COUNT (ID) FROM ";
931     query += DicomDatabaseStructure::Table(node->GetType()+1);
932     if (node->GetType() != DicomNode::Database) 
933       {
934         query += " WHERE PARENT_ID='"+ node->GetFieldValue("ID")+"'";
935       }
936     query  += ";";
937     
938     //   std::cout << "**SQL = "<< query << std::endl;
939     
940     CppSQLite3Query q;
941     QUERYDB(query,q);
942    
943      //    std::cout << "**RES = "<< q.getIntField(0) <<std::endl;
944
945     return q.getIntField(0);
946   }
947   //=====================================================================
948  
949   //========================================================================
950   std::string& format_sql2(std::string& str)
951   {
952     // quote must be doubled
953     //    crea::Utils::Replace( str, "'", "''" );
954     boost::algorithm::replace_all(str,"'","''");
955     // Found strange strings which contained NULL char INSIDE string 
956     int i,size=str.size();
957     for (i=0;i<size;++i) 
958       {
959         if (str[i]==0) 
960           {
961             str = str.substr(0,i);
962             break;
963           }
964       }
965     //    if (i<str.size())
966     return str;
967   }
968    //========================================================================
969
970   //=====================================================================
971   void DicomDatabase::BuildSQLFieldsValues(DicomNode* n,
972                                         std::string& str)
973   {
974     //    std::cout << "BuildSQLFieldsValues('"<<n->GetLabel()<<"')"<<std::endl;
975
976     std::string atts="";
977     std::string values="";
978     DicomNode::FieldValueMapType::iterator i;
979     for (i =  n->GetFieldValueMap().begin();
980          i != n->GetFieldValueMap().end();
981          i++)
982       {
983         if (i->first=="ID") 
984           {
985             continue;
986           }
987         //      std::cout << "("<<i->first<<","<<i->second<<")"<<std::endl;
988         atts += "'" + i->first + "'";
989         values += "'" + format_sql2(i->second) + "'"; 
990         atts += ",";
991         values += ",";
992       }
993     atts[atts.size()-1]=' ';
994     values[values.size()-1]=' ';
995
996     str = "("+atts+") VALUES ("+values+")";
997
998   }
999   //=====================================================================
1000
1001   //=====================================================================
1002   bool DicomDatabase::IsHandledFile( const std::string& filename)
1003   {
1004     return (mReader.CanRead(filename,""));
1005   }
1006   //=====================================================================
1007
1008   //=====================================================================
1009   bool DicomDatabase::AddFiles( const std::vector<std::string>& filenames,
1010                                 wxProgressDialog* progress, 
1011                                 UpdateSummary& summary)
1012   {
1013      for (int swi=0;swi<10;swi++) 
1014       {
1015         msw[swi].Start(0);
1016         msw[swi].Pause();
1017       }
1018     
1019     // Parse directory
1020     wxStopWatch sw; 
1021
1022
1023      summary.added_images = 0;
1024      unsigned int nbf = filenames.size(); 
1025      std::vector<std::string>::const_iterator i;
1026      for (i=filenames.begin();i!=filenames.end();++i)
1027        {
1028          summary.scanned_files++;
1029          if (IsHandledFile(*i)) 
1030            {
1031              summary.handled_images++;
1032              AddFile(*i,summary);
1033              
1034              if (progress)
1035                {
1036                  std::string mess("Adding ");
1037                  mess += *i;
1038                  if (!progress->Update( (int)(summary.added_images*999./nbf),
1039                                         std2wx(mess)))
1040                    {
1041                      // Some file was added hence we must return true !
1042                      summary.cancelled_by_user = true;
1043                      break;
1044                    }
1045                }
1046            }
1047        }
1048
1049
1050      sw.Pause();
1051      msw[0].Pause();
1052      msw[1].Pause();
1053      msw[2].Pause();
1054
1055      summary.total_time = sw.Time();
1056      summary.file_scan_time = msw[1].Time();
1057      summary.update_database_time = msw[2].Time();
1058      summary.update_structs_time = 
1059        summary.total_time - 
1060        summary.parse_time - 
1061        summary.file_scan_time - 
1062        summary.update_database_time;
1063      
1064      return true;
1065   }
1066   //=====================================================================
1067
1068   //=====================================================================
1069   bool DicomDatabase::AddFile( const std::string& filename,
1070                                UpdateSummary& summary)
1071    {
1072      // std::cout << "** DicomDatabase::AddFile '"<<filename<<"'"<<std::endl;
1073      // Create *unknown* patient / study / series 
1074      DicomNode* patient = new DicomNode(DicomNode::Patient,this,0);
1075      //  FillFields/*<GDCM_NAME_SPACE::DicomDir>*/(patient,0);
1076      //  patient->SetData(new DirDicomNodeData());
1077      // Insert 
1078      //GetChildrenList().push_back(patient);
1079      DicomNode* study = new DicomNode(DicomNode::Study,this,patient);
1080      //FillFields/*<GDCM_NAME_SPACE::DicomDir>*/(study,0);
1081      //study->SetData(new DirDicomNodeData());
1082      DicomNode* series = new DicomNode(DicomNode::Series,this,study);
1083      //FillFields/*<GDCM_NAME_SPACE::DicomDir>*/(series,0);
1084      //series->SetData(new DirDicomNodeData());
1085      DicomNode* image = new DicomNode(DicomNode::Image,this,series);
1086      
1087      msw[1].Resume();
1088      mReader.ReadDicomInfo(filename,image);
1089      msw[1].Pause();
1090
1091      image->SetFieldValue("FullFileName",filename);
1092      
1093      DBInsert(patient,summary);
1094      
1095      delete patient;
1096      
1097      //image->SetData(new DirDicomNodeData());
1098      // Fill Fields
1099      //FillFields/*<GDCM_NAME_SPACE::DicomDir>*/(image,0);
1100      //std::string f(Utilities::GetFileName(*i));
1101      //image->SetFieldValue("A0004_1500",cclean(f));
1102      //image->SetFieldValue("FullFileName",cclean(*i));   
1103      return true;
1104    }
1105   //=====================================================================
1106
1107   //=====================================================================
1108   /**
1109    * \brief   Explore a directory with possibility of recursion
1110    *          return number of files read
1111    * @param  dirpath   directory to explore
1112    * @param  recursive whether we want recursion or not
1113    */
1114   void DicomDatabase::ParseDirectory( const std::string &dirpath, 
1115                                       std::vector<std::string> &Filenames,
1116                                       bool recursive,
1117                                       wxProgressDialog* progress, 
1118                                       UpdateSummary& summary)
1119     
1120   {
1121     if (progress) 
1122       {
1123                 std::string mess("Parsing ");
1124                 mess += dirpath;
1125                 progress->Pulse(std2wx(mess));
1126       }
1127
1128         wxStopWatch sw; 
1129         sw.Start(0);
1130     
1131     std::string fileName;
1132     std::string dirName = dirpath;
1133
1134     summary.scanned_dirs++;
1135
1136     wxDir dir( std2wx(dirpath) );
1137
1138     if ( !dir.IsOpened() )
1139     {
1140         // deal with the error here - wxDir would already log an error message
1141         // explaining the exact reason of the failure
1142         return;
1143     }
1144
1145     wxString filename;
1146
1147     bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN );
1148     while ( cont )
1149     {
1150                 if ((progress)&&( sw.Time() >= 250 )) 
1151                 {
1152 //                      std::cout << "PULSE"<<std::endl;
1153                         sw.Start(0);
1154                         if (!progress->Pulse()) 
1155                         {
1156                                 summary.cancelled_by_user = true;
1157                                 break;
1158                         }
1159                 }
1160
1161                 summary.scanned_files++;
1162                 wxFileName wxffn(dir.GetName(),filename);
1163                 std::string ffn = wx2std(wxffn.GetFullPath());
1164 //              std::cout << ffn << std::endl;
1165                 if (mReader.CanRead(ffn,""))
1166                         {
1167                                 Filenames.push_back( ffn );
1168                                 summary.handled_images++;
1169                         }
1170         cont = dir.GetNext(&filename);
1171     }
1172
1173         // Recurse into subdirs
1174         if ( recursive )
1175                 {
1176                         cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN );
1177                         while ( cont )
1178                         {
1179
1180                                 wxFileName wxffn(dir.GetName(),filename);
1181                                 std::string ffn = wx2std(wxffn.GetFullPath());
1182                 
1183 //                              std::cout << "dir="<< ffn<< std::endl;
1184
1185                                 ParseDirectory( ffn, 
1186                                                                 Filenames,
1187                                                                 recursive,
1188                                                                 progress,
1189                                                                 summary);
1190                                 if (summary.cancelled_by_user) break;
1191
1192                                 cont = dir.GetNext(&filename);
1193                         }
1194                 }
1195     
1196   }
1197   //=======================================================================
1198
1199
1200   //=====================================================================
1201   bool DicomDatabase::AddDirectory( const std::string& directory,
1202                                     bool recurse,
1203                                     wxProgressDialog* progress, 
1204                                     UpdateSummary& summary
1205                                     )
1206   {
1207     //    std::cout << "** DicomDatabase::AddDirectory"
1208     //        << " '"<<directory<<"'"<<std::endl;
1209     //    std::cout << "------ Parsing directory ------"<<std::endl;
1210     
1211     if (progress)
1212       {
1213         progress->Pulse();
1214       }
1215
1216     for (int swi=0;swi<10;swi++) 
1217       {
1218         msw[swi].Start(0);
1219         msw[swi].Pause();
1220       }
1221     
1222     // Parse directory
1223     wxStopWatch sw; 
1224     
1225     bool was_canceled_by_user(false);
1226     std::vector<std::string> filenames;
1227     ParseDirectory( directory, 
1228                     filenames,
1229                     recurse, 
1230                     progress,
1231                     summary);
1232     
1233     if ( summary.cancelled_by_user ) 
1234       {
1235         return false;
1236       }
1237     
1238     summary.parse_time = sw.Time();
1239
1240
1241      summary.added_images = 0;
1242      unsigned int nbf = filenames.size(); // , nf = 0;
1243      std::vector<std::string>::iterator i;
1244      for (i=filenames.begin();i!=filenames.end();++i)
1245        {
1246          AddFile(*i,summary);
1247
1248          if (progress)
1249            {
1250              std::string mess("Adding ");
1251              mess += *i;
1252              if (!progress->Update( (int)(summary.added_images*999./nbf),
1253                                     std2wx(mess)))
1254                {
1255                  // Some file was added hence we must return true !
1256                  summary.cancelled_by_user = true;
1257                  break;
1258                }
1259            }
1260        }
1261
1262
1263      sw.Pause();
1264      msw[0].Pause();
1265      msw[1].Pause();
1266      msw[2].Pause();
1267
1268      summary.total_time = sw.Time();
1269      summary.file_scan_time = msw[1].Time();
1270      summary.update_database_time = msw[2].Time();
1271      summary.update_structs_time = 
1272        summary.total_time - 
1273        summary.parse_time - 
1274        summary.file_scan_time - 
1275        summary.update_database_time;
1276      
1277      return true;
1278   }
1279   //=====================================================================
1280
1281
1282
1283
1284
1285
1286
1287 } // namespace creaImageIO