otsdaq-utilities  3.08.00
ConfigurationGUISupervisor.cc
1 #include "otsdaq-utilities/ConfigurationGUI/ConfigurationGUISupervisor.h"
2 
3 #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
4 #include "otsdaq/Macros/CoutMacros.h"
5 #include "otsdaq/MessageFacility/MessageFacility.h"
6 #include "otsdaq/TablePlugins/IterateTable.h"
7 #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
8 
9 #include <boost/stacktrace.hpp>
10 
11 #include "otsdaq/GatewaySupervisor/GatewaySupervisor.h" //for saveModifiedVersionXML()
12 #include "otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.h" //for artdaq extraction
13 #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h" //for context relaunch
14 
15 #include <xdaq/NamespaceURI.h>
16 
17 #include <chrono>
18 #include <iostream>
19 #include <map>
20 #include <utility>
21 
22 using namespace ots;
23 
24 #undef __MF_SUBJECT__
25 #define __MF_SUBJECT__ "CfgGUI"
26 
27 #define TABLE_INFO_PATH std::string(__ENV__("TABLE_INFO_PATH")) + "/"
28 #define TABLE_INFO_EXT std::string("Info.xml")
29 
32 xdaq::Application* ConfigurationGUISupervisor::instantiate(xdaq::ApplicationStub* stub)
33 {
34  return new ConfigurationGUISupervisor(stub);
35 }
36 
37 //==============================================================================
42  : CoreSupervisorBase(stub)
43 {
44  __SUP_COUT__ << "Constructor started." << __E__;
45 
46  INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
47 
48  // make macro directories in case they don't exist
49  mkdir(((std::string)ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH).c_str(), 0755);
50 
51  init();
52  __SUP_COUT__ << "Constructor complete." << __E__;
53 } // end constructor()
54 
55 //==============================================================================
56 ConfigurationGUISupervisor::~ConfigurationGUISupervisor(void) { destroy(); }
57 
58 //==============================================================================
59 void ConfigurationGUISupervisor::init(void)
60 {
61  __SUP_COUT__ << "Initializing..." << __E__;
62 
63  try
64  {
65  __SUP_COUT__ << "Activating saved context, which may prepare for normal mode..."
66  << __E__;
67 
68  testXDAQContext(); // test context group activation
69 
70  __SUP_COUT__ << "Done with test context." << __E__;
71  }
72  catch(...)
73  {
74  __COUT_WARN__ << "Failed test context group activation. otsdaq, in Normal mode, "
75  "will not launch when this test fails. "
76  << "Check the active context group from within Wizard Mode."
77  << __E__;
78  }
79 
80  //initialize first config manager (pre-load for first user)
81  refreshUserSession("" /* userInfo.username_ */, 1); // (refresh == "1"));
82  //after this call, empty username : index=0 is in map userConfigurationManagers_[:0]
83 
84  if(CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
85  {
86  __SUP_COUT_INFO__
87  << "After successful Config GUI init, marking alive for wiz mode!" << __E__;
88  CorePropertySupervisorBase::
89  indicateOtsAlive(); // no parameters for wiz mode indication
90  }
91 } // end init()
92 
93 //==============================================================================
94 void ConfigurationGUISupervisor::destroy(void)
95 {
96  __SUP_COUT__ << "Destructing..." << __E__;
97 
98  // called by destructor
99  for(std::map<std::string, ConfigurationManagerRW*>::iterator it =
100  userConfigurationManagers_.begin();
101  it != userConfigurationManagers_.end();
102  ++it)
103  {
104  delete it->second;
105  it->second = 0;
106  }
107  userConfigurationManagers_.clear();
108 
109  if(ConfigurationInterface::getInstance() != nullptr)
110  delete ConfigurationInterface::getInstance();
111 
112 } // end destroy()
113 
114 //==============================================================================
115 void ConfigurationGUISupervisor::defaultPage(xgi::Input* in, xgi::Output* out)
116 {
117  cgicc::Cgicc cgiIn(in);
118  std::string configWindowName =
119  CgiDataUtilities::getData(cgiIn, "configWindowName"); // from GET
120  if(configWindowName == "tableEditor")
121  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
122  "src='/WebPath/html/ConfigurationTableEditor.html?urn="
123  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
124  if(configWindowName == "iterate")
125  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
126  "src='/WebPath/html/Iterate.html?urn="
127  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
128  else
129  *out << "<!DOCTYPE HTML><html lang='en'><frameset col='100%' row='100%'><frame "
130  "src='/WebPath/html/ConfigurationGUI.html?urn="
131  << this->getApplicationDescriptor()->getLocalId() << "'></frameset></html>";
132 } // end defaultPage()
133 
134 //==============================================================================
138 {
139  CorePropertySupervisorBase::setSupervisorProperty(
140  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
141  "*=10 | deleteTreeNodeRecords=255 | saveTableInfo=255 | "
142  "deleteTableInfo=255"); // experienced users to edit, admins to delete
143 
144  CorePropertySupervisorBase::setSupervisorProperty(
145  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
146  "*"); // all
147 
148  //Allow get* AutomatedRequestTypes to enable read-only access to the Configuration Tree:
149  CorePropertySupervisorBase::setSupervisorProperty(
150  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, "get*");
151 } // end setSupervisorPropertyDefaults()
152 
153 //==============================================================================
157 {
158  CorePropertySupervisorBase::addSupervisorProperty(
159  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
160  "getActiveTableGroups");
161 
162  //Allow get* to not require lock to enable read-only access to the Configuration Tree:
163  CorePropertySupervisorBase::setSupervisorProperty(
164  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes,
165  "!get*"); // all except read-only requests
166 } // end forceSupervisorPropertyValues()
167 
168 //==============================================================================
169 void ConfigurationGUISupervisor::request(const std::string& requestType,
170  cgicc::Cgicc& cgiIn,
171  HttpXmlDocument& xmlOut,
172  const WebUsers::RequestUserInfo& userInfo)
173 try
174 {
175  // Commands
176 
177  __COUTTV__(requestType);
178 
179  // gatewayLaunchOTS -- and other StartOTS commands
180 
181  // saveTableInfo
182  // deleteTableInfo
183  // flattenToSystemAliases
184  // versionTracking
185  // getColumnTypes
186  // getGroupAliases
187  // setGroupAliasInActiveBackbone
188  // setTableAliasInActiveBackbone
189  // setAliasOfGroupMembers
190  // getVersionAliases
191  // getTableGroups
192  // getTableGroupType
193  // getTables
194  // getContextMemberNames
195  // getBackboneMemberNames
196  // getIterateMemberNames
197  // getSpecificTableGroup
198  // saveNewTableGroup
199  // getSpecificTable
200  // saveSpecificTable
201  // clearTableTemporaryVersions
202  // clearTableCachedVersions
203  // getGroupHistory
204  //
205  // ---- associated with JavaScript Table API
206  // getTreeView
207  // getTreeNodeCommonFields
208  // getUniqueFieldValuesForRecords
209  // getTreeNodeFieldValues
210  // setTreeNodeFieldValues
211  // addTreeNodeRecords
212  // deleteTreeNodeRecords
213  // renameTreeNodeRecords
214  // copyTreeNodeRecords
215  // getTableStructureStatusAsJSON
216  // ---- end associated with JavaScript Table API
217  //
218  // ---- associated with JavaScript artdaq API
219  // getArtdaqNodes
220  // saveArtdaqNodes
221  // getArtdaqNodeLayout
222  // saveArtdaqNodeLayout
223  // ---- end associated with JavaScript artdaq API
224  //
225  // activateTableGroup
226  // getActiveTableGroups
227  // copyViewToCurrentColumns
228  // saveTreeNodeEdit
229  // checkAffectedActiveGroups (formerly 'getAffectedActiveGroups' but then was considered automated req, when it is more correctly associated with write tasks)
230  // getLinkToChoices
231  // getLastTableGroups
232  // getSubsytemTableGroups
233  // diffWithActiveGroup
234  // diffWithGroupKey
235  // diffTableVersions
236  // findGroupsWithTable
237  // SearchFieldInGroup
238  // SearchFieldInTableVersions
239  // mergeGroups
240  //
241  // ---- associated with JavaScript Iterate App
242  // savePlanCommandSequence
243  // ---- end associated with JavaScript Iterate App
244 
245  // acquire user's configuration manager based on username& activeSessionIndex
246  std::string refresh = CgiDataUtilities::getData(cgiIn, "refresh"); // from GET
247 
248  // refresh to reload from info files and db (maintains temporary views!)
249  ConfigurationManagerRW* cfgMgr =
250  refreshUserSession(userInfo.username_, (refresh == "1"));
251  __COUTTV__(userInfo.username_);
252  __COUTTV__(cfgMgr->getUsername());
253 
254  if(0) //for debugging/optimizing cache resets!
255  {
256  const GroupInfo& groupInfo = cfgMgr->getGroupInfo("MC2TriggerContext");
257  const std::set<TableGroupKey>& sortedKeys = groupInfo.getKeys(); // rename
258  __COUTTV__(sortedKeys.size());
259  }
260 
261  if(requestType == "saveTableInfo")
262  {
263  std::string tableName =
264  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
265  std::string columnCSV =
266  CgiDataUtilities::postData(cgiIn, "columnCSV"); // from POST
267  std::string allowOverwrite =
268  CgiDataUtilities::getData(cgiIn, "allowOverwrite"); // from GET
269  std::string tableDescription =
270  CgiDataUtilities::postData(cgiIn, "tableDescription"); // from POST
271  std::string columnChoicesCSV =
272  CgiDataUtilities::postData(cgiIn, "columnChoicesCSV"); // from POST
273 
274  __SUP_COUT__ << "tableName: " << tableName << __E__;
275  __SUP_COUT__ << "columnCSV: " << columnCSV << __E__;
276  __SUP_COUT__ << "tableDescription: " << tableDescription << __E__;
277  __SUP_COUT__ << "columnChoicesCSV: " << columnChoicesCSV << __E__;
278  __SUP_COUT__ << "allowOverwrite: " << allowOverwrite << __E__;
279 
280  if(!allSupervisorInfo_.isWizardMode())
281  {
282  __SUP_SS__ << "Improper permissions for saving table info." << __E__;
283  xmlOut.addTextElementToData("Error", ss.str());
284  }
285  else
286  handleSaveTableInfoXML(xmlOut,
287  cfgMgr,
288  tableName,
289  columnCSV,
290  tableDescription,
291  columnChoicesCSV,
292  allowOverwrite == "1");
293  }
294  else if(requestType == "deleteTableInfo")
295  {
296  std::string tableName =
297  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
298  __SUP_COUT__ << "tableName: " << tableName << __E__;
299  handleDeleteTableInfoXML(xmlOut, cfgMgr, tableName);
300  }
301  else if(requestType == "gatewayLaunchOTS" || requestType == "gatewayLaunchWiz" ||
302  requestType == "flattenToSystemAliases")
303  {
304  // NOTE: similar to Supervisor version but does not keep active sessions
305  __SUP_COUT_WARN__ << requestType << " command received! " << __E__;
306  __COUT_WARN__ << requestType << " command received! " << __E__;
307 
308  // now launch
309  __SUP_COUT_INFO__ << "Launching " << requestType << "... " << __E__;
310 
311  __SUP_COUT__ << "Extracting target context hostnames... " << __E__;
312  std::vector<std::string> hostnames;
313 
314  // flattenToSystemAliases should always work in wiz mode!
315  if(requestType == "flattenToSystemAliases" &&
316  CorePropertySupervisorBase::allSupervisorInfo_.isWizardMode())
317  {
318  hostnames.push_back(__ENV__("OTS_CONFIGURATION_WIZARD_SUPERVISOR_SERVER"));
319  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
320  }
321  else
322  {
323  try
324  {
325  cfgMgr->init(); // completely reset to re-align with any changes
326 
327  const XDAQContextTable* contextTable =
328  cfgMgr->__GET_CONFIG__(XDAQContextTable);
329 
330  auto contexts = contextTable->getContexts();
331  unsigned int i, j;
332  for(const auto& context : contexts)
333  {
334  if(!context.status_)
335  continue;
336 
337  // find last slash
338  j = 0; // default to whole string
339  for(i = 0; i < context.address_.size(); ++i)
340  if(context.address_[i] == '/')
341  j = i + 1;
342  hostnames.push_back(context.address_.substr(j));
343  __SUP_COUT__ << "hostname = " << hostnames.back() << __E__;
344  }
345  }
346  catch(...)
347  {
348  __SUP_SS__ << "The Configuration Manager could not be initialized to "
349  "extract contexts."
350  << __E__;
351  try
352  {
353  throw;
354  } //one more try to printout extra info
355  catch(const std::exception& e)
356  {
357  ss << "Exception message: " << e.what();
358  }
359  catch(...)
360  {
361  }
362 
363  __SUP_COUT_ERR__ << "\n" << ss.str();
364  return;
365  }
366  }
367 
368  if(hostnames.size() == 0)
369  {
370  __SUP_SS__ << "No hostnames found to launch command '" + requestType +
371  "'... Is there a valid Context group activated?"
372  << __E__;
373  __SUP_COUT_ERR__ << "\n" << ss.str();
374 
375  xmlOut.addTextElementToData("Error", ss.str());
376  }
377 
378  for(const auto& hostname : hostnames)
379  {
380  std::string fn = (std::string(__ENV__("SERVICE_DATA_PATH")) +
381  "/StartOTS_action_" + hostname + ".cmd");
382  FILE* fp = fopen(fn.c_str(), "w");
383  if(fp)
384  {
385  if(requestType == "gatewayLaunchOTS")
386  fprintf(fp, "LAUNCH_OTS");
387  else if(requestType == "gatewayLaunchWiz")
388  fprintf(fp, "LAUNCH_WIZ");
389  else if(requestType == "flattenToSystemAliases")
390  {
391  fprintf(fp, "FLATTEN_TO_SYSTEM_ALIASES");
392  fclose(fp);
393  break; // only do at one host
394  }
395 
396  fclose(fp);
397  }
398  else
399  __SUP_COUT_ERR__ << "Unable to open command file: " << fn << __E__;
400  }
401  }
402  else if(requestType == "versionTracking" || requestType == "getVersionTracking")
403  {
404  std::string type;
405  if(requestType == "getVersionTracking")
406  type = "Get";
407  else
408  type = CgiDataUtilities::getData(cgiIn, "Type"); // from GET
409  __SUP_COUT__ << "type: " << type << __E__;
410 
411  if(type == "Get")
412  xmlOut.addTextElementToData(
413  "versionTrackingStatus",
414  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
415  else if(type == "ON")
416  {
417  ConfigurationInterface::setVersionTrackingEnabled(true);
418  xmlOut.addTextElementToData(
419  "versionTrackingStatus",
420  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
421  }
422  else if(type == "OFF")
423  {
424  ConfigurationInterface::setVersionTrackingEnabled(false);
425  xmlOut.addTextElementToData(
426  "versionTrackingStatus",
427  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
428  }
429  }
430  else if(requestType == "getColumnTypes")
431  {
432  // return the possible column types and their defaults
433  std::vector<std::string> allTypes = TableViewColumnInfo::getAllTypesForGUI();
434  std::vector<std::string> allDataTypes =
435  TableViewColumnInfo::getAllDataTypesForGUI();
436  std::map<std::pair<std::string, std::string>, std::string> allDefaults =
438  // TODO maybe here a new function will be needed to get allminmaxforGUI
439  for(const auto& type : allTypes)
440  xmlOut.addTextElementToData("columnTypeForGUI", type);
441  for(const auto& dataType : allDataTypes)
442  xmlOut.addTextElementToData("columnDataTypeForGUI", dataType);
443 
444  for(const auto& colDefault : allDefaults)
445  {
446  xmlOut.addTextElementToData("columnDefaultDataType", colDefault.first.first);
447  xmlOut.addTextElementToData("columnDefaultTypeFilter",
448  colDefault.first.second);
449  xmlOut.addTextElementToData("columnDefaultValue", colDefault.second);
450  }
451  // TODO add min and max responses.
452  }
453  else if(requestType == "getGroupAliases")
454  {
455  // Since this is called from setting up System View in the table GUI
456  // give option for reloading "persistent" active configurations
457  bool reloadActive =
458  1 == CgiDataUtilities::getDataAsInt(cgiIn, "reloadActiveGroups"); // from GET
459 
460  __SUP_COUT__ << "reloadActive: " << reloadActive << __E__;
461  if(reloadActive)
462  {
463  try
464  {
465  cfgMgr->clearAllCachedVersions();
466  cfgMgr->restoreActiveTableGroups(true);
467  }
468  catch(std::runtime_error& e)
469  {
470  __SUP_SS__ << ("Error loading active groups!\n\n" + std::string(e.what()))
471  << __E__;
472  __SUP_COUT_ERR__ << "\n" << ss.str();
473  xmlOut.addTextElementToData("Error", ss.str());
474  }
475  catch(...)
476  {
477  __SUP_SS__ << ("Error loading active groups!\n\n") << __E__;
478  try
479  {
480  throw;
481  } //one more try to printout extra info
482  catch(const std::exception& e)
483  {
484  ss << "Exception message: " << e.what();
485  }
486  catch(...)
487  {
488  }
489  __SUP_COUT_ERR__ << "\n" << ss.str();
490  xmlOut.addTextElementToData("Error", ss.str());
491  }
492  }
493 
494  handleGroupAliasesXML(xmlOut, cfgMgr);
495  }
496  else if(requestType == "setGroupAliasInActiveBackbone")
497  {
498  std::string groupAliasCSV =
499  CgiDataUtilities::getData(cgiIn, "groupAlias"); // from GET
500  std::string groupNameCSV =
501  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
502  std::string groupKeyCSV =
503  CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
504 
505  __SUP_COUTV__(groupAliasCSV);
506  __SUP_COUTV__(groupNameCSV);
507  __SUP_COUTV__(groupKeyCSV);
508 
509  handleSetGroupAliasInBackboneXML(
510  xmlOut, cfgMgr, groupAliasCSV, groupNameCSV, groupKeyCSV, userInfo.username_);
511  }
512  else if(requestType == "setTableAliasInActiveBackbone")
513  {
514  std::string tableAlias =
515  CgiDataUtilities::getData(cgiIn, "tableAlias"); // from GET
516  std::string tableName =
517  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
518  std::string version = CgiDataUtilities::getData(cgiIn, "version"); // from GET
519 
520  __SUP_COUT__ << "tableAlias: " << tableAlias << __E__;
521  __SUP_COUT__ << "tableName: " << tableName << __E__;
522  __SUP_COUT__ << "version: " << version << __E__;
523 
524  handleSetTableAliasInBackboneXML(xmlOut,
525  cfgMgr,
526  tableAlias,
527  tableName,
528  TableVersion(version),
529  userInfo.username_);
530  }
531  else if(requestType == "setAliasOfGroupMembers")
532  {
533  std::string versionAlias =
534  CgiDataUtilities::getData(cgiIn, "versionAlias"); // from GET
535  std::string groupName =
536  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
537  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
538 
539  __SUP_COUT__ << "versionAlias: " << versionAlias << __E__;
540  __SUP_COUT__ << "groupName: " << groupName << __E__;
541  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
542 
543  handleAliasGroupMembersInBackboneXML(xmlOut,
544  cfgMgr,
545  versionAlias,
546  groupName,
547  TableGroupKey(groupKey),
548  userInfo.username_);
549  }
550  else if(requestType == "getVersionAliases")
551  {
552  handleVersionAliasesXML(xmlOut, cfgMgr);
553  }
554  else if(requestType == "getTableGroups")
555  {
556  bool doNotReturnMembers =
557  CgiDataUtilities::getDataAsInt(cgiIn, "doNotReturnMembers") == 1
558  ? true
559  : false; // from GET
560 
561  __SUP_COUT__ << "doNotReturnMembers: " << doNotReturnMembers << __E__;
562  handleTableGroupsXML(xmlOut, cfgMgr, !doNotReturnMembers);
563  }
564  else if(requestType == "getTableGroupType")
565  {
566  std::string tableList =
567  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
568  __SUP_COUT__ << "tableList: " << tableList << __E__;
569 
570  handleGetTableGroupTypeXML(xmlOut, cfgMgr, tableList);
571  }
572  else if(requestType == "getTables")
573  {
574  std::string filterStartTime = CgiDataUtilities::getData(cgiIn, "startTime");
575  std::string filterEndTime = CgiDataUtilities::getData(cgiIn, "endTime");
576  std::string filterMode = CgiDataUtilities::getData(cgiIn, "filterMode");
577  if(filterMode == "")
578  filterMode = "created";
579  __COUT__ << "startTime: " << filterStartTime << __E__;
580  __COUT__ << "endTime: " << filterEndTime << __E__;
581  __COUT__ << "filterMode: " << filterMode << __E__;
582 
583  handleTablesXML(xmlOut, cfgMgr, filterStartTime, filterEndTime, filterMode);
584  }
585  else if(requestType == "getContextMemberNames")
586  {
587  std::set<std::string> members = cfgMgr->getFixedContextMemberNames();
588 
589  for(auto& member : members)
590  xmlOut.addTextElementToData("ContextMember", member);
591  }
592  else if(requestType == "getBackboneMemberNames")
593  {
594  std::set<std::string> members = cfgMgr->getBackboneMemberNames();
595 
596  for(auto& member : members)
597  xmlOut.addTextElementToData("BackboneMember", member);
598  }
599  else if(requestType == "getIterateMemberNames")
600  {
601  std::set<std::string> members = cfgMgr->getIterateMemberNames();
602 
603  for(auto& member : members)
604  xmlOut.addTextElementToData("IterateMember", member);
605  }
606  else if(requestType == "getSpecificTableGroup")
607  {
608  std::string groupName =
609  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
610  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
611 
612  __SUP_COUT__ << "groupName: " << groupName << __E__;
613  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
614 
616  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey));
617  }
618  else if(requestType == "saveNewTableGroup")
619  {
620  std::string groupName =
621  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
622  bool ignoreWarnings =
623  CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings"); // from GET
624  bool allowDuplicates =
625  CgiDataUtilities::getDataAsInt(cgiIn, "allowDuplicates"); // from GET
626  bool lookForEquivalent =
627  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
628  std::string tableList =
629  CgiDataUtilities::postData(cgiIn, "tableList"); // from POST
630  std::string comment =
631  CgiDataUtilities::getData(cgiIn, "groupComment"); // from GET
632 
633  __SUP_COUT__ << "saveNewTableGroup: " << groupName << __E__;
634  __SUP_COUT__ << "tableList: " << tableList << __E__;
635  __SUP_COUT__ << "ignoreWarnings: " << ignoreWarnings << __E__;
636  __SUP_COUT__ << "allowDuplicates: " << allowDuplicates << __E__;
637  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
638  __SUP_COUT__ << "comment: " << comment << __E__;
639 
641  cfgMgr,
642  groupName,
643  tableList,
644  allowDuplicates,
645  ignoreWarnings,
646  comment,
647  lookForEquivalent);
648  }
649  else if(requestType == "getSpecificTable")
650  {
651  std::string tableName =
652  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
653  std::string versionStr = CgiDataUtilities::getData(cgiIn, "version"); // from GET
654  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
655  int chunkSize = CgiDataUtilities::getDataAsInt(
656  cgiIn,
657  "chunkSize"); // from GET (chunkSize is currently ignored, could use to get a few rows at a time)
658  bool descriptionOnly = CgiDataUtilities::getDataAsInt(
659  cgiIn, "descriptionOnly"); // from GET (used to get tooltip info)
660  bool allowIllegalColumns =
661  CgiDataUtilities::getDataAsInt(cgiIn, "allowIllegalColumns"); // from GET
662  bool rawData = CgiDataUtilities::getDataAsInt(cgiIn, "rawData"); // from GET
663 
664  __SUP_COUT__ << "getSpecificTable: " << tableName << " versionStr: " << versionStr
665  << " chunkSize: " << chunkSize << " dataOffset: " << dataOffset
666  << " descriptionOnly: " << descriptionOnly
667  << " allowIllegalColumns: " << allowIllegalColumns
668  << " rawData: " << rawData << __E__;
669 
670  TableVersion version;
671  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
672 
673  if(allTableInfo.find(tableName) != allTableInfo.end())
674  {
675  if(versionStr == "" && // take latest version if no version specified
676  allTableInfo.at(tableName).versions_.size())
677  {
678  // Start from the last element
679  auto it = allTableInfo.at(tableName).versions_.rbegin();
680  if(it->isScratchVersion()) //do not allow SCRATCH_VERSION as default selection
681  ++it; // Move to the second-to-last element
682  version = *it;
683  }
684  else if(versionStr.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
685  {
686  // convert alias to version
687  std::map<std::string /*table*/,
688  std::map<std::string /*alias*/, TableVersion>>
689  versionAliases = cfgMgr->getVersionAliases();
690 
691  std::string versionAlias;
692  versionAlias = versionStr.substr(
693  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
694  // if(versionAlias ==
695  // ConfigurationManager::SCRATCH_VERSION_ALIAS)
697  // {
698  // version = TableVersion::SCRATCH;
699  // __SUP_COUT__ << "version alias translated to: " << version
700  //<<
701  //__E__;
702  // }
703  // else
704  if(versionAliases.find(tableName) != versionAliases.end() &&
705  versionAliases[tableName].find(versionAlias) !=
706  versionAliases[tableName].end())
707  {
708  version = versionAliases[tableName][versionAlias];
709  __SUP_COUT__ << "version alias translated to: " << version << __E__;
710  }
711  else
712  __SUP_COUT_WARN__
713  << "version alias '"
714  << versionStr.substr(
715  ConfigurationManager::ALIAS_VERSION_PREAMBLE.size())
716  << "'was not found in active version aliases!" << __E__;
717  }
718  else // else take specified version
719  version = atoi(versionStr.c_str());
720  }
721 
722  __SUP_COUT__ << "version: " << version << __E__;
723 
724  handleGetTableXML(xmlOut,
725  cfgMgr,
726  tableName,
727  TableVersion(version),
728  allowIllegalColumns,
729  rawData,
730  descriptionOnly);
731  // append author column default value
732  xmlOut.addTextElementToData("DefaultRowValue", userInfo.username_);
733  }
734  else if(requestType == "saveSpecificTable")
735  {
736  std::string tableName =
737  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
738  int version = CgiDataUtilities::getDataAsInt(cgiIn, "version"); // from GET
739  int dataOffset = CgiDataUtilities::getDataAsInt(cgiIn, "dataOffset"); // from GET
740  bool sourceTableAsIs =
741  CgiDataUtilities::getDataAsInt(cgiIn, "sourceTableAsIs"); // from GET
742  bool lookForEquivalent =
743  CgiDataUtilities::getDataAsInt(cgiIn, "lookForEquivalent"); // from GET
744  int temporary = CgiDataUtilities::getDataAsInt(cgiIn, "temporary"); // from GET
745  std::string comment =
746  CgiDataUtilities::getData(cgiIn, "tableComment"); // from GET
747 
748  std::string data = CgiDataUtilities::postData(cgiIn, "data"); // from POST
749  // data format: commas and semi-colons indicate new row
750  // r0c0,r0c1,...,r0cN,;r1c0,...
751 
752  __SUP_COUT__ << "tableName: " << tableName << " version: " << version
753  << " temporary: " << temporary << " dataOffset: " << dataOffset
754  << __E__;
755  __SUP_COUT__ << "comment: " << comment << __E__;
756  __SUP_COUT__ << "data: " << data << __E__;
757  __SUP_COUT__ << "sourceTableAsIs: " << sourceTableAsIs << __E__;
758  __SUP_COUT__ << "lookForEquivalent: " << lookForEquivalent << __E__;
759 
761  cfgMgr,
762  tableName,
763  TableVersion(version),
764  temporary,
765  data,
766  dataOffset,
767  userInfo.username_,
768  comment,
769  sourceTableAsIs,
770  lookForEquivalent);
771  }
772  else if(requestType == "clearTableTemporaryVersions")
773  {
774  std::string tableName =
775  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
776  __SUP_COUT__ << "tableName: " << tableName << __E__;
777 
778  try
779  {
780  cfgMgr->eraseTemporaryVersion(tableName);
781  }
782  catch(std::runtime_error& e)
783  {
784  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
785  xmlOut.addTextElementToData(
786  "Error", "Error clearing temporary views!\n " + std::string(e.what()));
787  }
788  catch(...)
789  {
790  __SUP_COUT__ << "Error detected!\n\n " << __E__;
791  xmlOut.addTextElementToData("Error", "Error clearing temporary views! ");
792  }
793  }
794  else if(requestType == "clearTableCachedVersions")
795  {
796  std::string tableName =
797  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
798  __SUP_COUT__ << "tableName: " << tableName << __E__;
799 
800  try
801  {
802  if(tableName == "*")
803  cfgMgr->clearAllCachedVersions();
804  else
805  cfgMgr->clearCachedVersions(tableName);
806 
807  // Force manual reload... not cfgMgr->getAllTableInfo(true /*refresh*/);
808  }
809  catch(std::runtime_error& e)
810  {
811  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
812  xmlOut.addTextElementToData(
813  "Error", "Error clearing cached views!\n " + std::string(e.what()));
814  }
815  catch(...)
816  {
817  __SUP_COUT__ << "Error detected!\n\n " << __E__;
818  xmlOut.addTextElementToData("Error", "Error clearing cached views! ");
819  }
820  }
821  else if(requestType == "getGroupHistory")
822  {
823  std::string groupAction = StringMacros::decodeURIComponent(
824  CgiDataUtilities::getData(cgiIn, "groupAction")); // from GET
825  std::string groupType = StringMacros::decodeURIComponent(
826  CgiDataUtilities::getData(cgiIn, "groupType")); // from GET
827 
828  __SUP_COUTV__(groupAction);
829  __SUP_COUTV__(groupType);
830 
831  std::vector<std::map<std::string /* group field key */,
832  std::string /* group field value */>>
834  groupAction, groupType, true /* formatTime */);
835 
836  for(const auto& group : groups)
837  {
838  auto parentEl = xmlOut.addTextElementToData(
839  "GroupHistoryEntry", ""); // create parent element for each entry
840  for(const auto& field : group)
841  xmlOut.addTextElementToParent(field.first, field.second, parentEl);
842  }
843  }
844  else if(requestType == "getTreeView")
845  {
846  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
847  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
848  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
849  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
850  std::string filterList = CgiDataUtilities::postData(cgiIn, "filterList");
851  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
852  bool hideStatusFalse = CgiDataUtilities::getDataAsInt(cgiIn, "hideStatusFalse");
853  std::string diffGroup = CgiDataUtilities::getData(cgiIn, "diffGroup");
854  std::string diffGroupKey = CgiDataUtilities::getData(cgiIn, "diffGroupKey");
855 
856  __SUP_COUTT__ << "tableGroup: " << tableGroup << __E__;
857  __SUP_COUTT__ << "tableGroupKey: " << tableGroupKey << __E__;
858  __SUP_COUTT__ << "startPath: " << startPath << __E__;
859  __SUP_COUTT__ << "depth: " << depth << __E__;
860  __SUP_COUTT__ << "hideStatusFalse: " << hideStatusFalse << __E__;
861  __SUP_COUTT__ << "modifiedTables: " << modifiedTables << __E__;
862  __SUP_COUTT__ << "filterList: " << filterList << __E__;
863 
864  handleFillTreeViewXML(xmlOut,
865  cfgMgr,
866  tableGroup,
867  TableGroupKey(tableGroupKey),
868  startPath,
869  depth,
870  hideStatusFalse,
871  modifiedTables,
872  filterList,
873  diffGroup,
874  TableGroupKey(diffGroupKey));
875  }
876  else if(requestType == "getTreeNodeCommonFields")
877  {
878  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
879  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
880  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
881  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
882  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
883  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
884  int depth = CgiDataUtilities::getDataAsInt(cgiIn, "depth");
885 
886  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
887  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
888  __SUP_COUT__ << "startPath: " << startPath << __E__;
889  __SUP_COUT__ << "depth: " << depth << __E__;
890  if(depth == -1)
891  depth = 10; // protect users who probably do not actually mean -1
892  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
893  __SUP_COUT__ << "recordList: " << recordList << __E__;
894  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
895 
896  handleFillTreeNodeCommonFieldsXML(xmlOut,
897  cfgMgr,
898  tableGroup,
899  TableGroupKey(tableGroupKey),
900  startPath,
901  depth,
902  modifiedTables,
903  recordList,
904  fieldList);
905  }
906  else if(requestType == "getUniqueFieldValuesForRecords")
907  {
908  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
909  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
910  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
911  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
912  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
913  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
914 
915  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
916  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
917  __SUP_COUT__ << "startPath: " << startPath << __E__;
918  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
919  __SUP_COUT__ << "recordList: " << recordList << __E__;
920  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
921 
922  handleFillUniqueFieldValuesForRecordsXML(xmlOut,
923  cfgMgr,
924  tableGroup,
925  TableGroupKey(tableGroupKey),
926  startPath,
927  modifiedTables,
928  recordList,
929  fieldList);
930  }
931  else if(requestType == "getTreeNodeFieldValues")
932  {
933  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
934  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
935  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
936  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
937  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
938  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
939 
940  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
941  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
942  __SUP_COUT__ << "startPath: " << startPath << __E__;
943  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
944  __SUP_COUT__ << "recordList: " << recordList << __E__;
945  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
946 
947  handleFillGetTreeNodeFieldValuesXML(xmlOut,
948  cfgMgr,
949  tableGroup,
950  TableGroupKey(tableGroupKey),
951  startPath,
952  modifiedTables,
953  recordList,
954  fieldList);
955  }
956  else if(requestType == "setTreeNodeFieldValues")
957  {
958  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
959  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
960  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
961  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
962  std::string fieldList = CgiDataUtilities::postData(cgiIn, "fieldList");
963  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
964  std::string valueList = CgiDataUtilities::postData(cgiIn, "valueList");
965 
966  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
967  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
968  __SUP_COUT__ << "startPath: " << startPath << __E__;
969  __SUP_COUT__ << "fieldList: " << fieldList << __E__;
970  __SUP_COUT__ << "valueList: " << valueList << __E__;
971  __SUP_COUT__ << "recordList: " << recordList << __E__;
972  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
973 
974  handleFillSetTreeNodeFieldValuesXML(xmlOut,
975  cfgMgr,
976  tableGroup,
977  TableGroupKey(tableGroupKey),
978  startPath,
979  modifiedTables,
980  recordList,
981  fieldList,
982  valueList,
983  userInfo.username_);
984  }
985  else if(requestType == "addTreeNodeRecords")
986  {
987  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
988  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
989  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
990  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
991  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
992 
993  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
994  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
995  __SUP_COUT__ << "startPath: " << startPath << __E__;
996  __SUP_COUT__ << "recordList: " << recordList << __E__;
997  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
998 
999  handleFillCreateTreeNodeRecordsXML(xmlOut,
1000  cfgMgr,
1001  tableGroup,
1002  TableGroupKey(tableGroupKey),
1003  startPath,
1004  modifiedTables,
1005  recordList,
1006  userInfo.username_);
1007  }
1008  else if(requestType == "deleteTreeNodeRecords")
1009  {
1010  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1011  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1012  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1013  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1014  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1015 
1016  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1017  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1018  __SUP_COUT__ << "startPath: " << startPath << __E__;
1019  __SUP_COUT__ << "recordList: " << recordList << __E__;
1020  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1021 
1022  handleFillDeleteTreeNodeRecordsXML(xmlOut,
1023  cfgMgr,
1024  tableGroup,
1025  TableGroupKey(tableGroupKey),
1026  startPath,
1027  modifiedTables,
1028  recordList);
1029  }
1030  else if(requestType == "renameTreeNodeRecords")
1031  {
1032  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1033  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1034  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1035  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1036  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1037  std::string newRecordList = CgiDataUtilities::postData(cgiIn, "newRecordList");
1038 
1039  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1040  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1041  __SUP_COUT__ << "startPath: " << startPath << __E__;
1042  __SUP_COUT__ << "recordList: " << recordList << __E__;
1043  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1044  __SUP_COUTV__(newRecordList);
1045 
1046  handleFillRenameTreeNodeRecordsXML(xmlOut,
1047  cfgMgr,
1048  tableGroup,
1049  TableGroupKey(tableGroupKey),
1050  startPath,
1051  modifiedTables,
1052  recordList,
1053  newRecordList);
1054  }
1055  else if(requestType == "copyTreeNodeRecords")
1056  {
1057  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1058  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1059  std::string startPath = CgiDataUtilities::postData(cgiIn, "startPath");
1060  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1061  std::string recordList = CgiDataUtilities::postData(cgiIn, "recordList");
1062  unsigned int numberOfCopies =
1063  CgiDataUtilities::getDataAsInt(cgiIn, "numberOfCopies");
1064  if(!numberOfCopies)
1065  numberOfCopies = 1; // default to 1
1066 
1067  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1068  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1069  __SUP_COUT__ << "startPath: " << startPath << __E__;
1070  __SUP_COUT__ << "recordList: " << recordList << __E__;
1071  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1072  __SUP_COUTV__(numberOfCopies);
1073 
1074  handleFillCopyTreeNodeRecordsXML(xmlOut,
1075  cfgMgr,
1076  tableGroup,
1077  TableGroupKey(tableGroupKey),
1078  startPath,
1079  modifiedTables,
1080  recordList,
1081  numberOfCopies);
1082  }
1083  else if(requestType == "getTableStructureStatusAsJSON")
1084  {
1085  std::string tableGroup = CgiDataUtilities::getData(cgiIn, "tableGroup");
1086  std::string tableGroupKey = CgiDataUtilities::getData(cgiIn, "tableGroupKey");
1087  std::string tableName = CgiDataUtilities::getData(cgiIn, "tableName");
1088  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1089 
1090  __SUP_COUT__ << "tableGroup: " << tableGroup << __E__;
1091  __SUP_COUT__ << "tableGroupKey: " << tableGroupKey << __E__;
1092  __SUP_COUT__ << "tableName: " << tableName << __E__;
1093  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1094 
1095  // setup active tables based on active groups and modified tables
1096  setupActiveTablesXML(xmlOut,
1097  cfgMgr,
1098  tableGroup,
1099  TableGroupKey(tableGroupKey),
1100  modifiedTables,
1101  false /* refreshAll */);
1102 
1103  try
1104  {
1105  xmlOut.addTextElementToData(
1106  "StructureStatusAsJSON",
1107  cfgMgr->getTableByName(tableName)->getStructureAsJSON(cfgMgr));
1108  }
1109  catch(const std::runtime_error& e)
1110  {
1111  __SUP_SS__ << "The table plugin feature getStructureStatusAsJSON(), does not "
1112  "seem to be supported for the table '"
1113  << tableName
1114  << ".' Make sure you have the expected table plugin in your path, "
1115  "or contact system admins."
1116  << __E__;
1117  ss << "Here is the error: " << e.what() << __E__;
1118  __SUP_SS_THROW__;
1119  }
1120  }
1121  else if(requestType == "getArtdaqNodes")
1122  {
1123  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1124 
1125  __SUP_COUTV__(modifiedTables);
1126 
1127  handleGetArtdaqNodeRecordsXML(xmlOut, cfgMgr, modifiedTables);
1128  }
1129  else if(requestType == "saveArtdaqNodes")
1130  {
1131  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1132  std::string nodeString = CgiDataUtilities::postData(cgiIn, "nodeString");
1133  std::string subsystemString =
1134  CgiDataUtilities::postData(cgiIn, "subsystemString");
1135 
1136  __SUP_COUTV__(modifiedTables);
1137  __SUP_COUTV__(nodeString);
1138  __SUP_COUTV__(subsystemString);
1139 
1140  handleSaveArtdaqNodeRecordsXML(
1141  nodeString, subsystemString, xmlOut, cfgMgr, modifiedTables);
1142  }
1143  else if(requestType == "getArtdaqNodeLayout")
1144  {
1145  std::string contextGroupName =
1146  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1147  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1148 
1149  __SUP_COUTV__(contextGroupName);
1150  __SUP_COUTV__(contextGroupKey);
1151 
1152  handleLoadArtdaqNodeLayoutXML(
1153  xmlOut, cfgMgr, contextGroupName, TableGroupKey(contextGroupKey));
1154  }
1155  else if(requestType == "saveArtdaqNodeLayout")
1156  {
1157  std::string layout = CgiDataUtilities::postData(cgiIn, "layout");
1158  std::string contextGroupName =
1159  CgiDataUtilities::getData(cgiIn, "contextGroupName");
1160  std::string contextGroupKey = CgiDataUtilities::getData(cgiIn, "contextGroupKey");
1161 
1162  __SUP_COUTV__(layout);
1163  __SUP_COUTV__(contextGroupName);
1164  __SUP_COUTV__(contextGroupKey);
1165 
1166  handleSaveArtdaqNodeLayoutXML(
1167  xmlOut, cfgMgr, layout, contextGroupName, TableGroupKey(contextGroupKey));
1168  }
1169  else if(requestType == "checkAffectedActiveGroups")
1170  {
1171  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1172  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1173  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1174  __SUP_COUT__ << "modifiedTables: " << modifiedTables << __E__;
1175  __SUP_COUT__ << "groupName: " << groupName << __E__;
1176  __SUP_COUT__ << "groupKey: " << groupKey << __E__;
1177 
1178  handleGetAffectedGroupsXML(
1179  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey), modifiedTables);
1180  }
1181  else if(requestType == "SearchFieldInGroup")
1182  {
1183  std::string searchText = CgiDataUtilities::getData(cgiIn, "searchText");
1184  std::string filterValue = CgiDataUtilities::getData(cgiIn, "filterValue");
1185  std::string groupType = CgiDataUtilities::getData(cgiIn, "groupType");
1186  std::string optionGroups = CgiDataUtilities::getData(cgiIn, "optionGroups");
1187  std::string versionsToCheck = CgiDataUtilities::getData(cgiIn, "versionsToCheck");
1188 
1189  searchText = StringMacros::decodeURIComponent(searchText);
1190  filterValue = StringMacros::decodeURIComponent(filterValue);
1191  groupType = StringMacros::decodeURIComponent(groupType);
1192  optionGroups = StringMacros::decodeURIComponent(optionGroups);
1193  versionsToCheck = StringMacros::decodeURIComponent(versionsToCheck);
1194 
1195  __SUP_COUT__ << "searchText: " << searchText << __E__;
1196  __SUP_COUT__ << "filterValue: " << filterValue << __E__;
1197  __SUP_COUT__ << "groupType: " << groupType << __E__;
1198  __SUP_COUT__ << "optionGroups: " << optionGroups << __E__;
1199  __SUP_COUT__ << "versionsToCheck: " << versionsToCheck << __E__;
1200 
1201  handleSearchFieldInGroupXML(xmlOut,
1202  cfgMgr,
1203  searchText,
1204  (filterValue == "true"),
1205  groupType,
1206  optionGroups,
1207  versionsToCheck);
1208  }
1209  else if(requestType == "SearchFieldInTableVersions")
1210  {
1211  std::string searchText = CgiDataUtilities::getData(cgiIn, "searchText");
1212  std::string tableName = CgiDataUtilities::getData(cgiIn, "tableName");
1213  std::string searchVersionToCheck =
1214  CgiDataUtilities::getData(cgiIn, "searchVersionToCheck");
1215  std::string activeTablesOnly = CgiDataUtilities::getData(cgiIn, "activeOnly");
1216 
1217  searchText = StringMacros::decodeURIComponent(searchText);
1218  tableName = StringMacros::decodeURIComponent(tableName);
1219  searchVersionToCheck = StringMacros::decodeURIComponent(searchVersionToCheck);
1220  activeTablesOnly = StringMacros::decodeURIComponent(activeTablesOnly);
1221 
1222  __SUP_COUT__ << "searchText: " << searchText << __E__;
1223  __SUP_COUT__ << "tableName: " << tableName << __E__;
1224  __SUP_COUT__ << "searchVersionToCheck: " << searchVersionToCheck << __E__;
1225  __SUP_COUT__ << "activeTablesOnly: " << activeTablesOnly << __E__;
1226 
1227  handleSearchFieldInTableXML(xmlOut,
1228  cfgMgr,
1229  searchText,
1230  tableName,
1231  searchVersionToCheck,
1232  (activeTablesOnly == "true"));
1233  }
1234  else if(requestType == "saveTreeNodeEdit")
1235  {
1236  std::string editNodeType = CgiDataUtilities::getData(cgiIn, "editNodeType");
1237  std::string targetTable = CgiDataUtilities::getData(cgiIn, "targetTable");
1238  std::string targetTableVersion =
1239  CgiDataUtilities::getData(cgiIn, "targetTableVersion");
1240  std::string targetUID = CgiDataUtilities::getData(cgiIn, "targetUID");
1241  std::string targetColumn = CgiDataUtilities::getData(cgiIn, "targetColumn");
1242  std::string newValue = CgiDataUtilities::postData(cgiIn, "newValue");
1243 
1244  __SUP_COUT__ << "editNodeType: " << editNodeType << __E__;
1245  __SUP_COUT__ << "targetTable: " << targetTable << __E__;
1246  __SUP_COUT__ << "targetTableVersion: " << targetTableVersion << __E__;
1247  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
1248  __SUP_COUT__ << "targetColumn: " << targetColumn << __E__;
1249  __SUP_COUT__ << "newValue: " << newValue << __E__;
1250 
1251  handleSaveTreeNodeEditXML(xmlOut,
1252  cfgMgr,
1253  targetTable,
1254  TableVersion(targetTableVersion),
1255  editNodeType,
1257  StringMacros::decodeURIComponent(targetColumn),
1258  newValue,
1259  userInfo.username_);
1260  }
1261  else if(requestType == "getLinkToChoices")
1262  {
1263  std::string linkToTableName = CgiDataUtilities::getData(cgiIn, "linkToTableName");
1264  std::string linkToTableVersion =
1265  CgiDataUtilities::getData(cgiIn, "linkToTableVersion");
1266  std::string linkIdType = CgiDataUtilities::getData(cgiIn, "linkIdType");
1267  std::string linkIndex = StringMacros::decodeURIComponent(
1268  CgiDataUtilities::getData(cgiIn, "linkIndex"));
1269  std::string linkInitId = CgiDataUtilities::getData(cgiIn, "linkInitId");
1270 
1271  __SUP_COUT__ << "linkToTableName: " << linkToTableName << __E__;
1272  __SUP_COUT__ << "linkToTableVersion: " << linkToTableVersion << __E__;
1273  __SUP_COUT__ << "linkIdType: " << linkIdType << __E__;
1274  __SUP_COUT__ << "linkIndex: " << linkIndex << __E__;
1275  __SUP_COUT__ << "linkInitId: " << linkInitId << __E__;
1276 
1277  handleGetLinkToChoicesXML(xmlOut,
1278  cfgMgr,
1279  linkToTableName,
1280  TableVersion(linkToTableVersion),
1281  linkIdType,
1282  linkIndex,
1283  linkInitId);
1284  }
1285  else if(requestType == "activateTableGroup")
1286  {
1287  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1288  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1289  bool ignoreWarnings = CgiDataUtilities::getDataAsInt(cgiIn, "ignoreWarnings");
1290 
1291  __SUP_COUT__ << "Activating group: " << groupName << "(" << groupKey << ")"
1292  << __E__;
1293  __SUP_COUTV__(ignoreWarnings);
1294 
1295  // add flag for GUI handling
1296  xmlOut.addTextElementToData("AttemptedGroupActivation", "1");
1297  xmlOut.addTextElementToData("AttemptedGroupActivationName", groupName);
1298  xmlOut.addTextElementToData("AttemptedGroupActivationKey", groupKey);
1299 
1300  try
1301  {
1302  std::string accumulatedErrors, groupTypeString;
1303 
1304  // if ignore warnings,
1305  // then only print errors, do not add to xml
1306 
1307  __COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
1308 
1309  cfgMgr->activateTableGroup(
1310  groupName, TableGroupKey(groupKey), &accumulatedErrors, &groupTypeString);
1311 
1312  if(accumulatedErrors != "")
1313  {
1314  if(!ignoreWarnings)
1315  {
1316  __SS__ << "Throwing exception on accumulated errors: "
1317  << accumulatedErrors << __E__;
1318  __SS_ONLY_THROW__;
1319  }
1320  // else just print
1321  __COUT_WARN__ << "Ignoring warnings so ignoring this error:"
1322  << accumulatedErrors << __E__;
1323  __COUT_WARN__ << "Done ignoring the above error(s)." << __E__;
1324  }
1325  xmlOut.addTextElementToData("AttemptedGroupActivationType", groupTypeString);
1326  }
1327  catch(std::runtime_error& e)
1328  {
1329  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1330  // single quotes around the groupName and groupKey and have them be
1331  // the first single quotes encountered in the error mesage!
1332  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1333  xmlOut.addTextElementToData(
1334  "Error",
1335  "Error activating table group '" + groupName + "(" + groupKey + ")" +
1336  ".' Please see details below:\n\n" + std::string(e.what()));
1337  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1338  << " (" << groupKey << ")" << __E__;
1339  try // just in case any lingering pieces, lets deactivate
1340  {
1341  cfgMgr->destroyTableGroup(groupName, true);
1342  }
1343  catch(...)
1344  {
1345  }
1346  }
1347  catch(cet::exception& e)
1348  {
1349  // NOTE it is critical for flimsy error parsing in JS GUI to leave
1350  // single quotes around the groupName and groupKey and have them be
1351  // the first single quotes encountered in the error mesage!
1352 
1353  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1354  xmlOut.addTextElementToData("Error",
1355  "Error activating table group '" + groupName +
1356  "(" + groupKey + ")" + "!'\n\n" +
1357  std::string(e.what()));
1358  __SUP_COUT_ERR__ << "Errors detected so de-activating group: " << groupName
1359  << " (" << groupKey << ")" << __E__;
1360  try // just in case any lingering pieces, lets deactivate
1361  {
1362  cfgMgr->destroyTableGroup(groupName, true);
1363  }
1364  catch(...)
1365  {
1366  }
1367  }
1368  catch(...)
1369  {
1370  __SUP_COUT__ << "Unknown error detected!" << __E__;
1371  try // just in case any lingering pieces, lets deactivate
1372  {
1373  cfgMgr->destroyTableGroup(groupName, true);
1374  }
1375  catch(...)
1376  {
1377  }
1378 
1379  throw; // unexpected exception!
1380  }
1381  }
1382  else if(requestType == "getActiveTableGroups")
1383  ; // do nothing, since they are always returned
1384  else if(requestType == "copyViewToCurrentColumns")
1385  {
1386  std::string tableName =
1387  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1388  std::string sourceVersion = CgiDataUtilities::getData(cgiIn, "sourceVersion");
1389 
1390  __SUP_COUT__ << "tableName: " << tableName << __E__;
1391  __SUP_COUT__ << "sourceVersion: " << sourceVersion << __E__;
1392  __SUP_COUT__ << "userInfo.username_: " << userInfo.username_ << __E__;
1393 
1394  // copy source version to new temporary version
1395  TableVersion newTemporaryVersion;
1396  try
1397  {
1398  newTemporaryVersion =
1399  cfgMgr->copyViewToCurrentColumns(tableName, TableVersion(sourceVersion));
1400 
1401  __SUP_COUT__ << "New temporary version = " << newTemporaryVersion << __E__;
1402  }
1403  catch(std::runtime_error& e)
1404  {
1405  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
1406  xmlOut.addTextElementToData("Error",
1407  "Error copying view from '" + tableName + "_v" +
1408  sourceVersion + "'! " +
1409  std::string(e.what()));
1410  }
1411  catch(...)
1412  {
1413  __SUP_COUT__ << "Error detected!\n\n " << __E__;
1414  xmlOut.addTextElementToData(
1415  "Error",
1416  "Error copying view from '" + tableName + "_v" + sourceVersion + "'! ");
1417  }
1418 
1419  handleGetTableXML(xmlOut, cfgMgr, tableName, newTemporaryVersion);
1420  }
1421  else if(requestType == "getLastTableGroups")
1422  {
1423  // std::string timeString;
1424  std::map<std::string /* group type */,
1425  std::tuple<std::string /*group name*/,
1426  TableGroupKey,
1427  std::string /* time string*/>>
1428  theGroups;
1429 
1430  theRemoteWebUsers_.getLastTableGroups(theGroups);
1431 
1432  for(const auto& theGroup : theGroups)
1433  {
1434  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupName",
1435  std::get<0>(theGroup.second));
1436  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupKey",
1437  std::get<1>(theGroup.second).toString());
1438  xmlOut.addTextElementToData("Last" + theGroup.first + "GroupTime",
1439  std::get<2>(theGroup.second));
1440  }
1441 
1442  // theGroup = theRemoteWebUsers_.getLastTableGroup("Configured", timeString);
1443  // xmlOut.addTextElementToData("LastConfiguredGroupName", theGroup.first);
1444  // xmlOut.addTextElementToData("LastConfiguredGroupKey", theGroup.second.toString());
1445  // xmlOut.addTextElementToData("LastConfiguredGroupTime", timeString);
1446  // theGroup = theRemoteWebUsers_.getLastTableGroup("Started", timeString);
1447  // xmlOut.addTextElementToData("LastStartedGroupName", theGroup.first);
1448  // xmlOut.addTextElementToData("LastStartedGroupKey", theGroup.second.toString());
1449  // xmlOut.addTextElementToData("LastStartedGroupTime", timeString);
1450  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedConfig", timeString);
1451  // xmlOut.addTextElementToData("LastActivatedConfigGroupName", theGroup.first);
1452  // xmlOut.addTextElementToData("LastActivatedConfigGroupKey",
1453  // theGroup.second.toString());
1454  // xmlOut.addTextElementToData("LastActivatedConfigGroupTime", timeString);
1455  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedContext", timeString);
1456  // xmlOut.addTextElementToData("LastActivatedContextGroupName", theGroup.first);
1457  // xmlOut.addTextElementToData("LastActivatedContextGroupKey",
1458  // theGroup.second.toString());
1459  // xmlOut.addTextElementToData("LastActivatedContextGroupTime", timeString);
1460  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedBackbone", timeString);
1461  // xmlOut.addTextElementToData("LastActivatedBackboneGroupName", theGroup.first);
1462  // xmlOut.addTextElementToData("LastActivatedBackboneGroupKey",
1463  // theGroup.second.toString());
1464  // xmlOut.addTextElementToData("LastActivatedBackboneGroupTime", timeString);
1465  // theGroup = theRemoteWebUsers_.getLastTableGroup("ActivatedIterator", timeString);
1466  // xmlOut.addTextElementToData("LastActivatedIteratorGroupName", theGroup.first);
1467  // xmlOut.addTextElementToData("LastActivatedIteratorGroupKey",
1468  // theGroup.second.toString());
1469  // xmlOut.addTextElementToData("LastActivatedIteratorGroupTime", timeString);
1470 
1471  //check other subsystems active groups
1472  handleOtherSubsystemActiveGroups(xmlOut, cfgMgr, false /* getFullList */);
1473  }
1474  else if(requestType == "getSubsytemTableGroups")
1475  {
1476  std::string subsystem =
1477  CgiDataUtilities::getData(cgiIn, "subsystem"); // from GET
1478  __SUP_COUTV__(subsystem);
1479  handleOtherSubsystemActiveGroups(
1480  xmlOut, cfgMgr, true /* getFullList */, subsystem);
1481  } //end getSubsytemTableGroups
1482  else if(requestType == "diffWithActiveGroup")
1483  {
1484  std::string groupName =
1485  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1486  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1487  __SUP_COUTV__(groupName);
1488  __SUP_COUTV__(groupKey);
1489 
1490  handleGroupDiff(
1491  xmlOut, cfgMgr, groupName, TableGroupKey(groupKey)); //diff with active group
1492  } //end diffWithActiveGroup
1493  else if(requestType == "diffWithGroupKey")
1494  {
1495  std::string groupName =
1496  CgiDataUtilities::getData(cgiIn, "groupName"); // from GET
1497  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey"); // from GET
1498  std::string diffKey = CgiDataUtilities::getData(cgiIn, "diffKey"); // from GET
1499  std::string diffGroupName =
1500  CgiDataUtilities::getData(cgiIn, "diffGroupName"); // from GET
1501  __SUP_COUTV__(groupName);
1502  __SUP_COUTV__(groupKey);
1503  __SUP_COUTV__(diffKey);
1504  __SUP_COUTV__(diffGroupName);
1505 
1506  handleGroupDiff(xmlOut,
1507  cfgMgr,
1508  groupName,
1509  TableGroupKey(groupKey),
1510  TableGroupKey(diffKey),
1511  diffGroupName);
1512  } //end diffWithGroupKey
1513  else if(requestType == "diffTableVersions")
1514  {
1515  std::string tableName =
1516  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1517  std::string vA = CgiDataUtilities::getData(cgiIn, "vA"); // from GET
1518  std::string vB = CgiDataUtilities::getData(cgiIn, "vB"); // from GET
1519  __SUP_COUTV__(tableName);
1520  __SUP_COUTV__(vA);
1521  __SUP_COUTV__(vB);
1522 
1523  TableVersion versionA, versionB;
1524  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
1525 
1526  //convert aliases if specified
1527  if(allTableInfo.find(tableName) != allTableInfo.end())
1528  {
1529  if(vA.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1530  {
1531  // convert alias to version
1532  std::map<std::string /*table*/,
1533  std::map<std::string /*alias*/, TableVersion>>
1534  versionAliases = cfgMgr->getVersionAliases();
1535 
1536  std::string versionAlias;
1537  versionAlias =
1538  vA.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1539 
1540  if(versionAliases.find(tableName) != versionAliases.end() &&
1541  versionAliases[tableName].find(versionAlias) !=
1542  versionAliases[tableName].end())
1543  {
1544  versionA = versionAliases[tableName][versionAlias];
1545  __SUP_COUT__ << "version alias translated to: " << versionA << __E__;
1546  }
1547  else
1548  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1549  << "'was not found in active version aliases!"
1550  << __E__;
1551  }
1552  else // else take specified version
1553  versionA = atoi(vA.c_str());
1554 
1555  if(vB.find(ConfigurationManager::ALIAS_VERSION_PREAMBLE) == 0)
1556  {
1557  // convert alias to version
1558  std::map<std::string /*table*/,
1559  std::map<std::string /*alias*/, TableVersion>>
1560  versionAliases = cfgMgr->getVersionAliases();
1561 
1562  std::string versionAlias;
1563  versionAlias =
1564  vB.substr(ConfigurationManager::ALIAS_VERSION_PREAMBLE.size());
1565 
1566  if(versionAliases.find(tableName) != versionAliases.end() &&
1567  versionAliases[tableName].find(versionAlias) !=
1568  versionAliases[tableName].end())
1569  {
1570  versionB = versionAliases[tableName][versionAlias];
1571  __SUP_COUT__ << "version alias translated to: " << versionB << __E__;
1572  }
1573  else
1574  __SUP_COUT_WARN__ << "version alias '" << versionAlias
1575  << "'was not found in active version aliases!"
1576  << __E__;
1577  }
1578  else // else take specified version
1579  versionB = atoi(vB.c_str());
1580  }
1581  else
1582  {
1583  versionA = atoi(vA.c_str());
1584  versionB = atoi(vB.c_str());
1585  }
1586 
1587  __SUP_COUTV__(versionA);
1588  __SUP_COUTV__(versionB);
1589 
1590  handleTableDiff(xmlOut, cfgMgr, tableName, versionA, versionB);
1591  } //end diffTableVersions
1592  else if(requestType == "findGroupsWithTable")
1593  {
1594  std::string tableName =
1595  CgiDataUtilities::getData(cgiIn, "tableName"); // from GET
1596  std::string tableVersion =
1597  CgiDataUtilities::getData(cgiIn, "tableVersion"); // from GET
1598 
1599  __SUP_COUTV__(tableName);
1600  __SUP_COUTV__(tableVersion);
1601 
1602  std::set<std::string> groupsContainingTable =
1603  cfgMgr->getConfigurationInterface()->findGroupsWithTable(
1604  tableName, TableVersion(tableVersion));
1605  __SUP_COUT__ << "Groups containing " << tableName << "-v" << tableVersion
1606  << " count: " << groupsContainingTable.size() << __E__;
1607  std::string groupsContainingTableString =
1608  StringMacros::setToString(groupsContainingTable);
1609  __SUP_COUTTV__(groupsContainingTableString);
1610 
1611  xmlOut.addNumberElementToData("GroupsContainingCount",
1612  groupsContainingTable.size());
1613  xmlOut.addTextElementToData("GroupsContainingCSV", groupsContainingTableString);
1614 
1615  } //end findGroupsWithTable
1616  else if(requestType == "savePlanCommandSequence")
1617  {
1618  std::string planName = CgiDataUtilities::getData(cgiIn, "planName"); // from GET
1619  std::string commands =
1620  CgiDataUtilities::postData(cgiIn, "commands"); // from POST
1621  std::string modifiedTables = CgiDataUtilities::postData(cgiIn, "modifiedTables");
1622  std::string groupName = CgiDataUtilities::getData(cgiIn, "groupName");
1623  std::string groupKey = CgiDataUtilities::getData(cgiIn, "groupKey");
1624 
1625  __SUP_COUTV__(modifiedTables);
1626  __SUP_COUTV__(planName);
1627  __SUP_COUTV__(commands);
1628  __SUP_COUTV__(groupName);
1629  __SUP_COUTV__(groupKey);
1630 
1631  handleSavePlanCommandSequenceXML(xmlOut,
1632  cfgMgr,
1633  groupName,
1634  TableGroupKey(groupKey),
1635  modifiedTables,
1636  userInfo.username_,
1637  planName,
1638  commands);
1639  }
1640  else if(requestType == "mergeGroups")
1641  {
1642  std::string groupANameContext =
1643  CgiDataUtilities::getData(cgiIn, "groupANameContext");
1644  std::string groupAKeyContext =
1645  CgiDataUtilities::getData(cgiIn, "groupAKeyContext");
1646  std::string groupBNameContext =
1647  CgiDataUtilities::getData(cgiIn, "groupBNameContext");
1648  std::string groupBKeyContext =
1649  CgiDataUtilities::getData(cgiIn, "groupBKeyContext");
1650  std::string groupANameConfig =
1651  CgiDataUtilities::getData(cgiIn, "groupANameConfig");
1652  std::string groupAKeyConfig = CgiDataUtilities::getData(cgiIn, "groupAKeyConfig");
1653  std::string groupBNameConfig =
1654  CgiDataUtilities::getData(cgiIn, "groupBNameConfig");
1655  std::string groupBKeyConfig = CgiDataUtilities::getData(cgiIn, "groupBKeyConfig");
1656  std::string mergeApproach = CgiDataUtilities::getData(cgiIn, "mergeApproach");
1657 
1658  __SUP_COUTV__(groupANameContext);
1659  __SUP_COUTV__(groupAKeyContext);
1660  __SUP_COUTV__(groupBNameContext);
1661  __SUP_COUTV__(groupBKeyContext);
1662  __SUP_COUTV__(groupANameConfig);
1663  __SUP_COUTV__(groupAKeyConfig);
1664  __SUP_COUTV__(groupBNameConfig);
1665  __SUP_COUTV__(groupBKeyConfig);
1666  __SUP_COUTV__(mergeApproach);
1667 
1668  handleMergeGroupsXML(xmlOut,
1669  cfgMgr,
1670  groupANameContext,
1671  TableGroupKey(groupAKeyContext),
1672  groupBNameContext,
1673  TableGroupKey(groupBKeyContext),
1674  groupANameConfig,
1675  TableGroupKey(groupAKeyConfig),
1676  groupBNameConfig,
1677  TableGroupKey(groupBKeyConfig),
1678  userInfo.username_,
1679  mergeApproach);
1680  }
1681  else
1682  {
1683  __SUP_SS__ << "requestType Request, " << requestType
1684  << ", not recognized by the Configuration GUI Supervisor (was it "
1685  "intended for another Supervisor?)."
1686  << __E__;
1687  __SUP_COUT__ << "\n" << ss.str();
1688  xmlOut.addTextElementToData("Error", ss.str());
1689  }
1690 
1691  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1692  // always add active table groups to xml response
1694  xmlOut, cfgMgr, userInfo.username_);
1695  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
1696 
1697 } // end ::request()
1698 catch(const std::runtime_error& e)
1699 {
1700  __SS__ << "A fatal error occurred while handling the request '" << requestType
1701  << ".' Error: " << e.what() << __E__;
1702  __COUT_ERR__ << "\n" << ss.str();
1703  xmlOut.addTextElementToData("Error", ss.str());
1704 
1705  try
1706  {
1707  // always add version tracking bool
1708  xmlOut.addTextElementToData(
1709  "versionTracking",
1710  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1711  }
1712  catch(...)
1713  {
1714  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1715  }
1716 } // end ::request() catch
1717 catch(...)
1718 {
1719  __SS__ << "An unknown fatal error occurred while handling the request '"
1720  << requestType << ".'" << __E__;
1721  try
1722  {
1723  throw;
1724  } //one more try to printout extra info
1725  catch(const std::exception& e)
1726  {
1727  ss << "Exception message: " << e.what();
1728  }
1729  catch(...)
1730  {
1731  }
1732  __COUT_ERR__ << "\n" << ss.str();
1733  xmlOut.addTextElementToData("Error", ss.str());
1734 
1735  try
1736  {
1737  // always add version tracking bool
1738  xmlOut.addTextElementToData(
1739  "versionTracking",
1740  ConfigurationInterface::isVersionTrackingEnabled() ? "ON" : "OFF");
1741  }
1742  catch(...)
1743  {
1744  __COUT_ERR__ << "Error getting version tracking status!" << __E__;
1745  }
1746 
1747 } // end ::request() catch
1748 
1749 //==============================================================================
1764 void ConfigurationGUISupervisor::handleGetAffectedGroupsXML(
1765  HttpXmlDocument& xmlOut,
1766  ConfigurationManagerRW* cfgMgr,
1767  const std::string& rootGroupName,
1768  const TableGroupKey& rootGroupKey,
1769  const std::string& modifiedTables)
1770 try
1771 {
1772  __SUP_COUT__ << "rootGroupName " << rootGroupName << "(" << rootGroupKey
1773  << "). modifiedTables = " << modifiedTables << __E__;
1774 
1775  std::map<std::string, std::pair<std::string, TableGroupKey>> consideredGroups =
1776  cfgMgr->getActiveTableGroups();
1777 
1778  // check that there is a context and table group to consider
1779  // if there is not, then pull from failed list
1780  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT].second.isInvalid())
1781  {
1782  __SUP_COUT__ << "Finding a context group to consider..." << __E__;
1783  if(cfgMgr->getFailedTableGroups().find(
1784  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT) !=
1785  cfgMgr->getFailedTableGroups().end())
1786  {
1787  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1788  cfgMgr->getFailedTableGroups().at(
1789  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT);
1790  }
1791  else if(cfgMgr->getFailedTableGroups().find(
1792  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1793  cfgMgr->getFailedTableGroups().end())
1794  {
1795  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONTEXT] =
1796  cfgMgr->getFailedTableGroups().at(
1797  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1798  }
1799  }
1800  if(consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION]
1801  .second.isInvalid())
1802  {
1803  __SUP_COUT__ << "Finding a table group to consider..." << __E__;
1804  if(cfgMgr->getFailedTableGroups().find(
1805  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION) !=
1806  cfgMgr->getFailedTableGroups().end())
1807  {
1808  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1809  cfgMgr->getFailedTableGroups().at(
1810  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION);
1811  }
1812  else if(cfgMgr->getFailedTableGroups().find(
1813  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN) !=
1814  cfgMgr->getFailedTableGroups().end())
1815  {
1816  consideredGroups[ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION] =
1817  cfgMgr->getFailedTableGroups().at(
1818  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN);
1819  }
1820  }
1821 
1822  __SUP_COUTV__(StringMacros::mapToString(consideredGroups));
1823 
1824  // determine the type of table group
1825  try
1826  {
1827  std::map<std::string /*name*/, TableVersion /*version*/> rootGroupMemberMap;
1828 
1829  cfgMgr->loadTableGroup(rootGroupName,
1830  rootGroupKey,
1831  0,
1832  &rootGroupMemberMap,
1833  0,
1834  0,
1835  0,
1836  0,
1837  0, // defaults
1838  true); // doNotLoadMember
1839 
1840  const std::string& groupType = cfgMgr->getTypeNameOfGroup(rootGroupMemberMap);
1841 
1842  consideredGroups[groupType] =
1843  std::pair<std::string, TableGroupKey>(rootGroupName, rootGroupKey);
1844  }
1845  catch(const std::runtime_error& e)
1846  {
1847  // if actual group name was attempted re-throw
1848  if(rootGroupName.size())
1849  {
1850  __SUP_SS__ << "Failed to determine type of table group for " << rootGroupName
1851  << "(" << rootGroupKey << ")! " << e.what() << __E__;
1852  __SUP_COUT_ERR__ << "\n" << ss.str();
1853  //__SS_THROW__;
1854  }
1855 
1856  // else assume it was the intention to just consider the active groups
1857  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1858  "name - assuming this was intentional."
1859  << __E__;
1860  }
1861  catch(...)
1862  {
1863  // if actual group name was attempted re-throw
1864  if(rootGroupName.size())
1865  {
1866  __SUP_COUT_ERR__ << "Failed to determine type of table group for "
1867  << rootGroupName << "(" << rootGroupKey << ")!" << __E__;
1868  // throw;
1869  }
1870 
1871  // else assume it was the intention to just consider the active groups
1872  __SUP_COUT__ << "Did not modify considered active groups due to empty root group "
1873  "name - assuming this was intentional."
1874  << __E__;
1875  }
1876 
1877  std::map<std::string /*name*/,
1878  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>
1879  modifiedTablesMap;
1880  std::map<std::string /*name*/,
1881  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>::iterator
1882  modifiedTablesMapIt;
1883  {
1884  std::istringstream f(modifiedTables);
1885  std::string table, version;
1886  while(getline(f, table, ','))
1887  {
1888  getline(f, version, ',');
1889  modifiedTablesMap.insert(
1890  std::pair<
1891  std::string /*name*/,
1892  std::pair<bool /*foundAffectedGroup*/, TableVersion /*version*/>>(
1893  table,
1894  std::make_pair(false /*foundAffectedGroup*/, TableVersion(version))));
1895  }
1896  __SUP_COUT__ << modifiedTables << __E__;
1897  for(auto& pair : modifiedTablesMap)
1898  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second.second
1899  << __E__;
1900  }
1901 
1902  bool affected;
1903  xercesc::DOMElement* parentEl = nullptr;
1904  std::string groupComment;
1905  std::vector<std::string> orderedGroupTypes(
1906  {ConfigurationManager::GROUP_TYPE_NAME_CONTEXT,
1907  ConfigurationManager::GROUP_TYPE_NAME_BACKBONE,
1908  ConfigurationManager::GROUP_TYPE_NAME_ITERATE,
1909  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION});
1910  for(auto groupType : orderedGroupTypes)
1911  {
1912  if(consideredGroups.find(groupType) == consideredGroups.end())
1913  continue; // skip missing
1914 
1915  const std::pair<std::string, TableGroupKey>& group = consideredGroups[groupType];
1916 
1917  if(group.second.isInvalid())
1918  continue; // skip invalid
1919 
1920  __SUP_COUT__ << "Considering " << groupType << " group " << group.first << " ("
1921  << group.second << ")" << __E__;
1922 
1923  affected = false;
1924  parentEl = nullptr;
1925 
1926  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
1927  cfgMgr->loadTableGroup(group.first,
1928  group.second,
1929  0,
1930  &memberMap,
1931  0,
1932  0,
1933  &groupComment,
1934  0,
1935  0, // mostly defaults
1936  true /*doNotLoadMember*/);
1937 
1938  __SUP_COUTV__(StringMacros::mapToString(memberMap));
1939  __SUP_COUT__ << "groupComment = " << groupComment << __E__;
1940 
1941  for(auto& table : memberMap)
1942  {
1943  if((modifiedTablesMapIt = modifiedTablesMap.find(table.first)) !=
1944  modifiedTablesMap
1945  .end() && // check if version is different for member table
1946  table.second != (*modifiedTablesMapIt).second.second)
1947  {
1948  __SUP_COUT__ << "Affected by " << (*modifiedTablesMapIt).first << ":"
1949  << (*modifiedTablesMapIt).second.second << __E__;
1950 
1951  memberMap[table.first] = (*modifiedTablesMapIt).second.second;
1952  (*modifiedTablesMapIt).second.first = true; // found affected group
1953 
1954  affected = true;
1955  if(!parentEl)
1956  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1957  }
1958  }
1959 
1960  if(groupType == ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
1961  {
1962  __SUP_COUT__ << "Considering mockup tables for Configuration Group..."
1963  << __E__;
1964  for(auto& table : modifiedTablesMap)
1965  {
1966  if(table.second.first) // already found affected group
1967  continue;
1968 
1969  if(table.second.second.isMockupVersion() &&
1970  memberMap.find(table.first) == memberMap.end())
1971  {
1972  __SUP_COUT__ << "Found mockup table '" << table.first
1973  << "' for Configuration Group." << __E__;
1974  memberMap[table.first] = table.second.second;
1975 
1976  if(!parentEl)
1977  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1978  //indicate to client this table needs to be added to group!
1979  xmlOut.addTextElementToParent("AddMemberName", table.first, parentEl);
1980  xmlOut.addTextElementToParent(
1981  "AddMemberVersion", table.second.second.toString(), parentEl);
1982 
1983  affected = true;
1984  }
1985  }
1986  }
1987 
1988  __SUP_COUTV__(affected);
1989  if(affected)
1990  {
1991  if(!parentEl)
1992  parentEl = xmlOut.addTextElementToData("AffectedActiveGroup", "");
1993  xmlOut.addTextElementToParent("GroupName", group.first, parentEl);
1994  xmlOut.addTextElementToParent("GroupKey", group.second.toString(), parentEl);
1995  xmlOut.addTextElementToParent("GroupComment", groupComment, parentEl);
1996 
1997  for(auto& table : memberMap)
1998  {
1999  xmlOut.addTextElementToParent("MemberName", table.first, parentEl);
2000  xmlOut.addTextElementToParent(
2001  "MemberVersion", table.second.toString(), parentEl);
2002  }
2003  }
2004  } // end affected group loop
2005 }
2006 catch(std::runtime_error& e)
2007 {
2008  __SUP_COUT__ << "Error detected!\n\n " << e.what() << __E__;
2009  xmlOut.addTextElementToData(
2010  "Error", "Error getting affected groups! " + std::string(e.what()));
2011 }
2012 catch(...)
2013 {
2014  __SUP_COUT__ << "Error detected!\n\n " << __E__;
2015  xmlOut.addTextElementToData("Error", "Error getting affected groups! ");
2016 }
2017 
2018 //==============================================================================
2026 void ConfigurationGUISupervisor::setupActiveTablesXML(
2027  HttpXmlDocument& xmlOut,
2028  ConfigurationManagerRW* cfgMgr,
2029  const std::string& groupName,
2030  const TableGroupKey& groupKey,
2031  const std::string& modifiedTables,
2032  bool refreshAll,
2033  bool doGetGroupInfo,
2034  std::map<std::string /*name*/, TableVersion /*version*/>* returnMemberMap,
2035  bool outputActiveTables,
2036  std::string* accumulatedErrors)
2037 try
2038 {
2039  xmlOut.addTextElementToData("tableGroup", groupName);
2040  xmlOut.addTextElementToData("tableGroupKey", groupKey.toString());
2041 
2042  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
2043 
2044  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2045 
2046  // reload all tables so that partially loaded tables are not allowed
2047  if( //usingActiveGroups ||
2048  refreshAll)
2049  {
2050  __SUP_COUT__ << "Refreshing all table info, ignoring warnings..." << __E__;
2051  std::string accumulatedWarnings = "";
2052  cfgMgr->getAllTableInfo(true /* refresh */,
2053  &accumulatedWarnings,
2054  "" /* errorFilterName */,
2055  false /* getGroupKeys */,
2056  false /* getGroupInfo */,
2057  true /* initializeActiveGroups */);
2058  }
2059  else //make sure expected active tables are setup, for standard starting point
2060  {
2061  //This is needed, for example, when on Context group which could point into the
2062  // Configure group; need common starting point (which is the Active group tables)
2063  // Then bring the modified tables over top.
2064  // Context --> Configure is simplest example
2065  // ...but Configure can also point at Context, or Iterator, or Backbone.
2066 
2067  __SUP_COUT__ << "Restoring active table group tables..." << __E__;
2068  auto activeGroups = cfgMgr->getActiveTableGroups();
2069  for(const auto& activeGroup : activeGroups)
2070  {
2071  if(activeGroup.second.first == groupName &&
2072  activeGroup.second.second == groupKey)
2073  {
2074  __SUP_COUTT__ << "Skipping target active group." << __E__;
2075  continue;
2076  }
2077  __SUP_COUTT__ << "Loading " << activeGroup.first << " "
2078  << activeGroup.second.first << "(" << activeGroup.second.second
2079  << ")..." << __E__;
2080  try
2081  {
2082  cfgMgr->loadTableGroup(activeGroup.second.first,
2083  activeGroup.second.second,
2084  false /*doActivate*/
2085  );
2086  }
2087  catch(...) //ignore errors
2088  {
2089  __SUP_COUT__ << "Ignoring errors while setting up active tables for "
2090  << activeGroup.second.first << "("
2091  << activeGroup.second.second << ")..." << __E__;
2092  }
2093  } //end load tables as active, but do not activate groups
2094  }
2095  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2096 
2097  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
2098 
2099  std::map<std::string /*name*/, TableVersion /*version*/> modifiedTablesMap;
2100  std::map<std::string /*name*/, TableVersion /*version*/>::iterator
2101  modifiedTablesMapIt;
2102 
2103  if(usingActiveGroups)
2104  {
2105  // no need to load a target group
2106  __SUP_COUT__ << "Using active groups." << __E__;
2107  }
2108  else
2109  {
2110  __SUP_COUT__ << "Loading group '" << groupName << "(" << groupKey << ")'"
2111  << __E__;
2112 
2113  std::string groupComment, groupAuthor, tableGroupCreationTime, groupType;
2114 
2115  // only same member map if object pointer was passed
2116  cfgMgr->loadTableGroup(groupName,
2117  groupKey,
2118  false /*doActivate*/,
2119  returnMemberMap,
2120  0 /*progressBar*/,
2121  accumulatedErrors,
2122  doGetGroupInfo ? &groupComment : 0,
2123  doGetGroupInfo ? &groupAuthor : 0,
2124  doGetGroupInfo ? &tableGroupCreationTime : 0,
2125  false /*doNotLoadMembers*/,
2126  doGetGroupInfo ? &groupType : 0);
2127 
2128  if(doGetGroupInfo)
2129  {
2130  xmlOut.addTextElementToData("tableGroupComment", groupComment);
2131  xmlOut.addTextElementToData("tableGroupAuthor", groupAuthor);
2132  xmlOut.addTextElementToData("tableGroupCreationTime", tableGroupCreationTime);
2133  xmlOut.addTextElementToData("tableGroupType", groupType);
2134  }
2135 
2136  if(accumulatedErrors && *accumulatedErrors != "")
2137  __SUP_COUTV__(*accumulatedErrors);
2138  }
2139  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2140 
2141  // extract modified tables
2142  {
2143  std::istringstream f(modifiedTables);
2144  std::string table, version;
2145  while(getline(f, table, ','))
2146  {
2147  getline(f, version, ',');
2148  modifiedTablesMap.insert(
2149  std::pair<std::string /*name*/, TableVersion /*version*/>(
2150  table, TableVersion(version)));
2151  }
2152  //__SUP_COUT__ << modifiedTables << __E__;
2153  for(auto& pair : modifiedTablesMap)
2154  __SUP_COUT__ << "modified table " << pair.first << ":" << pair.second
2155  << __E__;
2156  }
2157 
2158  // add all active table pairs to xmlOut
2159  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
2160  xmlOut.addTextElementToData("DefaultNoLink",
2161  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
2162 
2163  // construct specially ordered table name set
2164  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
2165  for(const auto& tablePair : allActivePairs)
2166  orderedTableSet.emplace(tablePair.first);
2167 
2168  std::map<std::string, TableInfo>::const_iterator tableInfoIt;
2169  for(auto& orderedTableName : orderedTableSet)
2170  {
2171  tableInfoIt = allTableInfo.find(orderedTableName);
2172  if(tableInfoIt == allTableInfo.end())
2173  {
2174  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
2175  << __E__;
2176  __SS_THROW__;
2177  }
2178 
2179  if(outputActiveTables)
2180  xmlOut.addTextElementToData("ActiveTableName", orderedTableName);
2181 
2182  // check if name is in modifiedTables
2183  // if so, activate the temporary version
2184  if((modifiedTablesMapIt = modifiedTablesMap.find(orderedTableName)) !=
2185  modifiedTablesMap.end())
2186  {
2187  __SUP_COUT__ << "Found modified table " << (*modifiedTablesMapIt).first
2188  << ": trying... " << (*modifiedTablesMapIt).second << __E__;
2189 
2190  try
2191  {
2192  tableInfoIt->second.tablePtr_->setActiveView(
2193  (*modifiedTablesMapIt).second);
2194  }
2195  catch(...)
2196  {
2197  __SUP_SS__ << "Modified table version v" << (*modifiedTablesMapIt).second
2198  << " failed. Reverting to v"
2199  << tableInfoIt->second.tablePtr_->getView().getVersion() << "."
2200  << __E__;
2201  __SUP_COUT_WARN__ << "Warning detected!\n\n " << ss.str() << __E__;
2202  xmlOut.addTextElementToData(
2203  "Warning",
2204  "Error setting up active tables!\n\n" + std::string(ss.str()));
2205  }
2206  }
2207 
2208  if(outputActiveTables)
2209  {
2210  xmlOut.addTextElementToData(
2211  "ActiveTableVersion",
2212  tableInfoIt->second.tablePtr_->getView().getVersion().toString());
2213  xmlOut.addTextElementToData(
2214  "ActiveTableComment",
2215  tableInfoIt->second.tablePtr_->getView().getAuthor() + ": " +
2216  tableInfoIt->second.tablePtr_->getView().getComment());
2217  }
2218 
2219  } // end ordered table loop
2220 
2221  __SUP_COUTTV__(StringMacros::mapToString(cfgMgr->getActiveVersions()));
2222 
2223 } // end setupActiveTablesXML()
2224 catch(std::runtime_error& e)
2225 {
2226  __SUP_SS__ << ("Error setting up active tables!\n\n" + std::string(e.what()))
2227  << __E__;
2228  __SUP_COUT_ERR__ << "\n" << ss.str();
2229  xmlOut.addTextElementToData("Error", ss.str());
2230  throw; // throw to get info from special errors at a parent level
2231 }
2232 catch(...)
2233 {
2234  __SUP_SS__ << ("Error setting up active tables!\n\n") << __E__;
2235  try
2236  {
2237  throw;
2238  } //one more try to printout extra info
2239  catch(const std::exception& e)
2240  {
2241  ss << "Exception message: " << e.what();
2242  }
2243  catch(...)
2244  {
2245  }
2246  __SUP_COUT_ERR__ << "\n" << ss.str();
2247  xmlOut.addTextElementToData("Error", ss.str());
2248  throw; // throw to get info from special errors at a parent level
2249 } // end setupActiveTablesXML() throw
2250 
2251 //==============================================================================
2266 void ConfigurationGUISupervisor::handleFillCreateTreeNodeRecordsXML(
2267  HttpXmlDocument& xmlOut,
2268  ConfigurationManagerRW* cfgMgr,
2269  const std::string& groupName,
2270  const TableGroupKey& groupKey,
2271  const std::string& startPath,
2272  const std::string& modifiedTables,
2273  const std::string& recordList,
2274  const std::string& author)
2275 {
2276  // setup active tables based on input group and modified tables
2277  setupActiveTablesXML(xmlOut,
2278  cfgMgr,
2279  groupName,
2280  groupKey,
2281  modifiedTables,
2282  true /* refresh all */,
2283  false /* getGroupInfo */,
2284  0 /* returnMemberMap */,
2285  false /* outputActiveTables */);
2286 
2287  try
2288  {
2289  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2290  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2291 
2292  __SUP_COUT__ << table->getTableName() << __E__;
2293  TableVersion temporaryVersion;
2294 
2295  // if current version is not temporary
2296  // create temporary
2297  // else re-modify temporary version
2298  // edit temporary version directly
2299  // then after all edits return active versions
2300  //
2301 
2302  bool firstSave = true;
2303 
2304  // save current version
2305  TableView backupView(targetNode.getTableName());
2306 
2307  // extract record list
2308  {
2309  std::istringstream f(recordList);
2310  std::string recordUID;
2311 
2312  while(getline(f, recordUID, ',')) // for each record
2313  {
2314  recordUID = StringMacros::decodeURIComponent(recordUID);
2315 
2316  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2317 
2318  if(firstSave) // handle version bookkeeping
2319  {
2320  if(!(temporaryVersion = targetNode.getTableVersion())
2321  .isTemporaryVersion())
2322  {
2323  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2324  // create temporary version for editing
2325  temporaryVersion = table->createTemporaryView(temporaryVersion);
2326  cfgMgr->saveNewTable(
2327  targetNode.getTableName(),
2328  temporaryVersion,
2329  true); // proper bookkeeping for temporary version with the new version
2330 
2331  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2332  << __E__;
2333  }
2334  else // else table is already temporary version
2335  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2336  << __E__;
2337 
2338  firstSave = false;
2339 
2340  // copy original to backup before modifying
2341  backupView.copy(table->getView(), temporaryVersion, author);
2342  }
2343 
2344  // at this point have valid temporary version to edit
2345 
2346  // copy "table-newRow" type edit from handleSaveTreeNodeEditXML()
2347  // functionality
2348 
2349  // add row
2350  unsigned int row = table->getViewP()->addRow(
2351  author,
2352  true /*incrementUniqueData*/); // increment all unique data fields to void conflict
2353 
2354  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
2355  try
2356  {
2357  unsigned int col = table->getViewP()->getColStatus();
2358  table->getViewP()->setURIEncodedValue("1", row, col);
2359  }
2360  catch(...)
2361  {
2362  } // if not, ignore
2363 
2364  // set UID value
2365  table->getViewP()->setURIEncodedValue(
2366  recordUID, row, table->getViewP()->getColUID());
2367  }
2368  }
2369 
2370  if(!firstSave) // only test table if there was a change
2371  {
2372  try
2373  {
2374  table->getViewP()->init(); // verify new table (throws runtime_errors)
2375  }
2376  catch(...)
2377  {
2378  __SUP_COUT_INFO__ << "Reverting to original view." << __E__;
2379  __SUP_COUT__ << "Before:" << __E__;
2380  table->getViewP()->print();
2381  table->getViewP()->copy(backupView, temporaryVersion, author);
2382  __SUP_COUT__ << "After:" << __E__;
2383  table->getViewP()->print();
2384 
2385  throw; // rethrow
2386  }
2387  }
2388 
2389  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2390  }
2391  catch(std::runtime_error& e)
2392  {
2393  __SUP_SS__ << ("Error creating new record(s)!\n\n" + std::string(e.what()))
2394  << __E__;
2395  __SUP_COUT_ERR__ << "\n" << ss.str();
2396  xmlOut.addTextElementToData("Error", ss.str());
2397  }
2398  catch(...)
2399  {
2400  __SUP_SS__ << ("Error creating new record(s)!\n\n") << __E__;
2401  try
2402  {
2403  throw;
2404  } //one more try to printout extra info
2405  catch(const std::exception& e)
2406  {
2407  ss << "Exception message: " << e.what();
2408  }
2409  catch(...)
2410  {
2411  }
2412  __SUP_COUT_ERR__ << "\n" << ss.str();
2413  xmlOut.addTextElementToData("Error", ss.str());
2414  }
2415 } //end handleFillCreateTreeNodeRecordsXML()
2416 
2417 //==============================================================================
2420 void ConfigurationGUISupervisor::handleFillModifiedTablesXML(
2421  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr)
2422 try
2423 {
2424  // return modified <modified tables>
2425  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
2426  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
2427  for(auto& activePair : allActivePairs)
2428  {
2429  xmlOut.addTextElementToData("NewActiveTableName", activePair.first);
2430  xmlOut.addTextElementToData("NewActiveTableVersion",
2431  allTableInfo.at(activePair.first)
2432  .tablePtr_->getView()
2433  .getVersion()
2434  .toString());
2435  xmlOut.addTextElementToData(
2436  "NewActiveTableComment",
2437  allTableInfo.at(activePair.first).tablePtr_->getView().getAuthor() + ": " +
2438  allTableInfo.at(activePair.first).tablePtr_->getView().getComment());
2439  }
2440 } //end handleFillModifiedTablesXML()
2441 catch(std::runtime_error& e)
2442 {
2443  __SUP_SS__ << ("Error!\n\n" + std::string(e.what())) << __E__;
2444  __SUP_COUT_ERR__ << "\n" << ss.str();
2445  xmlOut.addTextElementToData("Error", ss.str());
2446 }
2447 catch(...)
2448 {
2449  __SUP_SS__ << ("Error!\n\n") << __E__;
2450  try
2451  {
2452  throw;
2453  } //one more try to printout extra info
2454  catch(const std::exception& e)
2455  {
2456  ss << "Exception message: " << e.what();
2457  }
2458  catch(...)
2459  {
2460  }
2461  __SUP_COUT_ERR__ << "\n" << ss.str();
2462  xmlOut.addTextElementToData("Error", ss.str());
2463 } //end handleFillModifiedTablesXML() catch
2464 
2465 //==============================================================================
2480 void ConfigurationGUISupervisor::handleFillDeleteTreeNodeRecordsXML(
2481  HttpXmlDocument& xmlOut,
2482  ConfigurationManagerRW* cfgMgr,
2483  const std::string& groupName,
2484  const TableGroupKey& groupKey,
2485  const std::string& startPath,
2486  const std::string& modifiedTables,
2487  const std::string& recordList)
2488 {
2489  // setup active tables based on input group and modified tables
2490  setupActiveTablesXML(xmlOut,
2491  cfgMgr,
2492  groupName,
2493  groupKey,
2494  modifiedTables,
2495  true /* refresh all */,
2496  false /* getGroupInfo */,
2497  0 /* returnMemberMap */,
2498  false /* outputActiveTables */);
2499 
2500  try
2501  {
2502  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2503  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2504 
2505  __SUP_COUT__ << table->getTableName() << __E__;
2506  TableVersion temporaryVersion;
2507 
2508  // if current version is not temporary
2509  // create temporary
2510  // else re-modify temporary version
2511  // edit temporary version directly
2512  // then after all edits return active versions
2513  //
2514 
2515  bool firstSave = true;
2516 
2517  // extract record list
2518  {
2519  std::istringstream f(recordList);
2520  std::string recordUID;
2521  // unsigned int i;
2522 
2523  while(getline(f, recordUID, ',')) // for each record
2524  {
2525  recordUID = StringMacros::decodeURIComponent(recordUID);
2526 
2527  __SUP_COUT__ << "recordUID " << recordUID << __E__;
2528 
2529  if(firstSave) // handle version bookkeeping
2530  {
2531  if(!(temporaryVersion = targetNode.getTableVersion())
2532  .isTemporaryVersion())
2533  {
2534  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2535  // create temporary version for editing
2536  temporaryVersion = table->createTemporaryView(temporaryVersion);
2537  cfgMgr->saveNewTable(
2538  targetNode.getTableName(),
2539  temporaryVersion,
2540  true); // proper bookkeeping for temporary version with the new version
2541 
2542  __SUP_COUT__ << "Created temporary version " << temporaryVersion
2543  << __E__;
2544  }
2545  else // else table is already temporary version
2546  __SUP_COUT__ << "Using temporary version " << temporaryVersion
2547  << __E__;
2548 
2549  firstSave = false;
2550  }
2551 
2552  // at this point have valid temporary version to edit
2553 
2554  // copy "delete-uid" type edit from handleSaveTreeNodeEditXML()
2555  // functionality
2556  unsigned int row =
2557  table->getViewP()->findRow(table->getViewP()->getColUID(), recordUID);
2558  table->getViewP()->deleteRow(row);
2559  }
2560  }
2561 
2562  if(!firstSave) // only test table if there was a change
2563  table->getViewP()->init(); // verify new table (throws runtime_errors)
2564 
2565  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2566  }
2567  catch(std::runtime_error& e)
2568  {
2569  __SUP_SS__ << ("Error removing record(s)!\n\n" + std::string(e.what())) << __E__;
2570  __SUP_COUT_ERR__ << "\n" << ss.str();
2571  xmlOut.addTextElementToData("Error", ss.str());
2572  }
2573  catch(...)
2574  {
2575  __SUP_SS__ << ("Error removing record(s)!\n\n") << __E__;
2576  try
2577  {
2578  throw;
2579  } //one more try to printout extra info
2580  catch(const std::exception& e)
2581  {
2582  ss << "Exception message: " << e.what();
2583  }
2584  catch(...)
2585  {
2586  }
2587  __SUP_COUT_ERR__ << "\n" << ss.str();
2588  xmlOut.addTextElementToData("Error", ss.str());
2589  }
2590 } // end handleFillDeleteTreeNodeRecordsXML()
2591 
2592 //==============================================================================
2608 void ConfigurationGUISupervisor::handleFillRenameTreeNodeRecordsXML(
2609  HttpXmlDocument& xmlOut,
2610  ConfigurationManagerRW* cfgMgr,
2611  const std::string& groupName,
2612  const TableGroupKey& groupKey,
2613  const std::string& startPath,
2614  const std::string& modifiedTables,
2615  const std::string& recordList,
2616  const std::string& newRecordList)
2617 {
2618  // setup active tables based on input group and modified tables
2619  setupActiveTablesXML(xmlOut,
2620  cfgMgr,
2621  groupName,
2622  groupKey,
2623  modifiedTables,
2624  true /* refresh all */,
2625  false /* getGroupInfo */,
2626  0 /* returnMemberMap */,
2627  false /* outputActiveTables */);
2628 
2629  try
2630  {
2631  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2632  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2633 
2634  __SUP_COUT__ << table->getTableName() << __E__;
2635  TableVersion temporaryVersion;
2636 
2637  // if current version is not temporary
2638  // create temporary
2639  // else re-modify temporary version
2640  // edit temporary version directly
2641  // then after all edits return active versions
2642  //
2643 
2644  // extract record list
2645  std::vector<std::string> recordArray =
2647  std::vector<std::string> newRecordArray =
2648  StringMacros::getVectorFromString(newRecordList);
2649 
2650  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2651  __SUP_COUTV__(StringMacros::vectorToString(newRecordArray));
2652 
2653  if(recordArray.size() == 0 || recordArray.size() != newRecordArray.size())
2654  {
2655  __SUP_SS__
2656  << "Invalid record size vs new record name size, they must be the same: "
2657  << recordArray.size() << " vs " << newRecordArray.size() << __E__;
2658  __SUP_SS_THROW__;
2659  }
2660 
2661  // handle version bookkeeping
2662  {
2663  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2664  {
2665  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2666  // create temporary version for editing
2667  temporaryVersion = table->createTemporaryView(temporaryVersion);
2668  cfgMgr->saveNewTable(
2669  targetNode.getTableName(),
2670  temporaryVersion,
2671  true); // proper bookkeeping for temporary version with the new version
2672 
2673  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2674  }
2675  else // else table is already temporary version
2676  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2677  }
2678 
2679  // at this point have valid temporary version to edit
2680 
2681  // for every record, change name
2682  unsigned int row;
2683  for(unsigned int i = 0; i < recordArray.size(); ++i)
2684  {
2685  row = table->getViewP()->findRow(
2686  table->getViewP()->getColUID(),
2687  StringMacros::decodeURIComponent(recordArray[i]));
2688 
2689  table->getViewP()->setValueAsString(
2690  newRecordArray[i], row, table->getViewP()->getColUID());
2691  }
2692 
2693  table->getViewP()->init(); // verify new table (throws runtime_errors)
2694 
2695  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2696  }
2697  catch(std::runtime_error& e)
2698  {
2699  __SUP_SS__ << ("Error renaming record(s)!\n\n" + std::string(e.what())) << __E__;
2700  __SUP_COUT_ERR__ << "\n" << ss.str();
2701  xmlOut.addTextElementToData("Error", ss.str());
2702  }
2703  catch(...)
2704  {
2705  __SUP_SS__ << ("Error renaming record(s)!\n\n") << __E__;
2706  try
2707  {
2708  throw;
2709  } //one more try to printout extra info
2710  catch(const std::exception& e)
2711  {
2712  ss << "Exception message: " << e.what();
2713  }
2714  catch(...)
2715  {
2716  }
2717  __SUP_COUT_ERR__ << "\n" << ss.str();
2718  xmlOut.addTextElementToData("Error", ss.str());
2719  }
2720 } // end handleFillRenameTreeNodeRecordsXML()
2721 
2722 //==============================================================================
2739 void ConfigurationGUISupervisor::handleFillCopyTreeNodeRecordsXML(
2740  HttpXmlDocument& xmlOut,
2741  ConfigurationManagerRW* cfgMgr,
2742  const std::string& groupName,
2743  const TableGroupKey& groupKey,
2744  const std::string& startPath,
2745  const std::string& modifiedTables,
2746  const std::string& recordList,
2747  unsigned int numberOfCopies /* = 1 */)
2748 {
2749  if(!numberOfCopies)
2750  numberOfCopies = 1; // force 0 to 1, assuming user meant to get one copy
2751 
2752  // setup active tables based on input group and modified tables
2753  setupActiveTablesXML(xmlOut,
2754  cfgMgr,
2755  groupName,
2756  groupKey,
2757  modifiedTables,
2758  true /* refresh all */,
2759  false /* getGroupInfo */,
2760  0 /* returnMemberMap */,
2761  false /* outputActiveTables */);
2762 
2763  try
2764  {
2765  ConfigurationTree targetNode = cfgMgr->getNode(startPath);
2766  TableBase* table = cfgMgr->getTableByName(targetNode.getTableName());
2767 
2768  __SUP_COUT__ << table->getTableName() << __E__;
2769  TableVersion temporaryVersion;
2770 
2771  // if current version is not temporary
2772  // create temporary
2773  // else re-modify temporary version
2774  // edit temporary version directly
2775  // then after all edits return active versions
2776  //
2777 
2778  // extract record list
2779  std::vector<std::string> recordArray =
2781  __SUP_COUTV__(StringMacros::vectorToString(recordArray));
2782 
2783  // handle version bookkeeping
2784  {
2785  if(!(temporaryVersion = targetNode.getTableVersion()).isTemporaryVersion())
2786  {
2787  __SUP_COUT__ << "Start version " << temporaryVersion << __E__;
2788  // create temporary version for editing
2789  temporaryVersion = table->createTemporaryView(temporaryVersion);
2790  cfgMgr->saveNewTable(
2791  targetNode.getTableName(),
2792  temporaryVersion,
2793  true); // proper bookkeeping for temporary version with the new version
2794 
2795  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
2796  }
2797  else // else table is already temporary version
2798  __SUP_COUT__ << "Using temporary version " << temporaryVersion << __E__;
2799  }
2800 
2801  // at this point have valid temporary version to edit
2802 
2803  // for every record, copy spec'd number of times
2804  unsigned int row;
2805  for(const auto& recordUID : recordArray)
2806  {
2807  row = table->getViewP()->findRow(table->getViewP()->getColUID(),
2809  for(unsigned int i = 0; i < numberOfCopies; ++i)
2810  table->getViewP()->copyRows(
2811  cfgMgr->getUsername(),
2812  table->getView(),
2813  row,
2814  1 /*srcRowsToCopy*/,
2815  -1 /*destOffsetRow*/,
2816  true /*generateUniqueDataColumns*/,
2817  recordUID /*baseNameAutoUID*/); // make the name similar end record loop
2818  }
2819 
2820  table->getViewP()->init(); // verify new table (throws runtime_errors)
2821 
2822  handleFillModifiedTablesXML(xmlOut, cfgMgr);
2823  }
2824  catch(std::runtime_error& e)
2825  {
2826  __SUP_SS__ << ("Error copying record(s)!\n\n" + std::string(e.what())) << __E__;
2827  __SUP_COUT_ERR__ << "\n" << ss.str();
2828  xmlOut.addTextElementToData("Error", ss.str());
2829  }
2830  catch(...)
2831  {
2832  __SUP_SS__ << ("Error copying record(s)!\n\n") << __E__;
2833  try
2834  {
2835  throw;
2836  } //one more try to printout extra info
2837  catch(const std::exception& e)
2838  {
2839  ss << "Exception message: " << e.what();
2840  }
2841  catch(...)
2842  {
2843  }
2844  __SUP_COUT_ERR__ << "\n" << ss.str();
2845  xmlOut.addTextElementToData("Error", ss.str());
2846  }
2847 } // end handleFillCopyTreeNodeRecordsXML()
2848 
2849 //==============================================================================
2866 void ConfigurationGUISupervisor::handleFillSetTreeNodeFieldValuesXML(
2867  HttpXmlDocument& xmlOut,
2868  ConfigurationManagerRW* cfgMgr,
2869  const std::string& groupName,
2870  const TableGroupKey& groupKey,
2871  const std::string& startPath,
2872  const std::string& modifiedTables,
2873  const std::string& recordList,
2874  const std::string& fieldList,
2875  const std::string& valueList,
2876  const std::string& author)
2877 {
2878  // setup active tables based on input group and modified tables
2879  setupActiveTablesXML(xmlOut,
2880  cfgMgr,
2881  groupName,
2882  groupKey,
2883  modifiedTables,
2884  true /* refresh all */,
2885  false /* getGroupInfo */,
2886  0 /* returnMemberMap */,
2887  false /* outputActiveTables */);
2888 
2889  // for each field
2890  // return field/value pair in xml
2891 
2892  try
2893  {
2894  std::vector<std::string /*relative-path*/> fieldPaths;
2895  // extract field list
2896  {
2897  std::istringstream f(fieldList);
2898  std::string fieldPath;
2899  while(getline(f, fieldPath, ','))
2900  {
2901  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
2902  }
2903  __SUP_COUT__ << fieldList << __E__;
2904  for(const auto& field : fieldPaths)
2905  __SUP_COUT__ << "fieldPath " << field << __E__;
2906  }
2907 
2908  std::vector<std::string /*relative-path*/> fieldValues;
2909  // extract value list
2910  {
2911  std::istringstream f(valueList);
2912  std::string fieldValue;
2913  while(getline(f, fieldValue, ','))
2914  {
2915  fieldValues.push_back(fieldValue); // setURIEncodedValue is expected
2916  // StringMacros::decodeURIComponent(fieldValue));
2917  }
2918 
2919  // if last value is "" then push empty value
2920  if(valueList.size() && valueList[valueList.size() - 1] == ',')
2921  fieldValues.push_back("");
2922 
2923  __SUP_COUT__ << valueList << __E__;
2924  for(const auto& value : fieldValues)
2925  __SUP_COUT__ << "fieldValue " << value << __E__;
2926  }
2927 
2928  if(fieldPaths.size() != fieldValues.size())
2929  {
2930  __SUP_SS__;
2931  __THROW__(ss.str() + "Mismatch in fields and values array size!");
2932  }
2933 
2934  // extract record list
2935  {
2936  TableBase* table;
2937  TableVersion temporaryVersion;
2938  std::istringstream f(recordList);
2939  std::string recordUID;
2940  unsigned int i;
2941 
2942  while(getline(f, recordUID, ',')) // for each record
2943  {
2944  recordUID = StringMacros::decodeURIComponent(recordUID);
2945 
2946  /*xercesc::DOMElement* parentEl =*/
2947  xmlOut.addTextElementToData("fieldValues", recordUID);
2948 
2949  // for each field, set value
2950  for(i = 0; i < fieldPaths.size(); ++i)
2951  {
2952  __SUP_COUT__ << "fieldPath " << fieldPaths[i] << __E__;
2953  __SUP_COUT__ << "fieldValue " << fieldValues[i] << __E__;
2954 
2955  // doNotThrowOnBrokenUIDLinks so that link UIDs can be edited like
2956  // other fields
2957  ConfigurationTree targetNode =
2958  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPaths[i],
2959  true /*doNotThrowOnBrokenUIDLinks*/);
2960 
2961  // need table, uid, columnName to set a value
2962 
2963  // assume correct table version is loaded by setupActiveTablesXML()
2964  // table = cfgMgr->getTableByName(
2965  // targetNode.getTableName());
2966  //
2967  //__SUP_COUT__ << "Active version is " << table->getViewVersion() <<
2968  //__E__;
2969 
2970  // mimic handleSaveTreeNodeEditXML L 1750
2971  // Actually call it! ..
2972  // with a modifier?
2973  // or
2974  // handleSaveTreeNodeEditXML(xmlOut,
2975  // cfgMgr,
2976  // targetNode.getTableName(),
2977  // targetNode.getTableVersion(),
2978  // "value",
2979  // targetNode.getUIDAsString(),
2980  // targetNode.getValueName(), //col name
2981  // fieldValues[i]
2982  // );
2983 
2984  // or
2985  // (because problem is this would create a new temporary version each
2986  // time) if current version is not temporary
2987  // create temporary
2988  // else re-modify temporary version
2989  // edit temporary version directly
2990  // then after all edits return active versions
2991  //
2992 
2993  __SUP_COUT__ << "Getting table " << targetNode.getFieldTableName()
2994  << __E__;
2995 
2996  // if link must get parent table name
2997  table = cfgMgr->getTableByName(
2998  targetNode.getFieldTableName()); // NOT getTableName!
2999  if(!(temporaryVersion = table->getViewP()->getVersion())
3000  .isTemporaryVersion())
3001  {
3002  // create temporary version for editing
3003  temporaryVersion =
3004  table->createTemporaryView(table->getViewP()->getVersion());
3005  cfgMgr->saveNewTable(table->getTableName(),
3006  temporaryVersion,
3007  true); // proper bookkeeping for temporary
3008  // version with the new version
3009 
3010  __SUP_COUT__ << "Created temporary version "
3011  << table->getTableName() << "-v" << temporaryVersion
3012  << __E__;
3013  }
3014  // else //else table is already temporary version
3015  __SUP_COUT__ << "Using temporary version " << table->getTableName()
3016  << "-v" << temporaryVersion << __E__;
3017 
3018  // copy "value" type edit from handleSaveTreeNodeEditXML()
3019  // functionality
3020  table->getViewP()->setURIEncodedValue(fieldValues[i],
3021  targetNode.getFieldRow(),
3022  targetNode.getFieldColumn(),
3023  author);
3024 
3025  table->getViewP()
3026  ->init(); // verify new table (throws runtime_errors)
3027  }
3028  }
3029  }
3030 
3031  handleFillModifiedTablesXML(xmlOut, cfgMgr);
3032  }
3033  catch(std::runtime_error& e)
3034  {
3035  __SUP_SS__ << ("Error setting field values!\n\n" + std::string(e.what()))
3036  << __E__;
3037  __SUP_COUT_ERR__ << "\n" << ss.str();
3038  xmlOut.addTextElementToData("Error", ss.str());
3039  }
3040  catch(...)
3041  {
3042  __SUP_SS__ << ("Error setting field values!\n\n") << __E__;
3043  try
3044  {
3045  throw;
3046  } //one more try to printout extra info
3047  catch(const std::exception& e)
3048  {
3049  ss << "Exception message: " << e.what();
3050  }
3051  catch(...)
3052  {
3053  }
3054  __SUP_COUT_ERR__ << "\n" << ss.str();
3055  xmlOut.addTextElementToData("Error", ss.str());
3056  }
3057 } //end handleFillSetTreeNodeFieldValuesXML()
3058 
3059 //==============================================================================
3074 void ConfigurationGUISupervisor::handleFillGetTreeNodeFieldValuesXML(
3075  HttpXmlDocument& xmlOut,
3076  ConfigurationManagerRW* cfgMgr,
3077  const std::string& groupName,
3078  const TableGroupKey& groupKey,
3079  const std::string& startPath,
3080  const std::string& modifiedTables,
3081  const std::string& recordList,
3082  const std::string& fieldList)
3083 {
3084  // setup active tables based on input group and modified tables
3085  setupActiveTablesXML(
3086  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3087 
3088  // for each field
3089  // return field/value pair in xml
3090 
3091  try
3092  {
3093  std::vector<std::string /*relative-path*/> fieldPaths;
3094  // extract field list
3095  {
3096  std::istringstream f(fieldList);
3097  std::string fieldPath;
3098  while(getline(f, fieldPath, ','))
3099  {
3100  fieldPaths.push_back(StringMacros::decodeURIComponent(fieldPath));
3101  }
3102  __SUP_COUT__ << fieldList << __E__;
3103  }
3104 
3105  // extract record list
3106  {
3107  std::istringstream f(recordList);
3108  std::string recordUID;
3109  while(getline(f, recordUID, ',')) // for each record
3110  {
3111  recordUID = StringMacros::decodeURIComponent(recordUID);
3112 
3113  __SUP_COUT__ << "recordUID " << recordUID << __E__;
3114 
3115  xercesc::DOMElement* parentEl =
3116  xmlOut.addTextElementToData("fieldValues", recordUID);
3117 
3118  // for each field, get value
3119  for(const auto& fieldPath : fieldPaths)
3120  {
3121  // __SUP_COUT__ << "fieldPath " << fieldPath << __E__;
3122  // __SUP_COUT__ << "fullPath " << (startPath + "/" + recordUID + "/" + fieldPath) << __E__;
3123 
3124  ConfigurationTree node =
3125  cfgMgr->getNode(startPath + "/" + recordUID + "/" + fieldPath);
3126 
3127  xmlOut.addTextElementToParent("FieldPath", fieldPath, parentEl);
3128 
3129  xmlOut.addTextElementToParent(
3130  "FieldValue",
3131  node.getValueAsString(true /*returnLinkTableValue*/),
3132  parentEl);
3133  }
3134  }
3135  }
3136  }
3137  catch(std::runtime_error& e)
3138  {
3139  __SUP_SS__ << ("Error getting field values!\n\n" + std::string(e.what()))
3140  << __E__;
3141  __SUP_COUT_ERR__ << "\n" << ss.str();
3142  xmlOut.addTextElementToData("Error", ss.str());
3143  }
3144  catch(...)
3145  {
3146  __SUP_SS__ << ("Error getting field values!\n\n") << __E__;
3147  try
3148  {
3149  throw;
3150  } //one more try to printout extra info
3151  catch(const std::exception& e)
3152  {
3153  ss << "Exception message: " << e.what();
3154  }
3155  catch(...)
3156  {
3157  }
3158  __SUP_COUT_ERR__ << "\n" << ss.str();
3159  xmlOut.addTextElementToData("Error", ss.str());
3160  }
3161 } //end handleFillGetTreeNodeFieldValuesXML()
3162 
3163 //==============================================================================
3182 void ConfigurationGUISupervisor::handleFillTreeNodeCommonFieldsXML(
3183  HttpXmlDocument& xmlOut,
3184  ConfigurationManagerRW* cfgMgr,
3185  const std::string& groupName,
3186  const TableGroupKey& groupKey,
3187  const std::string& startPath,
3188  unsigned int depth,
3189  const std::string& modifiedTables,
3190  const std::string& recordList,
3191  const std::string& fieldList)
3192 {
3193  // setup active tables based on input group and modified tables
3194  setupActiveTablesXML(
3195  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3196 
3197  try
3198  {
3199  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("fields", startPath);
3200 
3201  if(depth == 0)
3202  {
3203  __SUP_SS__ << "Depth of search must be greater than 0." << __E__;
3204  __SUP_COUT__ << ss.str();
3205  __SS_THROW__; // done if 0 depth, no fields
3206  }
3207 
3208  // do not allow traversing for common fields from root level
3209  // the tree view should be used for such a purpose
3210  // if(startPath == "/")
3211  // return;
3212 
3213  std::vector<ConfigurationTree::RecordField> retFieldList;
3214 
3215  {
3216  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3217  if(startNode.isLinkNode() && startNode.isDisconnected())
3218  {
3219  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3220  __SUP_SS_THROW__;
3221  return; // quietly ignore disconnected links at depth
3222  // note: at the root level they will be flagged for the user
3223  }
3224 
3225  std::vector<std::string /*relative-path*/> fieldAcceptList, fieldRejectList;
3226  if(fieldList != "")
3227  {
3228  // extract field filter list
3229  {
3230  std::istringstream f(fieldList);
3231  std::string fieldPath, decodedFieldPath;
3232  while(getline(f, fieldPath, ','))
3233  {
3234  decodedFieldPath = StringMacros::decodeURIComponent(fieldPath);
3235 
3236  if(decodedFieldPath[0] == '!') // reject field
3237  fieldRejectList.push_back(decodedFieldPath.substr(1));
3238  else
3239  fieldAcceptList.push_back(decodedFieldPath);
3240  }
3241  __SUP_COUT__ << fieldList << __E__;
3242  for(auto& field : fieldAcceptList)
3243  __SUP_COUT__ << "fieldAcceptList " << field << __E__;
3244  for(auto& field : fieldRejectList)
3245  __SUP_COUT__ << "fieldRejectList " << field << __E__;
3246  }
3247  }
3248 
3249  std::vector<std::string /*relative-path*/> records;
3250  if(recordList == "*") // handle all records case
3251  {
3252  records.clear();
3253  records = startNode.getChildrenNames();
3254  __SUP_COUT__ << "Translating wildcard..." << __E__;
3255  for(auto& record : records)
3256  __SUP_COUT__ << "recordList " << record << __E__;
3257  }
3258  else if(recordList != "")
3259  {
3260  // extract record list
3261  {
3262  std::istringstream f(recordList);
3263  std::string recordStr;
3264  while(getline(f, recordStr, ','))
3265  {
3266  records.push_back(StringMacros::decodeURIComponent(recordStr));
3267  }
3268  __SUP_COUT__ << recordList << __E__;
3269  for(auto& record : records)
3270  __SUP_COUT__ << "recordList " << record << __E__;
3271  }
3272  }
3273 
3274  //=== get common fields call!
3275  retFieldList = startNode.getCommonFields(
3276  records, fieldAcceptList, fieldRejectList, depth);
3277  //=== end get common fields call!
3278  }
3279 
3280  xercesc::DOMElement* parentTypeEl;
3281  for(const auto& fieldInfo : retFieldList)
3282  {
3283  xmlOut.addTextElementToParent(
3284  "FieldTableName", fieldInfo.tableName_, parentEl);
3285  xmlOut.addTextElementToParent(
3286  "FieldColumnName", fieldInfo.columnName_, parentEl);
3287  xmlOut.addTextElementToParent(
3288  "FieldRelativePath", fieldInfo.relativePath_, parentEl);
3289  xmlOut.addTextElementToParent(
3290  "FieldColumnType", fieldInfo.columnInfo_->getType(), parentEl);
3291  xmlOut.addTextElementToParent(
3292  "FieldColumnDataType", fieldInfo.columnInfo_->getDataType(), parentEl);
3293  xmlOut.addTextElementToParent("FieldColumnDefaultValue",
3294  fieldInfo.columnInfo_->getDefaultValue(),
3295  parentEl);
3296  // again, should min and max be included here?
3297  parentTypeEl =
3298  xmlOut.addTextElementToParent("FieldColumnDataChoices", "", parentEl);
3299 
3300  // if there are associated data choices, send info
3301  auto dataChoices = fieldInfo.columnInfo_->getDataChoices();
3302  xmlOut.addTextElementToParent(
3303  "FieldColumnDataChoice", // add default to list to mimic tree handling
3304  fieldInfo.columnInfo_->getDefaultValue(),
3305  parentTypeEl);
3306  for(const auto& dataChoice : dataChoices)
3307  xmlOut.addTextElementToParent(
3308  "FieldColumnDataChoice", dataChoice, parentTypeEl);
3309  }
3310  }
3311  catch(std::runtime_error& e)
3312  {
3313  __SUP_SS__ << ("Error getting common fields!\n\n" + std::string(e.what()))
3314  << __E__;
3315  __SUP_COUT_ERR__ << "\n" << ss.str();
3316  xmlOut.addTextElementToData("Error", ss.str());
3317  }
3318  catch(...)
3319  {
3320  __SUP_SS__ << ("Error getting common fields!\n\n") << __E__;
3321  try
3322  {
3323  throw;
3324  } //one more try to printout extra info
3325  catch(const std::exception& e)
3326  {
3327  ss << "Exception message: " << e.what();
3328  }
3329  catch(...)
3330  {
3331  }
3332  __SUP_COUT_ERR__ << "\n" << ss.str();
3333  xmlOut.addTextElementToData("Error", ss.str());
3334  }
3335 } //end handleFillTreeNodeCommonFieldsXML()
3336 
3337 //==============================================================================
3365 void ConfigurationGUISupervisor::handleFillUniqueFieldValuesForRecordsXML(
3366  HttpXmlDocument& xmlOut,
3367  ConfigurationManagerRW* cfgMgr,
3368  const std::string& groupName,
3369  const TableGroupKey& groupKey,
3370  const std::string& startPath,
3371  const std::string& modifiedTables,
3372  const std::string& recordList,
3373  const std::string& fieldList)
3374 {
3375  // setup active tables based on input group and modified tables
3376  setupActiveTablesXML(
3377  xmlOut, cfgMgr, groupName, groupKey, modifiedTables, false /* refreshAll */);
3378 
3379  try
3380  {
3381  // do not allow traversing for common fields from root level
3382  // the tree view should be used for such a purpose
3383  if(startPath == "/")
3384  return;
3385 
3386  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3387  if(startNode.isLinkNode() && startNode.isDisconnected())
3388  {
3389  __SUP_SS__ << "Start path was a disconnected link node!" << __E__;
3390  __SUP_COUT_ERR__ << "\n" << ss.str();
3391  __SS_THROW__;
3392  }
3393 
3394  // extract records list
3395  std::vector<std::string /*relative-path*/> records;
3396  if(recordList == "*") // handle all records case
3397  {
3398  records.clear();
3399  records = startNode.getChildrenNames();
3400  __SUP_COUT__ << "Translating wildcard..." << __E__;
3401  for(auto& record : records)
3402  __SUP_COUT__ << "recordList " << record << __E__;
3403  }
3404  else if(recordList != "")
3405  {
3406  // extract record list
3407  {
3408  std::istringstream f(recordList);
3409  std::string recordStr;
3410  while(getline(f, recordStr, ','))
3411  {
3412  records.push_back(StringMacros::decodeURIComponent(recordStr));
3413  }
3414  __SUP_COUT__ << recordList << __E__;
3415  for(auto& record : records)
3416  __SUP_COUT__ << "recordList " << record << __E__;
3417  }
3418  } // end records extraction
3419 
3420  // extract fields to get
3421  std::vector<std::string /*relative-path*/> fieldsToGet;
3422  if(fieldList != "")
3423  {
3424  // extract field filter list
3425 
3426  if(fieldList == "AUTO")
3427  {
3428  // automatically choose 3 fields, with preference
3429  // for GroupID, On/Off, and FixedChoice fields.
3430 
3431  __SUP_COUT__ << "Getting AUTO filter fields!" << __E__;
3432 
3433  std::vector<ConfigurationTree::RecordField> retFieldList;
3434  std::vector<std::string /*relative-path*/> fieldAcceptList,
3435  fieldRejectList;
3436  fieldRejectList.push_back("*" + TableViewColumnInfo::COL_NAME_COMMENT);
3437  retFieldList = startNode.getCommonFields(
3438  records, fieldAcceptList, fieldRejectList, 5, true /*auto*/);
3439 
3440  for(const auto& retField : retFieldList)
3441  fieldsToGet.push_back(retField.relativePath_ + retField.columnName_);
3442  }
3443  else
3444  {
3445  std::istringstream f(fieldList);
3446  std::string fieldPath;
3447  while(getline(f, fieldPath, ','))
3448  {
3449  fieldsToGet.push_back(StringMacros::decodeURIComponent(fieldPath));
3450  }
3451  __SUP_COUTV__(fieldList);
3452  }
3453  } // end fields extraction
3454 
3455  __SUP_COUTV__(StringMacros::vectorToString(fieldsToGet));
3456 
3457  // loop through each field and get unique values among records
3458  {
3459  ConfigurationTree startNode = cfgMgr->getNode(startPath);
3460  std::string fieldGroupIDChildLinkIndex;
3461  for(auto& field : fieldsToGet)
3462  {
3463  __SUP_COUTV__(field);
3464 
3465  xercesc::DOMElement* parentEl =
3466  xmlOut.addTextElementToData("field", field);
3467 
3468  // if groupID field, give child link index
3469  // this can be used to pre-select particular group(s)
3470 
3471  // use set to force sorted unique values
3472  std::set<std::string /*unique-values*/> uniqueValues =
3473  startNode.getUniqueValuesForField(
3474  records, field, &fieldGroupIDChildLinkIndex);
3475 
3476  if(fieldGroupIDChildLinkIndex != "")
3477  xmlOut.addTextElementToParent(
3478  "childLinkIndex", fieldGroupIDChildLinkIndex, parentEl);
3479 
3480  for(auto& uniqueValue : uniqueValues)
3481  {
3482  __SUP_COUT__ << "uniqueValue " << uniqueValue << __E__;
3483 
3484  xmlOut.addTextElementToParent("uniqueValue", uniqueValue, parentEl);
3485  }
3486  }
3487  }
3488  }
3489  catch(std::runtime_error& e)
3490  {
3491  __SUP_SS__ << "Error getting unique field values from path '" << startPath
3492  << "' and field list '" << fieldList << "!'\n\n"
3493  << e.what() << __E__;
3494  __SUP_COUT_ERR__ << "\n" << ss.str();
3495  xmlOut.addTextElementToData("Error", ss.str());
3496  }
3497  catch(...)
3498  {
3499  __SUP_SS__ << "Error getting unique field values from path '" << startPath
3500  << "' and field list '" << fieldList << "!'\n\n"
3501  << __E__;
3502  try
3503  {
3504  throw;
3505  } //one more try to printout extra info
3506  catch(const std::exception& e)
3507  {
3508  ss << "Exception message: " << e.what();
3509  }
3510  catch(...)
3511  {
3512  }
3513  __SUP_COUT_ERR__ << "\n" << ss.str();
3514  xmlOut.addTextElementToData("Error", ss.str());
3515  }
3516 } // end handleFillUniqueFieldValuesForRecordsXML()
3517 
3518 //==============================================================================
3538 void ConfigurationGUISupervisor::handleFillTreeViewXML(
3539  HttpXmlDocument& xmlOut,
3540  ConfigurationManagerRW* cfgMgr,
3541  const std::string& groupName,
3542  const TableGroupKey& groupKey,
3543  const std::string& startPath,
3544  unsigned int depth,
3545  bool hideStatusFalse,
3546  const std::string& modifiedTables,
3547  const std::string& filterList,
3548  const std::string& diffGroupName /* = "" */,
3549  const TableGroupKey& diffGroupKey /* = TableGroupKey() */)
3550 {
3551  __SUP_COUTT__ << "get Tree View: " << groupName << "(" << groupKey << ")" << __E__;
3552 
3553  // return xml
3554  // <groupName="groupName"/>
3555  // <tree="path">
3556  // <node="...">
3557  // <node="...">
3558  // <node="...">
3559  // <value="...">
3560  // </node>
3561  // <node="...">
3562  // <value="...">
3563  // </node>
3564  // </node>
3565  // <node="...">
3566  // <value="..">
3567  // </node>
3568  // ...
3569  // </node>
3570  // </tree>
3571 
3572  // return the startPath as root "tree" element
3573  // and then display all children if depth > 0
3574 
3575  //------------------
3576  //First, if doing diff, load tables into cache and copy.
3577  // Loading will leave tables active in the user cfgMgr..
3578  // which will mess up diff. So order:
3579  // 1. load diff tables in user cfgMgr
3580  // 2. copy from cfgMgr cache to diffCfgMgr
3581  // 3. load tree tables in user cfgMgr
3582 
3583  bool doDiff = (diffGroupName != "" && !diffGroupKey.isInvalid());
3584 
3585  std::map<std::string /*name*/, TableVersion /*version*/> diffMemberMap;
3586  ConfigurationManagerRW tmpCfgMgr("TreeDiff");
3587  ConfigurationManagerRW* diffCfgMgr = &tmpCfgMgr;
3588  std::string diffAccumulateErrors;
3589  if(doDiff)
3590  {
3591  //Load diff tables in cfgMgr so that tables are cached,
3592  // then copy to diffCfgMgr as active tables for tree comparison.
3593  // This is more efficient than loading diff tables from db every tree access.
3594 
3595  for(auto& activeTable : cfgMgr->getActiveVersions())
3596  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3597  << __E__;
3598 
3599  cfgMgr->loadTableGroup(diffGroupName,
3600  diffGroupKey,
3601  false /*doActivate*/,
3602  &diffMemberMap,
3603  0 /*progressBar*/,
3604  0 /*accumulateErrors*/,
3605  0 /*groupComment*/,
3606  0 /*groupAuthor*/,
3607  0 /*groupCreationTime*/,
3608  false /*doNotLoadMember*/,
3609  0 /*groupTypeString*/
3610  );
3611 
3612  for(auto& activeTable : cfgMgr->getActiveVersions())
3613  __SUP_COUT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3614  << __E__;
3615 
3616  __SUP_COUTT__ << "Diff Group tables loaded." << __E__;
3617  diffCfgMgr->copyTableGroupFromCache(
3618  *cfgMgr, diffMemberMap, diffGroupName, diffGroupKey);
3619  __SUP_COUTT__ << "Diff Group tables copied to local diff config manager."
3620  << __E__;
3621 
3622  //now activate diff table for tree traversal (without calling init())
3623  for(auto& memberPair : diffMemberMap)
3624  diffCfgMgr->getTableByName(memberPair.first)
3625  ->setActiveView(memberPair.second);
3626 
3627  for(const auto& lastGroupLoaded : cfgMgr->getLastTableGroups())
3628  __SUP_COUT__ << "cfgMgr Last loaded " << lastGroupLoaded.first << ": "
3629  << lastGroupLoaded.second.first.first << "("
3630  << lastGroupLoaded.second.first.second << ")";
3631 
3632  for(const auto& lastGroupLoaded : diffCfgMgr->getLastTableGroups())
3633  __SUP_COUT__ << "diffCfgMgr Last loaded " << lastGroupLoaded.first << ": "
3634  << lastGroupLoaded.second.first.first << "("
3635  << lastGroupLoaded.second.first.second << ")";
3636 
3637  //for complete tree traversal, if config type, then load context tables in diff, if context type, then load config tables in diff
3638  if(diffCfgMgr->getLastTableGroups().size() == 1)
3639  {
3640  __SUP_COUT__ << "Type already loaded to diff = "
3641  << diffCfgMgr->getLastTableGroups().begin()->first << __E__;
3642  try
3643  {
3644  auto groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3645  if(diffCfgMgr->getLastTableGroups().begin()->first ==
3646  ConfigurationManager::GROUP_TYPE_NAME_CONTEXT)
3647  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION;
3648  else if(diffCfgMgr->getLastTableGroups().begin()->first ==
3649  ConfigurationManager::GROUP_TYPE_NAME_CONFIGURATION)
3650  groupTypeToLoad = ConfigurationManager::GROUP_TYPE_NAME_CONTEXT;
3651 
3652  __SUP_COUTT__
3653  << "Loading " << groupTypeToLoad
3654  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first << "("
3655  << cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second
3656  << ")" << __E__;
3657 
3658  diffCfgMgr->copyTableGroupFromCache(
3659  *cfgMgr,
3660  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second,
3661  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.first,
3662  cfgMgr->getLastTableGroups().at(groupTypeToLoad).first.second);
3663 
3664  //now activate diff table for tree traversal (without calling init())
3665  for(auto& memberPair :
3666  cfgMgr->getLastTableGroups().at(groupTypeToLoad).second)
3667  diffCfgMgr->getTableByName(memberPair.first)
3668  ->setActiveView(memberPair.second);
3669  }
3670  catch(...)
3671  {
3672  } //ignore extra group loading errors
3673  }
3674 
3675  for(auto& activeTable : cfgMgr->getActiveVersions())
3676  __SUP_COUTT__ << "cfgMgr " << activeTable.first << "-v" << activeTable.second
3677  << __E__;
3678  for(auto& activeTable : diffCfgMgr->getActiveVersions())
3679  __SUP_COUTT__ << "diffCfgMgr " << activeTable.first << "-v"
3680  << activeTable.second << __E__;
3681 
3682  __SUP_COUTT__ << "Diff Group tables are setup: " << diffAccumulateErrors << __E__;
3683  } // end do diff load
3684 
3685  //------------------
3686  //Setup active tables based on input group and modified tables
3687  bool usingActiveGroups = (groupName == "" || groupKey.isInvalid());
3688  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
3689 
3690  std::string accumulatedErrors = "";
3691  setupActiveTablesXML(
3692  xmlOut,
3693  cfgMgr,
3694  groupName,
3695  groupKey,
3696  modifiedTables,
3697  false, //changed to not refresh all, assume no partially loaded tables (startPath == "/"), // refreshAll, if at root node, reload all tables so that partially loaded tables are not allowed
3698  (startPath == "/"), // get group info
3699  &memberMap, // get group member map
3700  true, // output active tables (default)
3701  &accumulatedErrors // accumulate errors
3702  );
3703 
3704  if(memberMap.size() > ConfigurationManager::getFixedContextMemberNames().size() +
3705  1 /* for optional table */
3706  && startPath == "/")
3707  {
3708  __COUTT__ << "Checking for orphaned tables..." << __E__;
3709 
3710  //check Tree for orphaned tables
3711  std::set<std::string /* table name that is linked to */> linkingTables;
3712  for(const auto& tableInfo : cfgMgr->getAllTableInfo())
3713  {
3714  //for each existing active table, check table for links to tables
3715 
3716  __COUTS__(30) << "Table " << tableInfo.first << __E__;
3717  if(!tableInfo.second.tablePtr_->isActive())
3718  continue; //skip if no active view for table
3719  else
3720  __COUTS__(30) << "Table active " << tableInfo.first << __E__;
3721 
3722  const TableView& view = tableInfo.second.tablePtr_->getView();
3723 
3724  bool addedThisTable = false;
3725  for(unsigned int col = 0; col < view.getNumberOfColumns(); ++col)
3726  {
3727  if(!view.getColumnInfo(col).isChildLink())
3728  continue;
3729 
3730  __COUTS__(30) << "Table " << tableInfo.first
3731  << " col: " << view.getColumnInfo(col).getName() << __E__;
3732 
3733  for(unsigned int r = 0; r < view.getNumberOfRows(); ++r)
3734  {
3735  if(view.getDataView()[r][col] == "" ||
3736  view.getDataView()[r][col] ==
3737  TableViewColumnInfo::DATATYPE_STRING_DEFAULT ||
3738  view.getDataView()[r][col] ==
3739  TableViewColumnInfo::DATATYPE_STRING_ALT_DEFAULT)
3740  continue;
3741 
3742  if(!addedThisTable) //add this table to since it seems to have a link!
3743  {
3744  linkingTables.emplace(tableInfo.first);
3745  addedThisTable = true;
3746  }
3747  linkingTables.emplace(
3748  view.getDataView()[r][col]); //add linked table name to set
3749  }
3750  }
3751  }
3752  __COUTTV__(StringMacros::setToString(linkingTables));
3753 
3754  std::string missingTables = "";
3755  for(const auto& member : memberMap)
3756  {
3757  //if not in linking tables set, then note
3758  if(linkingTables.find(member.first) != linkingTables.end())
3759  continue; //linked-to table, so no warning
3760 
3761  if(missingTables.size())
3762  missingTables += ", ";
3763  missingTables += member.first;
3764  }
3765 
3766  if(missingTables.size())
3767  {
3768  __COUTV__(missingTables);
3769  std::stringstream ss;
3770  ss << "The following member tables of table group '" << groupName << "("
3771  << groupKey
3772  << ")' were identified as possibly orphaned (i.e. no active tables link "
3773  "to these tables, and these tables have no links to other tables):\n\n"
3774  << missingTables << ".\n"
3775  << __E__;
3776  xmlOut.addTextElementToData("NoTreeLinkWarning", ss.str());
3777  }
3778  } //end orphaned table check
3779 
3780  if(accumulatedErrors != "")
3781  {
3782  xmlOut.addTextElementToData("Warning", accumulatedErrors);
3783 
3784  __SUP_COUT__ << "Active tables are setup. Warning string: '" << accumulatedErrors
3785  << "'" << __E__;
3786 
3787  __SUP_COUT__ << "Active table versions: "
3788  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3789  }
3790  else
3791  {
3792  __SUP_COUTT__ << "Active tables are setup. No issues found." << __E__;
3793  __SUP_COUTT__ << "Active table versions: "
3794  << StringMacros::mapToString(cfgMgr->getActiveVersions()) << __E__;
3795  }
3796 
3797  try
3798  {
3799  xercesc::DOMElement* parentEl = xmlOut.addTextElementToData("tree", startPath);
3800 
3801  if(depth == 0)
3802  return; // already returned root node in itself
3803 
3804  std::vector<std::pair<std::string, ConfigurationTree>> rootMap;
3805  std::map<std::string, ConfigurationTree> diffRootMap;
3806 
3807  if(startPath == "/")
3808  {
3809  // then consider the configurationManager the root node
3810 
3811  std::string accumulateTreeErrs;
3812 
3813  if(usingActiveGroups)
3814  rootMap = cfgMgr->getChildren(0, &accumulateTreeErrs);
3815  else
3816  rootMap = cfgMgr->getChildren(&memberMap, &accumulateTreeErrs);
3817 
3818  if(doDiff)
3819  {
3820  diffRootMap =
3821  diffCfgMgr->getChildrenMap(&diffMemberMap, &diffAccumulateErrors);
3822  __SUP_COUTV__(diffRootMap.size());
3823  for(auto& diffChild : diffRootMap)
3824  __SUP_COUTV__(diffChild.first);
3825  }
3826 
3827  __SUP_COUTV__(accumulateTreeErrs);
3828 
3829  if(accumulateTreeErrs != "")
3830  xmlOut.addTextElementToData("TreeErrors", accumulateTreeErrs);
3831  }
3832  else
3833  {
3834  ConfigurationTree startNode =
3835  cfgMgr->getNode(startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3836  if(startNode.isLinkNode() && startNode.isDisconnected())
3837  {
3838  xmlOut.addTextElementToData("DisconnectedStartNode", "1");
3839  return; // quietly ignore disconnected links at depth
3840  // note: at the root level they will be flagged for the user
3841  }
3842 
3843  std::map<std::string /*relative-path*/, std::string /*value*/> filterMap;
3845  filterList,
3846  filterMap,
3847  std::set<char>({';'}) /*pair delimiters*/,
3848  std::set<char>({'='}) /*name/value delimiters*/);
3849 
3850  __COUTV__(StringMacros::mapToString(filterMap));
3851 
3852  rootMap = cfgMgr->getNode(startPath).getChildren(filterMap);
3853 
3854  if(doDiff)
3855  {
3856  try
3857  {
3858  ConfigurationTree diffStartNode = diffCfgMgr->getNode(
3859  startPath, true /*doNotThrowOnBrokenUIDLinks*/);
3860 
3861  if(diffStartNode.isLinkNode() && diffStartNode.isDisconnected())
3862  __SUP_COUTT__ << "Diff Group disconnected node." << __E__;
3863  else
3864  diffRootMap =
3865  diffCfgMgr->getNode(startPath).getChildrenMap(filterMap);
3866  }
3867  catch(const std::runtime_error& e)
3868  {
3869  //if diff node does not exist, user was already notified at parent diff, so ignore error.
3870  __SUP_COUTT__ << "Diff Group node does not exist." << __E__;
3871  }
3872  }
3873  }
3874 
3875  if(!doDiff)
3876  {
3877  for(auto& treePair : rootMap)
3878  recursiveTreeToXML(
3879  treePair.second, depth - 1, xmlOut, parentEl, hideStatusFalse);
3880  }
3881  else //doDiff
3882  {
3883  __SUP_COUTT__ << "Diff Tree recursive handling." << __E__;
3884 
3885  //convert vector rootMap to set for searching
3886  std::set<std::string /* treeNodeName */> rootMapToSearch;
3887  for(const auto& rootMember : rootMap)
3888  rootMapToSearch.emplace(rootMember.first);
3889 
3890  std::stringstream rootSs;
3891  for(const auto& rootMember : rootMap)
3892  rootSs << ", " << rootMember.first;
3893 
3894  //add all tables in diff group that are missing to parentEl
3895  std::stringstream diffRootSs;
3896  for(const auto& diffMember : diffRootMap) //diffMemberMap)
3897  {
3898  diffRootSs << ", " << diffMember.first << ":"
3899  << diffMember.second.getNodeType();
3900  if(rootMapToSearch.find(diffMember.first) ==
3901  rootMapToSearch
3902  .end()) //memberMap.find(diffMember.first) == memberMap.end())
3903  {
3904  std::stringstream missingSs;
3905  missingSs << diffMember.first << //" <<< Not in " <<
3906  // groupName << "(" << groupKey << "), present in " <<
3907  " <<< Only in " << diffGroupName << "(" << diffGroupKey
3908  << ") >>>";
3909  xmlOut.addTextElementToParent(
3910  "diffNodeMissing", missingSs.str(), parentEl);
3911  }
3912 
3913  if(diffMember.second.getNodeType() == "UIDLinkNode")
3914  {
3915  __SUP_COUTT__
3916  << "diff active "
3918  << __E__;
3919  __SUP_COUTT__
3920  << "root active "
3922  << __E__;
3923 
3924  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3925  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3926 
3927  __SUP_COUTT__ << "\t\t" << diffMember.second.getValueName() << ": "
3928  << diffMember.second.getValueAsString() << __E__;
3929 
3930  __SUP_COUTT__ << diffMember.second.nodeDump();
3931  }
3932  }
3933 
3934  __SUP_COUTT__ << "diff map " << diffRootSs.str() << __E__;
3935  __SUP_COUTT__ << "root map " << rootSs.str() << __E__;
3936 
3937  //recurse
3938  for(auto& treePair : rootMap)
3939  {
3940  if(diffRootMap.find(treePair.first) == diffRootMap.end())
3941  {
3942  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3943  << __E__;
3944  ConfigurationTree rootNode(diffCfgMgr, nullptr /* table */);
3945  recursiveTreeToXML(
3946  treePair.second,
3947  depth - 1,
3948  xmlOut,
3949  parentEl,
3950  hideStatusFalse,
3951  rootNode /* root node diffTree to indicate record not found in diff group */);
3952  }
3953  else
3954  {
3955  __SUP_COUTT__ << "Diff Tree recursive handling... " << treePair.first
3956  << __E__;
3957  recursiveTreeToXML(treePair.second,
3958  depth - 1,
3959  xmlOut,
3960  parentEl,
3961  hideStatusFalse,
3962  diffRootMap.at(treePair.first));
3963  }
3964  }
3965  }
3966  }
3967  catch(std::runtime_error& e)
3968  {
3969  __SUP_SS__ << "Error detected generating XML tree!\n\n " << e.what() << __E__;
3970  __SUP_COUT_ERR__ << "\n" << ss.str();
3971  xmlOut.addTextElementToData("Error", ss.str());
3972  }
3973  catch(...)
3974  {
3975  __SUP_SS__ << "Error detected generating XML tree!" << __E__;
3976  try
3977  {
3978  throw;
3979  } //one more try to printout extra info
3980  catch(const std::exception& e)
3981  {
3982  ss << "Exception message: " << e.what();
3983  }
3984  catch(...)
3985  {
3986  }
3987  __SUP_COUT_ERR__ << "\n" << ss.str();
3988  xmlOut.addTextElementToData("Error", ss.str());
3989  }
3990 } // end handleFillTreeViewXML()
3991 
3992 //==============================================================================
3998 void ConfigurationGUISupervisor::recursiveTreeToXML(
3999  const ConfigurationTree& t,
4000  unsigned int depth,
4001  HttpXmlDocument& xmlOut,
4002  xercesc::DOMElement* parentEl,
4003  bool hideStatusFalse,
4004  std::optional<std::reference_wrapper<const ConfigurationTree>> diffTree)
4005 {
4006  __COUTS__(30) << t.getValueAsString() << __E__;
4007 
4008  if(t.isValueNode())
4009  {
4010  __COUTS__(30) << "\t" << t.getValueName() << ": " << t.getValueAsString()
4011  << __E__;
4012 
4013  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
4014  if(diffTree.has_value() &&
4015  t.getValueName() != TableViewColumnInfo::COL_NAME_COMMENT &&
4016  t.getValueName() != TableViewColumnInfo::COL_NAME_AUTHOR &&
4017  t.getValueName() != TableViewColumnInfo::COL_NAME_CREATION)
4018  {
4019  __COUTS__(30) << "\t\t diff type " << diffTree->get().getNodeType() << __E__;
4020 
4021  if(diffTree->get().isValueNode())
4022  {
4023  __COUTS__(30) << "\t" << diffTree->get().getValueAsString() << " ? "
4024  << t.getValueAsString() << __E__;
4025  __COUTS__(30) << "\t" << diffTree->get().getTableName() << "-v"
4026  << diffTree->get().getTableVersion() << " ? "
4027  << t.getTableName() << "-v" << t.getTableVersion() << __E__;
4028 
4029  if(t.getValueAsString() != diffTree->get().getValueAsString())
4030  {
4031  std::stringstream missingSs; //assume only one group loaded for diff
4032  auto diffGroupPair =
4033  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4034  diffTree->get().getTableName());
4035  missingSs << "<<< '" << diffTree->get().getValueAsString() << "' in "
4036  << diffGroupPair.first << "(" << diffGroupPair.second
4037  << ") >>>";
4038  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4039  }
4040  }
4041  else
4042  {
4043  std::stringstream missingSs; //assume only one group loaded for diff
4044  //lookup group name in diffManager based on current node's table (best proxy info for missing diff node at this point)
4045  auto diffGroupPair =
4046  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4047  t.getTableName());
4048  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
4049  << diffGroupPair.second << ") >>>";
4050  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4051  }
4052 
4053  __COUTS__(30) << "\t" << t.getValueName() << ": " << t.getValueAsString()
4054  << __E__;
4055 
4056  } //end diff tree handling
4057 
4058  xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
4059  parentEl = xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
4060 
4061  // fixed choice and bitmap both use fixed choices strings
4062  // so output them to xml
4063  if(t.getValueType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
4064  t.getValueType() == TableViewColumnInfo::TYPE_BITMAP_DATA)
4065  {
4066  __COUTS__(30) << t.getValueType() << __E__;
4067 
4068  std::vector<std::string> choices = t.getFixedChoices();
4069  for(const auto& choice : choices)
4070  xmlOut.addTextElementToParent("fixedChoice", choice, parentEl);
4071  }
4072  }
4073  else
4074  {
4075  __COUTS__(30) << "\t" << t.getValueAsString() << __E__;
4076 
4077  if(t.isLinkNode())
4078  {
4079  __COUTS__(30) << "\t\t" << t.getValueName() << ": " << t.getValueAsString()
4080  << __E__;
4081 
4082  // Note: The order of xml fields is required by JavaScript, so do NOT change
4083  // order.
4084  parentEl = xmlOut.addTextElementToParent("node", t.getValueName(), parentEl);
4085 
4086  if(diffTree.has_value())
4087  {
4088  __COUTS__(30) << "\t\t diff type " << diffTree->get().getNodeType()
4089  << __E__;
4090 
4091  if(diffTree->get()
4092  .isRootNode()) //then diff group does not have this uid!
4093  {
4094  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4095  std::stringstream missingSs; //assume only one group loaded for diff
4096  //lookup group name in diffManager based on current node's parent's table (best proxy info for missing diff node at this point)
4097  auto diffGroupPair =
4098  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4099  t.getParentTableName());
4100  missingSs << "<<< Path not found in " << diffGroupPair.first << "("
4101  << diffGroupPair.second << ") >>>";
4102  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4103  }
4104  else if(t.isDisconnected() != diffTree->get().isDisconnected())
4105  {
4106  __COUTS__(30) << "\t\t diff isDisconnected "
4107  << diffTree->get().isDisconnected() << __E__;
4108 
4109  std::stringstream missingSs; //assume only one group loaded for diff
4110  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4111  auto diffGroupPair =
4112  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4113  t.getParentTableName());
4114  missingSs << "<<< Link is "
4115  << (diffTree->get().isDisconnected() ? "DISCONNECTED"
4116  : "connected")
4117  << " in " << diffGroupPair.first << "("
4118  << diffGroupPair.second << ") >>>";
4119  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4120  }
4121  else if(!t.isDisconnected() &&
4122  t.isUIDLinkNode() != diffTree->get().isUIDLinkNode())
4123  {
4124  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4125  std::stringstream missingSs; //assume only one group loaded for diff
4126  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4127  auto diffGroupPair =
4128  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4129  t.getParentTableName());
4130  missingSs << "<<< Link is "
4131  << (diffTree->get().isUIDLinkNode() ? "a UID Link"
4132  : "a Group Link")
4133  << " in " << diffGroupPair.first << "("
4134  << diffGroupPair.second << ") >>>";
4135  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4136  }
4137  else if(!t.isDisconnected() && t.isUIDLinkNode() &&
4138  t.getValueAsString() !=
4139  diffTree->get().getValueAsString()) //both are UID link
4140  {
4141  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4142  std::stringstream missingSs; //assume only one group loaded for diff
4143  //lookup group name in diffManager based on current node's parent's table (best proxy info for diff node at this point)
4144  auto diffGroupPair =
4145  diffTree->get().getConfigurationManager()->getGroupOfLoadedTable(
4146  t.getParentTableName());
4147  missingSs << "<<< Link to '" << diffTree->get().getValueAsString()
4148  << "' in " << diffGroupPair.first << "("
4149  << diffGroupPair.second << ") >>>";
4150  xmlOut.addTextElementToParent("nodeDiff", missingSs.str(), parentEl);
4151  }
4152  else if(!t.isDisconnected() && !t.isUIDLinkNode()) //both are Group links
4153  {
4154  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4155  std::stringstream missingSs; //assume only one group loaded for diff
4156 
4157  auto tchildren = t.getChildrenMap();
4158  auto dtchildren = diffTree->get().getChildrenMap();
4159  missingSs << "<<< Group link";
4160  if(tchildren.size() != dtchildren.size())
4161  missingSs << " has " << tchildren.size() << " vs "
4162  << dtchildren.size() << " children..";
4163  for(auto& tchild : tchildren)
4164  if(dtchildren.find(tchild.first) == dtchildren.end())
4165  missingSs << " '" << tchild.first << "' missing..";
4166  for(auto& dtchild : dtchildren)
4167  if(tchildren.find(dtchild.first) == tchildren.end())
4168  missingSs << " '" << dtchild.first << "' present...";
4169 
4170  //only add nodeDiff if ss has been appended
4171  if(missingSs.str().length() > std::string("<<< Group link").length())
4172  {
4173  auto diffGroupPair =
4174  diffTree->get()
4175  .getConfigurationManager()
4176  ->getGroupOfLoadedTable(diffTree->get().getTableName());
4177  missingSs << " in " << diffGroupPair.first << "("
4178  << diffGroupPair.second << ") >>>";
4179  xmlOut.addTextElementToParent(
4180  "nodeDiff", missingSs.str(), parentEl);
4181  }
4182  }
4183  else
4184  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4185  } //end diff tree handling
4186 
4187  if(t.isDisconnected())
4188  {
4189  __COUTS__(30) << t.getValueName() << __E__;
4190 
4191  // xmlOut.addTextElementToParent("value", t.getValueAsString(), parentEl);
4192  // xmlOut.addTextElementToParent("DisconnectedLink", t.getValueAsString(),
4193  // parentEl);
4194 
4195  xmlOut.addTextElementToParent("valueType", t.getValueType(), parentEl);
4196 
4197  // add extra fields for disconnected link
4198  xmlOut.addTextElementToParent(
4199  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4201  parentEl);
4202  xmlOut.addTextElementToParent(
4203  "LinkTableName", t.getDisconnectedTableName(), parentEl);
4204  xmlOut.addTextElementToParent(
4205  "LinkIndex", t.getChildLinkIndex(), parentEl);
4206 
4207  // add fixed choices (in case link has them)
4208  xercesc::DOMElement* choicesParentEl =
4209  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4210  // try
4211  //{
4212 
4213  std::vector<std::string> choices = t.getFixedChoices();
4214  __COUTS__(30) << "choices.size() " << choices.size() << __E__;
4215 
4216  for(const auto& choice : choices)
4217  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4218  //}
4219  // catch(...)
4220  //{
4221  // __COUT__ << "Ignoring unknown fixed choice error"
4222  //} //ignore no fixed choices for disconnected
4223 
4224  return;
4225  }
4226  // else handle connected links
4227 
4228  xmlOut.addTextElementToParent(
4229  (t.isGroupLinkNode() ? "Group" : "U") + std::string("ID"),
4230  t.getValueAsString(),
4231  parentEl);
4232 
4233  xmlOut.addTextElementToParent("LinkTableName", t.getTableName(), parentEl);
4234  xmlOut.addTextElementToParent("LinkIndex", t.getChildLinkIndex(), parentEl);
4235 
4236  // add fixed choices (in case link has them)
4237  {
4238  xercesc::DOMElement* choicesParentEl =
4239  xmlOut.addTextElementToParent("fixedChoices", "", parentEl);
4240  std::vector<std::string> choices = t.getFixedChoices();
4241 
4242  for(const auto& choice : choices)
4243  xmlOut.addTextElementToParent("fixedChoice", choice, choicesParentEl);
4244  }
4245  }
4246  else // uid node (or root node)
4247  {
4248  __COUTS__(30) << "\t\t" << t.getValueAsString() << __E__;
4249  bool returnNode = true; // default to shown
4250 
4251  if(t.isUIDNode() && hideStatusFalse) // only show if status evaluates to true
4252  returnNode = t.isEnabled();
4253 
4254  if(returnNode)
4255  {
4256  parentEl =
4257  xmlOut.addTextElementToParent("node", t.getValueAsString(), parentEl);
4258  if(t.isUIDNode())
4259  {
4260  xmlOut.addTextElementToParent(
4261  "comment", t.getAuthor() + ": " + t.getComment(), parentEl);
4262  xmlOut.addTextElementToParent(
4263  "nodeStatus", t.isEnabled() ? "1" : "0", parentEl);
4264  }
4265 
4266  if(diffTree.has_value())
4267  {
4268  __COUTS__(30)
4269  << "\t\t diff type " << diffTree->get().getNodeType() << __E__;
4270 
4271  if(diffTree->get()
4272  .isRootNode()) //then diff group does not have this uid!
4273  {
4274  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4275  std::stringstream
4276  missingSs; //assume only one group loaded for diff
4277  //lookup group name in diffManager based on current node's table (best proxy info for diff node at this point)
4278  auto diffGroupPair =
4279  diffTree->get()
4280  .getConfigurationManager()
4281  ->getGroupOfLoadedTable(t.getTableName());
4282  missingSs << "<<< Not in " << diffGroupPair.first << "("
4283  << diffGroupPair.second << ") >>>";
4284  xmlOut.addTextElementToParent(
4285  "nodeDiff", missingSs.str(), parentEl);
4286  }
4287  else
4288  __COUTS__(30) << "" << t.getValueAsString() << __E__;
4289  } //end diff tree handling
4290  }
4291  else //hiding node
4292  return; // done.. no further depth needed for node that is not shown
4293  }
4294 
4295  // if depth>=1 toXml all children
4296  // child.toXml(depth-1)
4297  if(depth >= 1)
4298  {
4299  __COUTS__(30) << "\t\t\t" << t.getValueAsString() << __E__;
4300  auto C = t.getChildren();
4301  for(auto& c : C)
4302  recursiveTreeToXML( //TODO -- implement diffTree for depth > 1 requests
4303  c.second,
4304  depth - 1,
4305  xmlOut,
4306  parentEl,
4307  hideStatusFalse);
4308  }
4309  }
4310 } // end recursiveTreeToXML()
4311 
4312 //==============================================================================
4319 void ConfigurationGUISupervisor::handleGetLinkToChoicesXML(
4320  HttpXmlDocument& xmlOut,
4321  ConfigurationManagerRW* cfgMgr,
4322  const std::string& linkToTableName,
4323  const TableVersion& linkToTableVersion,
4324  const std::string& linkIdType,
4325  const std::string& linkIndex,
4326  const std::string& linkInitId)
4327 try
4328 {
4329  // get table
4330  // if uid link
4331  // return all uids
4332  // if groupid link
4333  // find target column
4334  // create the set of values (unique values only)
4335  // note: insert group unions individually (i.e. groups | separated)
4336 
4337  // get table and activate target version
4338  // rename to re-use code template
4339  const std::string& tableName = linkToTableName;
4340  const TableVersion& version = linkToTableVersion;
4341  TableBase* table = cfgMgr->getTableByName(tableName);
4342  try
4343  {
4344  table->setActiveView(version);
4345  }
4346  catch(...)
4347  {
4348  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
4349  << version << __E__;
4350  cfgMgr->getVersionedTableByName(tableName, version);
4351  }
4352 
4353  if(version != table->getViewVersion())
4354  {
4355  __SUP_SS__ << "Target table version (" << version
4356  << ") is not the currently active version (" << table->getViewVersion()
4357  << ". Try refreshing the tree." << __E__;
4358  __SUP_COUT_WARN__ << ss.str();
4359  __SS_THROW__;
4360  }
4361 
4362  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
4363 
4364  if(linkIdType == "UID")
4365  {
4366  // give all UIDs
4367  unsigned int col = table->getView().getColUID();
4368  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4369  xmlOut.addTextElementToData("linkToChoice",
4370  table->getView().getDataView()[row][col]);
4371  }
4372  else if(linkIdType == "GroupID")
4373  {
4374  // find target column
4375  // create the set of values (unique values only)
4376  // note: insert group unions individually (i.e. groups | separated)
4377 
4378  __SUP_COUTV__(linkIndex);
4379  __SUP_COUTV__(linkInitId);
4380 
4381  std::set<std::string> setOfGroupIDs =
4382  table->getView().getSetOfGroupIDs(linkIndex);
4383 
4384  // build list of groupids
4385  // always include initial link group id in choices
4386  // (even if not in set of group ids)
4387  bool foundInitId = false;
4388  for(const auto& groupID : setOfGroupIDs)
4389  {
4390  if(!foundInitId && linkInitId == groupID)
4391  foundInitId = true; // mark init id found
4392 
4393  xmlOut.addTextElementToData("linkToChoice", groupID);
4394  }
4395  // if init id was not found, add to list
4396  if(!foundInitId)
4397  xmlOut.addTextElementToData("linkToChoice", linkInitId);
4398 
4399  // give all UIDs
4400  unsigned int col = table->getView().getColUID();
4401  for(unsigned int row = 0; row < table->getView().getNumberOfRows(); ++row)
4402  {
4403  xmlOut.addTextElementToData("groupChoice",
4404  table->getView().getDataView()[row][col]);
4405  if(table->getView().isEntryInGroup(row, linkIndex, linkInitId))
4406  xmlOut.addTextElementToData("groupMember",
4407  table->getView().getDataView()[row][col]);
4408  }
4409  }
4410  else
4411  {
4412  __SUP_SS__ << "Unrecognized linkIdType '" << linkIdType << ".'" << __E__;
4413  __SS_THROW__;
4414  }
4415 } //end handleGetLinkToChoicesXML()
4416 catch(std::runtime_error& e)
4417 {
4418  __SUP_SS__ << "Error detected saving tree node!\n\n " << e.what() << __E__;
4419  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4420  xmlOut.addTextElementToData("Error", ss.str());
4421 }
4422 catch(...)
4423 {
4424  __SUP_SS__ << "Error detected saving tree node!\n\n " << __E__;
4425  try
4426  {
4427  throw;
4428  } //one more try to printout extra info
4429  catch(const std::exception& e)
4430  {
4431  ss << "Exception message: " << e.what();
4432  }
4433  catch(...)
4434  {
4435  }
4436  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4437  xmlOut.addTextElementToData("Error", ss.str());
4438 } //end handleGetLinkToChoicesXML() catch
4439 
4440 //==============================================================================
4442 void ConfigurationGUISupervisor::handleMergeGroupsXML(
4443  HttpXmlDocument& xmlOut,
4444  ConfigurationManagerRW* cfgMgr,
4445  const std::string& groupANameContext,
4446  const TableGroupKey& groupAKeyContext,
4447  const std::string& groupBNameContext,
4448  const TableGroupKey& groupBKeyContext,
4449  const std::string& groupANameConfig,
4450  const TableGroupKey& groupAKeyConfig,
4451  const std::string& groupBNameConfig,
4452  const TableGroupKey& groupBKeyConfig,
4453  const std::string& author,
4454  const std::string& mergeApproach)
4455 try
4456 {
4457  __SUP_COUT__ << "Merging context group pair " << groupANameContext << " ("
4458  << groupAKeyContext << ") & " << groupBNameContext << " ("
4459  << groupBKeyContext << ") and table group pair " << groupANameConfig
4460  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4461  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4462 
4463  // Merges group A and group B
4464  // with consideration for UID conflicts
4465  // Result is a new key of group A's name
4466  //
4467  // There 3 modes:
4468  // Rename -- All records from both groups are maintained, but conflicts from B
4469  // are renamed.
4470  // Must maintain a map of UIDs that are remapped to new name for
4471  // groupB, because linkUID fields must be preserved. Replace --
4472  // Any UID conflicts for a record are replaced by the record from group B.
4473  // Skip -- Any UID conflicts for a record are skipped so that group A record
4474  // remains
4475 
4476  // check valid mode
4477  if(!(mergeApproach == "Rename" || mergeApproach == "Replace" ||
4478  mergeApproach == "Skip"))
4479  {
4480  __SS__ << "Error! Invalid merge approach '" << mergeApproach << ".'" << __E__;
4481  __SS_THROW__;
4482  }
4483 
4484  std::map<std::string /*name*/, TableVersion /*version*/> memberMapAContext,
4485  memberMapBContext, memberMapAConfig, memberMapBConfig;
4486 
4487  // check if skipping group pairs
4488  bool skippingContextPair = false;
4489  bool skippingConfigPair = false;
4490  if(groupANameContext.size() == 0 || groupANameContext[0] == ' ' ||
4491  groupBNameContext.size() == 0 || groupBNameContext[0] == ' ')
4492  {
4493  skippingContextPair = true;
4494  __SUP_COUTV__(skippingContextPair);
4495  }
4496  if(groupANameConfig.size() == 0 || groupANameConfig[0] == ' ' ||
4497  groupBNameConfig.size() == 0 || groupBNameConfig[0] == ' ')
4498  {
4499  skippingConfigPair = true;
4500  __SUP_COUTV__(skippingConfigPair);
4501  }
4502 
4503  // get context group member maps
4504  if(!skippingContextPair)
4505  {
4506  cfgMgr->loadTableGroup(groupANameContext,
4507  groupAKeyContext,
4508  false /*doActivate*/,
4509  &memberMapAContext,
4510  0 /*progressBar*/,
4511  0 /*accumulateErrors*/,
4512  0 /*groupComment*/,
4513  0 /*groupAuthor*/,
4514  0 /*groupCreationTime*/,
4515  false /*doNotLoadMember*/,
4516  0 /*groupTypeString*/
4517  );
4518  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4519 
4520  cfgMgr->loadTableGroup(groupBNameContext,
4521  groupBKeyContext,
4522  false /*doActivate*/,
4523  &memberMapBContext,
4524  0 /*progressBar*/,
4525  0 /*accumulateErrors*/,
4526  0 /*groupComment*/,
4527  0 /*groupAuthor*/,
4528  0 /*groupCreationTime*/,
4529  false /*doNotLoadMember*/,
4530  0 /*groupTypeString*/
4531  );
4532 
4533  __SUP_COUTV__(StringMacros::mapToString(memberMapBContext));
4534  }
4535 
4536  // get table group member maps
4537  if(!skippingConfigPair)
4538  {
4539  cfgMgr->loadTableGroup(groupANameConfig,
4540  groupAKeyConfig,
4541  false /*doActivate*/,
4542  &memberMapAConfig,
4543  0 /*progressBar*/,
4544  0 /*accumulateErrors*/,
4545  0 /*groupComment*/,
4546  0 /*groupAuthor*/,
4547  0 /*groupCreationTime*/,
4548  false /*doNotLoadMember*/,
4549  0 /*groupTypeString*/
4550  );
4551  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4552 
4553  cfgMgr->loadTableGroup(groupBNameConfig,
4554  groupBKeyConfig,
4555  false /*doActivate*/,
4556  &memberMapBConfig,
4557  0 /*progressBar*/,
4558  0 /*accumulateErrors*/,
4559  0 /*groupComment*/,
4560  0 /*groupAuthor*/,
4561  0 /*groupCreationTime*/,
4562  false /*doNotLoadMember*/,
4563  0 /*groupTypeString*/
4564  );
4565 
4566  __SUP_COUTV__(StringMacros::mapToString(memberMapBConfig));
4567  }
4568 
4569  // for each member of B
4570  // if not found in A member map, add it
4571  // if found in both member maps, and versions are different, load both tables and
4572  // merge
4573 
4574  std::map<std::pair<std::string /*original table*/, std::string /*original uidB*/>,
4575  std::string /*converted uidB*/>
4576  uidConversionMap;
4577  std::map<
4578  std::pair<std::string /*original table*/,
4579  std::pair<std::string /*group linkid*/, std::string /*original gidB*/>>,
4580  std::string /*converted gidB*/>
4581  groupidConversionMap;
4582 
4583  std::stringstream mergeReport;
4584  mergeReport << "======================================" << __E__;
4585  mergeReport << "Time of merge: " << StringMacros::getTimestampString() << __E__;
4586  mergeReport << "Merging context group pair " << groupANameContext << " ("
4587  << groupAKeyContext << ") & " << groupBNameContext << " ("
4588  << groupBKeyContext << ") and table group pair " << groupANameConfig
4589  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4590  << groupBKeyConfig << ") with approach '" << mergeApproach << __E__;
4591  mergeReport << "======================================" << __E__;
4592 
4593  // first loop create record conversion map, second loop implement merge (using
4594  // conversion map if Rename)
4595  for(unsigned int i = 0; i < 2; ++i)
4596  {
4597  if(i == 0 && mergeApproach != "Rename")
4598  continue; // only need to construct uidConversionMap for rename approach
4599 
4600  // loop for context and table pair types
4601  for(unsigned int j = 0; j < 2; ++j)
4602  {
4603  if(j == 0 && skippingContextPair) // context
4604  {
4605  __COUT__ << "Skipping context pair..." << __E__;
4606  continue;
4607  }
4608  else if(j == 1 && skippingConfigPair)
4609  {
4610  __COUT__ << "Skipping table pair..." << __E__;
4611  continue;
4612  }
4613 
4614  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapAref =
4615  j == 0 ? memberMapAContext : memberMapAConfig;
4616 
4617  std::map<std::string /*name*/, TableVersion /*version*/>& memberMapBref =
4618  j == 0 ? memberMapBContext : memberMapBConfig;
4619 
4620  if(j == 0) // context
4621  __COUT__ << "Context pair..." << __E__;
4622  else
4623  __COUT__ << "Table pair..." << __E__;
4624 
4625  __COUT__ << "Starting member map B scan." << __E__;
4626  for(const auto& bkey : memberMapBref)
4627  {
4628  __SUP_COUTV__(bkey.first);
4629 
4630  if(memberMapAref.find(bkey.first) == memberMapAref.end())
4631  {
4632  mergeReport << "\n'" << mergeApproach << "'-Missing table '"
4633  << bkey.first << "' A=v" << -1 << ", adding B=v"
4634  << bkey.second << __E__;
4635 
4636  // not found, so add to A member map
4637  memberMapAref[bkey.first] = bkey.second;
4638  }
4639  else if(memberMapAref[bkey.first] != bkey.second)
4640  {
4641  // found table version confict
4642  __SUP_COUTV__(memberMapAref[bkey.first]);
4643  __SUP_COUTV__(bkey.second);
4644 
4645  // load both tables, and merge
4646  TableBase* table = cfgMgr->getTableByName(bkey.first);
4647 
4648  __SUP_COUT__ << "Got table." << __E__;
4649 
4650  TableVersion newVersion = table->mergeViews(
4651  cfgMgr
4652  ->getVersionedTableByName(bkey.first,
4653  memberMapAref[bkey.first])
4654  ->getView(),
4655  cfgMgr->getVersionedTableByName(bkey.first, bkey.second)
4656  ->getView(),
4657  TableVersion() /* destinationVersion*/,
4658  author,
4659  mergeApproach == "Rename"
4660  ? TableBase::MergeApproach::RENAME
4661  : (mergeApproach == "Replace"
4662  ? TableBase::MergeApproach::REPLACE
4663  : TableBase::MergeApproach::SKIP),
4664  uidConversionMap,
4665  groupidConversionMap,
4666  i == 0 /* fillRecordConversionMaps */,
4667  i == 1 /* applyRecordConversionMaps */,
4668  table->getTableName() ==
4669  ConfigurationManager::XDAQ_APPLICATION_TABLE_NAME
4670  /* generateUniqueDataColumns */,
4671  &mergeReport); // dont make destination version the first time
4672 
4673  if(i == 1)
4674  {
4675  __SUP_COUTV__(newVersion);
4676 
4677  try
4678  {
4679  // save all temporary tables to persistent tables
4680  // finish off the version creation
4681  newVersion =
4683  xmlOut,
4684  cfgMgr,
4685  bkey.first,
4686  TableVersion() /*original source version*/,
4687  false /* makeTemporary */,
4688  table,
4689  newVersion /*temporary modified version*/,
4690  false /*ignore duplicates*/,
4691  true /*look for equivalent*/);
4692  }
4693  catch(std::runtime_error& e)
4694  {
4695  __SUP_SS__
4696  << "There was an error saving the '"
4697  << table->getTableName()
4698  << "' merge result to a persistent table version. "
4699  << "Perhaps you can modify this table in one of the "
4700  "groups to resolve this issue, and then re-merge."
4701  << __E__ << e.what();
4702  __SS_THROW__;
4703  }
4704 
4705  __SUP_COUTV__(newVersion);
4706 
4707  memberMapAref[bkey.first] = newVersion;
4708  }
4709  } // end member version conflict handling
4710  } // end B member map loop
4711  } // end context and table loop
4712  } // end top level conversion map or not loop
4713 
4714  // Now save groups
4715 
4716  if(!skippingContextPair)
4717  {
4718  __SUP_COUT__ << "New context member map complete." << __E__;
4719  __SUP_COUTV__(StringMacros::mapToString(memberMapAContext));
4720 
4721  // save the new table group
4722  TableGroupKey newKeyContext = cfgMgr->saveNewTableGroup(
4723  groupANameContext,
4724  memberMapAContext,
4725  "Merger of group " + groupANameContext + " (" + groupAKeyContext.toString() +
4726  ") and " + groupBNameContext + " (" + groupBKeyContext.toString() + ").");
4727 
4728  // return new resulting group
4729  xmlOut.addTextElementToData("ContextGroupName", groupANameContext);
4730  xmlOut.addTextElementToData("ContextGroupKey", newKeyContext.toString());
4731  }
4732  if(!skippingConfigPair)
4733  {
4734  __SUP_COUT__ << "New table member map complete." << __E__;
4735  __SUP_COUTV__(StringMacros::mapToString(memberMapAConfig));
4736 
4737  // save the new table group
4738  TableGroupKey newKeyConfig = cfgMgr->saveNewTableGroup(
4739  groupANameConfig,
4740  memberMapAConfig,
4741  "Merger of group " + groupANameConfig + " (" + groupAKeyConfig.toString() +
4742  ") and " + groupBNameConfig + " (" + groupBKeyConfig.toString() + ").");
4743 
4744  // return new resulting group
4745  xmlOut.addTextElementToData("ConfigGroupName", groupANameConfig);
4746  xmlOut.addTextElementToData("ConfigGroupKey", newKeyConfig.toString());
4747  }
4748 
4749  // output merge report
4750  {
4751  std::string mergeReportBasePath = std::string(__ENV__("USER_DATA"));
4752  std::string mergeReportPath = "/ServiceData/";
4753  // make merge report directories in case they don't exist
4754  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4755  mergeReportPath += "ConfigurationGUI_mergeReports/";
4756  // make merge report directories in case they don't exist
4757  mkdir((mergeReportBasePath + mergeReportPath).c_str(), 0755);
4758 
4759  mergeReportPath +=
4760  "merge_" + std::to_string(time(0)) + "_" + std::to_string(clock()) + ".txt";
4761  __SUP_COUTV__(mergeReportPath);
4762 
4763  FILE* fp = fopen((mergeReportBasePath + mergeReportPath).c_str(), "w");
4764  if(fp)
4765  {
4766  fprintf(fp, "%s", mergeReport.str().c_str());
4767  fclose(fp);
4768  xmlOut.addTextElementToData("MergeReportFile",
4769  "/$USER_DATA/" + mergeReportPath);
4770  }
4771  else
4772  xmlOut.addTextElementToData("MergeReportFile", "FILE FAILURE");
4773  } // end output merge report
4774 
4775 } // end handleMergeGroupsXML()
4776 catch(std::runtime_error& e)
4777 {
4778  __SUP_SS__ << "Error merging context group pair " << groupANameContext << " ("
4779  << groupAKeyContext << ") & " << groupBNameContext << " ("
4780  << groupBKeyContext << ") and table group pair " << groupANameConfig
4781  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4782  << groupBKeyConfig << ") with approach '" << mergeApproach << "': \n\n"
4783  << e.what() << __E__;
4784  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4785  xmlOut.addTextElementToData("Error", ss.str());
4786 }
4787 catch(...)
4788 {
4789  __SUP_SS__ << "Unknown error merging context group pair " << groupANameContext << " ("
4790  << groupAKeyContext << ") & " << groupBNameContext << " ("
4791  << groupBKeyContext << ") and table group pair " << groupANameConfig
4792  << " (" << groupAKeyConfig << ") & " << groupBNameConfig << " ("
4793  << groupBKeyConfig << ") with approach '" << mergeApproach << ".' \n\n";
4794  try
4795  {
4796  throw;
4797  } //one more try to printout extra info
4798  catch(const std::exception& e)
4799  {
4800  ss << "Exception message: " << e.what();
4801  }
4802  catch(...)
4803  {
4804  }
4805  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
4806  xmlOut.addTextElementToData("Error", ss.str());
4807 } // end handleMergeGroupsXML() catch
4808 
4809 //==============================================================================
4811 void ConfigurationGUISupervisor::handleSavePlanCommandSequenceXML(
4812  HttpXmlDocument& xmlOut,
4813  ConfigurationManagerRW* cfgMgr,
4814  const std::string& groupName,
4815  const TableGroupKey& groupKey,
4816  const std::string& modifiedTables,
4817  const std::string& author,
4818  const std::string& planName,
4819  const std::string& commandString)
4820 try
4821 {
4822  __COUT__ << "handleSavePlanCommandSequenceXML " << planName << __E__;
4823 
4824  // setup active tables based on input group and modified tables
4825  setupActiveTablesXML(xmlOut,
4826  cfgMgr,
4827  groupName,
4828  groupKey,
4829  modifiedTables,
4830  true /* refresh all */,
4831  false /* getGroupInfo */,
4832  0 /* returnMemberMap */,
4833  false /* outputActiveTables */);
4834 
4835  TableEditStruct planTable(IterateTable::PLAN_TABLE,
4836  cfgMgr); // Table ready for editing!
4837  TableEditStruct targetTable(IterateTable::TARGET_TABLE,
4838  cfgMgr); // Table ready for editing!
4839 
4840  // create table-edit struct for each table that an iterate command type can use
4841  //if two command types have same table, TableEditStruct returns the same temporary version of the table, but then modified_
4842  // will be maintained separately and saving the table becomes a mess.
4843  std::map<std::string /* table name */, TableEditStruct> commandTableToEditMap;
4844  for(const auto& commandPair : IterateTable::commandToTableMap_)
4845  if(commandPair.second != "") // skip tables with no parameters
4846  commandTableToEditMap.emplace(std::pair<std::string, TableEditStruct>(
4847  commandPair.second, TableEditStruct(commandPair.second, cfgMgr)));
4848 
4849  // try to catch any errors while editing..
4850  // if errors delete temporary plan view (if created here)
4851  try
4852  {
4853  // Steps:
4854  // Reset plan commands
4855  // Remove all commands in group "<plan>-Plan"
4856  // Delete linked command parameters row (in separate table)
4857  // If no group remaining, then delete row.
4858  //
4859  // Save plan commands (if modified)
4860  // Create rows and add them to group "<plan>-Plan"
4861  // create row for command paramaters and add to proper table
4862 
4863  std::string groupName = planName + "-Plan";
4864  __SUP_COUT__ << "Handling commands for group " << groupName << __E__;
4865 
4866  unsigned int groupIdCol =
4867  planTable.tableView_->findCol(IterateTable::planTableCols_.GroupID_);
4868  unsigned int cmdTypeCol =
4869  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandType_);
4870 
4871  unsigned int targetGroupIdCol =
4872  targetTable.tableView_->findCol(IterateTable::targetCols_.GroupID_);
4873  unsigned int targetTableCol =
4874  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLink_);
4875  unsigned int targetUIDCol =
4876  targetTable.tableView_->findCol(IterateTable::targetCols_.TargetLinkUID_);
4877 
4878  std::string groupLinkIndex =
4879  planTable.tableView_->getColumnInfo(groupIdCol).getChildLinkIndex();
4880  __SUP_COUT__ << "groupLinkIndex: " << groupLinkIndex << __E__;
4881 
4882  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> commandUidLink;
4883  {
4884  bool isGroup; // local because we know is uid link
4885  planTable.tableView_->getChildLink(
4886  planTable.tableView_->findCol(IterateTable::planTableCols_.CommandLink_),
4887  isGroup,
4888  commandUidLink);
4889  }
4890 
4891  unsigned int cmdRow, cmdCol;
4892  std::string targetGroupName;
4893 
4894  // Reset existing plan commands
4895  {
4896  std::string targetUID, cmdType;
4897 
4898  for(unsigned int row = 0; row < planTable.tableView_->getNumberOfRows();
4899  ++row)
4900  {
4901  targetUID = planTable.tableView_
4902  ->getDataView()[row][planTable.tableView_->getColUID()];
4903  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
4904 
4905  // remove command from plan group.. if no more groups, delete
4906  if(planTable.tableView_->isEntryInGroup(row, groupLinkIndex, groupName))
4907  {
4908  __SUP_COUT__ << "Removing." << __E__;
4909 
4910  // delete linked command
4911  // find linked UID in table (mapped by type)
4912  cmdType = planTable.tableView_->getDataView()[row][cmdTypeCol];
4913  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(cmdType);
4914  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
4915  cmdTypeTableIt->second !=
4916  "") // skip if invalid command type or if no command parameter table
4917  {
4918  TableEditStruct& cmdTypeTableEdit =
4919  commandTableToEditMap.at(cmdTypeTableIt->second);
4920  cmdRow = cmdTypeTableEdit.tableView_->findRow(
4921  cmdTypeTableEdit.tableView_->getColUID(),
4922  planTable.tableView_
4923  ->getDataView()[row][commandUidLink.second]);
4924 
4925  // before deleting row...
4926  // look for target group
4927  // remove all targets in group
4928  try
4929  {
4930  cmdCol = cmdTypeTableEdit.tableView_->findCol(
4931  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
4932  targetGroupName = cmdTypeTableEdit.tableView_
4933  ->getDataView()[cmdRow][cmdCol];
4934 
4935  for(unsigned int trow = 0;
4936  trow < targetTable.tableView_->getNumberOfRows();
4937  ++trow)
4938  {
4939  // remove command from target group..
4940  if(targetTable.tableView_->isEntryInGroup(
4941  trow,
4942  cmdTypeTableEdit.tableView_->getColumnInfo(cmdCol)
4943  .getChildLinkIndex(),
4944  targetGroupName))
4945  {
4946  __SUP_COUT__ << "Removing target." << __E__;
4947  // remove command entry in plan table
4948  if(targetTable.tableView_->removeRowFromGroup(
4949  trow,
4950  targetGroupIdCol,
4951  targetGroupName,
4952  true /*deleteRowIfNoGroup*/))
4953  --trow; // since row was deleted, go back!
4954  }
4955  }
4956  }
4957  catch(...)
4958  {
4959  __SUP_COUT__ << "No targets." << __E__;
4960  }
4961 
4962  // now no more targets, delete row
4963 
4964  cmdTypeTableEdit.tableView_->deleteRow(cmdRow);
4965 
4966  cmdTypeTableEdit.modified_ = true;
4967  }
4968 
4969  // remove command entry in plan table
4970  if(planTable.tableView_->removeRowFromGroup(
4971  row, groupIdCol, groupName, true /*deleteRowIfNoGroup*/))
4972  --row; // since row was deleted, go back!
4973  }
4974  }
4975  }
4976 
4977  // Done resetting existing plan
4978  // Now save new commands
4979 
4980  std::vector<IterateTable::Command> commands;
4981 
4982  // extract command sequence and add to table
4983  // into vector with type, and params
4984  {
4985  std::istringstream f(commandString);
4986  std::string commandSubString, paramSubString, paramValue;
4987  int i;
4988  while(getline(f, commandSubString, ';'))
4989  {
4990  __SUP_COUTT__ << "commandSubString " << commandSubString << __E__;
4991  std::istringstream g(commandSubString);
4992 
4993  i = 0;
4994  while(getline(g, paramSubString, ','))
4995  {
4996  __SUP_COUTT__ << "paramSubString " << paramSubString << __E__;
4997  if(i == 0) // type
4998  {
4999  if(paramSubString != "type")
5000  {
5001  __SUP_SS__ << "Invalid command sequence" << __E__;
5002  __SS_THROW__;
5003  }
5004  // create command object
5005  commands.push_back(IterateTable::Command());
5006 
5007  getline(g, paramValue, ',');
5008  ++i;
5009  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
5010  commands.back().type_ = paramValue;
5011  }
5012  else // params
5013  {
5014  getline(g, paramValue, ',');
5015  ++i;
5016  __SUP_COUTT__ << "paramValue " << paramValue << __E__;
5017 
5018  commands.back().params_.emplace(
5019  std::pair<std::string /*param name*/,
5020  std::string /*param value*/>(
5021  paramSubString,
5022  StringMacros::decodeURIComponent(paramValue)));
5023  }
5024 
5025  ++i;
5026  }
5027  }
5028 
5029  } // end extract command sequence
5030 
5031  __SUP_COUT__ << "commands size " << commands.size() << __E__;
5032 
5033  // at this point, have extracted commands
5034 
5035  // now save commands to plan group
5036  // group should be "<plan>-Plan"
5037 
5038  unsigned int row, tgtRow;
5039  unsigned int targetIndex;
5040  std::string targetStr, cmdUID;
5041 
5042  for(auto& command : commands)
5043  {
5044  __SUP_COUT__ << "command " << command.type_ << __E__;
5045  __SUP_COUT__ << "table " << IterateTable::commandToTableMap_.at(command.type_)
5046  << __E__;
5047 
5048  // create command entry at plan level
5049  row = planTable.tableView_->addRow(
5050  author, true /*incrementUniqueData*/, "planCommand");
5051  planTable.tableView_->addRowToGroup(row, groupIdCol, groupName);
5052 
5053  // set command type
5054  planTable.tableView_->setURIEncodedValue(command.type_, row, cmdTypeCol);
5055 
5056  // set command status true
5057  planTable.tableView_->setValueAsString(
5058  "1", row, planTable.tableView_->getColStatus());
5059 
5060  // create command specifics
5061  auto cmdTypeTableIt = IterateTable::commandToTableMap_.find(command.type_);
5062  if(cmdTypeTableIt != IterateTable::commandToTableMap_.end() &&
5063  cmdTypeTableIt->second !=
5064  "") // skip if invalid command type or if no command parameter table
5065  {
5066  TableEditStruct& cmdTypeTableEdit =
5067  commandTableToEditMap.at(cmdTypeTableIt->second);
5068  __SUP_COUT__ << "table " << cmdTypeTableEdit.tableName_ << __E__;
5069 
5070  // at this point have table, tempVersion, and createdFlag
5071 
5072  // create command parameter entry at command level
5073  cmdRow = cmdTypeTableEdit.tableView_->addRow(
5074  author, true /*incrementUniqueData*/, command.type_ + "_COMMAND_");
5075 
5076  // parameters are linked
5077  // now set value of all parameters
5078  // find parameter column, and set value
5079  // if special target parameter, extract targets
5080  for(auto& param : command.params_)
5081  {
5082  __SUP_COUT__ << "\t param " << param.first << " : " << param.second
5083  << __E__;
5084 
5085  if(param.first == IterateTable::targetParams_.Tables_)
5086  {
5087  __SUP_COUT__ << "\t\t found target tables" << __E__;
5088  std::istringstream f(param.second);
5089 
5090  targetIndex = 0;
5091  while(getline(f, targetStr, '='))
5092  {
5093  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
5094  if(!command.targets_.size() ||
5095  command.targets_.back().table_ != "")
5096  {
5097  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
5098  << __E__;
5099  // make new target
5100  command.addTarget();
5101  command.targets_.back().table_ = targetStr;
5102  }
5103  else // file existing target
5104  command.targets_[targetIndex++].table_ = targetStr;
5105  }
5106 
5107  continue; // go to next parameter
5108  }
5109 
5110  if(param.first == IterateTable::targetParams_.UIDs_)
5111  {
5112  __SUP_COUT__ << "\t\t found target UIDs" << __E__;
5113  std::istringstream f(param.second);
5114 
5115  targetIndex = 0;
5116  while(getline(f, targetStr, '='))
5117  {
5118  __SUP_COUT__ << "\t\t targetStr = " << targetStr << __E__;
5119  if(!command.targets_.size() ||
5120  command.targets_.back().UID_ != "")
5121  {
5122  __SUP_COUT__ << "\t\t make targetStr = " << targetStr
5123  << __E__;
5124  // make new target
5125  command.addTarget();
5126  command.targets_.back().UID_ = targetStr;
5127  }
5128  else // file existing target
5129  command.targets_[targetIndex++].UID_ = targetStr;
5130  }
5131  continue;
5132  }
5133 
5134  cmdCol = cmdTypeTableEdit.tableView_->findCol(param.first);
5135 
5136  __SUP_COUT__ << "param col " << cmdCol << __E__;
5137 
5138  cmdTypeTableEdit.tableView_->setURIEncodedValue(
5139  param.second, cmdRow, cmdCol);
5140  } // end parameter loop
5141 
5142  cmdUID =
5143  cmdTypeTableEdit.tableView_
5144  ->getDataView()[cmdRow][cmdTypeTableEdit.tableView_->getColUID()];
5145 
5146  if(command.targets_.size())
5147  {
5148  // if targets, create group in target table
5149 
5150  __SUP_COUT__ << "targets found for command UID=" << cmdUID << __E__;
5151 
5152  // create link from command table to target
5153  cmdCol = cmdTypeTableEdit.tableView_->findCol(
5154  IterateTable::commandTargetCols_.TargetsLink_);
5155  cmdTypeTableEdit.tableView_->setValueAsString(
5156  IterateTable::TARGET_TABLE, cmdRow, cmdCol);
5157 
5158  cmdCol = cmdTypeTableEdit.tableView_->findCol(
5159  IterateTable::commandTargetCols_.TargetsLinkGroupID_);
5160  cmdTypeTableEdit.tableView_->setValueAsString(
5161  cmdUID + "_Targets", cmdRow, cmdCol);
5162 
5163  // create row(s) for each target in target table with correct groupID
5164 
5165  for(const auto& target : command.targets_)
5166  {
5167  __SUP_COUT__ << target.table_ << " " << target.UID_ << __E__;
5168 
5169  // create target entry in target table in group
5170  tgtRow = targetTable.tableView_->addRow(
5171  author, true /*incrementUniqueData*/, "commandTarget");
5172  targetTable.tableView_->addRowToGroup(
5173  tgtRow, targetGroupIdCol, cmdUID + "_Targets");
5174 
5175  // set target table
5176  targetTable.tableView_->setValueAsString(
5177  target.table_, tgtRow, targetTableCol);
5178 
5179  // set target UID
5180  targetTable.tableView_->setValueAsString(
5181  target.UID_, tgtRow, targetUIDCol);
5182  }
5183  } // end target handling
5184 
5185  // add link at plan level to created UID
5186  planTable.tableView_->setValueAsString(
5187  cmdTypeTableEdit.tableName_, row, commandUidLink.first);
5188  planTable.tableView_->setValueAsString(
5189  cmdUID, row, commandUidLink.second);
5190 
5191  __SUP_COUT__ << "linked to uid = " << cmdUID << __E__;
5192 
5193  cmdTypeTableEdit.modified_ = true;
5194  } // done with command specifics
5195 
5196  } // end command loop
5197 
5198  // commands are created in the temporary tables
5199  // validate with init
5200 
5201  planTable.tableView_->print();
5202  planTable.tableView_->init(); // verify new table (throws runtime_errors)
5203 
5204  __SUP_COUT__ << "requestType tables:" << __E__;
5205 
5206  for(auto& modifiedConfig : commandTableToEditMap)
5207  {
5208  __SUP_COUTV__(modifiedConfig.second.modified_);
5209  modifiedConfig.second.tableView_->print();
5210  modifiedConfig.second.tableView_->init();
5211  }
5212 
5213  targetTable.tableView_->print();
5214  targetTable.tableView_->init(); // verify new table (throws runtime_errors)
5215 
5216  } // end try for plan
5217  catch(...)
5218  {
5219  __SUP_COUT__ << "Handling command table errors while saving. Erasing all newly "
5220  "created versions."
5221  << __E__;
5222 
5223  // erase all temporary tables if created here
5224 
5225  if(planTable.createdTemporaryVersion_) // if temporary version created here
5226  {
5227  __SUP_COUT__ << "Erasing temporary version " << planTable.tableName_ << "-v"
5228  << planTable.temporaryVersion_ << __E__;
5229  // erase with proper version management
5230  cfgMgr->eraseTemporaryVersion(planTable.tableName_,
5231  planTable.temporaryVersion_);
5232  }
5233 
5234  if(targetTable.createdTemporaryVersion_) // if temporary version created here
5235  {
5236  __SUP_COUT__ << "Erasing temporary version " << targetTable.tableName_ << "-v"
5237  << targetTable.temporaryVersion_ << __E__;
5238  // erase with proper version management
5239  cfgMgr->eraseTemporaryVersion(targetTable.tableName_,
5240  targetTable.temporaryVersion_);
5241  }
5242 
5243  for(auto& modifiedConfig : commandTableToEditMap)
5244  {
5245  if(modifiedConfig.second
5246  .createdTemporaryVersion_) // if temporary version created here
5247  {
5248  __SUP_COUT__ << "Erasing temporary version "
5249  << modifiedConfig.second.tableName_ << "-v"
5250  << modifiedConfig.second.temporaryVersion_ << __E__;
5251  // erase with proper version management
5252  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5253  modifiedConfig.second.temporaryVersion_);
5254  }
5255  }
5256 
5257  throw; // re-throw
5258  }
5259 
5260  // all edits are complete and tables verified
5261  // need to save all edits properly
5262  // if not modified, discard
5263 
5265  xmlOut,
5266  cfgMgr,
5267  planTable.tableName_,
5268  planTable.originalVersion_,
5269  true /*make temporary*/,
5270  planTable.table_,
5271  planTable.temporaryVersion_,
5272  true /*ignoreDuplicates*/); // save temporary version properly
5273 
5274  __SUP_COUT__ << "Final plan version is " << planTable.tableName_ << "-v"
5275  << finalVersion << __E__;
5276 
5278  xmlOut,
5279  cfgMgr,
5280  targetTable.tableName_,
5281  targetTable.originalVersion_,
5282  true /*make temporary*/,
5283  targetTable.table_,
5284  targetTable.temporaryVersion_,
5285  true /*ignoreDuplicates*/); // save temporary version properly
5286 
5287  __SUP_COUT__ << "Final target version is " << targetTable.tableName_ << "-v"
5288  << finalVersion << __E__;
5289 
5290  for(auto& modifiedConfig : commandTableToEditMap)
5291  {
5292  if(!modifiedConfig.second.modified_)
5293  {
5294  if(modifiedConfig.second
5295  .createdTemporaryVersion_) // if temporary version created here
5296  {
5297  __SUP_COUT__ << "Erasing unmodified temporary version "
5298  << modifiedConfig.second.tableName_ << "-v"
5299  << modifiedConfig.second.temporaryVersion_ << __E__;
5300  // erase with proper version management
5301  cfgMgr->eraseTemporaryVersion(modifiedConfig.second.tableName_,
5302  modifiedConfig.second.temporaryVersion_);
5303  }
5304  continue;
5305  }
5306 
5308  xmlOut,
5309  cfgMgr,
5310  modifiedConfig.second.tableName_,
5311  modifiedConfig.second.originalVersion_,
5312  true /*make temporary*/,
5313  modifiedConfig.second.table_,
5314  modifiedConfig.second.temporaryVersion_,
5315  true /*ignoreDuplicates*/); // save temporary version properly
5316 
5317  __SUP_COUT__ << "Final version is " << modifiedConfig.second.tableName_ << "-v"
5318  << finalVersion << __E__;
5319  }
5320 
5321  handleFillModifiedTablesXML(xmlOut, cfgMgr);
5322 } // end handleSavePlanCommandSequenceXML()
5323 catch(std::runtime_error& e)
5324 {
5325  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << e.what() << __E__;
5326  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5327  xmlOut.addTextElementToData("Error", ss.str());
5328 }
5329 catch(...)
5330 {
5331  __SUP_SS__ << "Error detected saving Iteration Plan!\n\n " << __E__;
5332  try
5333  {
5334  throw;
5335  } //one more try to printout extra info
5336  catch(const std::exception& e)
5337  {
5338  ss << "Exception message: " << e.what();
5339  }
5340  catch(...)
5341  {
5342  }
5343  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
5344  xmlOut.addTextElementToData("Error", ss.str());
5345 } // end handleSavePlanCommandSequenceXML() catch
5346 
5347 //==============================================================================
5357 void ConfigurationGUISupervisor::handleSaveTreeNodeEditXML(HttpXmlDocument& xmlOut,
5358  ConfigurationManagerRW* cfgMgr,
5359  const std::string& tableName,
5360  TableVersion version,
5361  const std::string& type,
5362  const std::string& uid,
5363  const std::string& colName,
5364  const std::string& newValue,
5365  const std::string& author)
5366 try
5367 {
5368  __SUP_COUT__ << "Editing table " << tableName << "(" << version << ") uid=" << uid
5369  << " type=" << type << __E__;
5370 
5371  // get the current table/version
5372  // check if the value is new
5373  // if new edit value (in a temporary version only)
5374 
5375  // get table and activate target version
5376  TableBase* table = cfgMgr->getTableByName(tableName);
5377  try
5378  {
5379  table->setActiveView(version);
5380  }
5381  catch(...)
5382  {
5383  if(version.isTemporaryVersion())
5384  throw; // if temporary, there is no hope to find lost version
5385 
5386  __SUP_COUT__ << "Failed to find stored version, so attempting to load version: "
5387  << version << __E__;
5388  cfgMgr->getVersionedTableByName(tableName, version);
5389  }
5390 
5391  __SUP_COUT__ << "Active version is " << table->getViewVersion() << __E__;
5392  __SUP_COUTTV__(table->getView().getComment());
5393 
5394  if(version != table->getViewVersion())
5395  {
5396  __SUP_SS__ << "Target table version (" << version
5397  << ") is not the currently active version (" << table->getViewVersion()
5398  << "). Try refreshing the tree." << __E__;
5399  __SS_THROW__;
5400  }
5401 
5402  unsigned int col = -1;
5403  if(type == "uid" || type == "delete-uid" || type == "tree-copy")
5404  col = table->getView().getColUID();
5405  else if(type == "node-comment")
5406  col = table->getView().findCol(TableViewColumnInfo::COL_NAME_COMMENT);
5407  else if(type == "link-UID" || type == "link-GroupID" || type == "value" ||
5408  type == "value-groupid" || type == "value-bool" || type == "value-bitmap")
5409  col = table->getView().findCol(colName);
5410  else if(type == "table" || type == "link-comment" || type == "table-newGroupRow" ||
5411  type == "table-newUIDRow" || type == "table-newRow")
5412  ; // column N/A
5413  else
5414  {
5415  __SUP_SS__ << "Impossible! Unrecognized edit type: " << type << __E__;
5416  __SS_THROW__;
5417  }
5418 
5419  // check if the comment value is new before making temporary version
5420  if(type == "table" || type == "link-comment")
5421  {
5422  // editing comment, so check if comment is different
5423  if(table->getView().isURIEncodedCommentTheSame(newValue))
5424  {
5425  __SUP_SS__ << "Comment '" << StringMacros::decodeURIComponent(newValue)
5426  << "' is the same as the current comment. No need to save change."
5427  << __E__;
5428  __SS_THROW__;
5429  }
5430  }
5431 
5432  // version handling:
5433  // always make a new temporary-version from source-version
5434  // edit temporary-version
5435  // if edit fails
5436  // delete temporary-version
5437  // else
5438  // return new temporary-version
5439  // if source-version was temporary
5440  // then delete source-version
5441 
5442  TableVersion temporaryVersion = table->createTemporaryView(version);
5443 
5444  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5445 
5446  TableView* cfgView = table->getTemporaryView(temporaryVersion);
5447  cfgView->init(); // prepare maps
5448 
5449  __SUP_COUTTV__(table->getView().getComment());
5450 
5451  // edit/verify new table (throws runtime_errors)
5452  try
5453  {
5454  // have view so edit it
5455  if(type == "table" || type == "link-comment")
5456  {
5457  // edit comment
5458  cfgView->setURIEncodedComment(newValue);
5459  }
5460  else if(type == "table-newRow" || type == "table-newUIDRow")
5461  {
5462  // add row
5463  unsigned int row = cfgView->addRow(
5464  author, true /*incrementUniqueData*/, newValue /*baseNameAutoUID*/);
5465 
5466  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5467  try
5468  {
5469  col = cfgView->getColStatus();
5470  cfgView->setValueAsString("1", row, col);
5471  }
5472  catch(...)
5473  {
5474  } // if not, ignore
5475 
5476  // set UID value
5477  cfgView->setURIEncodedValue(newValue, row, cfgView->getColUID());
5478  }
5479  else if(type == "table-newGroupRow")
5480  {
5481  // get index value and group id value
5482  unsigned int csvIndex = newValue.find(',');
5483 
5484  std::string linkIndex = newValue.substr(0, csvIndex);
5485  std::string groupId = newValue.substr(csvIndex + 1);
5486 
5487  // get new row UID value from second part of string
5488  csvIndex = groupId.find(',');
5489  std::string newRowUID = groupId.substr(csvIndex + 1);
5490  groupId = groupId.substr(0, csvIndex);
5491 
5492  __SUP_COUT__ << "newValue " << linkIndex << "," << groupId << "," << newRowUID
5493  << __E__;
5494 
5495  // add row
5496  unsigned int row = cfgView->addRow(author,
5497  true /*incrementUniqueData*/,
5498  newRowUID /*baseNameAutoID*/,
5499  -1 /* rowToAdd */,
5500  linkIndex,
5501  groupId);
5502 
5503  // set UID value
5504  cfgView->setURIEncodedValue(newRowUID, row, cfgView->getColUID());
5505 
5506  // find groupId column from link index
5507  col = cfgView->getLinkGroupIDColumn(linkIndex);
5508 
5509  // set group id
5510  cfgView->setURIEncodedValue(groupId, row, col);
5511 
5512  // if TableViewColumnInfo::COL_NAME_STATUS exists, set it to true
5513  try
5514  {
5515  col = cfgView->getColStatus();
5516  cfgView->setValueAsString("1", row, col);
5517  }
5518  catch(...)
5519  {
5520  } // if not, ignore
5521  }
5522  else if(type == "delete-uid")
5523  {
5524  // delete row
5525  unsigned int row = cfgView->findRow(col, uid);
5526  cfgView->deleteRow(row);
5527  }
5528  else if(type == "tree-copy")
5529  {
5530  // recursively copy to depth
5531  __COUTV__(newValue);
5532  std::vector<std::string> paramArray =
5534  __COUTV__(StringMacros::vectorToString(paramArray));
5535 
5536  // accept either 2 params (count, depth) or 3 params (count, depth, customUID).
5537  // A non-empty customUID is honored only when copying a single instance.
5538  if(paramArray.size() < 2 || paramArray.size() > 3)
5539  {
5540  __SS__ << "Illegal parameters for tree copy request: must be number of "
5541  "copy instances & depth of copy (optionally followed by a "
5542  "custom UID for the new record)."
5543  << __E__;
5544  __SS_THROW__;
5545  }
5546 
5547  unsigned int row = cfgView->findRow(col, uid);
5548  __COUTV__(uid);
5549  __COUTV__(row);
5550  unsigned int numberOfInstances = atoi(paramArray[0].c_str());
5551  unsigned int depth = atoi(paramArray[1].c_str());
5552  std::string customUID = paramArray.size() == 3
5553  ? StringMacros::decodeURIComponent(paramArray[2])
5554  : std::string("");
5555  __COUTV__(depth);
5556  __COUTV__(numberOfInstances);
5557  __COUTV__(customUID);
5558  if(numberOfInstances > 1000)
5559  {
5560  __SS__ << "Illegal parameters - the maximum number of copy instances is "
5561  "1000. Number of instances provided was "
5562  << numberOfInstances << __E__;
5563  __SS_THROW__;
5564  }
5565  if(!customUID.empty() && numberOfInstances != 1)
5566  {
5567  __SS__ << "A custom UID can only be supplied when copying a single "
5568  "instance. Requested instances: "
5569  << numberOfInstances << __E__;
5570  __SS_THROW__;
5571  }
5572 
5573  std::map<std::string /*modified table*/, TableVersion /* modified version */>
5574  modifiedTablesMap = cfgMgr->getActiveVersions(); // handling copied from
5575  // ConfigurationGUISupervisor::handleFillModifiedTablesXML()
5576 
5577  unsigned int rowsBeforeCopy = cfgView->getNumberOfRows();
5578  ConfigurationSupervisorBase::recursiveCopyTreeUIDNode(xmlOut,
5579  cfgMgr,
5580  modifiedTablesMap,
5581  depth,
5582  depth,
5583  numberOfInstances,
5584  cfgView,
5585  uid);
5586 
5587  // if a custom UID was provided, rename the newly-copied row
5588  if(!customUID.empty() && cfgView->getNumberOfRows() > rowsBeforeCopy)
5589  {
5590  unsigned int uidCol = cfgView->getColUID();
5591  unsigned int newRow = cfgView->getNumberOfRows() - 1;
5592  cfgView->setValueAsString(customUID, newRow, uidCol);
5593  xmlOut.addTextElementToData("CopiedRecordUID", customUID);
5594  }
5595  }
5596  else if(type == "uid" || type == "value" || type == "value-groupid" ||
5597  type == "value-bool" || type == "value-bitmap" || type == "node-comment")
5598  {
5599  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5600  if(!cfgView->setURIEncodedValue(newValue, row, col, author))
5601  {
5602  // no change! so discard
5603  __SUP_SS__ << "Value '" << newValue
5604  << "' is the same as the current value. No need to save "
5605  "change to tree node."
5606  << __E__;
5607  __SS_THROW__;
5608  }
5609  }
5610  else if(type == "link-UID" || type == "link-GroupID")
5611  {
5612  bool isGroup;
5613  std::pair<unsigned int /*link col*/, unsigned int /*link id col*/> linkPair;
5614  if(!cfgView->getChildLink(col, isGroup, linkPair))
5615  {
5616  // not a link ?!
5617  __SUP_SS__ << "Col '" << colName << "' is not a link column." << __E__;
5618  __SS_THROW__;
5619  }
5620 
5621  __SUP_COUT__ << "linkPair " << linkPair.first << "," << linkPair.second
5622  << __E__;
5623 
5624  std::string linkIndex = cfgView->getColumnInfo(col).getChildLinkIndex();
5625 
5626  __SUP_COUT__ << "linkIndex " << linkIndex << __E__;
5627 
5628  // find table value and id value
5629  unsigned int csvIndexStart = 0, csvIndex = newValue.find(',');
5630 
5631  std::string newTable = newValue.substr(csvIndexStart, csvIndex);
5632  csvIndexStart = csvIndex + 1;
5633  csvIndex = newValue.find(',', csvIndexStart);
5634  std::string newLinkId = newValue.substr(
5635  csvIndexStart,
5636  csvIndex -
5637  csvIndexStart); // if no more commas will take the rest of string
5638 
5639  __SUP_COUT__ << "newValue " << newTable << "," << newLinkId << __E__;
5640 
5641  // change target table in two parts
5642  unsigned int row = cfgView->findRow(cfgView->getColUID(), uid);
5643  bool changed = false;
5644  bool needSecondaryChange = (type == "link-GroupID");
5645 
5646  if(!cfgView->setURIEncodedValue(newTable, row, linkPair.first, author))
5647  {
5648  // no change
5649  __SUP_COUT__ << "Value '" << newTable
5650  << "' is the same as the current value." << __E__;
5651  }
5652  else
5653  {
5654  changed = true;
5655  // do NOT need secondary change for UID
5656  }
5657 
5658  std::string originalValue = cfgView->getValueAsString(row, linkPair.second);
5659  if(!cfgView->setURIEncodedValue(newLinkId, row, linkPair.second, author))
5660  {
5661  // no change
5662  __SUP_COUT__ << "Value '" << newLinkId
5663  << "' is the same as the current value." << __E__;
5664  }
5665  else
5666  {
5667  if(!changed)
5668  needSecondaryChange =
5669  true; // if table was unchanged, then need secondary change for
5670  // UID (groupID is already assumed needed)
5671  changed = true;
5672  }
5673 
5674  if(needSecondaryChange) // do secondary changes to child table target
5675  {
5676  bool secondaryChanged = false;
5677  bool defaultIsInGroup =
5678  false; // use to indicate if a recent new member was created
5679 
5680  // first close out main target table
5681  if(!changed) // if no changes throw out new version
5682  {
5683  __SUP_COUT__ << "No changes to primary view. Erasing temporary table."
5684  << __E__;
5685  table->eraseView(temporaryVersion);
5686  }
5687  else // if changes, save it
5688  {
5689  try
5690  {
5691  cfgView->init(); // verify new table (throws runtime_errors)
5692 
5694  xmlOut,
5695  cfgMgr,
5696  tableName,
5697  version,
5698  true /*make temporary*/,
5699  table,
5700  temporaryVersion,
5701  true /*ignoreDuplicates*/); // save
5702  // temporary
5703  // version
5704  // properly
5705  }
5706  catch(std::runtime_error&
5707  e) // erase temporary view before re-throwing error
5708  {
5709  __SUP_COUT__ << "Caught error while editing main table. Erasing "
5710  "temporary version."
5711  << __E__;
5712  table->eraseView(temporaryVersion);
5713  changed = false; // undo changed bool
5714 
5715  // send warning so that, secondary table can still be changed
5716  xmlOut.addTextElementToData(
5717  "Warning",
5718  "Error saving primary tree node! " + std::string(e.what()));
5719  }
5720  }
5721 
5722  // now, onto linked table
5723 
5724  // get the current linked table/version
5725  // check if the value is new
5726  // if new edit value (in a temporary version only)
5727 
5728  __SUP_COUTV__(newValue);
5729  csvIndexStart = csvIndex + 1;
5730  csvIndex = newValue.find(',', csvIndexStart);
5731  version = TableVersion(newValue.substr(
5732  csvIndexStart, csvIndex - csvIndexStart)); // if no more commas will
5733  // take the rest of string
5734 
5735  if(newTable == TableViewColumnInfo::DATATYPE_LINK_DEFAULT)
5736  {
5737  // done, since init was already tested
5738  // the result should be purposely DISCONNECTED link
5739  return;
5740  }
5741 
5742  // get table and activate target version
5743  table = cfgMgr->getTableByName(newTable);
5744  try
5745  {
5746  table->setActiveView(version);
5747  }
5748  catch(...)
5749  {
5750  if(version.isTemporaryVersion())
5751  throw; // if temporary, there is no hope to find lost version
5752 
5753  __SUP_COUT__ << "Failed to find stored version, so attempting to "
5754  "load version: "
5755  << newTable << " v" << version << __E__;
5756  cfgMgr->getVersionedTableByName(newTable, version);
5757  }
5758 
5759  __SUP_COUT__ << newTable << " active version is "
5760  << table->getViewVersion() << __E__;
5761 
5762  if(version != table->getViewVersion())
5763  {
5764  __SUP_SS__;
5765  if(version.isMockupVersion())
5766  ss << "Target table '" << newTable
5767  << "' is likely not a member of the current table group "
5768  << "since the mock-up version was not successfully loaded. "
5769  << "\n\n"
5770  <<
5771  // same as ConfigurationGUI.html L:9833
5772  (std::string("") +
5773  "To add a table to a group, click the group name to go to "
5774  "the " +
5775  "group view, then click 'Add/Remove/Modify Member Tables.' "
5776  "You " +
5777  "can then add or remove tables and save the new group." +
5778  "\n\n" +
5779  "OR!!! Click the following button to add the table '" +
5780  newTable +
5781  "' to the currently active Configuration Group: " +
5782  "<input type='button' style='color:black !important;' " +
5783  "title='Click to add table to the active Configuration "
5784  "Group' " +
5785  "onclick='addTableToConfigurationGroup(\"" + newTable +
5786  "\"); Debug.closeErrorPop();event.stopPropagation();' "
5787  "value='Add Table'>" +
5788  "</input>")
5789  << __E__;
5790  else
5791  ss << "Target table version (" << version
5792  << ") is not the currently active version ("
5793  << table->getViewVersion() << "). Try refreshing the tree."
5794  << __E__;
5795  __SS_THROW__;
5796  }
5797 
5798  // create temporary version for editing
5799  temporaryVersion = table->createTemporaryView(version);
5800 
5801  __SUP_COUT__ << "Created temporary version " << temporaryVersion << __E__;
5802 
5803  cfgView = table->getTemporaryView(temporaryVersion);
5804 
5805  cfgView->init(); // prepare column lookup map
5806 
5807  if(type == "link-UID")
5808  {
5809  // handle UID links slightly differently
5810  // when editing link-UID,.. if specified name does not exist in child
5811  // table, then change the UID in the child table (rename target
5812  // record).
5813  // Otherwise, it is impossible to rename unique links targets in the
5814  // tree-view GUI.
5815 
5816  col = cfgView->getColUID();
5817  __SUP_COUT__ << "target col " << col << __E__;
5818 
5819  unsigned int row = -1;
5820  try
5821  {
5822  row = cfgView->findRow(col, newLinkId);
5823  }
5824  catch(...) // ignore not found error
5825  {
5826  }
5827  if(row == (unsigned int)-1) // if row not found then add a row
5828  {
5829  __SUP_COUT__ << "New link UID '" << newLinkId
5830  << "' was not found, so attempting to change UID of "
5831  "target record '"
5832  << originalValue << "'" << __E__;
5833  try
5834  {
5835  row = cfgView->findRow(col, originalValue);
5836  if(cfgView->setURIEncodedValue(newLinkId, row, col, author))
5837  {
5838  secondaryChanged = true;
5839  __SUP_COUT__ << "Original target record '"
5840  << originalValue << "' was changed to '"
5841  << newLinkId << "'" << __E__;
5842  }
5843  }
5844  catch(...) // ignore not found error
5845  {
5846  __SUP_COUT__ << "Original target record '" << originalValue
5847  << "' not found." << __E__;
5848  }
5849  }
5850  }
5851  else if(type == "link-GroupID")
5852  {
5853  // handle groupID links slightly differently
5854  // have to look at changing link table too!
5855  // if group ID, set all in member list to be members of group
5856 
5857  col = cfgView->getLinkGroupIDColumn(linkIndex);
5858 
5859  __SUP_COUT__ << "target col " << col << __E__;
5860 
5861  // extract vector of members to be
5862  std::vector<std::string> memberUIDs;
5863  do
5864  {
5865  csvIndexStart = csvIndex + 1;
5866  csvIndex = newValue.find(',', csvIndexStart);
5867  memberUIDs.push_back(
5868  newValue.substr(csvIndexStart, csvIndex - csvIndexStart));
5869  __SUP_COUT__ << "memberUIDs: " << memberUIDs.back() << __E__;
5870  } while(csvIndex !=
5871  (unsigned int)std::string::npos); // no more commas
5872 
5873  // for each row,
5874  // check if should be in group
5875  // if should be but is not
5876  // add to group, CHANGE
5877  // if should not be but is
5878  // remove from group, CHANGE
5879  //
5880 
5881  std::string targetUID;
5882  bool shouldBeInGroup;
5883  bool isInGroup;
5884 
5885  for(unsigned int row = 0; row < cfgView->getNumberOfRows(); ++row)
5886  {
5887  targetUID = cfgView->getDataView()[row][cfgView->getColUID()];
5888  __SUP_COUT__ << "targetUID: " << targetUID << __E__;
5889 
5890  shouldBeInGroup = false;
5891  for(unsigned int i = 0; i < memberUIDs.size(); ++i)
5892  if(targetUID == memberUIDs[i])
5893  {
5894  // found in member uid list
5895  shouldBeInGroup = true;
5896  break;
5897  }
5898 
5899  isInGroup = cfgView->isEntryInGroup(row, linkIndex, newLinkId);
5900 
5901  // if should be but is not
5902  if(shouldBeInGroup && !isInGroup)
5903  {
5904  __SUP_COUT__ << "Changed to YES: " << row << __E__;
5905  secondaryChanged = true;
5906 
5907  cfgView->addRowToGroup(row, col, newLinkId);
5908 
5909  } // if should not be but is
5910  else if(!shouldBeInGroup && isInGroup)
5911  {
5912  __SUP_COUT__ << "Changed to NO: " << row << __E__;
5913  secondaryChanged = true;
5914 
5915  cfgView->removeRowFromGroup(row, col, newLinkId);
5916  }
5917  else if(targetUID ==
5918  cfgView
5919  ->getDefaultRowValues()[cfgView->getColUID()] &&
5920  isInGroup)
5921  {
5922  // use to indicate if a recent new member was created
5923  defaultIsInGroup = true;
5924  }
5925  }
5926  } // end (type == "link-GroupID")
5927 
5928  // first close out main target table
5929  if(!secondaryChanged) // if no changes throw out new version
5930  {
5931  __SUP_COUT__
5932  << "No changes to secondary view. Erasing temporary table."
5933  << __E__;
5934  table->eraseView(temporaryVersion);
5935  }
5936  else // if changes, save it
5937  {
5938  try
5939  {
5940  cfgView->init(); // verify new table (throws runtime_errors)
5941 
5943  xmlOut,
5944  cfgMgr,
5945  newTable,
5946  version,
5947  true /*make temporary*/,
5948  table,
5949  temporaryVersion,
5950  true /*ignoreDuplicates*/); // save
5951  // temporary
5952  // version
5953  // properly
5954  }
5955  catch(std::runtime_error&
5956  e) // erase temporary view before re-throwing error
5957  {
5958  __SUP_COUT__ << "Caught error while editing secondary table. "
5959  "Erasing temporary version."
5960  << __E__;
5961  table->eraseView(temporaryVersion);
5962  secondaryChanged = false; // undo changed bool
5963 
5964  // send warning so that, secondary table can still be changed
5965  xmlOut.addTextElementToData(
5966  "Warning",
5967  "Error saving secondary tree node! " + std::string(e.what()));
5968  }
5969  }
5970 
5971  // Block error message if default is in group, assume new member was just
5972  // created. Blocked because its hard to detect if changes were recently
5973  // made (one idea: to check if all other values are defaults, to assume it
5974  // was just created)
5975  if(0 && !changed && !secondaryChanged && !defaultIsInGroup)
5976  {
5977  __SUP_SS__ << "Link to table '" << newTable << "', linkID '"
5978  << newLinkId
5979  << "', and selected group members are the same as the "
5980  "current value. "
5981  << "No need to save changes to tree." << __E__;
5982  __SS_THROW__;
5983  }
5984 
5985  return; // exit since table inits were already tested
5986  }
5987  else if(0 && !changed) // '0 &&' to block error message because sometimes
5988  // things get setup twice depending on the path of the
5989  // user (e.g. when editing links in tree-view)
5990  { // '0 &&' to block error message also because versions are temporary at
5991  // this point anyway, might as well abuse temporary versions
5992  __SUP_SS__ << "Link to table '" << newTable << "' and linkID '"
5993  << newLinkId
5994  << "' are the same as the current values. No need to save "
5995  "change to tree node."
5996  << __E__;
5997  __SS_THROW__;
5998  }
5999  }
6000 
6001  cfgView->init(); // verify new table (throws runtime_errors)
6002  }
6003  catch(...) // erase temporary view before re-throwing error
6004  {
6005  __SUP_COUT__ << "Caught error while editing. Erasing temporary version." << __E__;
6006  table->eraseView(temporaryVersion);
6007  throw;
6008  }
6009 
6011  xmlOut,
6012  cfgMgr,
6013  tableName,
6014  version,
6015  true /*make temporary*/,
6016  table,
6017  temporaryVersion,
6018  true /*ignoreDuplicates*/); // save temporary version properly
6019 } //end handleSaveTreeNodeEditXML()
6020 catch(std::runtime_error& e)
6021 {
6022  __SUP_SS__ << "Error saving tree node! " << e.what() << __E__;
6023  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
6024  xmlOut.addTextElementToData("Error", ss.str());
6025 }
6026 catch(...)
6027 {
6028  __SUP_SS__ << "Unknown Error saving tree node! " << __E__;
6029  try
6030  {
6031  throw;
6032  } //one more try to printout extra info
6033  catch(const std::exception& e)
6034  {
6035  ss << "Exception message: " << e.what();
6036  }
6037  catch(...)
6038  {
6039  }
6040  __SUP_COUT_ERR__ << "\n" << ss.str() << __E__;
6041  xmlOut.addTextElementToData("Error", ss.str());
6042 } //end handleSaveTreeNodeEditXML() catch
6043 
6044 //==============================================================================
6085 void ConfigurationGUISupervisor::handleGetTableXML(HttpXmlDocument& xmlOut,
6086  ConfigurationManagerRW* cfgMgr,
6087  const std::string& tableName,
6088  TableVersion version,
6089  bool allowIllegalColumns /* = false */,
6090  bool getRawData /* = false */,
6091  bool descriptionOnly /* = false */)
6092 try
6093 {
6094  char tmpIntStr[100];
6095  xercesc::DOMElement *parentEl, *subparentEl;
6096 
6097  std::string accumulatedErrors = "";
6098 
6099  if(allowIllegalColumns)
6100  xmlOut.addTextElementToData("allowIllegalColumns", "1");
6101 
6102  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
6103  allowIllegalColumns /* if allowIllegalColumns, then also refresh */,
6104  allowIllegalColumns ? &accumulatedErrors : 0,
6105  tableName); // filter errors by tableName
6106 
6107  TableBase* table = cfgMgr->getTableByName(tableName);
6108 
6109  if(!getRawData)
6110  {
6111  // send all table names along with
6112  // and check for specific version
6113  xmlOut.addTextElementToData("ExistingTableNames",
6114  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
6115  for(auto& configPair : allTableInfo)
6116  {
6117  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
6118  if(configPair.first == tableName && // check that version exists
6119  configPair.second.versions_.find(version) ==
6120  configPair.second.versions_.end())
6121  {
6122  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
6123  version = TableVersion(); // use INVALID
6124  }
6125  }
6126  }
6127 
6128  xmlOut.addTextElementToData("TableName", tableName); // table name
6129  xmlOut.addTextElementToData("TableDescription",
6130  table->getTableDescription()); // table name
6131 
6132  if(descriptionOnly)
6133  return;
6134 
6135  // existing table versions
6136  if(!getRawData)
6137  {
6138  // get version aliases for translation
6139  std::map<
6140  std::string /*table name*/,
6141  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
6142  versionAliases;
6143  try
6144  {
6145  // use whatever backbone is currently active
6146  versionAliases = cfgMgr->getVersionAliases();
6147  for(const auto& aliases : versionAliases)
6148  for(const auto& alias : aliases.second)
6149  __SUP_COUTT__ << "ALIAS: " << aliases.first << " " << alias.first
6150  << " ==> " << alias.second << __E__;
6151  }
6152  catch(const std::runtime_error& e)
6153  {
6154  __SUP_COUT__ << "Could not get backbone information for version aliases: "
6155  << e.what() << __E__;
6156  }
6157 
6158  auto tableIterator = versionAliases.find(tableName);
6159 
6160  parentEl = xmlOut.addTextElementToData("TableVersions", "");
6161  //add lo and hi spans, instead of each individual value
6162  TableVersion lo, hi;
6163  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
6164  {
6165  //Steps:
6166  // 1. check for version aliases
6167  // 2. if version aliases, leave as standalone version
6168  // else stack spans of versions for faster xml transfer
6169  std::vector<std::string> aliases;
6170  if(tableIterator != versionAliases.end())
6171  {
6172  // check if this version has one or many aliases
6173  for(const auto& aliasPair : tableIterator->second)
6174  {
6175  if(v == aliasPair.second)
6176  {
6177  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
6178  << aliasPair.first << __E__;
6179  aliases.push_back(aliasPair.first);
6180  }
6181  }
6182  }
6183  //now have version aliases or not
6184 
6185  if(aliases.size()) //keep versions with aliases standalone
6186  {
6187  __SUP_COUT__ << "Handling version w/aliases" << __E__;
6188  }
6189  else if(lo.version() ==
6190  TableVersion::INVALID) //establish start of potential span
6191  {
6192  hi = lo = v;
6193  continue;
6194  }
6195  else if(hi.version() + 1 == v.version()) //span is growing
6196  {
6197  hi = v;
6198  continue;
6199  }
6200  //else jump by more than one, so close out span
6201 
6202  if(lo.version() != TableVersion::INVALID)
6203  {
6204  if(lo == hi) //single value
6205  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6206  else //span
6207  xmlOut.addTextElementToParent(
6208  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6209  }
6210  hi = lo = v.version();
6211 
6212  if(versionAliases.size()) //keep versions with aliases standalone
6213  {
6214  subparentEl =
6215  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
6216  for(const auto& alias : aliases)
6217  xmlOut.addTextElementToParent("VersionAlias", alias, subparentEl);
6218  hi = lo = TableVersion::INVALID; //invalidate for fresh start
6219  } //end version alias handling
6220 
6221  } //end version loop
6222 
6223  if(lo.version() != TableVersion::INVALID) //check if last one to do!
6224  {
6225  if(lo == hi) //single value
6226  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6227  else //span
6228  xmlOut.addTextElementToParent(
6229  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6230  }
6231  } //end existing table version handling
6232 
6233  // table columns and then rows (from table view)
6234 
6235  // get view pointer
6236  TableView* tableViewPtr;
6237  if(version.isInvalid()) // use mock-up
6238  {
6239  tableViewPtr = table->getMockupViewP();
6240  }
6241  else // use view version
6242  {
6243  try
6244  {
6245  // locally accumulate 'manageable' errors getting the version to avoid
6246  // reverting to mockup
6247  std::string localAccumulatedErrors = "";
6248  tableViewPtr =
6249  cfgMgr
6250  ->getVersionedTableByName(tableName,
6251  version,
6252  allowIllegalColumns /*looseColumnMatching*/,
6253  &localAccumulatedErrors,
6254  getRawData)
6255  ->getViewP();
6256 
6257  if(getRawData)
6258  {
6259  xmlOut.addTextElementToData("TableRawData",
6260  tableViewPtr->getSourceRawData());
6261 
6262  const std::set<std::string>& srcColNames =
6263  tableViewPtr->getSourceColumnNames();
6264  for(auto& srcColName : srcColNames)
6265  xmlOut.addTextElementToData("ColumnHeader", srcColName);
6266 
6267  if(!version.isTemporaryVersion())
6268  {
6269  // if version is temporary, view is already ok
6270  table->eraseView(
6271  version); // clear so that the next get will fill the table
6272  tableViewPtr = cfgMgr
6274  tableName,
6275  version,
6276  allowIllegalColumns /*looseColumnMatching*/,
6277  &localAccumulatedErrors,
6278  false /* getRawData */)
6279  ->getViewP();
6280  }
6281  } // end rawData handling
6282 
6283  if(localAccumulatedErrors != "")
6284  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
6285  }
6286  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
6287  {
6288  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
6289  << "... defaulting to mock-up! " << __E__;
6290  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
6291 
6292  __SUP_COUT_ERR__ << "\n" << ss.str();
6293  version = TableVersion();
6294  tableViewPtr = table->getMockupViewP();
6295 
6296  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6297  }
6298  catch(...) // default to mock-up for fail-safe in GUI editor
6299  {
6300  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
6301  << "... defaulting to mock-up! "
6302  << "(You may want to try again to see what was partially loaded "
6303  "into cache before failure. "
6304  << "If you think, the failure is due to a column name change, "
6305  << "you can also try to Copy the failing view to the new column "
6306  "names using "
6307  << "'Copy and Move' functionality.)" << __E__;
6308  try
6309  {
6310  throw;
6311  } //one more try to printout extra info
6312  catch(const std::exception& e)
6313  {
6314  ss << "Exception message: " << e.what();
6315  }
6316  catch(...)
6317  {
6318  }
6319 
6320  __SUP_COUT_ERR__ << "\n" << ss.str();
6321  version = TableVersion();
6322  tableViewPtr = table->getMockupViewP();
6323 
6324  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6325  }
6326  }
6327  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
6328 
6329  if(getRawData)
6330  return; // no need to go further for rawData handling
6331 
6332  // get 'columns' of view
6333  xercesc::DOMElement* choicesParentEl;
6334  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
6335 
6336  std::vector<TableViewColumnInfo> colInfo = tableViewPtr->getColumnsInfo();
6337 
6338  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
6339  {
6340  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
6341  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
6342  xmlOut.addTextElementToParent(
6343  "ColumnDataType", colInfo[i].getDataType(), parentEl);
6344 
6345  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6346  // whereas DefaultRowValue are the defaults for the mockup
6347  xmlOut.addTextElementToParent(
6348  "ColumnDefaultValue", colInfo[i].getDefaultValue(), parentEl);
6349 
6350  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
6351  // add data choices if necessary
6352  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
6353  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
6354  colInfo[i].isChildLink())
6355  {
6356  for(auto& choice : colInfo[i].getDataChoices())
6357  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
6358  }
6359 
6360  xmlOut.addTextElementToParent(
6361  "ColumnMinValue", colInfo[i].getMinValue(), parentEl);
6362  xmlOut.addTextElementToParent(
6363  "ColumnMaxValue", colInfo[i].getMaxValue(), parentEl);
6364  }
6365 
6366  // verify mockup columns after columns are posted to xmlOut
6367  try
6368  {
6369  if(version.isInvalid())
6370  tableViewPtr->init();
6371  }
6372  catch(std::runtime_error& e)
6373  {
6374  // append accumulated errors, because they may be most useful
6375  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
6376  }
6377  catch(...)
6378  {
6379  throw;
6380  }
6381 
6382  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
6383 
6384  for(int r = 0; r < (int)tableViewPtr->getNumberOfRows(); ++r)
6385  {
6386  sprintf(tmpIntStr, "%d", r);
6387  xercesc::DOMElement* tmpParentEl =
6388  xmlOut.addTextElementToParent("Row", tmpIntStr, parentEl);
6389 
6390  for(int c = 0; c < (int)tableViewPtr->getNumberOfColumns(); ++c)
6391  {
6392  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
6393  {
6394  std::string timeAsString;
6395  tableViewPtr->getValue(timeAsString, r, c);
6396  xmlOut.addTextElementToParent("Entry", timeAsString, tmpParentEl);
6397  }
6398  else
6399  xmlOut.addTextElementToParent(
6400  "Entry", tableViewPtr->getDataView()[r][c], tmpParentEl);
6401  }
6402  }
6403 
6404  // add "other" fields associated with configView
6405  xmlOut.addTextElementToData("TableComment", tableViewPtr->getComment());
6406  xmlOut.addTextElementToData("TableAuthor", tableViewPtr->getAuthor());
6407  xmlOut.addTextElementToData("TableCreationTime",
6408  std::to_string(tableViewPtr->getCreationTime()));
6409  xmlOut.addTextElementToData("TableLastAccessTime",
6410  std::to_string(tableViewPtr->getLastAccessTime()));
6411 
6412  // add to xml the default row values
6413  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6414  // whereas DefaultRowValue are the defaults for the mockup
6415  std::vector<std::string> defaultRowValues =
6416  table->getMockupViewP()->getDefaultRowValues();
6417  // don't give author and time.. force default author, let JS fill time
6418  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
6419  {
6420  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
6421  }
6422 
6423  const std::set<std::string> srcColNames = tableViewPtr->getSourceColumnNames();
6424 
6425  if(accumulatedErrors != "") // add accumulated errors to xmlOut
6426  {
6427  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
6428  "perhaps you can ignore this, ") +
6429  "but please note the following warnings:\n" + accumulatedErrors)
6430  << __E__;
6431  __SUP_COUT_ERR__ << ss.str();
6432  xmlOut.addTextElementToData("TableWarnings", ss.str());
6433  }
6434  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
6435  // interface source)
6436  (srcColNames.size() != tableViewPtr->getNumberOfColumns() ||
6437  tableViewPtr->getSourceColumnMismatch() !=
6438  0)) // check for column size mismatch
6439  {
6440  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
6441  << ":v" << version << ". Please see the details below:\n\n"
6442  << tableViewPtr->getMismatchColumnInfo();
6443 
6444  __SUP_COUT__ << "\n" << ss.str();
6445  xmlOut.addTextElementToData("TableWarnings", ss.str());
6446  }
6447 
6448 } // end handleGetTableXML()
6449 catch(std::runtime_error& e)
6450 {
6451  __SUP_SS__ << "Error getting table view!\n\n " << e.what() << __E__;
6452  __SUP_COUT_ERR__ << ss.str();
6453  xmlOut.addTextElementToData("Error", ss.str());
6454 }
6455 catch(...)
6456 {
6457  __SUP_SS__ << "Error getting table view!\n\n " << __E__;
6458  try
6459  {
6460  throw;
6461  } //one more try to printout extra info
6462  catch(const std::exception& e)
6463  {
6464  ss << "Exception message: " << e.what();
6465  }
6466  catch(...)
6467  {
6468  }
6469  __SUP_COUT_ERR__ << ss.str();
6470  xmlOut.addTextElementToData("Error", ss.str());
6471 } // end handleGetTableXML() catch
6472 
6473 //==============================================================================
6480 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
6481  std::string username, bool refresh)
6482 {
6483  uint64_t sessionIndex =
6484  0; // make session by username for now! (may never want to change back)
6485 
6486  std::stringstream ssMapKey;
6487  ssMapKey << username << ":" << sessionIndex;
6488  std::string mapKey = ssMapKey.str();
6489  __SUP_COUTT__ << "Using Config Session " << mapKey
6490  << " ... Total Session Count: " << userConfigurationManagers_.size()
6491  << " refresh=" << refresh << __E__;
6492 
6493  time_t now = time(0);
6494 
6495  // create new table mgr if not one for active session index
6496 
6497  if(TTEST(1))
6498  {
6499  for(auto& pair : userConfigurationManagers_)
6500  __SUP_COUTTV__(pair.first);
6501  }
6502 
6503  const std::string preLoadCfgMgrName = ":0";
6504  if(userConfigurationManagers_.size() == 1 &&
6505  userConfigurationManagers_.find(preLoadCfgMgrName) !=
6506  userConfigurationManagers_.end())
6507  {
6508  __SUP_COUT__ << "Using pre-loaded Configuration Manager. time=" << time(0) << " "
6509  << clock() << " Setting author from "
6510  << userConfigurationManagers_.at(preLoadCfgMgrName)->getUsername()
6511  << " to " << username << __E__;
6512  userConfigurationManagers_[mapKey] =
6513  userConfigurationManagers_.at(preLoadCfgMgrName);
6514  userLastUseTime_[mapKey] = userLastUseTime_.at(preLoadCfgMgrName);
6515  //also set author!
6516  userConfigurationManagers_.at(mapKey)->setUsername(username);
6517  }
6518 
6519  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
6520  {
6521  __SUP_COUT__ << "Creating new Configuration Manager. time=" << time(0) << " "
6522  << clock() << __E__;
6523  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
6524 
6525  // update table info for each new configuration manager
6526  // IMPORTANTLY this also fills all configuration manager pointers with instances,
6527  // so we are not dealing with changing pointers later on
6528  userConfigurationManagers_[mapKey]->getAllTableInfo(
6529  true /* refresh */, // load empty instance of everything important
6530  0 /* accumulatedWarnings */,
6531  "" /* errorFilterName */,
6532  true /* getGroupKeys */,
6533  false /* getGroupInfo */,
6534  true /* initializeActiveGroups */);
6535  }
6536  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
6537  {
6538  __SUP_SS__ << "Fatal error managing user sessions! Check the logs for "
6539  "Configuration Interface failure."
6540  << __E__;
6541  __SUP_COUT_ERR__ << "\n" << ss.str();
6542  __SS_THROW__;
6543  }
6544  else if(
6545  refresh ||
6546  (now - userLastUseTime_[mapKey]) >
6547  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should refresh all table info
6548  {
6549  __SUP_COUT__ << "Refreshing all table info." << __E__;
6550  userConfigurationManagers_[mapKey]->getAllTableInfo(
6551  true /* refresh */,
6552  0 /* accumulatedWarnings */,
6553  "" /* errorFilterName */,
6554  false /* getGroupKeys */,
6555  false /* getGroupInfo */,
6556  true /* initializeActiveGroups */);
6557  }
6558  __SUP_COUTT__ << "Configuration Manager for author="
6559  << userConfigurationManagers_[mapKey]->getUsername()
6560  << " ready. time=" << time(0) << " " << clock() << " runTimeSeconds()="
6561  << userConfigurationManagers_[mapKey]->runTimeSeconds() << __E__;
6562 
6563  // update active sessionIndex last use time
6564  userLastUseTime_[mapKey] = now;
6565 
6566  // check for stale sessions and remove them (so table user maps do not grow forever)
6567  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
6568  it != userLastUseTime_.end();
6569  ++it)
6570  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
6571  {
6572  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
6573  << __E__;
6574  delete userConfigurationManagers_[it->first]; // call destructor
6575  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
6576  {
6577  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
6578  << __E__;
6579  __SUP_COUT_ERR__ << "\n" << ss.str();
6580  __SS_THROW__;
6581  }
6582  userLastUseTime_.erase(it); // erase by iterator
6583 
6584  it =
6585  userLastUseTime_
6586  .begin(); // fail safe.. reset it, to avoid trying to understand what happens with the next iterator
6587  }
6588 
6589  return userConfigurationManagers_[mapKey];
6590 } //end refreshUserSession()
6591 
6592 //==============================================================================
6597 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
6598  ConfigurationManagerRW* cfgMgr,
6599  std::string& tableName)
6600 {
6601  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
6602  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
6603  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
6604  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6605  << __E__;
6606  else
6607  {
6608  __SUP_COUT_ERR__ << ("Error renaming file to " +
6609  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6610  << __E__;
6611 
6612  xmlOut.addTextElementToData(
6613  "Error",
6614  ("Error renaming Table Info File to " +
6615  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
6616  return;
6617  }
6618 
6619  // reload all with refresh to remove new table
6620  cfgMgr->getAllTableInfo(true /* refresh */);
6621 } // end handleDeleteTableInfoXML()
6622 
6623 //==============================================================================
6630 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
6631  HttpXmlDocument& xmlOut,
6632  ConfigurationManagerRW* cfgMgr,
6633  std::string& tableName,
6634  const std::string& data,
6635  const std::string& tableDescription,
6636  const std::string& columnChoicesCSV,
6637  bool allowOverwrite)
6638 {
6639  // create all caps name and validate
6640  // only allow alpha-numeric names with "Table" at end
6641  std::string capsName;
6642  try
6643  {
6644  capsName = TableBase::convertToCaps(tableName, true);
6645  }
6646  catch(std::runtime_error& e)
6647  { // error! non-alpha
6648  xmlOut.addTextElementToData("Error", e.what());
6649  return;
6650  }
6651 
6652  if(!allowOverwrite)
6653  {
6654  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
6655  if(fp)
6656  {
6657  fclose(fp);
6658  xmlOut.addTextElementToData("TableName", tableName);
6659  xmlOut.addTextElementToData("OverwriteError", "1");
6660  xmlOut.addTextElementToData(
6661  "Error",
6662  "File already exists! ('" +
6663  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
6664  return;
6665  }
6666  }
6667 
6668  __SUP_COUT__ << "capsName=" << capsName << __E__;
6669  __SUP_COUT__ << "tableName=" << tableName << __E__;
6670  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
6671  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
6672 
6673  // create preview string to validate column info before write to file
6674  std::stringstream outss;
6675 
6676  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
6677  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
6678  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
6679  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
6680  outss << "\t\t\t<VIEW Name=\"" << capsName
6681  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
6682  << "\">\n";
6683 
6684  // each column is represented by 4 fields or 6
6685  // - type, name, dataType, defaultValue, minValue, maxValue
6686 
6687  std::istringstream columnChoicesISS(columnChoicesCSV);
6688  std::string columnChoicesString;
6689  std::string columnDefaultValue, columnMinValue, columnMaxValue;
6690  std::vector<std::string> columnParameters;
6691  std::vector<std::string> columnData =
6692  StringMacros::getVectorFromString(data, {';'} /*delimiter*/);
6693 
6694  for(unsigned int c = 0; c < columnData.size() - 1; ++c)
6695  {
6696  columnParameters =
6697  StringMacros::getVectorFromString(columnData[c], {','} /*delimiter*/);
6698  __COUT__ << "Column #" << c << ": "
6699  << StringMacros::vectorToString(columnParameters) << __E__;
6700  for(unsigned int p = 0; p < columnParameters.size(); ++p)
6701  {
6702  __COUT__ << "\t Parameter #" << p << ": " << columnParameters[p] << __E__;
6703  }
6704  __COUT__ << "\t creating the new xml" << __E__;
6705 
6706  std::string& columnType = columnParameters[0];
6707  std::string& columnName = columnParameters[1];
6708  std::string& columnDataType = columnParameters[2];
6709  const std::string columnStorageName =
6710  TableBase::convertToCaps(columnName); // now caps;
6711 
6712  outss << "\t\t\t\t<COLUMN Type=\"";
6713  outss << columnType;
6714  outss << "\" \t Name=\"";
6715  outss << columnName;
6716  outss << "\" \t StorageName=\"";
6717  try
6718  {
6719  outss << columnStorageName;
6720  }
6721  catch(std::runtime_error& e)
6722  { // error! non-alpha
6723  xmlOut.addTextElementToData(
6724  "Error",
6725  std::string("For column name '") + columnName + "' - " + e.what());
6726  return;
6727  }
6728  outss << "\" \t DataType=\"";
6729  outss << columnDataType;
6730 
6731  columnDefaultValue = StringMacros::decodeURIComponent(columnParameters[3]);
6732 
6733  std::string* columnDefaultValuePtr = nullptr;
6734  if(columnDefaultValue !=
6735  TableViewColumnInfo::getDefaultDefaultValue(columnType, columnDataType))
6736  {
6737  __SUP_COUT__ << "FOUND user spec'd default value '" << columnDefaultValue
6738  << "'" << __E__;
6739  outss << "\" \t DefaultValue=\"";
6740  outss << columnParameters[3];
6741  columnDefaultValuePtr = &columnParameters[3];
6742  }
6743  getline(columnChoicesISS, columnChoicesString, ';');
6744  outss << "\" \t DataChoices=\"";
6745  outss << columnChoicesString;
6746 
6747  std::string* columnMinValuePtr = nullptr;
6748  std::string* columnMaxValuePtr = nullptr;
6749 
6750  if(columnParameters.size() > 4 &&
6751  columnDataType == TableViewColumnInfo::DATATYPE_NUMBER)
6752  {
6753  columnMinValue = StringMacros::decodeURIComponent(columnParameters[4]);
6754  if(columnMinValue != "")
6755  {
6756  if(columnMinValue !=
6758  {
6759  __SUP_COUT__ << "FOUND user spec'd min value '" << columnParameters[4]
6760  << "'" << __E__;
6763  {
6764  __SS__ << "Inavlid user spec'd min value '" << columnParameters[4]
6765  << "' which evaluates to '" << columnMinValue
6766  << "' and is not a valid number. The minimum value must "
6767  "be a number (environment variables and math "
6768  "operations are allowed)."
6769  << __E__;
6770  __SS_THROW__;
6771  }
6772  outss << "\" \t MinValue=\"" << columnParameters[4];
6773  columnMinValuePtr = &columnParameters[4];
6774  }
6775  }
6776 
6777  columnMaxValue = StringMacros::decodeURIComponent(columnParameters[5]);
6778  if(columnMaxValue != "")
6779  {
6780  if(columnMaxValue !=
6782  {
6783  __SUP_COUT__ << "FOUND user spec'd max value = " << columnMaxValue
6784  << __E__;
6787  {
6788  __SS__ << "Inavlid user spec'd max value '" << columnParameters[5]
6789  << "' which evaluates to '" << columnMaxValue
6790  << "' and is not a valid number. The maximum value must "
6791  "be a number (environment variables and math "
6792  "operations are allowed)."
6793  << __E__;
6794  __SS_THROW__;
6795  }
6796  outss << "\" \t MaxValue=\"" << columnParameters[5];
6797  columnMaxValuePtr = &columnParameters[5];
6798  }
6799  }
6800  }
6801 
6802  //validate each column before saving a bad table file
6803  try
6804  {
6805  TableViewColumnInfo testCol(columnType,
6806  columnName,
6807  columnStorageName,
6808  columnDataType,
6809  columnDefaultValuePtr,
6810  columnChoicesString,
6811  columnMinValuePtr,
6812  columnMaxValuePtr,
6813  nullptr //capturedExceptionString
6814  );
6815  }
6816  catch(const std::runtime_error& e)
6817  {
6818  __SS__ << "Error identified with Column #" << c << ": \n" << e.what();
6819  __SS_THROW__;
6820  }
6821 
6822  outss << "\"/>\n";
6823  }
6824 
6825  outss << "\t\t\t</VIEW>\n";
6826  outss << "\t\t</TABLE>\n";
6827  outss << "\t</ROOT>\n";
6828 
6829  __SUP_COUT__ << outss.str() << __E__;
6830 
6831  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
6832  if(!fp)
6833  {
6834  xmlOut.addTextElementToData("Error",
6835  "Failed to open destination Table Info file:" +
6836  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
6837  return;
6838  }
6839 
6840  fprintf(fp, "%s", outss.str().c_str());
6841  fclose(fp);
6842 
6843  __SUP_COUT_INFO__ << "Finished saving Table Info for '" << tableName
6844  << ".' Looking for errors in all table column info..." << __E__;
6845 
6846  // reload all table info with refresh AND reset to pick up possibly new table
6847  // check for errors related to this tableName
6848  std::string accumulatedErrors = "";
6849  cfgMgr->getAllTableInfo(true /* refresh */, &accumulatedErrors, tableName);
6850 
6851  // if errors associated with this table name stop and report
6852  if(accumulatedErrors != "")
6853  {
6854  __SUP_SS__ << ("The new version of the '" + tableName +
6855  "' table column info was saved, however errors were detected "
6856  "reading back the table '" +
6857  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
6858  << __E__;
6859 
6860  __SUP_COUT_ERR__ << ss.str() << __E__;
6861  xmlOut.addTextElementToData("Error", ss.str());
6862 
6863  return;
6864  }
6865 
6866  // return the new table info
6867  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
6868 
6869  // After save, debug all table column info
6870  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
6871 
6872  // give a print out of currently illegal table column info
6873  for(const auto& cfgInfo : allTableInfo)
6874  {
6875  try
6876  {
6877  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
6878  }
6879  catch(std::runtime_error& e)
6880  {
6881  __SUP_COUT_WARN__ << "\n\n##############################################\n"
6882  << "Error identified in column info of table '"
6883  << cfgInfo.first << "':\n\n"
6884  << e.what() << "\n\n"
6885  << __E__;
6886  }
6887  }
6888 } // end handleSaveTableInfoXML()
6889 
6890 //==============================================================================
6898 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
6899  HttpXmlDocument& xmlOut,
6900  ConfigurationManagerRW* cfgMgr,
6901  const std::string& groupAliasCSV,
6902  const std::string& groupNameCSV,
6903  const std::string& groupKeyCSV,
6904  const std::string& author)
6905 try
6906 {
6907  cfgMgr->loadConfigurationBackbone();
6908  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6909 
6910  const std::string groupAliasesTableName =
6911  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6912  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6913  {
6914  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
6915  << __E__;
6916  xmlOut.addTextElementToData("Error", ss.str());
6917  return;
6918  }
6919 
6920  // put all old backbone versions in xmlOut
6921  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6922  for(auto& memberName : backboneMembers)
6923  {
6924  __SUP_COUT__ << "activeVersions[\"" << memberName
6925  << "\"]=" << activeVersions[memberName] << __E__;
6926 
6927  xmlOut.addTextElementToData("oldBackboneName", memberName);
6928  xmlOut.addTextElementToData("oldBackboneVersion",
6929  activeVersions[memberName].toString());
6930  }
6931 
6932  // make a temporary version from active view
6933  // modify the chosen groupAlias row
6934  // save as new version
6935 
6936  TableBase* table = cfgMgr->getTableByName(groupAliasesTableName);
6937  TableVersion originalVersion = activeVersions[groupAliasesTableName];
6938  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
6939 
6940  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6941  bool isDifferent = false;
6942 
6943  try
6944  {
6945  TableView* configView = table->getTemporaryView(temporaryVersion);
6946 
6947  unsigned int col = configView->findCol("GroupKeyAlias");
6948  unsigned int ccol = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
6949  unsigned int ncol = configView->findCol("GroupName");
6950  unsigned int kcol = configView->findCol("GroupKey");
6951 
6952  // only make a new version if we are changing compared to active backbone
6953  std::vector<std::string> groupAliases =
6954  StringMacros::getVectorFromString(groupAliasCSV);
6955  std::vector<std::string> groupNames =
6956  StringMacros::getVectorFromString(groupNameCSV);
6957  std::vector<std::string> groupKeys =
6958  StringMacros::getVectorFromString(groupKeyCSV);
6959  __SUP_COUTV__(StringMacros::vectorToString(groupAliases));
6960  __SUP_COUTV__(StringMacros::vectorToString(groupNames));
6961  __SUP_COUTV__(StringMacros::vectorToString(groupKeys));
6962 
6963  size_t i = 0;
6964  for(const auto& groupAlias : groupAliases)
6965  {
6966  if(groupAlias == "" || groupNames[i] == "" || groupKeys[i] == "")
6967  {
6968  //skip empty aliases
6969  __SUP_COUT_WARN__ << "Empty alias parameter found [" << i << "] = {"
6970  << groupAlias << ", " << groupNames[i] << "("
6971  << groupKeys[i] << ")}" << __E__;
6972  ++i;
6973  continue;
6974  }
6975 
6976  bool localIsDifferent = false;
6977  const std::string& groupName = groupNames[i];
6978  const TableGroupKey groupKey(groupKeys[i]);
6979  ++i;
6980 
6981  unsigned int row = -1;
6982  // find groupAlias row
6983  try
6984  {
6985  row = configView->findRow(col, groupAlias);
6986  }
6987  catch(...) // ignore not found error
6988  {
6989  }
6990 
6991  if(row == (unsigned int)-1) // if row not found then add a row
6992  {
6993  localIsDifferent = true;
6994  row = configView->addRow();
6995 
6996  // set all columns in new row
6997  configView->setValue(
6998  "This Group Alias was automatically setup by the server.", row, ccol);
6999  configView->setValue(groupAlias, row, col);
7000  }
7001 
7002  __SUP_COUT__ << "\t\t row: " << row << __E__;
7003 
7004  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
7005  << configView->getDataView()[row][ncol] << __E__;
7006  if(groupName != configView->getDataView()[row][ncol])
7007  {
7008  configView->setValue(groupName, row, ncol);
7009  localIsDifferent = true;
7010  }
7011 
7012  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
7013  << configView->getDataView()[row][kcol] << __E__;
7014  if(groupKey.toString() != configView->getDataView()[row][kcol])
7015  {
7016  configView->setValue(groupKey.toString(), row, kcol);
7017  localIsDifferent = true;
7018  }
7019 
7020  if(localIsDifferent) // set author/time of new record if different
7021  {
7022  configView->setValue(
7023  author,
7024  row,
7025  configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7026  configView->setValue(
7027  time(0),
7028  row,
7029  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7030  isDifferent = true;
7031  }
7032  } //end group alias modify loop
7033  }
7034  catch(...)
7035  {
7036  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
7037 
7038  // delete temporaryVersion
7039  table->eraseView(temporaryVersion);
7040  throw;
7041  }
7042 
7043  TableVersion newAssignedVersion;
7044  if(isDifferent) // make new version if different
7045  {
7046  __SUP_COUT__ << "\t\t**************************** Save as new table version"
7047  << __E__;
7048 
7049  // save or find equivalent
7051  xmlOut,
7052  cfgMgr,
7053  table->getTableName(),
7054  originalVersion,
7055  false /*makeTemporary*/,
7056  table,
7057  temporaryVersion,
7058  false /*ignoreDuplicates*/,
7059  true /*lookForEquivalent*/);
7060  }
7061  else // use existing version
7062  {
7063  __SUP_COUT__
7064  << "\t\t**************************** Using the existing table version"
7065  << __E__;
7066 
7067  // delete temporaryVersion
7068  table->eraseView(temporaryVersion);
7069  newAssignedVersion = activeVersions[groupAliasesTableName];
7070 
7071  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
7072  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7073  }
7074 
7075  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
7076 } //end handleSetGroupAliasInBackboneXML()
7077 catch(std::runtime_error& e)
7078 {
7079  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << e.what() << __E__;
7080  __SUP_COUT_ERR__ << ss.str();
7081  xmlOut.addTextElementToData("Error", ss.str());
7082 }
7083 catch(...)
7084 {
7085  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << __E__;
7086  try
7087  {
7088  throw;
7089  } //one more try to printout extra info
7090  catch(const std::exception& e)
7091  {
7092  ss << "Exception message: " << e.what();
7093  }
7094  catch(...)
7095  {
7096  }
7097  __SUP_COUT_ERR__ << ss.str();
7098  xmlOut.addTextElementToData("Error", ss.str());
7099 } //end handleSetGroupAliasInBackboneXML() catch
7100 
7101 //==============================================================================
7109 void ConfigurationGUISupervisor::handleSetTableAliasInBackboneXML(
7110  HttpXmlDocument& xmlOut,
7111  ConfigurationManagerRW* cfgMgr,
7112  const std::string& tableAlias,
7113  const std::string& tableName,
7114  TableVersion version,
7115  const std::string& author)
7116 try
7117 {
7118  cfgMgr->loadConfigurationBackbone();
7119  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7120 
7121  const std::string versionAliasesTableName =
7122  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7123  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7124  {
7125  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7126  << __E__;
7127  xmlOut.addTextElementToData("Error", ss.str());
7128  return;
7129  }
7130 
7131  // put all old backbone versions in xmlOut
7132  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7133  for(auto& memberName : backboneMembers)
7134  {
7135  __SUP_COUT__ << "activeVersions[\"" << memberName
7136  << "\"]=" << activeVersions[memberName] << __E__;
7137 
7138  xmlOut.addTextElementToData("oldBackboneName", memberName);
7139  xmlOut.addTextElementToData("oldBackboneVersion",
7140  activeVersions[memberName].toString());
7141  }
7142 
7143  // make a temporary version from active view
7144  // modify the chosen versionAlias row
7145  // save as new version
7146 
7147  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7148  TableVersion originalVersion = activeVersions[versionAliasesTableName];
7149  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
7150 
7151  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7152 
7153  bool isDifferent = false;
7154 
7155  try
7156  {
7157  TableView* configView = table->getTemporaryView(temporaryVersion);
7158 
7159  unsigned int col;
7160  unsigned int col2 = configView->findCol("VersionAlias");
7161  unsigned int col3 = configView->findCol("TableName");
7162 
7163  // only make a new version if we are changing compared to active backbone
7164 
7165  unsigned int row = -1;
7166  // find tableName, versionAlias pair
7167  // NOTE: only accept the first pair, repeats are ignored.
7168  try
7169  {
7170  unsigned int tmpRow = -1;
7171  do
7172  { // start looking from beyond last find
7173  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
7174  } while(configView->getDataView()[tmpRow][col2] != tableAlias);
7175  // at this point the first pair was found! (else exception was thrown)
7176  row = tmpRow;
7177  }
7178  catch(...)
7179  {
7180  }
7181  if(row == (unsigned int)-1) // if row not found then add a row
7182  {
7183  isDifferent = true;
7184  row = configView->addRow();
7185 
7186  // set all columns in new row
7187  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7188  configView->setValue(
7189  std::string("Entry was added by server in ") +
7190  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7191  row,
7192  col);
7193 
7194  col = configView->findCol("VersionAliasUID");
7195  configView->setValue(
7196  tableName.substr(0, tableName.rfind("Table")) + tableAlias, row, col);
7197 
7198  configView->setValue(tableAlias, row, col2);
7199  configView->setValue(tableName, row, col3);
7200  }
7201 
7202  __SUP_COUT__ << "\t\t row: " << row << __E__;
7203 
7204  col = configView->findCol("Version");
7205  __SUP_COUT__ << "\t\t version: " << version << " vs "
7206  << configView->getDataView()[row][col] << __E__;
7207  if(version.toString() != configView->getDataView()[row][col])
7208  {
7209  configView->setValue(version.toString(), row, col);
7210  isDifferent = true;
7211  }
7212 
7213  if(isDifferent) // set author/time of new version if different
7214  {
7215  configView->setValue(
7216  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7217  configView->setValue(
7218  time(0),
7219  row,
7220  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7221  }
7222  }
7223  catch(...)
7224  {
7225  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
7226 
7227  // delete temporaryVersion
7228  table->eraseView(temporaryVersion);
7229  throw;
7230  }
7231 
7232  TableVersion newAssignedVersion;
7233  if(isDifferent) // make new version if different
7234  {
7235  __SUP_COUT__ << "\t\t**************************** Save as new table version"
7236  << __E__;
7237 
7239  xmlOut,
7240  cfgMgr,
7241  table->getTableName(),
7242  originalVersion,
7243  false /*makeTemporary*/,
7244  table,
7245  temporaryVersion,
7246  false /*ignoreDuplicates*/,
7247  true /*lookForEquivalent*/);
7248  }
7249  else // use existing version
7250  {
7251  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7252  << __E__;
7253 
7254  // delete temporaryVersion
7255  table->eraseView(temporaryVersion);
7256  newAssignedVersion = activeVersions[versionAliasesTableName];
7257 
7258  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7259  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7260  }
7261 
7262  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
7263 } // end handleSetVersionAliasInBackboneXML()
7264 catch(std::runtime_error& e)
7265 {
7266  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7267  __SUP_COUT_ERR__ << ss.str();
7268  xmlOut.addTextElementToData("Error", ss.str());
7269 }
7270 catch(...)
7271 {
7272  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7273  try
7274  {
7275  throw;
7276  } //one more try to printout extra info
7277  catch(const std::exception& e)
7278  {
7279  ss << "Exception message: " << e.what();
7280  }
7281  catch(...)
7282  {
7283  }
7284  __SUP_COUT_ERR__ << ss.str();
7285  xmlOut.addTextElementToData("Error", ss.str());
7286 } // end handleSetVersionAliasInBackboneXML() catch
7287 
7288 //==============================================================================
7294 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
7295  HttpXmlDocument& xmlOut,
7296  ConfigurationManagerRW* cfgMgr,
7297  const std::string& versionAlias,
7298  const std::string& groupName,
7299  TableGroupKey groupKey,
7300  const std::string& author)
7301 try
7302 {
7303  cfgMgr->loadConfigurationBackbone();
7304  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7305 
7306  const std::string versionAliasesTableName =
7307  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7308  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7309  {
7310  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7311  << __E__;
7312  xmlOut.addTextElementToData("Error", ss.str());
7313  return;
7314  }
7315 
7316  // put all old backbone versions in xmlOut
7317  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7318  for(auto& memberName : backboneMembers)
7319  {
7320  __SUP_COUT__ << "activeVersions[\"" << memberName
7321  << "\"]=" << activeVersions[memberName] << __E__;
7322 
7323  xmlOut.addTextElementToData("oldBackboneName", memberName);
7324  xmlOut.addTextElementToData("oldBackboneVersion",
7325  activeVersions[memberName].toString());
7326  }
7327 
7328  // make a temporary version from active view
7329  // modify the chosen versionAlias row
7330  // save as new version
7331 
7332  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7333  TableVersion temporaryVersion =
7334  table->createTemporaryView(activeVersions[versionAliasesTableName]);
7335 
7336  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7337 
7338  TableView* configView = table->getTemporaryView(temporaryVersion);
7339 
7340  // only make a new version if we are changing compared to active backbone
7341  bool isDifferent = false;
7342 
7343  // get member names and versions
7344  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7345  try
7346  {
7347  cfgMgr->loadTableGroup(groupName,
7348  groupKey,
7349  false /*doActivate*/,
7350  &memberMap,
7351  0,
7352  0,
7353  0,
7354  0,
7355  0, // defaults
7356  true /*doNotLoadMember*/);
7357  }
7358  catch(...)
7359  {
7360  xmlOut.addTextElementToData(
7361  "Error",
7362  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
7363  "\" can not be retrieved!");
7364  return;
7365  }
7366 
7367  unsigned int col;
7368  unsigned int col2 = configView->findCol("VersionAlias");
7369  unsigned int col3 = configView->findCol("TableName");
7370 
7371  for(auto& memberPair : memberMap)
7372  {
7373  bool thisMemberIsDifferent = false;
7374  unsigned int row = -1;
7375 
7376  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
7377  << memberPair.second << " to " << versionAlias << __E__;
7378 
7379  // find tableName, versionAlias pair
7380  // NOTE: only accept the first pair, repeats are ignored.
7381  try
7382  {
7383  unsigned int tmpRow = -1;
7384  do
7385  { // start looking from beyond last find
7386  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
7387  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
7388  // at this point the first pair was found! (else exception was thrown)
7389  row = tmpRow;
7390  }
7391  catch(...)
7392  {
7393  }
7394  if(row == (unsigned int)-1) // if row not found then add a row
7395  {
7396  thisMemberIsDifferent = true;
7397  row = configView->addRow();
7398 
7399  // set all columns in new row
7400  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7401  configView->setValue(
7402  std::string("Entry was added by server in ") +
7403  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7404  row,
7405  col);
7406 
7407  col = configView->getColUID();
7408  configView->setValue(
7409  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
7410  versionAlias,
7411  row,
7412  col);
7413 
7414  configView->setValue(versionAlias, row, col2);
7415  configView->setValue(memberPair.first, row, col3);
7416  }
7417 
7418  col = configView->findCol("Version");
7419 
7420  if(memberPair.second.toString() != configView->getDataView()[row][col])
7421  {
7422  configView->setValue(memberPair.second.toString(), row, col);
7423  thisMemberIsDifferent = true;
7424  }
7425 
7426  if(thisMemberIsDifferent) // change author and time if row is different
7427  {
7428  configView->setValue(
7429  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7430  configView->setValue(
7431  time(0),
7432  row,
7433  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7434  }
7435 
7436  if(thisMemberIsDifferent)
7437  isDifferent = true;
7438  }
7439 
7440  // configView->print();
7441 
7442  TableVersion newAssignedVersion;
7443  if(isDifferent) // make new version if different
7444  {
7445  __SUP_COUT__ << "\t\t**************************** Save v" << temporaryVersion
7446  << " as new table version" << __E__;
7447 
7448  newAssignedVersion =
7449  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
7450  }
7451  else // use existing version
7452  {
7453  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7454  << __E__;
7455 
7456  // delete temporaryVersion
7457  table->eraseView(temporaryVersion);
7458  newAssignedVersion = activeVersions[versionAliasesTableName];
7459  }
7460 
7461  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7462  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7463  __SUP_COUT__ << "\t\t Resulting Version: " << newAssignedVersion << __E__;
7464 } // end handleAliasGroupMembersInBackboneXML()
7465 catch(std::runtime_error& e)
7466 {
7467  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7468  __SUP_COUT_ERR__ << ss.str();
7469  xmlOut.addTextElementToData("Error", ss.str());
7470 }
7471 catch(...)
7472 {
7473  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7474  try
7475  {
7476  throw;
7477  } //one more try to printout extra info
7478  catch(const std::exception& e)
7479  {
7480  ss << "Exception message: " << e.what();
7481  }
7482  catch(...)
7483  {
7484  }
7485  __SUP_COUT_ERR__ << ss.str();
7486  xmlOut.addTextElementToData("Error", ss.str());
7487 } // end handleAliasGroupMembersInBackboneXML() catch
7488 
7489 //==============================================================================
7500 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
7501  ConfigurationManagerRW* cfgMgr)
7502 {
7503  cfgMgr->loadConfigurationBackbone();
7504  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7505 
7506  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
7507  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
7508  {
7509  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
7510  << groupAliasesTableName
7511  << " is a required member of the Backbone table group."
7512  << "\n\nLikely you need to activate a valid Backbone table group."
7513  << __E__;
7514  __SUP_COUT__ << ss.str(); // just output findings, and return empty xml to avoid
7515  // infinite error loops in GUI
7516  // xmlOut.addTextElementToData("Error", ss.str());
7517  return;
7518  }
7519  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
7520  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
7521  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
7522  xmlOut.addTextElementToData("GroupAliasesTableVersion",
7523  activeVersions[groupAliasesTableName].toString());
7524 
7525  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7526  cfgMgr->getNode(groupAliasesTableName).getChildren();
7527 
7528  const int numOfThreads = ConfigurationManager::PROCESSOR_COUNT / 2;
7529  __SUP_COUT__ << " PROCESSOR_COUNT " << ConfigurationManager::PROCESSOR_COUNT
7530  << " ==> " << numOfThreads << " threads for alias group loads." << __E__;
7531 
7532  if(numOfThreads < 2) // no multi-threading
7533  {
7534  std::string groupName, groupKey, groupComment, groupAuthor, groupCreateTime,
7535  groupType;
7536  for(auto& aliasNodePair : aliasNodePairs)
7537  {
7538  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7539  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7540 
7541  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7542  xmlOut.addTextElementToData("GroupName", groupName);
7543  xmlOut.addTextElementToData("GroupKey", groupKey);
7544  xmlOut.addTextElementToData(
7545  "AliasComment",
7546  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7547  .getValueAsString());
7548 
7549  // get group comment
7550  groupComment =
7551  ConfigurationManager::UNKNOWN_INFO; // clear just in case failure
7552  groupType = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7553  try
7554  {
7555  cfgMgr->loadTableGroup(groupName,
7556  TableGroupKey(groupKey),
7557  false /* doActivate */,
7558  0 /* groupMembers */,
7559  0 /* progressBar */,
7560  0 /* accumulatedWarnings */,
7561  &groupComment,
7562  &groupAuthor,
7563  &groupCreateTime,
7564  true /*doNotLoadMembers*/,
7565  &groupType);
7566  }
7567  catch(...)
7568  {
7569  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7570  << groupKey << ")' to extract group comment and type."
7571  << __E__;
7572  }
7573  xmlOut.addTextElementToData("GroupComment", groupComment);
7574  xmlOut.addTextElementToData("GroupType", groupType);
7575  } // end alias pair loop
7576  }
7577  else //multi-threading
7578  {
7579  int threadsLaunched = 0;
7580  int foundThreadIndex = 0;
7581  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
7582  for(int i = 0; i < numOfThreads; ++i)
7583  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
7584 
7585  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
7586  std::string groupName, groupKey;
7587 
7588  for(auto& aliasNodePair : aliasNodePairs)
7589  {
7590  //make temporary group info for thread
7591  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
7592 
7593  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7594  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7595 
7596  if(threadsLaunched >= numOfThreads)
7597  {
7598  //find availableThreadIndex
7599  foundThreadIndex = -1;
7600  while(foundThreadIndex == -1)
7601  {
7602  for(int i = 0; i < numOfThreads; ++i)
7603  if(*(threadDone[i]))
7604  {
7605  foundThreadIndex = i;
7606  break;
7607  }
7608  if(foundThreadIndex == -1)
7609  {
7610  __SUP_COUTT__ << "Waiting for available thread..." << __E__;
7611  usleep(10000);
7612  }
7613  } //end thread search loop
7614  threadsLaunched = numOfThreads - 1;
7615  }
7616  __SUP_COUTT__ << "Starting load group thread... " << groupName << "("
7617  << groupKey << ")" << __E__;
7618  *(threadDone[foundThreadIndex]) = false;
7619 
7620  std::thread(
7621  [](ConfigurationManagerRW* theCfgMgr,
7622  std::string theGroupName,
7623  ots::TableGroupKey theGroupKey,
7624  std::shared_ptr<ots::GroupInfo> theGroupInfo,
7625  std::shared_ptr<std::atomic<bool>> theThreadDone) {
7627  theGroupName,
7628  theGroupKey,
7629  theGroupInfo,
7630  theThreadDone);
7631  },
7632  cfgMgr,
7633  groupName,
7634  TableGroupKey(groupKey),
7635  sharedGroupInfoPtrs.back(),
7636  threadDone[foundThreadIndex])
7637  .detach();
7638 
7639  ++threadsLaunched;
7640  ++foundThreadIndex;
7641 
7642  } //end alias group thread loop
7643 
7644  //check for all threads done
7645  do
7646  {
7647  foundThreadIndex = -1;
7648  for(int i = 0; i < numOfThreads; ++i)
7649  if(!*(threadDone[i]))
7650  {
7651  foundThreadIndex = i;
7652  break;
7653  }
7654  if(foundThreadIndex != -1)
7655  {
7656  __SUP_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
7657  << __E__;
7658  usleep(10000);
7659  }
7660  } while(foundThreadIndex != -1); //end thread done search loop
7661 
7662  //threads done now, so copy group info
7663  size_t i = 0;
7664  for(auto& aliasNodePair : aliasNodePairs)
7665  {
7666  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7667  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7668 
7669  if(groupKey != sharedGroupInfoPtrs[i]->getLatestKey().toString())
7670  {
7671  __SUP_SS__ << "Error loading group information for the group alias '"
7672  << aliasNodePair.first << "' mapping to group '" << groupName
7673  << "(" << groupKey << ")" << __E__;
7674  __SUP_SS_THROW__;
7675  }
7676 
7677  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7678  xmlOut.addTextElementToData("GroupName", groupName);
7679  xmlOut.addTextElementToData(
7680  "GroupKey", sharedGroupInfoPtrs[i]->getLatestKey().toString());
7681  xmlOut.addTextElementToData(
7682  "AliasComment",
7683  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7684  .getValueAsString());
7685 
7686  xmlOut.addTextElementToData(
7687  "GroupComment", sharedGroupInfoPtrs[i]->getLatestKeyGroupComment());
7688  xmlOut.addTextElementToData(
7689  "GroupAuthor", sharedGroupInfoPtrs[i]->getLatestKeyGroupAuthor());
7690  xmlOut.addTextElementToData(
7691  "GroupCreationTime",
7692  sharedGroupInfoPtrs[i]->getLatestKeyGroupCreationTime());
7693  xmlOut.addTextElementToData(
7694  "GroupType", sharedGroupInfoPtrs[i]->getLatestKeyGroupTypeString());
7695  // xmlOut.addTextElementToData("GroupMemberMap", sharedGroupInfoPtrs[i]->latestKeyMemberMap_);
7696  ++i;
7697  } //end copy group info loop
7698 
7699  } //end multi-thread handling
7700 } // end handleGroupAliasesXML
7701 
7702 //==============================================================================
7713 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
7714  ConfigurationManagerRW* cfgMgr)
7715 {
7716  cfgMgr->loadConfigurationBackbone();
7717  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7718 
7719  std::string versionAliasesTableName =
7720  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7721  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7722  {
7723  __SUP_SS__ << "Active version of VersionAliases missing!"
7724  << "Make sure you have a valid active Backbone Group." << __E__;
7725  xmlOut.addTextElementToData("Error", ss.str());
7726  return;
7727  }
7728  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
7729  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
7730  xmlOut.addTextElementToData("VersionAliasesVersion",
7731  activeVersions[versionAliasesTableName].toString());
7732 
7733  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7734  cfgMgr->getNode(versionAliasesTableName).getChildren();
7735 
7736  for(auto& aliasNodePair : aliasNodePairs)
7737  {
7738  // note : these are column names in the versionAliasesTableName table
7739  // VersionAlias, TableName, Version, CommentDescription
7740  xmlOut.addTextElementToData(
7741  "VersionAlias",
7742  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
7743  xmlOut.addTextElementToData(
7744  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
7745  xmlOut.addTextElementToData(
7746  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
7747  xmlOut.addTextElementToData(
7748  "Comment",
7749  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7750  .getValueAsString());
7751  }
7752 } // end handleVersionAliasesXML()
7753 
7754 //==============================================================================
7760 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
7761  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& tableList)
7762 {
7763  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7764  std::string name, versionStr;
7765  auto c = tableList.find(',', 0);
7766  auto i = c;
7767  i = 0; // auto used to get proper index/length type
7768  while(c < tableList.length())
7769  {
7770  // add the table name and version pair to the map
7771  name = tableList.substr(i, c - i);
7772  i = c + 1;
7773  c = tableList.find(',', i);
7774  if(c == std::string::npos) // missing version list entry?!
7775  {
7776  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
7777  __SUP_COUT_ERR__ << "\n" << ss.str();
7778  xmlOut.addTextElementToData("Error", ss.str());
7779  return;
7780  }
7781 
7782  versionStr = tableList.substr(i, c - i);
7783  i = c + 1;
7784  c = tableList.find(',', i);
7785 
7786  memberMap[name] = TableVersion(versionStr);
7787  }
7788 
7789  std::string groupTypeString = "";
7790  // try to determine type, dont report errors, just mark ots::GroupType::UNKNOWN_TYPE
7791  try
7792  {
7793  // determine the type of the table group
7794  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
7795  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7796  }
7797  catch(std::runtime_error& e)
7798  {
7799  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
7800  __SUP_COUT__ << "\n" << ss.str();
7801  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7802  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7803  }
7804  catch(...)
7805  {
7806  __SUP_SS__ << "Table group has invalid type! " << __E__;
7807  try
7808  {
7809  throw;
7810  } //one more try to printout extra info
7811  catch(const std::exception& e)
7812  {
7813  ss << "Exception message: " << e.what();
7814  }
7815  catch(...)
7816  {
7817  }
7818  __SUP_COUT__ << "\n" << ss.str();
7819  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7820  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7821  }
7822 } //end handleGetTableGroupTypeXML()
7823 
7824 //==============================================================================
7840 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
7841  ConfigurationManagerRW* cfgMgr,
7842  bool returnMembers)
7843 {
7844  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7845  // use xmlOut.dataSs_ since there is no need for escape the string and it can be a huge data block to escape and recursively print
7846  // xercesc::DOMElement* parentEl;
7847 
7848  // get all group info from cache (if no cache, get from interface)
7849 
7850  if(!cfgMgr->getAllGroupInfo().size() ||
7851  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() == "" ||
7852  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() ==
7853  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN ||
7854  ( //if active Context group type is not defined, then refresh
7855  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE) !=
7856  TableGroupKey::INVALID &&
7857  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7858  ConfigurationManager::GroupType::CONTEXT_TYPE)) !=
7859  cfgMgr->getAllGroupInfo().end() &&
7860  (cfgMgr->getAllGroupInfo()
7861  .at(cfgMgr->getActiveGroupName(
7862  ConfigurationManager::GroupType::CONTEXT_TYPE))
7863  .getLatestKeyGroupTypeString() == "" ||
7864  cfgMgr->getAllGroupInfo()
7865  .at(cfgMgr->getActiveGroupName(
7866  ConfigurationManager::GroupType::CONTEXT_TYPE))
7867  .getLatestKeyGroupTypeString() ==
7868  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)) ||
7869  ( //if active Config group type is not defined, then refresh
7870  cfgMgr->getActiveGroupKey(
7871  ConfigurationManager::GroupType::CONFIGURATION_TYPE) !=
7872  TableGroupKey::INVALID &&
7873  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7874  ConfigurationManager::GroupType::CONFIGURATION_TYPE)) !=
7875  cfgMgr->getAllGroupInfo().end() &&
7876  (cfgMgr->getAllGroupInfo()
7877  .at(cfgMgr->getActiveGroupName(
7878  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7879  .getLatestKeyGroupTypeString() == "" ||
7880  cfgMgr->getAllGroupInfo()
7881  .at(cfgMgr->getActiveGroupName(
7882  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7883  .getLatestKeyGroupTypeString() ==
7884  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)))
7885  {
7886  __SUP_COUT__
7887  << "Group Info cache appears empty or stale. Attempting to regenerate..."
7888  << __E__;
7889  cfgMgr->getAllTableInfo(true /*refresh*/,
7890  0 /* accumulatedWarnings */,
7891  "" /* errorFilterName */,
7892  true /* getGroupKeys */,
7893  true /* getGroupInfo */,
7894  true /* initializeActiveGroups */);
7895  }
7896 
7897  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
7898 
7899  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7900 
7901  std::string groupName;
7902  std::string groupString, groupTypeString, groupComment, groupCreationTime,
7903  groupAuthor;
7904  for(auto& groupInfo : allGroupInfo)
7905  {
7906  groupName = groupInfo.first;
7907 
7908  //get group info and force update of groupKeys from DB Interface cache if possible
7909  cfgMgr->getGroupInfo(groupName, true /* attemptToReloadKeys */);
7910 
7911  if(groupInfo.second.getKeys().size() == 0)
7912  {
7913  __SUP_COUT__ << "Group name '" << groupName
7914  << "' found, but no keys so ignoring." << __E__;
7915  continue;
7916  }
7917 
7918  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7919  xmlOut.dataSs_ << "<TableGroupKey value='" << groupInfo.second.getLatestKey()
7920  << "'/>" << __E__;
7921 
7922  // trusting the cache!
7923  xmlOut.dataSs_ << "<TableGroupType value='"
7924  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
7925  << __E__;
7926  xmlOut.dataSs_ << "<TableGroupComment value='"
7928  groupInfo.second.getLatestKeyGroupComment(),
7929  true /* allowWhiteSpace */)
7930  << "'/>" << __E__;
7931  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7932  << groupInfo.second.getLatestKeyGroupAuthor() << "'/>" << __E__;
7933  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7934  << groupInfo.second.getLatestKeyGroupCreationTime() << "'/>"
7935  << __E__;
7936 
7937  if(returnMembers)
7938  {
7939  // parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
7940  xmlOut.dataSs_ << "<TableGroupMembers value=''>" << __E__;
7941 
7942  for(auto& memberPair : groupInfo.second.getLatestKeyMemberMap())
7943  {
7944  xmlOut.dataSs_ << "\t<MemberName value='" << memberPair.first << "'/>"
7945  << __E__;
7946  xmlOut.dataSs_ << "\t<MemberVersion value='" << memberPair.second << "'/>"
7947  << __E__;
7948 
7949  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
7950  // xmlOut.addTextElementToParent(
7951  // "MemberVersion", memberPair.second.toString(), parentEl);
7952  }
7953  xmlOut.dataSs_ << "</TableGroupMembers>" << __E__;
7954  } // end if returnMembers
7955 
7956  // add other group keys to xml for this group name
7957  // but just empty members (not displayed anyway)
7958  for(auto& keyInSet : groupInfo.second.getKeys())
7959  {
7960  if(keyInSet == groupInfo.second.getLatestKey())
7961  continue; // skip the lastest
7962 
7963  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7964  xmlOut.dataSs_ << "<TableGroupKey value='" << keyInSet << "'/>" << __E__;
7965  // xmlOut.addTextElementToData("TableGroupName", groupName);
7966  // xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
7967 
7968  // TODO -- make loadingHistoricalInfo an input parameter
7969  bool loadingHistoricalInfo = false;
7970  if(loadingHistoricalInfo)
7971  {
7972  groupComment = ""; // clear just in case failure
7973  try
7974  {
7975  cfgMgr->loadTableGroup(groupName,
7976  keyInSet,
7977  0,
7978  0,
7979  0,
7980  0,
7981  &groupComment,
7982  0,
7983  0, // mostly defaults
7984  true /*doNotLoadMembers*/,
7985  &groupTypeString);
7986  }
7987  catch(...)
7988  {
7989  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7990  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7991  << keyInSet
7992  << ")' to extract group comment and type." << __E__;
7993  }
7994 
7995  xmlOut.dataSs_ << "<TableGroupType value='" << groupTypeString << "'/>"
7996  << __E__;
7997  xmlOut.dataSs_ << "<TableGroupComment value='"
7998  << StringMacros::escapeString(groupComment,
7999  true /* allowWhiteSpace */)
8000  << "'/>" << __E__;
8001  xmlOut.dataSs_ << "<TableGroupAuthor value='" << groupAuthor << "'/>"
8002  << __E__;
8003  xmlOut.dataSs_ << "<TableGroupCreationTime value='" << groupCreationTime
8004  << "'/>" << __E__;
8005  // xmlOut.addTextElementToData("TableGroupType", groupTypeString);
8006  // xmlOut.addTextElementToData("TableGroupComment", groupComment);
8007  // xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
8008  // xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
8009  }
8010  else
8011  {
8012  // just use guess that historical groups are of same type
8013  xmlOut.dataSs_ << "<TableGroupType value='"
8014  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
8015  << __E__;
8016  //leave place holder comment,author,time for javascript parsing
8017  xmlOut.dataSs_ << "<TableGroupComment value='"
8018  << ""
8019  << "'/>" << __E__;
8020  xmlOut.dataSs_ << "<TableGroupAuthor value='"
8021  << ""
8022  << "'/>" << __E__;
8023  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
8024  << ""
8025  << "'/>" << __E__;
8026  }
8027 
8028  if(returnMembers)
8029  {
8030  //need to add empty group members, event for historical groups, for easier Javascript extraction
8031  xmlOut.dataSs_ << "<TableGroupMembers/>" << __E__;
8032  // xmlOut.addTextElementToData("TableGroupMembers", "");
8033  }
8034 
8035  } // end other key loop
8036  __SUP_COUTT__ << groupName << " runtime=" << cfgMgr->runTimeSeconds() << __E__;
8037  } // end primary group loop
8038  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
8039 } // end handleTableGroupsXML()
8040 
8041 //==============================================================================
8060 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
8061  ConfigurationManagerRW* cfgMgr,
8062  const std::string& filterStartTimeStr,
8063  const std::string& filterEndTimeStr,
8064  const std::string& filterMode)
8065 {
8066  time_t filterStartTime = 0;
8067  time_t filterEndTime = 0;
8068  if(filterStartTimeStr != "")
8069  {
8070  try
8071  {
8072  filterStartTime = static_cast<time_t>(std::stoll(filterStartTimeStr));
8073  }
8074  catch(const std::exception& e)
8075  {
8076  __SUP_SS__ << "Error parsing startTime parameter: " << e.what() << __E__;
8077  __SUP_COUT_ERR__ << "\n" << ss.str();
8078  xmlOut.addTextElementToData("Error", ss.str());
8079  return;
8080  }
8081  }
8082  if(filterEndTimeStr != "")
8083  {
8084  try
8085  {
8086  filterEndTime = static_cast<time_t>(std::stoll(filterEndTimeStr));
8087  }
8088  catch(const std::exception& e)
8089  {
8090  __SUP_SS__ << "Error parsing endTime parameter: " << e.what() << __E__;
8091  __SUP_COUT_ERR__ << "\n" << ss.str();
8092  xmlOut.addTextElementToData("Error", ss.str());
8093  return;
8094  }
8095  }
8096  if(filterStartTime != 0 && filterEndTime != 0 && filterStartTime > filterEndTime)
8097  {
8098  __SUP_SS__ << "Invalid time range: startTime (" << filterStartTime
8099  << ") must be <= endTime (" << filterEndTime << ")." << __E__;
8100  __SUP_SS_THROW__;
8101  }
8102  if(filterMode != "created" && filterMode != "loaded")
8103  {
8104  __SUP_SS__ << "Invalid filterMode parameter '" << filterMode
8105  << ".' Expected 'created' or 'loaded.'" << __E__;
8106  __SUP_COUT_ERR__ << "\n" << ss.str();
8107  xmlOut.addTextElementToData("Error", ss.str());
8108  return;
8109  }
8110  // diagnostics: track where the time is going
8111  const auto diagStartTime = std::chrono::steady_clock::now();
8112  auto diagElapsedSec = [](const std::chrono::steady_clock::time_point& start) {
8113  return std::chrono::duration<double>(std::chrono::steady_clock::now() - start)
8114  .count();
8115  };
8116 
8117  if(cfgMgr->getAllGroupInfo().size() == 0 || cfgMgr->getActiveVersions().size() == 0)
8118  {
8119  __SUP_COUT__ << "Table Info cache appears empty. Attempting to regenerate."
8120  << __E__;
8121  cfgMgr->getAllTableInfo(true /*refresh*/,
8122  0 /* accumulatedWarnings */,
8123  "" /* errorFilterName */,
8124  false /* getGroupKeys */,
8125  false /* getGroupInfo */,
8126  true /* initializeActiveGroups */);
8127  __SUP_COUT__ << "getAllTableInfo() regenerate took "
8128  << diagElapsedSec(diagStartTime) << " s" << __E__;
8129  }
8130 
8131  xercesc::DOMElement* parentEl;
8132  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
8133 
8134  // construct specially ordered table name set
8135  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
8136  for(const auto& tablePair : allTableInfo)
8137  orderedTableSet.emplace(tablePair.first);
8138 
8139  // std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
8140 
8141  __SUP_COUT__ << "# of tables to consider: " << allTableInfo.size() << __E__;
8142 
8143  const auto diagAliasStartTime = std::chrono::steady_clock::now();
8144  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
8145  cfgMgr->getVersionAliases();
8146 
8147  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << " (took "
8148  << diagElapsedSec(diagAliasStartTime) << " s)" << __E__;
8149 
8150  if(filterStartTime != 0 && filterEndTime != 0 && filterMode == "created")
8151  {
8152  // parallel pre-warm of the process-wide creation time cache, so the
8153  // filter loop below gets immediate cache hits (only versions not yet in
8154  // the disk-persisted cache are loaded, so this is cheap after first use)
8155  const auto diagPreloadStartTime = std::chrono::steady_clock::now();
8156  cfgMgr->preloadVersionCreationTimes();
8157  __SUP_COUT__ << "Version creation time cache pre-warm took "
8158  << diagElapsedSec(diagPreloadStartTime) << " s" << __E__;
8159  }
8160 
8161  // diagnostics accumulated over the table loop
8162  size_t diagNumTablesFound = 0; //tables with at least one version listed
8163  size_t diagNumVersionsConsidered = 0;
8164  size_t diagNumVersionsMatched = 0;
8165  size_t diagNumTimeLookups = 0;
8166  double diagTimeLookupSec = 0; //cumulative time in creation/load time lookups
8167  double diagSlowestTableSec = 0;
8168  std::string diagSlowestTableName = "";
8169  size_t diagTableCount = 0;
8170 
8171  for(const auto& orderedTableName : orderedTableSet) // while(it !=
8172  // allTableInfo.end())
8173  {
8174  std::map<std::string, TableInfo>::const_iterator it =
8175  allTableInfo.find(orderedTableName);
8176  if(it == allTableInfo.end())
8177  {
8178  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
8179  << __E__;
8180  __SS_THROW__;
8181  }
8182 
8183  // for each table name
8184  // get existing version keys
8185 
8186  // add system table name
8187  xmlOut.addTextElementToData("TableName", it->first);
8188  parentEl = xmlOut.addTextElementToData("TableVersions", "");
8189 
8190  // include aliases for this table (if the versions exist)
8191  if(versionAliases.find(it->first) != versionAliases.end())
8192  for(auto& aliasVersion : versionAliases[it->first])
8193  if(it->second.versions_.find(aliasVersion.second) !=
8194  it->second.versions_
8195  .end()) //Note : scratch version is always an alias ==> ConfigurationManager::SCRATCH_VERSION_ALIAS)
8196  xmlOut.addTextElementToParent(
8197  "Version",
8198  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
8199  parentEl);
8200 
8201  // get all table versions for the current table
8202  // except skip scratch version
8203  // for speed, group versions into spans:
8204  //======
8206  auto vSpanToXML = [&diagNumVersionsConsidered,
8207  &diagNumVersionsMatched,
8208  &diagNumTimeLookups,
8209  &diagTimeLookupSec](auto const& sortedKeys,
8210  auto& xmlOut,
8211  auto& configEl,
8212  const std::string& tableName,
8213  ConfigurationManagerRW* cfgMgr,
8214  const time_t filterStartTime,
8215  const time_t filterEndTime,
8216  const std::string& filterMode) {
8217  //add lo and hi spans, instead of each individual value
8218  size_t lo = -1, hi = -1;
8219  bool allVersionsFiltered = true;
8220  for(auto& keyInOrder : sortedKeys)
8221  {
8222  //skip scratch version
8223  if(keyInOrder.isScratchVersion())
8224  continue;
8225 
8226  ++diagNumVersionsConsidered;
8227 
8228  if(filterStartTime != 0 && filterEndTime != 0)
8229  {
8230  const auto diagLookupStartTime = std::chrono::steady_clock::now();
8231  try
8232  {
8233  // Note: neither helper stamps the version's lastAccessTime
8234  // ("Last Load"), so filtering does not corrupt Last Load times.
8235  // - "created" times are immutable and cached, so each version is
8236  // loaded from the database at most once per process lifetime.
8237  // - "loaded" times are in-memory only; 0 (never loaded by this
8238  // process, or evicted from cache) falls outside any range.
8239  time_t tableVersionTime =
8240  filterMode == "loaded"
8241  ? cfgMgr->getVersionLastAccessTime(tableName, keyInOrder)
8242  : cfgMgr->getVersionCreationTime(tableName, keyInOrder);
8243 
8244  ++diagNumTimeLookups;
8245  diagTimeLookupSec +=
8246  std::chrono::duration<double>(
8247  std::chrono::steady_clock::now() - diagLookupStartTime)
8248  .count();
8249 
8250  if(tableVersionTime < filterStartTime ||
8251  tableVersionTime > filterEndTime)
8252  {
8253  //Note: trace-level to avoid log flooding (one line per
8254  // filtered version can be thousands of lines)
8255  __COUTT__ << "Table '" << tableName << "' version v"
8256  << keyInOrder << " " << filterMode
8257  << " time is outside the filter range, so "
8258  "skipping."
8259  << __E__;
8260  continue;
8261  }
8262  }
8263  catch(const std::runtime_error&)
8264  {
8265  ++diagNumTimeLookups;
8266  diagTimeLookupSec +=
8267  std::chrono::duration<double>(
8268  std::chrono::steady_clock::now() - diagLookupStartTime)
8269  .count();
8270  __COUT__ << "Failed to get " << filterMode << " time for table '"
8271  << tableName << "' version v" << keyInOrder
8272  << ", so skipping." << __E__;
8273  continue;
8274  }
8275  }
8276 
8277  allVersionsFiltered = false;
8278  ++diagNumVersionsMatched;
8279 
8280  if(lo == size_t(-1)) //establish start of potential span
8281  {
8282  hi = lo = keyInOrder.version();
8283  continue;
8284  }
8285  else if(hi + 1 == keyInOrder.version()) //span is growing
8286  {
8287  hi = keyInOrder.version();
8288  continue;
8289  }
8290  //else jump by more than one, so close out span
8291 
8292  if(lo == hi) //single value
8293  xmlOut.addNumberElementToParent("Version", lo, configEl);
8294  else //span
8295  xmlOut.addTextElementToParent(
8296  "Version",
8297  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8298  configEl);
8299  hi = lo = keyInOrder.version();
8300  }
8301 
8302  if(lo != size_t(-1)) //check if last one to do!
8303  {
8304  if(lo == hi) //single value
8305  xmlOut.addNumberElementToParent("Version", lo, configEl);
8306  else //span
8307  xmlOut.addTextElementToParent(
8308  "Version",
8309  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8310  configEl);
8311  }
8312  return allVersionsFiltered;
8313  }; //end local lambda vSpanToXML()
8314 
8315  const auto diagTableStartTime = std::chrono::steady_clock::now();
8316  if(vSpanToXML(it->second.versions_,
8317  xmlOut,
8318  parentEl,
8319  it->first,
8320  cfgMgr,
8321  filterStartTime,
8322  filterEndTime,
8323  filterMode) &&
8324  filterStartTime != 0 && filterEndTime != 0)
8325  {
8326  // Only remove tables when a time filter is active; without a filter,
8327  // tables with no persistent versions (e.g. definition-only tables)
8328  // should still be listed, as in the unfiltered Table View.
8329  // Remove the pair we just added: TableVersions then TableName.
8330  unsigned int childCount = xmlOut.getChildrenCount();
8331  if(childCount >= 2)
8332  {
8333  xmlOut.removeDataElement(childCount - 1);
8334  xmlOut.removeDataElement(childCount - 2);
8335  }
8336  }
8337  else
8338  ++diagNumTablesFound;
8339 
8340  // diagnostics: report slow tables and periodic progress
8341  double diagTableSec = diagElapsedSec(diagTableStartTime);
8342  if(diagTableSec > diagSlowestTableSec)
8343  {
8344  diagSlowestTableSec = diagTableSec;
8345  diagSlowestTableName = it->first;
8346  }
8347  if(diagTableSec > 1.0)
8348  __SUP_COUT__ << "Slow table filter: '" << it->first << "' with "
8349  << it->second.versions_.size() << " versions took "
8350  << diagTableSec << " s" << __E__;
8351  ++diagTableCount;
8352  if(diagTableCount % 50 == 0)
8353  __SUP_COUT__ << "getTables filter progress: " << diagTableCount << " of "
8354  << orderedTableSet.size() << " tables in "
8355  << diagElapsedSec(diagStartTime) << " s (" << diagNumTimeLookups
8356  << " version time lookups taking " << diagTimeLookupSec
8357  << " s so far)" << __E__;
8358 
8359  } // end table loop
8360 
8361  // always return the table and version counts, even if everything was filtered out
8362  xmlOut.addTextElementToData("NumberOfTablesConsidered",
8363  std::to_string(allTableInfo.size()));
8364  xmlOut.addTextElementToData("NumberOfTablesFound",
8365  std::to_string(diagNumTablesFound));
8366  xmlOut.addTextElementToData("NumberOfVersionsConsidered",
8367  std::to_string(diagNumVersionsConsidered));
8368  xmlOut.addTextElementToData("NumberOfVersionsFound",
8369  std::to_string(diagNumVersionsMatched));
8370 
8371  __SUP_COUT__ << "getTables filter summary: mode=" << filterMode << " range=["
8372  << filterStartTime << "," << filterEndTime << "]"
8373  << " tables considered=" << allTableInfo.size()
8374  << " found=" << diagNumTablesFound
8375  << "; versions considered=" << diagNumVersionsConsidered
8376  << " matched=" << diagNumVersionsMatched << "; " << diagNumTimeLookups
8377  << " version time lookups took " << diagTimeLookupSec
8378  << " s; slowest table '" << diagSlowestTableName << "' took "
8379  << diagSlowestTableSec << " s; total " << diagElapsedSec(diagStartTime)
8380  << " s" << __E__;
8381 
8382 } // end handleTablesXML()
8383 
8384 //==============================================================================
8391 void ConfigurationGUISupervisor::handleGetArtdaqNodeRecordsXML(
8392  HttpXmlDocument& xmlOut,
8393  ConfigurationManagerRW* cfgMgr,
8394  const std::string& modifiedTables)
8395 {
8396  __COUT__ << "Retrieving artdaq nodes..." << __E__;
8397 
8398  // setup active tables based on active groups and modified tables
8399  setupActiveTablesXML(
8400  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8401 
8402  std::map<std::string /*type*/,
8403  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8404  nodeTypeToObjectMap;
8405  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8406  subsystemObjectMap;
8407 
8408  std::vector<std::string /*property*/> artdaqSupervisorInfo;
8409 
8410  std::string artdaqSupervisorName;
8411  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::getARTDAQSystem(
8412  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap, artdaqSupervisorInfo);
8413 
8414  if(artdaqSupervisorInfo.size() != 4 /*expecting 4 artdaq Supervisor parameters*/)
8415  {
8416  __SUP_COUT__ << "No artdaq supervisor found." << __E__;
8417  return;
8418  }
8419 
8420  __SUP_COUT__ << "========== "
8421  << "Found " << info.subsystems.size() << " subsystems." << __E__;
8422 
8423  unsigned int paramIndex = 0; // start at first artdaq Supervisor parameter
8424 
8425  auto parentEl = xmlOut.addTextElementToData("artdaqSupervisor",
8426  artdaqSupervisorInfo[paramIndex++]);
8427 
8428  std::string typeString = "artdaqSupervisor";
8429 
8430  xmlOut.addTextElementToParent(
8431  typeString + "-status", artdaqSupervisorInfo[paramIndex++], parentEl);
8432  xmlOut.addTextElementToParent(
8433  typeString + "-contextAddress", artdaqSupervisorInfo[paramIndex++], parentEl);
8434  xmlOut.addTextElementToParent(
8435  typeString + "-contextPort", artdaqSupervisorInfo[paramIndex++], parentEl);
8436 
8437  for(auto& subsystem : info.subsystems)
8438  {
8439  typeString = "subsystem";
8440 
8441  __SUP_COUT__ << "\t\t"
8442  << "Found " << typeString << " " << subsystem.first << " \t := '"
8443  << subsystem.second.label << "'" << __E__;
8444 
8445  xmlOut.addTextElementToParent(typeString, subsystem.second.label, parentEl);
8446  xmlOut.addTextElementToParent(
8447  typeString + "-id", std::to_string(subsystem.first), parentEl);
8448 
8449  xmlOut.addTextElementToParent(typeString + "-sourcesCount",
8450  std::to_string(subsystem.second.sources.size()),
8451  parentEl);
8452 
8453  // destination
8454  xmlOut.addTextElementToParent(typeString + "-destination",
8455  std::to_string(subsystem.second.destination),
8456  parentEl);
8457 
8458  } // end subsystem handling
8459 
8460  __SUP_COUT__ << "========== "
8461  << "Found " << nodeTypeToObjectMap.size() << " process types." << __E__;
8462 
8463  for(auto& nameTypePair : nodeTypeToObjectMap)
8464  {
8465  typeString = nameTypePair.first;
8466 
8467  __SUP_COUT__ << "\t"
8468  << "Found " << nameTypePair.second.size() << " " << typeString
8469  << "(s)" << __E__;
8470 
8471  for(auto& artdaqNode : nameTypePair.second)
8472  {
8473  __SUP_COUT__ << "\t\t"
8474  << "Found '" << artdaqNode.first << "' " << typeString << __E__;
8475  __SUP_COUTV__(StringMacros::vectorToString(artdaqNode.second));
8476 
8477  if(artdaqNode.second.size() < 2)
8478  {
8479  __SUP_SS__ << "Impossible parameter size for node '" << artdaqNode.first
8480  << "' " << typeString << " - please notify admins!" << __E__;
8481  __SUP_SS_THROW__;
8482  }
8483 
8484  auto nodeEl =
8485  xmlOut.addTextElementToParent(typeString, artdaqNode.first, parentEl);
8486 
8487  paramIndex = 3; // start at 3 after subsystem parameter
8488  if(artdaqNode.second.size() > paramIndex)
8489  {
8490  __SUP_COUTT__ << "\t\t\t"
8491  << "-multinode: " << artdaqNode.second[paramIndex] << __E__;
8492  xmlOut.addTextElementToParent(
8493  typeString + "-multinode", artdaqNode.second[paramIndex++], nodeEl);
8494  }
8495  if(artdaqNode.second.size() > paramIndex)
8496  {
8497  __SUP_COUTT__ << "\t\t\t"
8498  << "-nodefixedwidth: " << artdaqNode.second[paramIndex]
8499  << __E__;
8500  xmlOut.addTextElementToParent(typeString + "-nodefixedwidth",
8501  artdaqNode.second[paramIndex++],
8502  nodeEl);
8503  }
8504  if(artdaqNode.second.size() > paramIndex)
8505  {
8506  __SUP_COUTT__ << "\t\t\t"
8507  << "-hostarray: " << artdaqNode.second[paramIndex] << __E__;
8508  xmlOut.addTextElementToParent(
8509  typeString + "-hostarray", artdaqNode.second[paramIndex++], nodeEl);
8510  }
8511  if(artdaqNode.second.size() > paramIndex)
8512  {
8513  __SUP_COUTT__ << "\t\t\t"
8514  << "-hostfixedwidth: " << artdaqNode.second[paramIndex]
8515  << __E__;
8516  xmlOut.addTextElementToParent(typeString + "-hostfixedwidth",
8517  artdaqNode.second[paramIndex++],
8518  nodeEl);
8519  }
8520 
8521  paramIndex = 0; // return to starting parameter
8522  __SUP_COUTT__ << "\t\t\t"
8523  << "-status: " << artdaqNode.second[paramIndex] << __E__;
8524  xmlOut.addTextElementToParent(
8525  typeString + "-status", artdaqNode.second[paramIndex++], parentEl);
8526  __SUP_COUTT__ << "\t\t\t"
8527  << "-hostname: " << artdaqNode.second[paramIndex] << __E__;
8528  xmlOut.addTextElementToParent(
8529  typeString + "-hostname", artdaqNode.second[paramIndex++], parentEl);
8530  __SUP_COUTT__ << "\t\t\t"
8531  << "-subsystem: " << artdaqNode.second[paramIndex] << __E__;
8532  xmlOut.addTextElementToParent(
8533  typeString + "-subsystem", artdaqNode.second[paramIndex], parentEl);
8534  }
8535  } // end processor type handling
8536 
8537  __SUP_COUT__ << "Done retrieving artdaq nodes." << __E__;
8538 
8539 } // end handleGetArtdaqNodeRecordsXML()
8540 
8541 //==============================================================================
8548 void ConfigurationGUISupervisor::handleSaveArtdaqNodeRecordsXML(
8549  const std::string& nodeString,
8550  const std::string& subsystemString,
8551  HttpXmlDocument& xmlOut,
8552  ConfigurationManagerRW* cfgMgr,
8553  const std::string& modifiedTables)
8554 {
8555  __SUP_COUT__ << "Saving artdaq nodes..." << __E__;
8556 
8557  // setup active tables based on active groups and modified tables
8558  setupActiveTablesXML(
8559  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8560 
8561  // start node object extraction from nodeString
8562  std::map<std::string /*type*/,
8563  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8564  nodeTypeToObjectMap;
8565  {
8566  // nodeString format:
8567  // <type>:<nodeName>=<originalName>,<hostname>,<subsystemName>;<nodeName>=<originalName>,<hostname>,<subsystemName>;
8568  // ... |<type>:...|
8569  // repeat | separated types
8570  std::map<std::string /*type*/, std::string /*typeRecordSetString*/>
8571  nodeTypeToStringMap;
8572  StringMacros::getMapFromString(nodeString, nodeTypeToStringMap, {'|'}, {':'});
8573 
8574  __SUP_COUTV__(StringMacros::mapToString(nodeTypeToStringMap));
8575 
8576  for(auto& typePair : nodeTypeToStringMap)
8577  {
8578  if(typePair.first == "")
8579  continue; // skip empty names
8580 
8581  __SUP_COUTV__(StringMacros::decodeURIComponent(typePair.first));
8582 
8583  nodeTypeToObjectMap.emplace(
8584  std::make_pair(StringMacros::decodeURIComponent(typePair.first),
8585  std::map<std::string /*record*/,
8586  std::vector<std::string /*property*/>>()));
8587 
8588  std::map<std::string /*node*/, std::string /*nodeRecordSetString*/>
8589  nodeRecordToStringMap;
8590 
8592  typePair.second, nodeRecordToStringMap, {';'}, {'='});
8593 
8594  __SUP_COUTV__(StringMacros::mapToString(nodeRecordToStringMap));
8595 
8596  for(auto& nodePair : nodeRecordToStringMap)
8597  {
8598  if(nodePair.first == "")
8599  continue; // skip empty names
8600 
8601  __SUP_COUTV__(StringMacros::decodeURIComponent(nodePair.first));
8602 
8603  std::vector<std::string /*property*/> nodePropertyVector;
8604 
8606  nodePair.second, nodePropertyVector, {','});
8607 
8608  __SUP_COUTV__(StringMacros::vectorToString(nodePropertyVector));
8609 
8610  // decode all properties
8611  for(unsigned int i = 0; i < nodePropertyVector.size(); ++i)
8612  {
8613  __SUP_COUTV__(
8614  StringMacros::decodeURIComponent(nodePropertyVector[i]));
8615 
8616  nodePropertyVector[i] =
8617  StringMacros::decodeURIComponent(nodePropertyVector[i]);
8618  }
8619 
8620  nodeTypeToObjectMap[typePair.first].emplace(
8621  std::make_pair(StringMacros::decodeURIComponent(nodePair.first),
8622  nodePropertyVector));
8623  }
8624  }
8625  } // end node object extraction from nodeString
8626 
8627  // start subsystem object extraction from subsystemString
8628  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8629  subsystemObjectMap;
8630  {
8631  // subsystemString format:
8632  // <name>:<destination>;<name>:<destination>; ...;
8633  // repeat ; separated subsystems
8634 
8635  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8636  tmpSubsystemObjectMap;
8638  subsystemString, tmpSubsystemObjectMap, {';'}, {':'});
8639 
8640  __SUP_COUTV__(StringMacros::mapToString(tmpSubsystemObjectMap));
8641 
8642  // decode all values (probably unnecessary, but more future proof)
8643  for(auto& subsystemPair : tmpSubsystemObjectMap)
8644  {
8645  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.first));
8646  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.second));
8647 
8648  subsystemObjectMap.emplace(
8649  std::make_pair(StringMacros::decodeURIComponent(subsystemPair.first),
8650  StringMacros::decodeURIComponent(subsystemPair.second)));
8651  }
8652  } // end subsystem object extraction from subsystemString
8653 
8655  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap);
8656 
8657  __SUP_COUT__ << "Done saving artdaq nodes." << __E__;
8658 } // end handleSaveArtdaqNodeRecordsXML()
8659 
8660 //==============================================================================
8667 void ConfigurationGUISupervisor::handleLoadArtdaqNodeLayoutXML(
8668  HttpXmlDocument& xmlOut,
8670  cfgMgr, //force read-only config manager to avoid requiring user-lock (i.e., not ConfigurationManagerRW)
8671  const std::string& contextGroupName /* = "" */,
8672  const TableGroupKey& contextGroupKey /* = INVALID */) const
8673 {
8674  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8675 
8676  //NOTE: must be same/similar code as otsdaq/otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.cc:2332
8677  const std::string& finalContextGroupName =
8678  usingActiveGroups
8679  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8680  : contextGroupName;
8681  const TableGroupKey& finalContextGroupKey =
8682  usingActiveGroups
8683  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8684  : contextGroupKey;
8685  const std::string& finalConfigGroupName =
8686  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8687  const TableGroupKey& finalConfigGroupKey =
8688  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8689 
8690  FILE* fp = nullptr;
8691  //first try context+config name only
8692  {
8693  std::stringstream layoutPath;
8694  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8695  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8696  << finalConfigGroupKey << ".dat";
8697 
8698  fp = fopen(layoutPath.str().c_str(), "r");
8699  if(!fp)
8700  {
8701  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8702  << finalContextGroupKey << ") + " << finalConfigGroupName << "("
8703  << finalConfigGroupKey << ")': " << layoutPath.str() << __E__;
8704  // return; //try context only!
8705  }
8706  else
8707  __SUP_COUTV__(layoutPath.str());
8708  }
8709  //last try context name only
8710  if(!fp)
8711  {
8712  std::stringstream layoutPath;
8713  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8714  << "_" << finalContextGroupKey << ".dat";
8715  __SUP_COUTV__(layoutPath.str());
8716 
8717  fp = fopen(layoutPath.str().c_str(), "r");
8718  if(!fp)
8719  {
8720  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8721  << finalContextGroupKey << ")': " << layoutPath.str() << __E__;
8722  return;
8723  }
8724  else
8725  __SUP_COUTV__(layoutPath.str());
8726  }
8727 
8728  // file format is line by line
8729  // line 0 -- grid: <rows> <cols>
8730  // line 1-N -- node: <type> <name> <x-grid> <y-grid>
8731 
8732  const size_t maxLineSz = 1000;
8733  char line[maxLineSz];
8734  if(!fgets(line, maxLineSz, fp))
8735  {
8736  fclose(fp);
8737  return;
8738  }
8739  else
8740  {
8741  // extract grid
8742 
8743  unsigned int rows, cols;
8744 
8745  sscanf(line, "%u %u", &rows, &cols);
8746 
8747  __COUT__ << "Grid rows,cols = " << rows << "," << cols << __E__;
8748 
8749  xmlOut.addTextElementToData("grid-rows", std::to_string(rows));
8750  xmlOut.addTextElementToData("grid-cols", std::to_string(cols));
8751  }
8752 
8753  char name[maxLineSz];
8754  char type[maxLineSz];
8755  unsigned int x, y;
8756  while(fgets(line, maxLineSz, fp))
8757  {
8758  // extract node
8759  sscanf(line, "%s %s %u %u", type, name, &x, &y);
8760 
8761  xmlOut.addTextElementToData("node-type", type);
8762  xmlOut.addTextElementToData("node-name", name);
8763  xmlOut.addTextElementToData("node-x", std::to_string(x));
8764  xmlOut.addTextElementToData("node-y", std::to_string(y));
8765  } // end node extraction loop
8766 
8767  fclose(fp);
8768 
8769 } // end handleLoadArtdaqNodeLayoutXML()
8770 
8771 //==============================================================================
8778 void ConfigurationGUISupervisor::handleSaveArtdaqNodeLayoutXML(
8779  HttpXmlDocument& /*xmlOut*/,
8780  ConfigurationManagerRW* cfgMgr,
8781  const std::string& layoutString,
8782  const std::string& contextGroupName,
8783  const TableGroupKey& contextGroupKey)
8784 {
8785  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8786 
8787  const std::string& finalContextGroupName =
8788  usingActiveGroups
8789  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8790  : contextGroupName;
8791  const TableGroupKey& finalContextGroupKey =
8792  usingActiveGroups
8793  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8794  : contextGroupKey;
8795  const std::string& finalConfigGroupName =
8796  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8797  const TableGroupKey& finalConfigGroupKey =
8798  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8799 
8800  __SUP_COUTV__(layoutString);
8801 
8802  std::stringstream layoutPath;
8803  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8804  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8805  << finalConfigGroupKey << ".dat";
8806  __SUP_COUTV__(layoutPath.str());
8807 
8808  std::vector<std::string> fields = StringMacros::getVectorFromString(layoutString);
8809  __SUP_COUTV__(StringMacros::vectorToString(fields));
8810 
8811  if(fields.size() < 2 || (fields.size() - 2) % 4 != 0)
8812  {
8813  __SUP_SS__ << "Invalid layout string fields size of " << fields.size() << __E__;
8814  __SUP_SS_THROW__;
8815  }
8816 
8817  FILE* fp = fopen(layoutPath.str().c_str(), "w");
8818  if(!fp)
8819  {
8820  __SUP_SS__ << "Could not open layout file for writing for '"
8821  << finalContextGroupName << "(" << finalContextGroupKey << ") + "
8822  << finalConfigGroupName << "(" << finalConfigGroupKey
8823  << ")': " << layoutPath.str() << __E__;
8824  __SUP_SS_THROW__;
8825  }
8826 
8827  // match load code at ::handleLoadArtdaqNodeLayoutXML()
8828 
8829  // write grid
8830  fprintf(fp, "%s %s\n", fields[0].c_str(), fields[1].c_str());
8831 
8832  // write nodes
8833  for(unsigned int i = 2; i < fields.size(); i += 4)
8834  fprintf(fp,
8835  "%s %s %s %s\n",
8836  fields[i + 0].c_str(),
8837  fields[i + 1].c_str(),
8838  fields[i + 2].c_str(),
8839  fields[i + 3].c_str());
8840 
8841  fclose(fp);
8842 
8843 } // end handleSaveArtdaqNodeLayoutXML()
8844 
8845 //==============================================================================
8847 void ConfigurationGUISupervisor::handleOtherSubsystemActiveGroups(
8848  HttpXmlDocument& xmlOut,
8849  ConfigurationManagerRW* cfgMgr,
8850  bool getFullList,
8851  std::string targetSubsystem /* = "" */)
8852 try
8853 {
8854  try
8855  {
8856  ConfigurationTree node =
8857  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8858  auto children = node.getChildren();
8859 
8860  for(auto subsystem : children)
8861  {
8862  __SUP_COUTV__(subsystem.first);
8863  __SUP_COUTV__(
8864  StringMacros::vectorToString(subsystem.second.getChildrenNames()));
8865 
8866  std::string userPath =
8867  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8868  __SUP_COUTV__(userPath);
8869  }
8870  }
8871  catch(const std::runtime_error& e)
8872  {
8873  __SUP_COUT__ << "Ignoring errors in handling other subsystem active groups "
8874  "(assuming the subsystem information map is not setup in "
8875  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8876  << ") -- here is the error: \n"
8877  << e.what() << __E__;
8878  return; //ignore errors if subsystems not defined
8879  }
8880 
8881  //else subsystems are defined, so do not ignore errors!
8882 
8883  ConfigurationTree node =
8884  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8885  auto children = node.getChildren();
8886  for(auto subsystem : children)
8887  {
8888  if(targetSubsystem != "" && targetSubsystem != subsystem.first)
8889  continue; //skip non-target subsystem
8890 
8891  xercesc::DOMElement* parent =
8892  xmlOut.addTextElementToData("SubsystemName", subsystem.first);
8893 
8894  if(!getFullList)
8895  continue;
8896 
8897  std::string filename, userDataPath;
8898  std::string username, hostname;
8899 
8900  std::map<std::string /*groupType*/,
8901  std::pair<std::string /*groupName*/, TableGroupKey>>
8902  retMap = cfgMgr->getOtherSubsystemActiveTableGroups(
8903  subsystem.first, &userDataPath, &hostname, &username);
8904 
8905  for(const auto& retPair : retMap)
8906  {
8907  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupName",
8908  retPair.second.first,
8909  parent);
8910  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupKey",
8911  retPair.second.second.toString(),
8912  parent);
8913  }
8914 
8915  std::vector<std::string> filenameTypes = {"Configured",
8916  "Started",
8917  "ActivatedConfig",
8918  "ActivatedContext",
8919  "ActivatedBackbone",
8920  "ActivatedIterator"};
8921 
8922  std::vector<std::string> filenames = {
8923  ConfigurationManager::LAST_CONFIGURED_CONFIG_GROUP_FILE,
8924  ConfigurationManager::LAST_STARTED_CONFIG_GROUP_FILE,
8925  ConfigurationManager::LAST_ACTIVATED_CONFIG_GROUP_FILE,
8926  ConfigurationManager::LAST_ACTIVATED_CONTEXT_GROUP_FILE,
8927  ConfigurationManager::LAST_ACTIVATED_BACKBONE_GROUP_FILE,
8928  ConfigurationManager::LAST_ACTIVATED_ITERATE_GROUP_FILE};
8929 
8930  std::string userPath =
8931  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8932  auto splitPath = StringMacros::getVectorFromString(userPath, {':'});
8933  std::string cmdResult;
8934  for(unsigned int i = 0; i < filenames.size(); ++i)
8935  {
8936  filename = userDataPath + "/ServiceData/RunControlData/" + filenames[i];
8937  __SUP_COUTV__(filename);
8938 
8939  std::string tmpSubsystemFilename =
8940  ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH + "/" + filenames[i] +
8941  "." + subsystem.first;
8942  __SUP_COUTV__(tmpSubsystemFilename);
8943 
8944  if(splitPath.size() == 2) //must scp
8945  {
8946  if(username.size()) //has username
8947  cmdResult = StringMacros::exec(
8948  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + username +
8949  "@" + hostname + ":" + filename + " " + tmpSubsystemFilename +
8950  " 2>&1; cat " + tmpSubsystemFilename + " 2>&1")
8951  .c_str());
8952  else
8953  cmdResult = StringMacros::exec(
8954  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + hostname +
8955  ":" + filename + " " + tmpSubsystemFilename + " 2>&1; cat " +
8956  tmpSubsystemFilename + " 2>&1")
8957  .c_str());
8958  }
8959  else if(splitPath.size() == 1) //then can just directly access the file
8960  {
8961  cmdResult = StringMacros::exec(("rm " + tmpSubsystemFilename +
8962  " 2>/dev/null; cp " + filename + " " +
8963  tmpSubsystemFilename + " 2>&1; cat " +
8964  tmpSubsystemFilename + " 2>&1")
8965  .c_str());
8966  }
8967 
8968  __SUP_COUTV__(cmdResult);
8969  std::string timeString;
8970  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
8972  filenames[i] + "." + subsystem.first, timeString);
8973 
8974  // fill return parameters
8975  xmlOut.addTextElementToParent(
8976  "Last" + filenameTypes[i] + "GroupName", theGroup.first, parent);
8977  xmlOut.addTextElementToParent("Last" + filenameTypes[i] + "GroupKey",
8978  theGroup.second.toString(),
8979  parent);
8980  xmlOut.addTextElementToParent(
8981  "Last" + filenameTypes[i] + "GroupTime", timeString, parent);
8982  } // end active/recent filename handling
8983 
8984  } //end subsystem loop
8985 } // end getSubsytemTableGroups()
8986 catch(const std::runtime_error& e)
8987 {
8988  __SUP_SS__
8989  << "An error occurred handling subsystem active groups (Please check the "
8990  "subsystem user data path information map setup in the Context group table "
8991  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8992  << ") -- here is the error: \n"
8993  << e.what() << __E__;
8994  __SUP_SS_THROW__;
8995 } // end getSubsytemTableGroups() catch
8996 
8997 //==============================================================================
8999 void ConfigurationGUISupervisor::handleSearchFieldInGroupXML(
9000  HttpXmlDocument& xmlOut,
9001  ConfigurationManagerRW* cfgMgr,
9002  const std::string& searchText,
9003  const bool activeGroupsOnly,
9004  const std::string& groupType,
9005  const std::string& optionGroups,
9006  const std::string& versionsToCheck)
9007 {
9008  // Validate searchText: empty string matches every cell and would be extremely expensive
9009  if(searchText.empty() || searchText.find_first_not_of(" \t\r\n") == std::string::npos)
9010  {
9011  __COUT_WARN__ << "searchText is empty or whitespace; aborting search." << __E__;
9012  xmlOut.addTextElementToData("Error", "Search text must not be empty.");
9013  return;
9014  }
9015 
9016  bool searchAllGroups = false;
9017  if(optionGroups.size() == 0)
9018  {
9019  __COUT__ << "No optionGroups specified, searching all groups of type "
9020  << groupType << __E__;
9021  searchAllGroups = true;
9022  }
9023  std::vector<std::string> optionGroupsList =
9024  StringMacros::getVectorFromString(optionGroups, {','});
9025 
9026  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
9027 
9028  int searchMinThreshold = 0;
9029  int searchMaxThreshold = 0;
9030  size_t dashPos = -1;
9031 
9032  // Parse versionsToCheck for version range
9033  if(versionsToCheck.empty())
9034  {
9035  searchMaxThreshold = INT_MAX;
9036  }
9037  else if(versionsToCheck.find('-') != std::string::npos)
9038  {
9039  __COUT__ << "Parsing versionsToCheck for range: " << versionsToCheck << __E__;
9040  dashPos = versionsToCheck.find('-');
9041 
9042  if(dashPos != 0)
9043  {
9044  // Format: "N-M" means version N to version M
9045  std::string minStr = versionsToCheck.substr(0, dashPos);
9046  std::string maxStr = versionsToCheck.substr(dashPos + 1);
9047 
9048  searchMinThreshold = atoi(minStr.c_str());
9049  searchMaxThreshold = atoi(maxStr.c_str());
9050  }
9051  }
9052  else if(versionsToCheck != "")
9053  {
9054  // Single version specified
9055  searchMinThreshold = atoi(versionsToCheck.c_str());
9056  searchMaxThreshold = INT_MAX; // effectively no upper limit
9057  }
9058  else
9059  {
9060  __COUT__ << "Unable to set min and max thresholds for versionsToCheck: "
9061  << versionsToCheck << __E__;
9062  xmlOut.addTextElementToData(
9063  "Error",
9064  "Unable to set min and max thresholds for versionsToCheck: " +
9065  versionsToCheck);
9066  }
9067  __SUP_COUT__ << "versionsToCheck range: " << searchMinThreshold << " to "
9068  << searchMaxThreshold << __E__;
9069 
9070  auto parentEl = xmlOut.addTextElementToData("SearchResults", "");
9071 
9072  for(auto& groupInfo : allGroupInfo)
9073  {
9074  __COUT__ << "Checking group: " << groupInfo.first
9075  << " group type : " << groupInfo.second.getLatestKeyGroupTypeString()
9076  << __E__;
9077 
9078  if(groupInfo.second.getLatestKeyGroupTypeString() != groupType)
9079  continue;
9080 
9081  // Check if groupInfo.first is in optionGroups
9082  if(!searchAllGroups &&
9083  std::find(optionGroupsList.begin(), optionGroupsList.end(), groupInfo.first) ==
9084  optionGroupsList.end())
9085  {
9086  __COUT__ << "\tSkipping group not in optionGroups list." << __E__;
9087  continue; // skip this group
9088  }
9089 
9090  std::set<TableGroupKey /*key*/> allGroupKeys = groupInfo.second.getKeys();
9091  // convert set to vector for easier string handling
9092  std::vector<TableGroupKey> groupKeysVec(allGroupKeys.begin(), allGroupKeys.end());
9093  // convert vector to string for output
9094  std::string groupKeysStr = StringMacros::vectorToString(groupKeysVec, {','});
9095  __COUT__ << "Group keys:" << groupKeysStr << __E__;
9096 
9097  // Dash is first position: "-N" means (maxVersion - N) to maxVersion
9098  if(dashPos == 0)
9099  {
9100  std::string offsetStr = versionsToCheck.substr(1);
9101  int offset =
9102  atoi(offsetStr.c_str()) - 1; // -1 to include the max version itself
9103 
9104  __SUP_COUT__ << "versionsToCheck offset from end: " << offset << __E__;
9105 
9106  // Find the max version from this group
9107  if(groupKeysVec.size() > 0)
9108  {
9109  TableGroupKey maxVersion = groupKeysVec.at(
9110  groupKeysVec.size() - 1); // get the last (max) version
9111  int maxVersionNum = std::stoi(maxVersion.toString());
9112  int minVersionNum = std::max(0, maxVersionNum - offset);
9113 
9114  searchMinThreshold = minVersionNum;
9115  searchMaxThreshold = maxVersionNum;
9116 
9117  __SUP_COUT__ << "Version range: " << searchMinThreshold << " to "
9118  << searchMaxThreshold << __E__;
9119  }
9120  }
9121 
9122  std::set<TableGroupKey /*key*/> groupKeys;
9123  // make set of single active key for easier handling below
9124  if(activeGroupsOnly)
9125  {
9126  std::set<TableGroupKey> singleKeySet;
9127  singleKeySet.insert(groupInfo.second.getLatestKey());
9128  groupKeys = std::move(singleKeySet);
9129  }
9130  else
9131  groupKeys = std::move(allGroupKeys);
9132 
9133  for(const auto& groupKey : groupKeys)
9134  {
9135  __COUT__ << "\tProcessing group: " << groupInfo.first << " v" << groupKey
9136  << __E__;
9137 
9138  if(!activeGroupsOnly &&
9139  (std::stoi(groupKey.toString()) < searchMinThreshold ||
9140  std::stoi(groupKey.toString()) > searchMaxThreshold))
9141  {
9142  __COUT__ << "\tSkipping group version outside of versionsToCheck range."
9143  << __E__;
9144  continue; // skip this group version
9145  }
9146 
9147  // Get the member map for this group
9148  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
9149  try
9150  {
9151  cfgMgr->loadTableGroup(groupInfo.first,
9152  groupKey,
9153  false /*doActivate*/,
9154  &memberMap,
9155  0 /*progressBar*/,
9156  0 /*accumulateErrors*/,
9157  0 /*groupComment*/,
9158  0 /*groupAuthor*/,
9159  0 /*groupCreationTime*/,
9160  true /*doNotLoadMembers*/);
9161 
9162  // Process each table in the group
9163  for(auto& tablePair : memberMap)
9164  {
9165  __COUT__ << "\t\tTable: " << tablePair.first << " v"
9166  << tablePair.second << __E__;
9167 
9168  // Load the table for this specific version and search that exact view
9169  try
9170  {
9171  TableBase* memberTable = cfgMgr->getVersionedTableByName(
9172  tablePair.first, tablePair.second);
9173 
9174  // Get the table view for the requested version
9175  const TableView& tableView = memberTable->getView();
9176 
9177  // Loop through all rows and columns to search for searchText
9178  for(unsigned int row = 0; row < tableView.getNumberOfRows();
9179  ++row)
9180  {
9181  for(unsigned int col = 0;
9182  col < tableView.getNumberOfColumns();
9183  ++col)
9184  {
9185  const std::string& cellValue =
9186  tableView.getDataView()[row][col];
9187 
9188  // Check if searchText is found in cell value
9189  if(cellValue.find(searchText) != std::string::npos)
9190  {
9191  __COUT__ << "\t\t\tMatch found in " << tablePair.first
9192  << " v" << tablePair.second << " row " << row
9193  << " column " << col
9194  << " value: " << cellValue << __E__;
9195  // Separate match element by group/table for better organization
9196  auto groupTableEl = xmlOut.addTextElementToParent(
9197  "GroupTableMatch",
9198  groupInfo.first + "/" + tablePair.first,
9199  parentEl);
9200  xmlOut.addTextElementToParent(
9201  "MatchRow", std::to_string(row), groupTableEl);
9202  xmlOut.addTextElementToParent(
9203  "MatchColumn",
9204  tableView.getColumnInfo(col).getName(),
9205  groupTableEl);
9206  xmlOut.addTextElementToParent("MatchGroupVersion",
9207  groupKey.toString(),
9208  groupTableEl);
9209  xmlOut.addTextElementToParent(
9210  "MatchTableVersion",
9211  tablePair.second.toString(),
9212  groupTableEl);
9213  }
9214  } // end columns
9215  } // end rows
9216  } // end try load versioned table
9217  catch(const std::runtime_error& e)
9218  {
9219  __COUT_WARN__ << "Failed to search table " << tablePair.first
9220  << " v" << tablePair.second << ": " << e.what()
9221  << __E__;
9222  }
9223  } // end try load table group members
9224  }
9225  catch(const std::runtime_error& e)
9226  {
9227  __COUT_WARN__ << "Failed to load group members for " << groupInfo.first
9228  << ": " << e.what() << __E__;
9229  }
9230  }
9231  } // end groupInfo loop
9232 } // end handleSearchFieldInGroupXML()
9233 
9234 //==============================================================================
9236 void ConfigurationGUISupervisor::handleSearchFieldInTableXML(
9237  HttpXmlDocument& xmlOut,
9238  ConfigurationManagerRW* cfgMgr,
9239  const std::string& searchText,
9240  const std::string& tableName,
9241  const std::string& searchVersionToCheck,
9242  bool activeTablesOnly)
9243 {
9244  // Validate searchText: empty string matches every cell and would be extremely expensive
9245  if(searchText.empty() || searchText.find_first_not_of(" \t\r\n") == std::string::npos)
9246  {
9247  __COUT_WARN__ << "searchText is empty or whitespace; aborting search." << __E__;
9248  xmlOut.addTextElementToData("Error", "Search text must not be empty.");
9249  return;
9250  }
9251 
9252  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
9253  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
9254 
9255  int searchMinThreshold = 0;
9256  int searchMaxThreshold = 0;
9257 
9258  // Parse searchVersionToCheck for version range
9259  if(searchVersionToCheck.empty())
9260  {
9261  searchMaxThreshold = INT_MAX;
9262  }
9263  else if(searchVersionToCheck.find('-') != std::string::npos)
9264  {
9265  size_t dashPos = searchVersionToCheck.find('-');
9266 
9267  if(dashPos == 0)
9268  {
9269  // Dash is first position: "-N" means (maxVersion - N) to maxVersion
9270  std::string offsetStr = searchVersionToCheck.substr(1);
9271  int offset =
9272  atoi(offsetStr.c_str()) - 1; // -1 to include the max version itself
9273 
9274  __SUP_COUT__ << "searchVersionToCheck offset from end: " << offset << __E__;
9275 
9276  // Find the max version from allTableInfo
9277  if(allTableInfo.find(tableName) != allTableInfo.end())
9278  {
9279  const TableInfo& tableInfo = allTableInfo.at(tableName);
9280  if(tableInfo.versions_.size() > 0)
9281  {
9282  auto maxVersionIter = tableInfo.versions_.rbegin();
9283  int maxVersionNum = maxVersionIter->version();
9284  int minVersionNum = std::max(0, maxVersionNum - offset);
9285 
9286  searchMinThreshold = minVersionNum;
9287  searchMaxThreshold = maxVersionNum;
9288 
9289  __SUP_COUT__ << "Version range: " << searchMinThreshold << " to "
9290  << searchMaxThreshold << __E__;
9291  }
9292  }
9293  }
9294  else
9295  {
9296  // Format: "N-M" means version N to version M
9297  std::string minStr = searchVersionToCheck.substr(0, dashPos);
9298  std::string maxStr = searchVersionToCheck.substr(dashPos + 1);
9299 
9300  searchMinThreshold = atoi(minStr.c_str());
9301  searchMaxThreshold = atoi(maxStr.c_str());
9302  }
9303  }
9304  else if(searchVersionToCheck != "")
9305  {
9306  // Single version specified
9307  searchMinThreshold = atoi(searchVersionToCheck.c_str());
9308  searchMaxThreshold = INT_MAX; // effectively no upper limit
9309  }
9310  else
9311  {
9312  __COUT__ << "Unable to set min and max thresholds for searchVersionToCheck: "
9313  << searchVersionToCheck << __E__;
9314  xmlOut.addTextElementToData(
9315  "Error",
9316  "Unable to set min and max thresholds for searchVersionToCheck: " +
9317  searchVersionToCheck);
9318  }
9319  __SUP_COUT__ << "searchVersionToCheck range: " << searchMinThreshold << " to "
9320  << searchMaxThreshold << __E__;
9321 
9322  try
9323  {
9324  if(allTableInfo.find(tableName) == allTableInfo.end())
9325  {
9326  __SUP_SS__ << "Table '" << tableName << "' not found." << __E__;
9327  xmlOut.addTextElementToData("Error", ss.str());
9328  return;
9329  }
9330 
9331  const TableInfo& tableInfo = allTableInfo.at(tableName);
9332  __COUT__ << "tableInfo versions length " << tableInfo.versions_.size() << __E__;
9333 
9334  unsigned int matchCount = 0;
9335  bool allowIllegalColumns = true;
9336  // Create a parent element for search results
9337  auto parentEl = xmlOut.addTextElementToData("SearchResults", "");
9338 
9339  // Loop through all versions of the table
9340  for(const auto& version : tableInfo.versions_)
9341  {
9342  try
9343  {
9344  auto activeIt = allActivePairs.find(tableName);
9345  bool isActiveVersion =
9346  (activeIt != allActivePairs.end() && activeIt->second == version);
9347  __COUT__ << "Active check for " << tableName << " v" << version << " => "
9348  << (isActiveVersion ? "active" : "inactive") << __E__;
9349  if(!isActiveVersion && activeTablesOnly)
9350  continue; // Skip versions that are not active
9351 
9352  if((std::stoi(version.toString()) < searchMinThreshold ||
9353  std::stoi(version.toString()) > searchMaxThreshold) &&
9354  activeTablesOnly == false /*ignore version threshold if active only*/)
9355  {
9356  __COUT__ << "Skipping version " << version << " out of range "
9357  << searchMinThreshold << " to " << searchMaxThreshold
9358  << __E__;
9359  continue;
9360  }
9361  TableBase* table = cfgMgr->getTableByName(tableName);
9362 
9363  // get view pointer
9364  TableView* tableViewPtr;
9365  if(version.isInvalid()) // use mock-up
9366  {
9367  __COUT__ << "Using mock-up for table " << tableName << " version "
9368  << version << __E__;
9369  tableViewPtr = table->getMockupViewP();
9370  }
9371  else // use view version
9372  {
9373  try
9374  {
9375  // locally accumulate 'manageable' errors getting the version to avoid
9376  // reverting to mockup
9377  std::string localAccumulatedErrors = "";
9378  tableViewPtr =
9379  cfgMgr
9381  tableName,
9382  version,
9383  allowIllegalColumns /*looseColumnMatching*/,
9384  &localAccumulatedErrors,
9385  false /*getRawData*/)
9386  ->getViewP();
9387 
9388  if(localAccumulatedErrors != "")
9389  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9390  }
9391  catch(const std::runtime_error& e)
9392  {
9393  __COUT__ << "Error loading version " << version << " for table "
9394  << tableName << ": " << e.what() << __E__;
9395  __SUP_COUT_WARN__ << "Could not load version " << version
9396  << " for table " << tableName << ": "
9397  << e.what() << __E__;
9398  // fallback to mockup
9399  tableViewPtr = table->getMockupViewP();
9400  }
9401  }
9402 
9403  __COUT__ << "Getting table " << tableName << " version " << version
9404  << __E__;
9405 
9406  // Search through all rows and columns
9407  for(unsigned int row = 0; row < tableViewPtr->getNumberOfRows(); ++row)
9408  {
9409  for(unsigned int col = 0; col < tableViewPtr->getNumberOfColumns();
9410  ++col)
9411  {
9412  const std::string& cellValue =
9413  tableViewPtr->getDataView()[row][col];
9414  // Check if searchText is found in cell value
9415  if(cellValue.find(searchText) != std::string::npos)
9416  {
9417  __COUT__ << "Match found in row " << row << " column " << col
9418  << " value: " << cellValue << __E__;
9419  auto matchEl = xmlOut.addTextElementToParent(
9420  "Match", cellValue, parentEl);
9421  xmlOut.addTextElementToParent(
9422  "MatchVersion", version.toString(), matchEl);
9423  xmlOut.addTextElementToParent(
9424  "MatchRow", std::to_string(row), matchEl);
9425  xmlOut.addTextElementToParent(
9426  "MatchColumn",
9427  tableViewPtr->getColumnInfo(col).getName(),
9428  matchEl);
9429 
9430  ++matchCount;
9431  }
9432  }
9433  } // end for rows and columns
9434  }
9435  catch(const std::runtime_error& e)
9436  {
9437  __COUT__ << "Error searching in version " << version << " for table "
9438  << tableName << ": " << e.what() << __E__;
9439  __SUP_COUT_WARN__ << "Could not load version " << version << " for table "
9440  << tableName << ": " << e.what() << __E__;
9441  }
9442  } //emd for version loop
9443 
9444  xmlOut.addTextElementToData("MatchCount", std::to_string(matchCount));
9445  }
9446  catch(std::runtime_error& e)
9447  {
9448  __SUP_SS__ << "Error searching in table '" << tableName << "'!\n\n " << e.what()
9449  << __E__;
9450  __SUP_COUT_ERR__ << ss.str();
9451  xmlOut.addTextElementToData("Error", ss.str());
9452  }
9453  catch(...)
9454  {
9455  __SUP_SS__ << "Error searching in table '" << tableName << "'!\n\n " << __E__;
9456  try
9457  {
9458  throw;
9459  }
9460  catch(const std::exception& e)
9461  {
9462  ss << "Exception message: " << e.what();
9463  }
9464  catch(...)
9465  {
9466  }
9467  __SUP_COUT_ERR__ << ss.str();
9468  xmlOut.addTextElementToData("Error", ss.str());
9469  }
9470 } // end handleSearchFieldInTableXML()
9471 
9472 //==============================================================================
9474 void ConfigurationGUISupervisor::handleGroupDiff(
9475  HttpXmlDocument& xmlOut,
9476  ConfigurationManagerRW* cfgMgr,
9477  const std::string& groupName,
9478  const TableGroupKey& groupKey,
9479  const TableGroupKey& diffKey /* = TableGroupKey() */,
9480  const std::string& diffGroupNameInput /* = "" */)
9481 {
9482  //Steps:
9483  // - Get group type and load table map
9484  // - Get match type active group table map
9485  // - For each table, compare
9486  std::string diffGroupName;
9487 
9488  if(diffKey.isInvalid())
9489  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
9490  << ") with the active group." << __E__;
9491  else
9492  {
9493  if(diffGroupNameInput == "")
9494  diffGroupName = groupName;
9495  else
9496  diffGroupName = diffGroupNameInput;
9497 
9498  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
9499  << ") with group " << diffGroupName << "(" << diffKey << ")"
9500  << __E__;
9501  }
9502 
9503  try
9504  {
9505  std::map<std::string /*name*/, TableVersion /*version*/> memberMap, diffMemberMap;
9506  std::string groupType, accumulateErrors;
9507  std::stringstream diffReport;
9508  bool noDifference = true;
9509 
9510  cfgMgr->loadTableGroup(
9511  groupName,
9512  groupKey,
9513  false /*doActivate*/,
9514  &memberMap /*groupMembers*/,
9515  0 /*progressBar*/,
9516  &accumulateErrors /*accumulateErrors*/,
9517  0 /*groupComment*/,
9518  0 /*groupAuthor*/,
9519  0 /*groupCreationTime*/,
9520  false /*doNotLoadMember*/,
9521  (diffKey.isInvalid()
9522  ? &groupType
9523  : 0)); //for specified diff group (not active), do not need groupType
9524 
9525  __SUP_COUTV__(StringMacros::mapToString(memberMap));
9526 
9527  std::map<std::string /* groupType */, std::pair<std::string, TableGroupKey>>
9528  activeGroups;
9529  if(diffKey.isInvalid())
9530  {
9531  activeGroups = cfgMgr->getActiveTableGroups();
9532 
9533  __SUP_COUTV__(StringMacros::mapToString(activeGroups));
9534  __SUP_COUTV__(groupType);
9535 
9536  if(activeGroups.find(groupType) == activeGroups.end() ||
9537  activeGroups.at(groupType).first == "" ||
9538  activeGroups.at(groupType).second.isInvalid())
9539  {
9540  __SUP_SS__ << "Could not find an active group of type '" << groupType
9541  << ".' Please check the expected active configuration groups "
9542  "for errors (going to 'System View' of the Config App may "
9543  "reveal errors)."
9544  << __E__;
9545  __SUP_SS_THROW__;
9546  }
9547 
9548  __SUP_COUT__ << "active " << groupType << " group is "
9549  << activeGroups.at(groupType).first << "("
9550  << activeGroups.at(groupType).second << ")" << __E__;
9551 
9552  diffReport << "This difference report is between " << groupType
9553  << " group <b>'" << groupName << "(" << groupKey << ")'</b>"
9554  << " and active group <b>'" << activeGroups.at(groupType).first
9555  << "(" << activeGroups.at(groupType).second << ")'</b>." << __E__;
9556 
9557  cfgMgr->loadTableGroup(activeGroups.at(groupType).first,
9558  activeGroups.at(groupType).second,
9559  false /*doActivate*/,
9560  &diffMemberMap /*groupMembers*/,
9561  0 /*progressBar*/,
9562  &accumulateErrors /*accumulateErrors*/,
9563  0 /*groupComment*/,
9564  0 /*groupAuthor*/,
9565  0 /*groupCreationTime*/,
9566  false /*doNotLoadMember*/);
9567 
9568  diffReport << "\n\n"
9569  << "'" << groupName << "(" << groupKey << ")' has <b>"
9570  << memberMap.size() << " member tables</b>, and "
9571  << "'" << activeGroups.at(groupType).first << "("
9572  << activeGroups.at(groupType).second << ")' has <b>"
9573  << diffMemberMap.size() << " member tables</b>." << __E__;
9574  }
9575  else //specified diff group (not active), so do not need groupType
9576  {
9577  diffReport << "This difference report is between group <b>'" << groupName
9578  << "(" << groupKey << ")'</b>"
9579  << " and group <b>'" << diffGroupName << "(" << diffKey
9580  << ")'</b>." << __E__;
9581 
9582  cfgMgr->loadTableGroup(diffGroupName,
9583  diffKey,
9584  false /*doActivate*/,
9585  &diffMemberMap /*groupMembers*/,
9586  0 /*progressBar*/,
9587  &accumulateErrors /*accumulateErrors*/,
9588  0 /*groupComment*/,
9589  0 /*groupAuthor*/,
9590  0 /*groupCreationTime*/,
9591  false /*doNotLoadMember*/);
9592 
9593  diffReport << "\n\n"
9594  << "'" << groupName << "(" << groupKey << ")' has <b>"
9595  << memberMap.size() << " member tables</b>, and "
9596  << "'" << diffGroupName << "(" << diffKey << ")' has <b>"
9597  << diffMemberMap.size() << " member tables</b>." << __E__;
9598  }
9599 
9600  __SUP_COUTV__(StringMacros::mapToString(diffMemberMap));
9601 
9602  diffReport << "<INDENT><ol>";
9603 
9604  unsigned int tableDifferences = 0;
9605 
9606  for(auto& member : memberMap)
9607  {
9608  if(diffMemberMap.find(member.first) == diffMemberMap.end())
9609  {
9610  diffReport << "\n\n<li>"
9611  << "Table <b>" << member.first << "-v" << member.second
9612  << "</b> not found in active group."
9613  << "</li>" << __E__;
9614  noDifference = false;
9615  ++tableDifferences;
9616  continue;
9617  }
9618 
9619  __SUP_COUTT__ << "Comparing " << member.first << "-v" << member.second
9620  << " ... " << member.first << "-v"
9621  << diffMemberMap.at(member.first) << __E__;
9622 
9623  if(member.second == diffMemberMap.at(member.first))
9624  continue;
9625 
9626  diffReport << "\n\n<li>"
9627  << "Table <b>" << member.first << " v" << member.second
9628  << "</b> in " << groupName << "(" << groupKey << ")' ...vs... "
9629  << " <b>v" << diffMemberMap.at(member.first) << "</b> in "
9630  << diffGroupName << "(" << diffKey << ")':" << __E__;
9631 
9632  TableBase* table = cfgMgr->getTableByName(member.first);
9633 
9634  diffReport << "<ul>";
9635  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
9636  modifiedRecords; //useful for tree diff view display
9637  if(!table->diffTwoVersions(member.second,
9638  diffMemberMap.at(member.first),
9639  &diffReport,
9640  &modifiedRecords))
9641  {
9642  //difference found!
9643  noDifference = false;
9644  ++tableDifferences;
9645  auto parentEl =
9646  xmlOut.addTextElementToData("TableWithDiff", member.first);
9647  for(auto& modifiedRecord : modifiedRecords)
9648  {
9649  auto recordParentEl = xmlOut.addTextElementToParent(
9650  "RecordWithDiff", modifiedRecord.first, parentEl);
9651  for(auto& modifiedColumn : modifiedRecord.second)
9652  xmlOut.addTextElementToParent(
9653  "ColNameWithDiff", modifiedColumn, recordParentEl);
9654  }
9655  }
9656  diffReport << "</ul></li>";
9657 
9658  } //end member table comparison loop
9659 
9660  for(auto& diffMember : diffMemberMap)
9661  {
9662  if(memberMap.find(diffMember.first) == memberMap.end())
9663  {
9664  if(diffKey.isInvalid())
9665  diffReport << "\n\n<li>"
9666  << "Active Group Table <b>" << diffMember.first << "-v"
9667  << diffMember.second << "</b> not found in '" << groupName
9668  << "(" << groupKey << ")'."
9669  << "</li>" << __E__;
9670  else
9671  diffReport << "\n\n<li>" << diffGroupName << "(" << diffKey
9672  << ") Table <b>" << diffMember.first << "-v"
9673  << diffMember.second << "</b> not found in '" << groupName
9674  << "(" << groupKey << ")'."
9675  << "</li>" << __E__;
9676 
9677  noDifference = false;
9678  ++tableDifferences;
9679  continue;
9680  }
9681  }
9682  diffReport << "\n</ol></INDENT>";
9683 
9684  if(diffKey.isInvalid())
9685  {
9686  if(noDifference)
9687  diffReport << "\n\nNo difference found between "
9688  << "<b>'" << groupName << "(" << groupKey
9689  << ")'</b> and active group "
9690  << "<b>'" << activeGroups.at(groupType).first << "("
9691  << activeGroups.at(groupType).second << ")'</b>." << __E__;
9692  else
9693  diffReport << "\n\n<b>" << tableDifferences
9694  << "</b> member table differences identified between "
9695  << "<b>'" << groupName << "(" << groupKey
9696  << ")'</b> and active group "
9697  << "<b>'" << activeGroups.at(groupType).first << "("
9698  << activeGroups.at(groupType).second << ")'</b>." << __E__;
9699  }
9700  else
9701  {
9702  if(noDifference)
9703  diffReport << "\n\nNo difference found between "
9704  << "<b>'" << groupName << "(" << groupKey
9705  << ")'</b> and group "
9706  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
9707  << __E__;
9708  else
9709  diffReport << "\n\n<b>" << tableDifferences
9710  << "</b> member table differences identified between "
9711  << "<b>'" << groupName << "(" << groupKey
9712  << ")'</b> and group "
9713  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
9714  << __E__;
9715  }
9716 
9717  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
9718  xmlOut.addTextElementToData("DiffReport", diffReport.str());
9719  }
9720  catch(const std::runtime_error& e)
9721  {
9722  __SUP_COUT_ERR__ << "Caught error while differencing group " << groupName << "("
9723  << groupKey << ") with group " << diffGroupName << "(" << diffKey
9724  << ")" << __E__ << e.what() << __E__;
9725  throw; //rethrow
9726  }
9727 } // end handleGroupDiff()
9728 
9729 //==============================================================================
9731 void ConfigurationGUISupervisor::handleTableDiff(HttpXmlDocument& xmlOut,
9732  ConfigurationManagerRW* cfgMgr,
9733  const std::string& tableName,
9734  const TableVersion& vA,
9735  const TableVersion& vB)
9736 {
9737  __SUP_COUT__ << "Differencing tableName " << tableName << " v" << vA << " with v"
9738  << vB << __E__;
9739 
9740  //first make sure tables are loaded
9741  TableBase* table = cfgMgr->getTableByName(tableName);
9742 
9743  try
9744  {
9745  // locally accumulate 'manageable' errors getting the version to avoid
9746  // reverting to mockup
9747  std::string localAccumulatedErrors = "";
9748  cfgMgr->getVersionedTableByName(tableName,
9749  vA,
9750  false /*looseColumnMatching*/,
9751  &localAccumulatedErrors,
9752  false /*getRawData*/);
9753 
9754  if(localAccumulatedErrors != "")
9755  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9756  }
9757  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
9758  {
9759  __SUP_SS__ << "Failed to get table " << tableName << " version " << vA;
9760  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
9761  __SUP_COUT_ERR__ << "\n" << ss.str();
9762 
9763  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9764  }
9765  catch(...) // default to mock-up for fail-safe in GUI editor
9766  {
9767  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vA << __E__;
9768  try
9769  {
9770  throw;
9771  } //one more try to printout extra info
9772  catch(const std::exception& e)
9773  {
9774  ss << "Exception message: " << e.what();
9775  }
9776  catch(...)
9777  {
9778  }
9779 
9780  __SUP_COUT_ERR__ << "\n" << ss.str();
9781  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9782  }
9783  try
9784  {
9785  // locally accumulate 'manageable' errors getting the version to avoid
9786  // reverting to mockup
9787  std::string localAccumulatedErrors = "";
9788  cfgMgr->getVersionedTableByName(tableName,
9789  vB,
9790  false /*looseColumnMatching*/,
9791  &localAccumulatedErrors,
9792  false /*getRawData*/);
9793 
9794  if(localAccumulatedErrors != "")
9795  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9796  }
9797  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
9798  {
9799  __SUP_SS__ << "Failed to get table " << tableName << " version " << vB;
9800  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
9801  __SUP_COUT_ERR__ << "\n" << ss.str();
9802 
9803  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9804  }
9805  catch(...) // default to mock-up for fail-safe in GUI editor
9806  {
9807  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vB << __E__;
9808  try
9809  {
9810  throw;
9811  } //one more try to printout extra info
9812  catch(const std::exception& e)
9813  {
9814  ss << "Exception message: " << e.what();
9815  }
9816  catch(...)
9817  {
9818  }
9819 
9820  __SUP_COUT_ERR__ << "\n" << ss.str();
9821  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9822  }
9823 
9824  bool noDifference = true;
9825  std::stringstream diffReport;
9826 
9827  diffReport << "This difference report is between table " << tableName << " v" << vA
9828  << " and v" << vB << "</b>." << __E__;
9829 
9830  diffReport << "<INDENT>";
9831  diffReport << "<ul>";
9832  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
9833  modifiedRecords; //useful for tree diff view display
9834  if(!table->diffTwoVersions(vA, vB, &diffReport))
9835  noDifference = false; //difference found!
9836  diffReport << "</ul></INDENT>";
9837 
9838  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
9839  xmlOut.addTextElementToData("DiffReport", diffReport.str());
9840 } // end handleTableDiff()
9841 
9842 //==============================================================================
9845 void ConfigurationGUISupervisor::testXDAQContext()
9846 {
9847  if(0) //keep for debugging
9848  {
9849  __COUT_INFO__ << "Hello0!";
9850  ConfigurationManagerRW cfgMgrInst("ExampleUser");
9851  __COUT_INFO__ << "Hello1!";
9852  ConfigurationManagerRW* cfgMgr = &cfgMgrInst;
9853  __COUT_INFO__ << "Hello2!";
9854  cfgMgr->testXDAQContext();
9855  __COUT_INFO__ << "Hello3!";
9856  return;
9857  }
9858 
9859  try
9860  {
9861  __SUP_COUT_INFO__ << "Attempting test activation of the context group." << __E__;
9863  cfgMgr; // create instance to activate saved context and backbone groups (not config group)
9864  }
9865  catch(const std::runtime_error& e)
9866  {
9867  __SUP_COUT_WARN__
9868  << "The test activation of the context group failed. Ignoring error: \n"
9869  << e.what() << __E__;
9870  return;
9871  }
9872  catch(...)
9873  {
9874  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
9875  << __E__;
9876  return;
9877  }
9878 
9879  __SUP_COUT_INFO__ << "Completed test activation of the context group." << __E__;
9880  return;
9881 
9883  // below has been used for debugging.
9884 
9885  // behave like a user
9886  // start with top level xdaq context
9887  // then add and delete rows proof-of-concept
9888  // export xml xdaq table file
9889 
9892  // behave like a new user
9893  //
9894  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
9895 
9896  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
9897 
9898  // // std::map<std::string, TableVersion> groupMembers;
9899  // // groupMembers["DesktopIcon"] = TableVersion(2);
9900  // // cfgMgr->saveNewTableGroup("test",
9901  // // groupMembers, "test comment");
9902 
9903  // //
9904  // const std::map<std::string, TableInfo>& allTableInfo =
9905  // cfgMgr->getAllTableInfo(true /* refresh*/);
9906  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
9907  // for(auto& mapIt : allTableInfo)
9908  // {
9909  // __SUP_COUT__ << "Table Name: " << mapIt.first << __E__;
9910  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size()
9911  // <<
9912  // __E__;
9913 
9914  // //get version key for the current system table key
9915  // for (auto& v:mapIt.second.versions_)
9916  // {
9917  // __SUP_COUT__ << "\t\t" << v << __E__;
9918  // }
9919  // }
9920  // __SUP_COUTT__ << "Group Info end runtime=" << cfgMgr->runTimeSeconds() << __E__;
9921  // testXDAQContext just a test bed for navigating the new config tree
9922  // cfgMgr->testXDAQContext();
9923 
9926 } // end testXDAQContext()
static void setAndActivateARTDAQSystem(ConfigurationManagerRW *cfgMgr, const std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, const std::map< std::string, std::string > &subsystemObjectMap)
static const ARTDAQInfo & getARTDAQSystem(ConfigurationManagerRW *cfgMgr, std::map< std::string, std::map< std::string, std::vector< std::string >>> &nodeTypeToObjectMap, std::map< std::string, std::string > &subsystemObjectMap, std::vector< std::string > &artdaqSupervisoInfo)
static std::string postData(cgicc::Cgicc &cgi, const std::string &needle)
static std::string getData(cgicc::Cgicc &cgi, const std::string &needle)
virtual void forceSupervisorPropertyValues(void) override
override to force supervisor property values (and ignore user settings)
virtual void setSupervisorPropertyDefaults(void) override
ConfigurationGUISupervisor(xdaq::ApplicationStub *s)
static xdaq::Application * instantiate(xdaq::ApplicationStub *s)
TableVersion saveNewTable(const std::string &tableName, TableVersion temporaryVersion=TableVersion(), bool makeTemporary=false)
const GroupInfo & getGroupInfo(const std::string &groupName, bool attemptToReloadKeys=false)
const std::map< std::string, TableInfo > & getAllTableInfo(bool refresh=false, std::string *accumulatedWarnings=0, const std::string &errorFilterName="", bool getGroupKeys=false, bool getGroupInfo=false, bool initializeActiveGroups=false)
void loadTableGroup(const std::string &tableGroupName, const TableGroupKey &tableGroupKey, bool doActivate=false, std::map< std::string, TableVersion > *groupMembers=0, ProgressBar *progressBar=0, std::string *accumulateWarnings=0, std::string *groupComment=0, std::string *groupAuthor=0, std::string *groupCreateTime=0, bool doNotLoadMember=false, std::string *groupTypeString=0, std::map< std::string, std::string > *groupAliases=0, ConfigurationManager::LoadGroupType groupTypeToLoad=ConfigurationManager::LoadGroupType::ALL_TYPES, bool ignoreVersionTracking=false)
TableVersion copyViewToCurrentColumns(const std::string &tableName, TableVersion sourceVersion)
TableGroupKey saveNewTableGroup(const std::string &groupName, std::map< std::string, TableVersion > &groupMembers, const std::string &groupComment=TableViewColumnInfo::DATATYPE_COMMENT_DEFAULT, std::map< std::string, std::string > *groupAliases=0)
void activateTableGroup(const std::string &tableGroupName, TableGroupKey tableGroupKey, std::string *accumulatedTreeErrors=0, std::string *groupTypeString=0)
void clearCachedVersions(const std::string &tableName)
const std::string & getUsername(void) const
std::map< std::string, std::map< std::string, TableVersion > > getVersionAliases(void) const
void eraseTemporaryVersion(const std::string &tableName, TableVersion targetVersion=TableVersion())
void preloadVersionCreationTimes(void)
static void loadTableGroupThread(ConfigurationManagerRW *cfgMgr, std::string groupName, ots::TableGroupKey groupKey, std::shared_ptr< ots::GroupInfo > theGroupInfo, std::shared_ptr< std::atomic< bool >> theThreadDone)
static const unsigned int PROCESSOR_COUNT
void restoreActiveTableGroups(bool throwErrors=false, const std::string &pathToActiveGroupsFile="", ConfigurationManager::LoadGroupType onlyLoadIfBackboneOrContext=ConfigurationManager::LoadGroupType::ALL_TYPES, std::string *accumulatedWarnings=0)
std::map< std::string, std::pair< std::string, TableGroupKey > > getActiveTableGroups(void) const
std::map< std::string, TableVersion > getActiveVersions(void) const
void copyTableGroupFromCache(const ConfigurationManager &cacheConfigMgr, const std::map< std::string, TableVersion > &groupMembers, const std::string &configGroupName="", const TableGroupKey &tableGroupKey=TableGroupKey(TableGroupKey::INVALID), bool doActivate=false, bool ignoreVersionTracking=false)
ConfigurationTree getNode(const std::string &nodeString, bool doNotThrowOnBrokenUIDLinks=false) const
TableBase * getVersionedTableByName(const std::string &tableName, TableVersion version, bool looseColumnMatching=false, std::string *accumulatedErrors=0, bool getRawData=false, bool touchLastAccessTime=true)
void init(std::string *accumulatedErrors=0, bool initForWriteAccess=false, std::string *accumulatedWarnings=0)
static const std::string & getTypeNameOfGroup(const std::map< std::string, TableVersion > &memberMap)
void destroyTableGroup(const std::string &theGroup="", bool onlyDeactivate=false)
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, TableVersion > *memberMap=0, std::string *accumulatedTreeErrors=0) const
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, TableVersion > *memberMap=0, std::string *accumulatedTreeErrors=0) const
static std::vector< std::map< std::string, std::string > > loadGroupHistory(const std::string &groupAction, const std::string &groupType, bool formatTime=false)
TableGroupKey loadConfigurationBackbone(void)
const TableBase * getTableByName(const std::string &configurationName) const
static std::pair< std::string, TableGroupKey > loadGroupNameAndKey(const std::string &fileName, std::string &returnedTimeString)
static void getConfigurationStatusXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &username)
static void handleGetTableGroupXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &groupName, TableGroupKey groupKey, bool ignoreWarnings=false, bool cacheOnly=false)
static TableVersion saveModifiedVersionXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &tableName, TableVersion originalVersion, bool makeTemporary, TableBase *config, TableVersion temporaryModifiedVersion, bool ignoreDuplicates=false, bool lookForEquivalent=false)
static void handleCreateTableXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &tableName, TableVersion version, bool makeTemporary, const std::string &data, const int &dataOffset, const std::string &author, const std::string &comment, bool sourceTableAsIs, bool lookForEquivalent)
static void handleCreateTableGroupXML(HttpXmlDocument &xmlOut, ConfigurationManagerRW *cfgMgr, const std::string &groupName, const std::string &configList, bool allowDuplicates=false, bool ignoreWarnings=false, const std::string &groupComment="", bool lookForEquivalent=false)
bool isUIDNode(void) const
const TableVersion & getTableVersion(void) const
bool isDisconnected(void) const
const std::string & getAuthor(void) const
const std::string & getComment(void) const
std::vector< std::string > getChildrenNames(bool byPriority=false, bool onlyStatusTrue=false) const
bool isEnabled(void) const
const std::string & getTableName(void) const
const unsigned int & getFieldRow(void) const
std::map< std::string, ConfigurationTree > getChildrenMap(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool onlyStatusTrue=false) const
const std::string & getValueName(void) const
const std::string & getValueAsString(bool returnLinkTableValue=false) const
const std::string & getChildLinkIndex(void) const
const std::string & getDisconnectedTableName(void) const
std::vector< std::pair< std::string, ConfigurationTree > > getChildren(std::map< std::string, std::string > filterMap=std::map< std::string, std::string >(), bool byPriority=false, bool onlyStatusTrue=false) const
std::vector< std::string > getFixedChoices(void) const
bool isLinkNode(void) const
std::vector< ConfigurationTree::RecordField > getCommonFields(const std::vector< std::string > &recordList, const std::vector< std::string > &fieldAcceptList, const std::vector< std::string > &fieldRejectList, unsigned int depth=-1, bool autoSelectFilterFields=false) const
bool isValueNode(void) const
std::set< std::string > getUniqueValuesForField(const std::vector< std::string > &recordList, const std::string &fieldName, std::string *fieldGroupIDChildLinkIndex=0) const
const std::string & getValueType(void) const
bool isGroupLinkNode(void) const
const std::string & getFieldTableName(void) const
const std::string & getDisconnectedLinkID(void) const
const std::string & getParentTableName(void) const
bool isUIDLinkNode(void) const
const unsigned int & getFieldColumn(void) const
static std::string convertToCaps(std::string &str, bool isConfigName=false)
std::string toString(void) const
bool isInvalid(void) const
std::string str() const
static std::string getFullGroupString(const std::string &groupName, const TableGroupKey &key, const std::string &preKey="_v", const std::string &postKey="")
std::string toString(void) const
unsigned int version(void) const
static const std::string DATATYPE_NUMBER
static std::map< std::pair< std::string, std::string >, std::string > getAllDefaultsForGUI(void)
static const std::string & getDefaultDefaultValue(const std::string &type, const std::string &dataType)
static const std::string & getMaxDefaultValue(const std::string &dataType)
static const std::string & getMinDefaultValue(const std::string &dataType)
unsigned int findRow(unsigned int col, const T &value, unsigned int offsetRow=0, bool doNotThrow=false) const
bool isEntryInGroup(const unsigned int &row, const std::string &childLinkIndex, const std::string &groupNeedle) const
void setValueAsString(const std::string &value, unsigned int row, unsigned int col)
void deleteRow(int r)
unsigned int getColStatus(void) const
unsigned int getLinkGroupIDColumn(const std::string &childLinkIndex) const
bool removeRowFromGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID, bool deleteRowIfNoGroupLeft=false)
bool getChildLink(const unsigned int &col, bool &isGroup, std::pair< unsigned int, unsigned int > &linkPair) const
void addRowToGroup(const unsigned int &row, const unsigned int &col, const std::string &groupID)
void init(void)
std::string getValueAsString(unsigned int row, unsigned int col, bool convertEnvironmentVariables=true) const
void getValue(T &value, unsigned int row, unsigned int col, bool doConvertEnvironmentVariables=true) const
unsigned int getColUID(void) const
bool setURIEncodedValue(const std::string &value, const unsigned int &row, const unsigned int &col, const std::string &author="")
unsigned int findCol(const std::string &name) const
void setValue(const T &value, unsigned int row, unsigned int col)
void setURIEncodedComment(const std::string &uriComment)
unsigned int addRow(const std::string &author="", unsigned char incrementUniqueData=false, const std::string &baseNameAutoUID="", unsigned int rowToAdd=(unsigned int) -1, std::string childLinkIndex="", std::string groupId="")
xercesc::DOMElement * addTextElementToParent(const std::string &childName, const std::string &childText, xercesc::DOMElement *parent)
void INIT_MF(const char *name)
static std::string getTimestampString(const std::string &linuxTimeInSeconds)
static void getVectorFromString(const std::string &inputString, std::vector< std::string > &listToReturn, const std::set< char > &delimiter={',', '|', '&'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'}, std::vector< char > *listOfDelimiters=0, bool decodeURIComponents=false)
static std::string exec(const char *cmd)
static std::string setToString(const std::set< T > &setToReturn, const std::string &delimeter=", ")
static std::string escapeString(std::string inString, bool allowWhiteSpace=false, bool forHtml=false)
static std::string vectorToString(const std::vector< T > &setToReturn, const std::string &delimeter=", ")
static std::string convertEnvironmentVariables(const std::string &data)
static bool isNumber(const std::string &stringToCheck)
static std::string mapToString(const std::map< std::string, T > &mapToReturn, const std::string &primaryDelimeter=", ", const std::string &secondaryDelimeter=": ")
static void getMapFromString(const std::string &inputString, std::map< S, T > &mapToReturn, const std::set< char > &pairPairDelimiter={',', '|', '&'}, const std::set< char > &nameValueDelimiter={'=', ':'}, const std::set< char > &whitespace={' ', '\t', '\n', '\r'})
static std::string decodeURIComponent(const std::string &data)