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  xercesc::DOMElement *parentEl, *subparentEl;
6095 
6096  std::string accumulatedErrors = "";
6097 
6098  if(allowIllegalColumns)
6099  xmlOut.addTextElementToData("allowIllegalColumns", "1");
6100 
6101  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo(
6102  allowIllegalColumns /* if allowIllegalColumns, then also refresh */,
6103  allowIllegalColumns ? &accumulatedErrors : 0,
6104  tableName); // filter errors by tableName
6105 
6106  TableBase* table = cfgMgr->getTableByName(tableName);
6107 
6108  if(!getRawData)
6109  {
6110  // send all table names along with
6111  // and check for specific version
6112  xmlOut.addTextElementToData("ExistingTableNames",
6113  TableViewColumnInfo::DATATYPE_LINK_DEFAULT);
6114  for(auto& configPair : allTableInfo)
6115  {
6116  xmlOut.addTextElementToData("ExistingTableNames", configPair.first);
6117  if(configPair.first == tableName && // check that version exists
6118  configPair.second.versions_.find(version) ==
6119  configPair.second.versions_.end())
6120  {
6121  __SUP_COUT__ << "Version not found, so using mockup." << __E__;
6122  version = TableVersion(); // use INVALID
6123  }
6124  }
6125  }
6126 
6127  xmlOut.addTextElementToData("TableName", tableName); // table name
6128  xmlOut.addTextElementToData("TableDescription",
6129  table->getTableDescription()); // table name
6130 
6131  if(descriptionOnly)
6132  return;
6133 
6134  // existing table versions
6135  if(!getRawData)
6136  {
6137  // get version aliases for translation
6138  std::map<
6139  std::string /*table name*/,
6140  std::map<std::string /*version alias*/, TableVersion /*aliased version*/>>
6141  versionAliases;
6142  try
6143  {
6144  // use whatever backbone is currently active
6145  versionAliases = cfgMgr->getVersionAliases();
6146  for(const auto& aliases : versionAliases)
6147  for(const auto& alias : aliases.second)
6148  __SUP_COUTT__ << "ALIAS: " << aliases.first << " " << alias.first
6149  << " ==> " << alias.second << __E__;
6150  }
6151  catch(const std::runtime_error& e)
6152  {
6153  __SUP_COUT__ << "Could not get backbone information for version aliases: "
6154  << e.what() << __E__;
6155  }
6156 
6157  auto tableIterator = versionAliases.find(tableName);
6158 
6159  parentEl = xmlOut.addTextElementToData("TableVersions", "");
6160  //add lo and hi spans, instead of each individual value
6161  TableVersion lo, hi;
6162  for(const TableVersion& v : allTableInfo.at(tableName).versions_)
6163  {
6164  //Steps:
6165  // 1. check for version aliases
6166  // 2. if version aliases, leave as standalone version
6167  // else stack spans of versions for faster xml transfer
6168  std::vector<std::string> aliases;
6169  if(tableIterator != versionAliases.end())
6170  {
6171  // check if this version has one or many aliases
6172  for(const auto& aliasPair : tableIterator->second)
6173  {
6174  if(v == aliasPair.second)
6175  {
6176  __SUP_COUT__ << "Found Alias " << aliasPair.second << " --> "
6177  << aliasPair.first << __E__;
6178  aliases.push_back(aliasPair.first);
6179  }
6180  }
6181  }
6182  //now have version aliases or not
6183 
6184  if(aliases.size()) //keep versions with aliases standalone
6185  {
6186  __SUP_COUT__ << "Handling version w/aliases" << __E__;
6187  }
6188  else if(lo.version() ==
6189  TableVersion::INVALID) //establish start of potential span
6190  {
6191  hi = lo = v;
6192  continue;
6193  }
6194  else if(hi.version() + 1 == v.version()) //span is growing
6195  {
6196  hi = v;
6197  continue;
6198  }
6199  //else jump by more than one, so close out span
6200 
6201  if(lo.version() != TableVersion::INVALID)
6202  {
6203  if(lo == hi) //single value
6204  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6205  else //span
6206  xmlOut.addTextElementToParent(
6207  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6208  }
6209  hi = lo = v.version();
6210 
6211  if(versionAliases.size()) //keep versions with aliases standalone
6212  {
6213  subparentEl =
6214  xmlOut.addTextElementToParent("Version", v.toString(), parentEl);
6215  for(const auto& alias : aliases)
6216  xmlOut.addTextElementToParent("VersionAlias", alias, subparentEl);
6217  hi = lo = TableVersion::INVALID; //invalidate for fresh start
6218  } //end version alias handling
6219 
6220  } //end version loop
6221 
6222  if(lo.version() != TableVersion::INVALID) //check if last one to do!
6223  {
6224  if(lo == hi) //single value
6225  xmlOut.addTextElementToParent("Version", lo.toString(), parentEl);
6226  else //span
6227  xmlOut.addTextElementToParent(
6228  "Version", "_" + lo.toString() + "_" + hi.toString(), parentEl);
6229  }
6230  } //end existing table version handling
6231 
6232  // table columns and then rows (from table view)
6233 
6234  // get view pointer
6235  TableView* tableViewPtr;
6236  if(version.isInvalid()) // use mock-up
6237  {
6238  tableViewPtr = table->getMockupViewP();
6239  }
6240  else // use view version
6241  {
6242  try
6243  {
6244  // locally accumulate 'manageable' errors getting the version to avoid
6245  // reverting to mockup
6246  std::string localAccumulatedErrors = "";
6247  tableViewPtr =
6248  cfgMgr
6249  ->getVersionedTableByName(tableName,
6250  version,
6251  allowIllegalColumns /*looseColumnMatching*/,
6252  &localAccumulatedErrors,
6253  getRawData)
6254  ->getViewP();
6255 
6256  if(getRawData)
6257  {
6258  xmlOut.addTextElementToData("TableRawData",
6259  tableViewPtr->getSourceRawData());
6260 
6261  const std::set<std::string>& srcColNames =
6262  tableViewPtr->getSourceColumnNames();
6263  for(auto& srcColName : srcColNames)
6264  xmlOut.addTextElementToData("ColumnHeader", srcColName);
6265 
6266  if(!version.isTemporaryVersion())
6267  {
6268  // if version is temporary, view is already ok
6269  table->eraseView(
6270  version); // clear so that the next get will fill the table
6271  tableViewPtr = cfgMgr
6273  tableName,
6274  version,
6275  allowIllegalColumns /*looseColumnMatching*/,
6276  &localAccumulatedErrors,
6277  false /* getRawData */)
6278  ->getViewP();
6279  }
6280  } // end rawData handling
6281 
6282  if(localAccumulatedErrors != "")
6283  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
6284  }
6285  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
6286  {
6287  __SUP_SS__ << "Failed to get table " << tableName << " version " << version
6288  << "... defaulting to mock-up! " << __E__;
6289  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
6290 
6291  __SUP_COUT_ERR__ << "\n" << ss.str();
6292  version = TableVersion();
6293  tableViewPtr = table->getMockupViewP();
6294 
6295  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6296  }
6297  catch(...) // default to mock-up for fail-safe in GUI editor
6298  {
6299  __SUP_SS__ << "Failed to get table " << tableName << " version: " << version
6300  << "... defaulting to mock-up! "
6301  << "(You may want to try again to see what was partially loaded "
6302  "into cache before failure. "
6303  << "If you think, the failure is due to a column name change, "
6304  << "you can also try to Copy the failing view to the new column "
6305  "names using "
6306  << "'Copy and Move' functionality.)" << __E__;
6307  try
6308  {
6309  throw;
6310  } //one more try to printout extra info
6311  catch(const std::exception& e)
6312  {
6313  ss << "Exception message: " << e.what();
6314  }
6315  catch(...)
6316  {
6317  }
6318 
6319  __SUP_COUT_ERR__ << "\n" << ss.str();
6320  version = TableVersion();
6321  tableViewPtr = table->getMockupViewP();
6322 
6323  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
6324  }
6325  }
6326  xmlOut.addTextElementToData("TableVersion", version.toString()); // table version
6327 
6328  if(getRawData)
6329  return; // no need to go further for rawData handling
6330 
6331  // get 'columns' of view
6332  xercesc::DOMElement* choicesParentEl;
6333  parentEl = xmlOut.addTextElementToData("CurrentVersionColumnHeaders", "");
6334 
6335  std::vector<TableViewColumnInfo> colInfo = tableViewPtr->getColumnsInfo();
6336 
6337  for(int i = 0; i < (int)colInfo.size(); ++i) // column headers and types
6338  {
6339  xmlOut.addTextElementToParent("ColumnHeader", colInfo[i].getName(), parentEl);
6340  xmlOut.addTextElementToParent("ColumnType", colInfo[i].getType(), parentEl);
6341  xmlOut.addTextElementToParent(
6342  "ColumnDataType", colInfo[i].getDataType(), parentEl);
6343 
6344  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6345  // whereas DefaultRowValue are the defaults for the mockup
6346  xmlOut.addTextElementToParent(
6347  "ColumnDefaultValue", colInfo[i].getDefaultValue(), parentEl);
6348 
6349  choicesParentEl = xmlOut.addTextElementToParent("ColumnChoices", "", parentEl);
6350  // add data choices if necessary
6351  if(colInfo[i].getType() == TableViewColumnInfo::TYPE_FIXED_CHOICE_DATA ||
6352  colInfo[i].getType() == TableViewColumnInfo::TYPE_BITMAP_DATA ||
6353  colInfo[i].isChildLink())
6354  {
6355  for(auto& choice : colInfo[i].getDataChoices())
6356  xmlOut.addTextElementToParent("ColumnChoice", choice, choicesParentEl);
6357  }
6358 
6359  xmlOut.addTextElementToParent(
6360  "ColumnMinValue", colInfo[i].getMinValue(), parentEl);
6361  xmlOut.addTextElementToParent(
6362  "ColumnMaxValue", colInfo[i].getMaxValue(), parentEl);
6363  }
6364 
6365  // verify mockup columns after columns are posted to xmlOut
6366  try
6367  {
6368  if(version.isInvalid())
6369  tableViewPtr->init();
6370  }
6371  catch(std::runtime_error& e)
6372  {
6373  // append accumulated errors, because they may be most useful
6374  __THROW__(e.what() + std::string("\n\n") + accumulatedErrors);
6375  }
6376  catch(...)
6377  {
6378  throw;
6379  }
6380 
6381  parentEl = xmlOut.addTextElementToData("CurrentVersionRows", "");
6382 
6383  int numRows = (int)tableViewPtr->getNumberOfRows();
6384  int numCols = (int)tableViewPtr->getNumberOfColumns();
6385 
6386  for(int c = 0; c < numCols; ++c)
6387  {
6388  std::string csvStr;
6389  csvStr.reserve(numRows * 20);
6390  for(int r = 0; r < numRows; ++r)
6391  {
6392  if(r > 0)
6393  csvStr += ",";
6394  if(colInfo[c].getDataType() == TableViewColumnInfo::DATATYPE_TIME)
6395  {
6396  std::string timeAsString;
6397  tableViewPtr->getValue(timeAsString, r, c);
6398  csvStr += StringMacros::encodeURIComponent(timeAsString);
6399  }
6400  else
6401  csvStr +=
6402  StringMacros::encodeURIComponent(tableViewPtr->getDataView()[r][c]);
6403  }
6404  xmlOut.addTextElementToParent("ColCSV", csvStr, parentEl);
6405  }
6406 
6407  // add "other" fields associated with configView
6408  xmlOut.addTextElementToData("TableComment", tableViewPtr->getComment());
6409  xmlOut.addTextElementToData("TableAuthor", tableViewPtr->getAuthor());
6410  xmlOut.addTextElementToData("TableCreationTime",
6411  std::to_string(tableViewPtr->getCreationTime()));
6412  xmlOut.addTextElementToData("TableLastAccessTime",
6413  std::to_string(tableViewPtr->getLastAccessTime()));
6414 
6415  // add to xml the default row values
6416  // NOTE!! ColumnDefaultValue defaults may be unique to this version of the table,
6417  // whereas DefaultRowValue are the defaults for the mockup
6418  std::vector<std::string> defaultRowValues =
6419  table->getMockupViewP()->getDefaultRowValues();
6420  // don't give author and time.. force default author, let JS fill time
6421  for(unsigned int c = 0; c < defaultRowValues.size() - 2; ++c)
6422  {
6423  xmlOut.addTextElementToData("DefaultRowValue", defaultRowValues[c]);
6424  }
6425 
6426  const std::set<std::string> srcColNames = tableViewPtr->getSourceColumnNames();
6427 
6428  if(accumulatedErrors != "") // add accumulated errors to xmlOut
6429  {
6430  __SUP_SS__ << (std::string("Column errors were allowed for this request, so "
6431  "perhaps you can ignore this, ") +
6432  "but please note the following warnings:\n" + accumulatedErrors)
6433  << __E__;
6434  __SUP_COUT_ERR__ << ss.str();
6435  xmlOut.addTextElementToData("TableWarnings", ss.str());
6436  }
6437  else if(!version.isTemporaryVersion() && // not temporary (these are not filled from
6438  // interface source)
6439  (srcColNames.size() != tableViewPtr->getNumberOfColumns() ||
6440  tableViewPtr->getSourceColumnMismatch() !=
6441  0)) // check for column size mismatch
6442  {
6443  __SUP_SS__ << "\n\nThere were warnings found when loading the table " << tableName
6444  << ":v" << version << ". Please see the details below:\n\n"
6445  << tableViewPtr->getMismatchColumnInfo();
6446 
6447  __SUP_COUT__ << "\n" << ss.str();
6448  xmlOut.addTextElementToData("TableWarnings", ss.str());
6449  }
6450 
6451 } // end handleGetTableXML()
6452 catch(std::runtime_error& e)
6453 {
6454  __SUP_SS__ << "Error getting table view!\n\n " << e.what() << __E__;
6455  __SUP_COUT_ERR__ << ss.str();
6456  xmlOut.addTextElementToData("Error", ss.str());
6457 }
6458 catch(...)
6459 {
6460  __SUP_SS__ << "Error getting table view!\n\n " << __E__;
6461  try
6462  {
6463  throw;
6464  } //one more try to printout extra info
6465  catch(const std::exception& e)
6466  {
6467  ss << "Exception message: " << e.what();
6468  }
6469  catch(...)
6470  {
6471  }
6472  __SUP_COUT_ERR__ << ss.str();
6473  xmlOut.addTextElementToData("Error", ss.str());
6474 } // end handleGetTableXML() catch
6475 
6476 //==============================================================================
6483 ConfigurationManagerRW* ConfigurationGUISupervisor::refreshUserSession(
6484  std::string username, bool refresh)
6485 {
6486  uint64_t sessionIndex =
6487  0; // make session by username for now! (may never want to change back)
6488 
6489  std::stringstream ssMapKey;
6490  ssMapKey << username << ":" << sessionIndex;
6491  std::string mapKey = ssMapKey.str();
6492  __SUP_COUTT__ << "Using Config Session " << mapKey
6493  << " ... Total Session Count: " << userConfigurationManagers_.size()
6494  << " refresh=" << refresh << __E__;
6495 
6496  time_t now = time(0);
6497 
6498  // create new table mgr if not one for active session index
6499 
6500  if(TTEST(1))
6501  {
6502  for(auto& pair : userConfigurationManagers_)
6503  __SUP_COUTTV__(pair.first);
6504  }
6505 
6506  const std::string preLoadCfgMgrName = ":0";
6507  if(userConfigurationManagers_.size() == 1 &&
6508  userConfigurationManagers_.find(preLoadCfgMgrName) !=
6509  userConfigurationManagers_.end())
6510  {
6511  __SUP_COUT__ << "Using pre-loaded Configuration Manager. time=" << time(0) << " "
6512  << clock() << " Setting author from "
6513  << userConfigurationManagers_.at(preLoadCfgMgrName)->getUsername()
6514  << " to " << username << __E__;
6515  userConfigurationManagers_[mapKey] =
6516  userConfigurationManagers_.at(preLoadCfgMgrName);
6517  userLastUseTime_[mapKey] = userLastUseTime_.at(preLoadCfgMgrName);
6518  //also set author!
6519  userConfigurationManagers_.at(mapKey)->setUsername(username);
6520  }
6521 
6522  if(userConfigurationManagers_.find(mapKey) == userConfigurationManagers_.end())
6523  {
6524  __SUP_COUT__ << "Creating new Configuration Manager. time=" << time(0) << " "
6525  << clock() << __E__;
6526  userConfigurationManagers_[mapKey] = new ConfigurationManagerRW(username);
6527 
6528  // update table info for each new configuration manager
6529  // IMPORTANTLY this also fills all configuration manager pointers with instances,
6530  // so we are not dealing with changing pointers later on
6531  userConfigurationManagers_[mapKey]->getAllTableInfo(
6532  true /* refresh */, // load empty instance of everything important
6533  0 /* accumulatedWarnings */,
6534  "" /* errorFilterName */,
6535  true /* getGroupKeys */,
6536  false /* getGroupInfo */,
6537  true /* initializeActiveGroups */);
6538  }
6539  else if(userLastUseTime_.find(mapKey) == userLastUseTime_.end())
6540  {
6541  __SUP_SS__ << "Fatal error managing user sessions! Check the logs for "
6542  "Configuration Interface failure."
6543  << __E__;
6544  __SUP_COUT_ERR__ << "\n" << ss.str();
6545  __SS_THROW__;
6546  }
6547  else if(
6548  refresh ||
6549  (now - userLastUseTime_[mapKey]) >
6550  CONFIGURATION_MANAGER_REFRESH_THRESHOLD) // check if should refresh all table info
6551  {
6552  __SUP_COUT__ << "Refreshing all table info." << __E__;
6553  userConfigurationManagers_[mapKey]->getAllTableInfo(
6554  true /* refresh */,
6555  0 /* accumulatedWarnings */,
6556  "" /* errorFilterName */,
6557  false /* getGroupKeys */,
6558  false /* getGroupInfo */,
6559  true /* initializeActiveGroups */);
6560  }
6561  __SUP_COUTT__ << "Configuration Manager for author="
6562  << userConfigurationManagers_[mapKey]->getUsername()
6563  << " ready. time=" << time(0) << " " << clock() << " runTimeSeconds()="
6564  << userConfigurationManagers_[mapKey]->runTimeSeconds() << __E__;
6565 
6566  // update active sessionIndex last use time
6567  userLastUseTime_[mapKey] = now;
6568 
6569  // check for stale sessions and remove them (so table user maps do not grow forever)
6570  for(std::map<std::string, time_t>::iterator it = userLastUseTime_.begin();
6571  it != userLastUseTime_.end();
6572  ++it)
6573  if(now - it->second > CONFIGURATION_MANAGER_EXPIRATION_TIME) // expired!
6574  {
6575  __SUP_COUT__ << now << ":" << it->second << " = " << now - it->second
6576  << __E__;
6577  delete userConfigurationManagers_[it->first]; // call destructor
6578  if(!(userConfigurationManagers_.erase(it->first))) // erase by key
6579  {
6580  __SUP_SS__ << "Fatal error erasing configuration manager by key!"
6581  << __E__;
6582  __SUP_COUT_ERR__ << "\n" << ss.str();
6583  __SS_THROW__;
6584  }
6585  userLastUseTime_.erase(it); // erase by iterator
6586 
6587  it =
6588  userLastUseTime_
6589  .begin(); // fail safe.. reset it, to avoid trying to understand what happens with the next iterator
6590  }
6591 
6592  return userConfigurationManagers_[mapKey];
6593 } //end refreshUserSession()
6594 
6595 //==============================================================================
6600 void ConfigurationGUISupervisor::handleDeleteTableInfoXML(HttpXmlDocument& xmlOut,
6601  ConfigurationManagerRW* cfgMgr,
6602  std::string& tableName)
6603 {
6604  if(0 == rename((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(),
6605  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused").c_str()))
6606  __SUP_COUT_INFO__ << ("Table Info File successfully renamed: " +
6607  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6608  << __E__;
6609  else
6610  {
6611  __SUP_COUT_ERR__ << ("Error renaming file to " +
6612  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused"))
6613  << __E__;
6614 
6615  xmlOut.addTextElementToData(
6616  "Error",
6617  ("Error renaming Table Info File to " +
6618  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT + ".unused")));
6619  return;
6620  }
6621 
6622  // reload all with refresh to remove new table
6623  cfgMgr->getAllTableInfo(true /* refresh */);
6624 } // end handleDeleteTableInfoXML()
6625 
6626 //==============================================================================
6633 void ConfigurationGUISupervisor::handleSaveTableInfoXML(
6634  HttpXmlDocument& xmlOut,
6635  ConfigurationManagerRW* cfgMgr,
6636  std::string& tableName,
6637  const std::string& data,
6638  const std::string& tableDescription,
6639  const std::string& columnChoicesCSV,
6640  bool allowOverwrite)
6641 {
6642  // create all caps name and validate
6643  // only allow alpha-numeric names with "Table" at end
6644  std::string capsName;
6645  try
6646  {
6647  capsName = TableBase::convertToCaps(tableName, true);
6648  }
6649  catch(std::runtime_error& e)
6650  { // error! non-alpha
6651  xmlOut.addTextElementToData("Error", e.what());
6652  return;
6653  }
6654 
6655  if(!allowOverwrite)
6656  {
6657  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "r");
6658  if(fp)
6659  {
6660  fclose(fp);
6661  xmlOut.addTextElementToData("TableName", tableName);
6662  xmlOut.addTextElementToData("OverwriteError", "1");
6663  xmlOut.addTextElementToData(
6664  "Error",
6665  "File already exists! ('" +
6666  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT) + "')");
6667  return;
6668  }
6669  }
6670 
6671  __SUP_COUT__ << "capsName=" << capsName << __E__;
6672  __SUP_COUT__ << "tableName=" << tableName << __E__;
6673  __SUP_COUT__ << "tableDescription=" << tableDescription << __E__;
6674  __SUP_COUT__ << "columnChoicesCSV=" << columnChoicesCSV << __E__;
6675 
6676  // create preview string to validate column info before write to file
6677  std::stringstream outss;
6678 
6679  outss << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n";
6680  outss << "\t<ROOT xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
6681  "xsi:noNamespaceSchemaLocation=\"TableInfo.xsd\">\n";
6682  outss << "\t\t<TABLE Name=\"" << tableName << "\">\n";
6683  outss << "\t\t\t<VIEW Name=\"" << capsName
6684  << "\" Type=\"File,Database,DatabaseTest\" Description=\"" << tableDescription
6685  << "\">\n";
6686 
6687  // each column is represented by 4 fields or 6
6688  // - type, name, dataType, defaultValue, minValue, maxValue
6689 
6690  std::istringstream columnChoicesISS(columnChoicesCSV);
6691  std::string columnChoicesString;
6692  std::string columnDefaultValue, columnMinValue, columnMaxValue;
6693  std::vector<std::string> columnParameters;
6694  std::vector<std::string> columnData =
6695  StringMacros::getVectorFromString(data, {';'} /*delimiter*/);
6696 
6697  std::set<std::string> childLinkIndices, childLinkUIDIndices, childLinkGroupIDIndices;
6698 
6699  for(unsigned int c = 0; c < columnData.size() - 1; ++c)
6700  {
6701  columnParameters =
6702  StringMacros::getVectorFromString(columnData[c], {','} /*delimiter*/);
6703  __COUT__ << "Column #" << c << ": "
6704  << StringMacros::vectorToString(columnParameters) << __E__;
6705  for(unsigned int p = 0; p < columnParameters.size(); ++p)
6706  {
6707  __COUT__ << "\t Parameter #" << p << ": " << columnParameters[p] << __E__;
6708  }
6709  __COUT__ << "\t creating the new xml" << __E__;
6710 
6711  std::string& columnType = columnParameters[0];
6712  std::string& columnName = columnParameters[1];
6713  std::string& columnDataType = columnParameters[2];
6714  const std::string columnStorageName =
6715  TableBase::convertToCaps(columnName); // now caps;
6716 
6717  outss << "\t\t\t\t<COLUMN Type=\"";
6718  outss << columnType;
6719  outss << "\" \t Name=\"";
6720  outss << columnName;
6721  outss << "\" \t StorageName=\"";
6722  try
6723  {
6724  outss << columnStorageName;
6725  }
6726  catch(std::runtime_error& e)
6727  { // error! non-alpha
6728  xmlOut.addTextElementToData(
6729  "Error",
6730  std::string("For column name '") + columnName + "' - " + e.what());
6731  return;
6732  }
6733  outss << "\" \t DataType=\"";
6734  outss << columnDataType;
6735 
6736  columnDefaultValue = StringMacros::decodeURIComponent(columnParameters[3]);
6737 
6738  std::string* columnDefaultValuePtr = nullptr;
6739  if(columnDefaultValue !=
6740  TableViewColumnInfo::getDefaultDefaultValue(columnType, columnDataType))
6741  {
6742  __SUP_COUT__ << "FOUND user spec'd default value '" << columnDefaultValue
6743  << "'" << __E__;
6744  outss << "\" \t DefaultValue=\"";
6745  outss << columnParameters[3];
6746  columnDefaultValuePtr = &columnParameters[3];
6747  }
6748  getline(columnChoicesISS, columnChoicesString, ';');
6749  outss << "\" \t DataChoices=\"";
6750  outss << columnChoicesString;
6751 
6752  std::string* columnMinValuePtr = nullptr;
6753  std::string* columnMaxValuePtr = nullptr;
6754 
6755  if(columnParameters.size() > 4 &&
6756  columnDataType == TableViewColumnInfo::DATATYPE_NUMBER)
6757  {
6758  columnMinValue = StringMacros::decodeURIComponent(columnParameters[4]);
6759  if(columnMinValue != "")
6760  {
6761  if(columnMinValue !=
6763  {
6764  __SUP_COUT__ << "FOUND user spec'd min value '" << columnParameters[4]
6765  << "'" << __E__;
6768  {
6769  __SS__ << "Inavlid user spec'd min value '" << columnParameters[4]
6770  << "' which evaluates to '" << columnMinValue
6771  << "' and is not a valid number. The minimum value must "
6772  "be a number (environment variables and math "
6773  "operations are allowed)."
6774  << __E__;
6775  __SS_THROW__;
6776  }
6777  outss << "\" \t MinValue=\"" << columnParameters[4];
6778  columnMinValuePtr = &columnParameters[4];
6779  }
6780  }
6781 
6782  columnMaxValue = StringMacros::decodeURIComponent(columnParameters[5]);
6783  if(columnMaxValue != "")
6784  {
6785  if(columnMaxValue !=
6787  {
6788  __SUP_COUT__ << "FOUND user spec'd max value = " << columnMaxValue
6789  << __E__;
6792  {
6793  __SS__ << "Inavlid user spec'd max value '" << columnParameters[5]
6794  << "' which evaluates to '" << columnMaxValue
6795  << "' and is not a valid number. The maximum value must "
6796  "be a number (environment variables and math "
6797  "operations are allowed)."
6798  << __E__;
6799  __SS_THROW__;
6800  }
6801  outss << "\" \t MaxValue=\"" << columnParameters[5];
6802  columnMaxValuePtr = &columnParameters[5];
6803  }
6804  }
6805  }
6806 
6807  //validate each column before saving a bad table file
6808  try
6809  {
6810  TableViewColumnInfo testCol(columnType,
6811  columnName,
6812  columnStorageName,
6813  columnDataType,
6814  columnDefaultValuePtr,
6815  columnChoicesString,
6816  columnMinValuePtr,
6817  columnMaxValuePtr,
6818  nullptr //capturedExceptionString
6819  );
6820  }
6821  catch(const std::runtime_error& e)
6822  {
6823  __SS__ << "Error identified with Column #" << c << ": \n" << e.what();
6824  __SS_THROW__;
6825  }
6826 
6827  if(TableViewColumnInfo::isChildLink(columnType))
6828  childLinkIndices.insert(columnType.substr(sizeof("ChildLink-") - 1));
6829  else if(columnType.find("ChildLinkUID-") == 0)
6830  childLinkUIDIndices.insert(columnType.substr(sizeof("ChildLinkUID-") - 1));
6831  else if(columnType.find("ChildLinkGroupID-") == 0)
6832  childLinkGroupIDIndices.insert(
6833  columnType.substr(sizeof("ChildLinkGroupID-") - 1));
6834 
6835  outss << "\"/>\n";
6836  }
6837 
6838  // Cross-column validation: every ChildLinkUID and ChildLinkGroupID must
6839  // have a matching ChildLink with the same index.
6840  for(const auto& idx : childLinkUIDIndices)
6841  if(childLinkIndices.find(idx) == childLinkIndices.end())
6842  {
6843  __SS__ << "Column type 'ChildLinkUID-" << idx
6844  << "' has no matching 'ChildLink-" << idx
6845  << "' column. A ChildLinkUID column must be paired with a "
6846  "ChildLink column using the same link index."
6847  << __E__;
6848  __SS_THROW__;
6849  }
6850  for(const auto& idx : childLinkGroupIDIndices)
6851  if(childLinkIndices.find(idx) == childLinkIndices.end())
6852  {
6853  __SS__ << "Column type 'ChildLinkGroupID-" << idx
6854  << "' has no matching 'ChildLink-" << idx
6855  << "' column. A ChildLinkGroupID column must be paired with a "
6856  "ChildLink column using the same link index. "
6857  "Did you intend to make a target of a Group Link? "
6858  "If so, use the 'GroupID' column type instead."
6859  << __E__;
6860  __SS_THROW__;
6861  }
6862  for(const auto& idx : childLinkIndices)
6863  if(childLinkUIDIndices.find(idx) == childLinkUIDIndices.end() &&
6864  childLinkGroupIDIndices.find(idx) == childLinkGroupIDIndices.end())
6865  {
6866  __SS__ << "Column type 'ChildLink-" << idx
6867  << "' has no matching 'ChildLinkUID-" << idx
6868  << "' or 'ChildLinkGroupID-" << idx
6869  << "' column. A ChildLink column must be paired with either a "
6870  "ChildLinkUID or ChildLinkGroupID column using the same link index."
6871  << __E__;
6872  __SS_THROW__;
6873  }
6874 
6875  outss << "\t\t\t</VIEW>\n";
6876  outss << "\t\t</TABLE>\n";
6877  outss << "\t</ROOT>\n";
6878 
6879  __SUP_COUT__ << outss.str() << __E__;
6880 
6881  FILE* fp = fopen((TABLE_INFO_PATH + tableName + TABLE_INFO_EXT).c_str(), "w");
6882  if(!fp)
6883  {
6884  xmlOut.addTextElementToData("Error",
6885  "Failed to open destination Table Info file:" +
6886  (TABLE_INFO_PATH + tableName + TABLE_INFO_EXT));
6887  return;
6888  }
6889 
6890  fprintf(fp, "%s", outss.str().c_str());
6891  fclose(fp);
6892 
6893  __SUP_COUT_INFO__ << "Finished saving Table Info for '" << tableName
6894  << ".' Looking for errors in all table column info..." << __E__;
6895 
6896  // reload all table info with refresh AND reset to pick up possibly new table
6897  // check for errors related to this tableName
6898  std::string accumulatedErrors = "";
6899  cfgMgr->getAllTableInfo(true /* refresh */, &accumulatedErrors, tableName);
6900 
6901  // if errors associated with this table name stop and report
6902  if(accumulatedErrors != "")
6903  {
6904  __SUP_SS__ << ("The new version of the '" + tableName +
6905  "' table column info was saved, however errors were detected "
6906  "reading back the table '" +
6907  tableName + "' after the save attempt:\n\n" + accumulatedErrors)
6908  << __E__;
6909 
6910  __SUP_COUT_ERR__ << ss.str() << __E__;
6911  xmlOut.addTextElementToData("Error", ss.str());
6912 
6913  return;
6914  }
6915 
6916  // return the new table info
6917  handleGetTableXML(xmlOut, cfgMgr, tableName, TableVersion());
6918 
6919  // After save, debug all table column info
6920  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
6921 
6922  // give a print out of currently illegal table column info
6923  for(const auto& cfgInfo : allTableInfo)
6924  {
6925  try
6926  {
6927  cfgMgr->getTableByName(cfgInfo.first)->getMockupViewP()->init();
6928  }
6929  catch(std::runtime_error& e)
6930  {
6931  __SUP_COUT_WARN__ << "\n\n##############################################\n"
6932  << "Error identified in column info of table '"
6933  << cfgInfo.first << "':\n\n"
6934  << e.what() << "\n\n"
6935  << __E__;
6936  }
6937  }
6938 } // end handleSaveTableInfoXML()
6939 
6940 //==============================================================================
6948 void ConfigurationGUISupervisor::handleSetGroupAliasInBackboneXML(
6949  HttpXmlDocument& xmlOut,
6950  ConfigurationManagerRW* cfgMgr,
6951  const std::string& groupAliasCSV,
6952  const std::string& groupNameCSV,
6953  const std::string& groupKeyCSV,
6954  const std::string& author)
6955 try
6956 {
6957  cfgMgr->loadConfigurationBackbone();
6958  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
6959 
6960  const std::string groupAliasesTableName =
6961  ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
6962  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
6963  {
6964  __SUP_SS__ << "Active version of " << groupAliasesTableName << " missing!"
6965  << __E__;
6966  xmlOut.addTextElementToData("Error", ss.str());
6967  return;
6968  }
6969 
6970  // put all old backbone versions in xmlOut
6971  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
6972  for(auto& memberName : backboneMembers)
6973  {
6974  __SUP_COUT__ << "activeVersions[\"" << memberName
6975  << "\"]=" << activeVersions[memberName] << __E__;
6976 
6977  xmlOut.addTextElementToData("oldBackboneName", memberName);
6978  xmlOut.addTextElementToData("oldBackboneVersion",
6979  activeVersions[memberName].toString());
6980  }
6981 
6982  // make a temporary version from active view
6983  // modify the chosen groupAlias row
6984  // save as new version
6985 
6986  TableBase* table = cfgMgr->getTableByName(groupAliasesTableName);
6987  TableVersion originalVersion = activeVersions[groupAliasesTableName];
6988  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
6989 
6990  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
6991  bool isDifferent = false;
6992 
6993  try
6994  {
6995  TableView* configView = table->getTemporaryView(temporaryVersion);
6996 
6997  unsigned int col = configView->findCol("GroupKeyAlias");
6998  unsigned int ccol = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
6999  unsigned int ncol = configView->findCol("GroupName");
7000  unsigned int kcol = configView->findCol("GroupKey");
7001 
7002  // only make a new version if we are changing compared to active backbone
7003  std::vector<std::string> groupAliases =
7004  StringMacros::getVectorFromString(groupAliasCSV);
7005  std::vector<std::string> groupNames =
7006  StringMacros::getVectorFromString(groupNameCSV);
7007  std::vector<std::string> groupKeys =
7008  StringMacros::getVectorFromString(groupKeyCSV);
7009  __SUP_COUTV__(StringMacros::vectorToString(groupAliases));
7010  __SUP_COUTV__(StringMacros::vectorToString(groupNames));
7011  __SUP_COUTV__(StringMacros::vectorToString(groupKeys));
7012 
7013  size_t i = 0;
7014  for(const auto& groupAlias : groupAliases)
7015  {
7016  if(groupAlias == "" || groupNames[i] == "" || groupKeys[i] == "")
7017  {
7018  //skip empty aliases
7019  __SUP_COUT_WARN__ << "Empty alias parameter found [" << i << "] = {"
7020  << groupAlias << ", " << groupNames[i] << "("
7021  << groupKeys[i] << ")}" << __E__;
7022  ++i;
7023  continue;
7024  }
7025 
7026  bool localIsDifferent = false;
7027  const std::string& groupName = groupNames[i];
7028  const TableGroupKey groupKey(groupKeys[i]);
7029  ++i;
7030 
7031  unsigned int row = -1;
7032  // find groupAlias row
7033  try
7034  {
7035  row = configView->findRow(col, groupAlias);
7036  }
7037  catch(...) // ignore not found error
7038  {
7039  }
7040 
7041  if(row == (unsigned int)-1) // if row not found then add a row
7042  {
7043  localIsDifferent = true;
7044  row = configView->addRow();
7045 
7046  // set all columns in new row
7047  configView->setValue(
7048  "This Group Alias was automatically setup by the server.", row, ccol);
7049  configView->setValue(groupAlias, row, col);
7050  }
7051 
7052  __SUP_COUT__ << "\t\t row: " << row << __E__;
7053 
7054  __SUP_COUT__ << "\t\t groupName: " << groupName << " vs "
7055  << configView->getDataView()[row][ncol] << __E__;
7056  if(groupName != configView->getDataView()[row][ncol])
7057  {
7058  configView->setValue(groupName, row, ncol);
7059  localIsDifferent = true;
7060  }
7061 
7062  __SUP_COUT__ << "\t\t groupKey: " << groupKey << " vs "
7063  << configView->getDataView()[row][kcol] << __E__;
7064  if(groupKey.toString() != configView->getDataView()[row][kcol])
7065  {
7066  configView->setValue(groupKey.toString(), row, kcol);
7067  localIsDifferent = true;
7068  }
7069 
7070  if(localIsDifferent) // set author/time of new record if different
7071  {
7072  configView->setValue(
7073  author,
7074  row,
7075  configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7076  configView->setValue(
7077  time(0),
7078  row,
7079  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7080  isDifferent = true;
7081  }
7082  } //end group alias modify loop
7083  }
7084  catch(...)
7085  {
7086  __SUP_COUT_ERR__ << "Error editing Group Alias view!" << __E__;
7087 
7088  // delete temporaryVersion
7089  table->eraseView(temporaryVersion);
7090  throw;
7091  }
7092 
7093  TableVersion newAssignedVersion;
7094  if(isDifferent) // make new version if different
7095  {
7096  __SUP_COUT__ << "\t\t**************************** Save as new table version"
7097  << __E__;
7098 
7099  // save or find equivalent
7101  xmlOut,
7102  cfgMgr,
7103  table->getTableName(),
7104  originalVersion,
7105  false /*makeTemporary*/,
7106  table,
7107  temporaryVersion,
7108  false /*ignoreDuplicates*/,
7109  true /*lookForEquivalent*/);
7110  }
7111  else // use existing version
7112  {
7113  __SUP_COUT__
7114  << "\t\t**************************** Using the existing table version"
7115  << __E__;
7116 
7117  // delete temporaryVersion
7118  table->eraseView(temporaryVersion);
7119  newAssignedVersion = activeVersions[groupAliasesTableName];
7120 
7121  xmlOut.addTextElementToData("savedName", groupAliasesTableName);
7122  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7123  }
7124 
7125  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
7126 } //end handleSetGroupAliasInBackboneXML()
7127 catch(std::runtime_error& e)
7128 {
7129  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << e.what() << __E__;
7130  __SUP_COUT_ERR__ << ss.str();
7131  xmlOut.addTextElementToData("Error", ss.str());
7132 }
7133 catch(...)
7134 {
7135  __SUP_SS__ << "Error saving new Group Alias view!\n\n " << __E__;
7136  try
7137  {
7138  throw;
7139  } //one more try to printout extra info
7140  catch(const std::exception& e)
7141  {
7142  ss << "Exception message: " << e.what();
7143  }
7144  catch(...)
7145  {
7146  }
7147  __SUP_COUT_ERR__ << ss.str();
7148  xmlOut.addTextElementToData("Error", ss.str());
7149 } //end handleSetGroupAliasInBackboneXML() catch
7150 
7151 //==============================================================================
7159 void ConfigurationGUISupervisor::handleSetTableAliasInBackboneXML(
7160  HttpXmlDocument& xmlOut,
7161  ConfigurationManagerRW* cfgMgr,
7162  const std::string& tableAlias,
7163  const std::string& tableName,
7164  TableVersion version,
7165  const std::string& author)
7166 try
7167 {
7168  cfgMgr->loadConfigurationBackbone();
7169  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7170 
7171  const std::string versionAliasesTableName =
7172  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7173  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7174  {
7175  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7176  << __E__;
7177  xmlOut.addTextElementToData("Error", ss.str());
7178  return;
7179  }
7180 
7181  // put all old backbone versions in xmlOut
7182  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7183  for(auto& memberName : backboneMembers)
7184  {
7185  __SUP_COUT__ << "activeVersions[\"" << memberName
7186  << "\"]=" << activeVersions[memberName] << __E__;
7187 
7188  xmlOut.addTextElementToData("oldBackboneName", memberName);
7189  xmlOut.addTextElementToData("oldBackboneVersion",
7190  activeVersions[memberName].toString());
7191  }
7192 
7193  // make a temporary version from active view
7194  // modify the chosen versionAlias row
7195  // save as new version
7196 
7197  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7198  TableVersion originalVersion = activeVersions[versionAliasesTableName];
7199  TableVersion temporaryVersion = table->createTemporaryView(originalVersion);
7200 
7201  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7202 
7203  bool isDifferent = false;
7204 
7205  try
7206  {
7207  TableView* configView = table->getTemporaryView(temporaryVersion);
7208 
7209  unsigned int col;
7210  unsigned int col2 = configView->findCol("VersionAlias");
7211  unsigned int col3 = configView->findCol("TableName");
7212 
7213  // only make a new version if we are changing compared to active backbone
7214 
7215  unsigned int row = -1;
7216  // find tableName, versionAlias pair
7217  // NOTE: only accept the first pair, repeats are ignored.
7218  try
7219  {
7220  unsigned int tmpRow = -1;
7221  do
7222  { // start looking from beyond last find
7223  tmpRow = configView->findRow(col3, tableName, tmpRow + 1);
7224  } while(configView->getDataView()[tmpRow][col2] != tableAlias);
7225  // at this point the first pair was found! (else exception was thrown)
7226  row = tmpRow;
7227  }
7228  catch(...)
7229  {
7230  }
7231  if(row == (unsigned int)-1) // if row not found then add a row
7232  {
7233  isDifferent = true;
7234  row = configView->addRow();
7235 
7236  // set all columns in new row
7237  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7238  configView->setValue(
7239  std::string("Entry was added by server in ") +
7240  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7241  row,
7242  col);
7243 
7244  col = configView->findCol("VersionAliasUID");
7245  configView->setValue(
7246  tableName.substr(0, tableName.rfind("Table")) + tableAlias, row, col);
7247 
7248  configView->setValue(tableAlias, row, col2);
7249  configView->setValue(tableName, row, col3);
7250  }
7251 
7252  __SUP_COUT__ << "\t\t row: " << row << __E__;
7253 
7254  col = configView->findCol("Version");
7255  __SUP_COUT__ << "\t\t version: " << version << " vs "
7256  << configView->getDataView()[row][col] << __E__;
7257  if(version.toString() != configView->getDataView()[row][col])
7258  {
7259  configView->setValue(version.toString(), row, col);
7260  isDifferent = true;
7261  }
7262 
7263  if(isDifferent) // set author/time of new version if different
7264  {
7265  configView->setValue(
7266  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7267  configView->setValue(
7268  time(0),
7269  row,
7270  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7271  }
7272  }
7273  catch(...)
7274  {
7275  __SUP_COUT_ERR__ << "Error editing Version Alias view!" << __E__;
7276 
7277  // delete temporaryVersion
7278  table->eraseView(temporaryVersion);
7279  throw;
7280  }
7281 
7282  TableVersion newAssignedVersion;
7283  if(isDifferent) // make new version if different
7284  {
7285  __SUP_COUT__ << "\t\t**************************** Save as new table version"
7286  << __E__;
7287 
7289  xmlOut,
7290  cfgMgr,
7291  table->getTableName(),
7292  originalVersion,
7293  false /*makeTemporary*/,
7294  table,
7295  temporaryVersion,
7296  false /*ignoreDuplicates*/,
7297  true /*lookForEquivalent*/);
7298  }
7299  else // use existing version
7300  {
7301  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7302  << __E__;
7303 
7304  // delete temporaryVersion
7305  table->eraseView(temporaryVersion);
7306  newAssignedVersion = activeVersions[versionAliasesTableName];
7307 
7308  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7309  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7310  }
7311 
7312  __SUP_COUT__ << "\t\t newAssignedVersion: " << newAssignedVersion << __E__;
7313 } // end handleSetVersionAliasInBackboneXML()
7314 catch(std::runtime_error& e)
7315 {
7316  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7317  __SUP_COUT_ERR__ << ss.str();
7318  xmlOut.addTextElementToData("Error", ss.str());
7319 }
7320 catch(...)
7321 {
7322  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7323  try
7324  {
7325  throw;
7326  } //one more try to printout extra info
7327  catch(const std::exception& e)
7328  {
7329  ss << "Exception message: " << e.what();
7330  }
7331  catch(...)
7332  {
7333  }
7334  __SUP_COUT_ERR__ << ss.str();
7335  xmlOut.addTextElementToData("Error", ss.str());
7336 } // end handleSetVersionAliasInBackboneXML() catch
7337 
7338 //==============================================================================
7344 void ConfigurationGUISupervisor::handleAliasGroupMembersInBackboneXML(
7345  HttpXmlDocument& xmlOut,
7346  ConfigurationManagerRW* cfgMgr,
7347  const std::string& versionAlias,
7348  const std::string& groupName,
7349  TableGroupKey groupKey,
7350  const std::string& author)
7351 try
7352 {
7353  cfgMgr->loadConfigurationBackbone();
7354  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7355 
7356  const std::string versionAliasesTableName =
7357  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7358  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7359  {
7360  __SUP_SS__ << "Active version of " << versionAliasesTableName << " missing!"
7361  << __E__;
7362  xmlOut.addTextElementToData("Error", ss.str());
7363  return;
7364  }
7365 
7366  // put all old backbone versions in xmlOut
7367  const std::set<std::string> backboneMembers = cfgMgr->getBackboneMemberNames();
7368  for(auto& memberName : backboneMembers)
7369  {
7370  __SUP_COUT__ << "activeVersions[\"" << memberName
7371  << "\"]=" << activeVersions[memberName] << __E__;
7372 
7373  xmlOut.addTextElementToData("oldBackboneName", memberName);
7374  xmlOut.addTextElementToData("oldBackboneVersion",
7375  activeVersions[memberName].toString());
7376  }
7377 
7378  // make a temporary version from active view
7379  // modify the chosen versionAlias row
7380  // save as new version
7381 
7382  TableBase* table = cfgMgr->getTableByName(versionAliasesTableName);
7383  TableVersion temporaryVersion =
7384  table->createTemporaryView(activeVersions[versionAliasesTableName]);
7385 
7386  __SUP_COUT__ << "\t\t temporaryVersion: " << temporaryVersion << __E__;
7387 
7388  TableView* configView = table->getTemporaryView(temporaryVersion);
7389 
7390  // only make a new version if we are changing compared to active backbone
7391  bool isDifferent = false;
7392 
7393  // get member names and versions
7394  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7395  try
7396  {
7397  cfgMgr->loadTableGroup(groupName,
7398  groupKey,
7399  false /*doActivate*/,
7400  &memberMap,
7401  0,
7402  0,
7403  0,
7404  0,
7405  0, // defaults
7406  true /*doNotLoadMember*/);
7407  }
7408  catch(...)
7409  {
7410  xmlOut.addTextElementToData(
7411  "Error",
7412  "Table group \"" + TableGroupKey::getFullGroupString(groupName, groupKey) +
7413  "\" can not be retrieved!");
7414  return;
7415  }
7416 
7417  unsigned int col;
7418  unsigned int col2 = configView->findCol("VersionAlias");
7419  unsigned int col3 = configView->findCol("TableName");
7420 
7421  for(auto& memberPair : memberMap)
7422  {
7423  bool thisMemberIsDifferent = false;
7424  unsigned int row = -1;
7425 
7426  __SUP_COUT__ << "Adding alias for " << memberPair.first << "_v"
7427  << memberPair.second << " to " << versionAlias << __E__;
7428 
7429  // find tableName, versionAlias pair
7430  // NOTE: only accept the first pair, repeats are ignored.
7431  try
7432  {
7433  unsigned int tmpRow = -1;
7434  do
7435  { // start looking from beyond last find
7436  tmpRow = configView->findRow(col3, memberPair.first, tmpRow + 1);
7437  } while(configView->getDataView()[tmpRow][col2] != versionAlias);
7438  // at this point the first pair was found! (else exception was thrown)
7439  row = tmpRow;
7440  }
7441  catch(...)
7442  {
7443  }
7444  if(row == (unsigned int)-1) // if row not found then add a row
7445  {
7446  thisMemberIsDifferent = true;
7447  row = configView->addRow();
7448 
7449  // set all columns in new row
7450  col = configView->findCol(TableViewColumnInfo::COL_NAME_COMMENT);
7451  configView->setValue(
7452  std::string("Entry was added by server in ") +
7453  "ConfigurationGUISupervisor::setTableAliasInActiveBackbone().",
7454  row,
7455  col);
7456 
7457  col = configView->getColUID();
7458  configView->setValue(
7459  memberPair.first.substr(0, memberPair.first.rfind("Table")) +
7460  versionAlias,
7461  row,
7462  col);
7463 
7464  configView->setValue(versionAlias, row, col2);
7465  configView->setValue(memberPair.first, row, col3);
7466  }
7467 
7468  col = configView->findCol("Version");
7469 
7470  if(memberPair.second.toString() != configView->getDataView()[row][col])
7471  {
7472  configView->setValue(memberPair.second.toString(), row, col);
7473  thisMemberIsDifferent = true;
7474  }
7475 
7476  if(thisMemberIsDifferent) // change author and time if row is different
7477  {
7478  configView->setValue(
7479  author, row, configView->findCol(TableViewColumnInfo::COL_NAME_AUTHOR));
7480  configView->setValue(
7481  time(0),
7482  row,
7483  configView->findCol(TableViewColumnInfo::COL_NAME_CREATION));
7484  }
7485 
7486  if(thisMemberIsDifferent)
7487  isDifferent = true;
7488  }
7489 
7490  // configView->print();
7491 
7492  TableVersion newAssignedVersion;
7493  if(isDifferent) // make new version if different
7494  {
7495  __SUP_COUT__ << "\t\t**************************** Save v" << temporaryVersion
7496  << " as new table version" << __E__;
7497 
7498  newAssignedVersion =
7499  cfgMgr->saveNewTable(versionAliasesTableName, temporaryVersion);
7500  }
7501  else // use existing version
7502  {
7503  __SUP_COUT__ << "\t\t**************************** Using existing table version"
7504  << __E__;
7505 
7506  // delete temporaryVersion
7507  table->eraseView(temporaryVersion);
7508  newAssignedVersion = activeVersions[versionAliasesTableName];
7509  }
7510 
7511  xmlOut.addTextElementToData("savedName", versionAliasesTableName);
7512  xmlOut.addTextElementToData("savedVersion", newAssignedVersion.toString());
7513  __SUP_COUT__ << "\t\t Resulting Version: " << newAssignedVersion << __E__;
7514 } // end handleAliasGroupMembersInBackboneXML()
7515 catch(std::runtime_error& e)
7516 {
7517  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << e.what() << __E__;
7518  __SUP_COUT_ERR__ << ss.str();
7519  xmlOut.addTextElementToData("Error", ss.str());
7520 }
7521 catch(...)
7522 {
7523  __SUP_SS__ << "Error saving new Version Alias view!\n\n " << __E__;
7524  try
7525  {
7526  throw;
7527  } //one more try to printout extra info
7528  catch(const std::exception& e)
7529  {
7530  ss << "Exception message: " << e.what();
7531  }
7532  catch(...)
7533  {
7534  }
7535  __SUP_COUT_ERR__ << ss.str();
7536  xmlOut.addTextElementToData("Error", ss.str());
7537 } // end handleAliasGroupMembersInBackboneXML() catch
7538 
7539 //==============================================================================
7550 void ConfigurationGUISupervisor::handleGroupAliasesXML(HttpXmlDocument& xmlOut,
7551  ConfigurationManagerRW* cfgMgr)
7552 {
7553  cfgMgr->loadConfigurationBackbone();
7554  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7555 
7556  std::string groupAliasesTableName = ConfigurationManager::GROUP_ALIASES_TABLE_NAME;
7557  if(activeVersions.find(groupAliasesTableName) == activeVersions.end())
7558  {
7559  __SUP_SS__ << "\nActive version of " << groupAliasesTableName << " missing! "
7560  << groupAliasesTableName
7561  << " is a required member of the Backbone table group."
7562  << "\n\nLikely you need to activate a valid Backbone table group."
7563  << __E__;
7564  __SUP_COUT__ << ss.str(); // just output findings, and return empty xml to avoid
7565  // infinite error loops in GUI
7566  // xmlOut.addTextElementToData("Error", ss.str());
7567  return;
7568  }
7569  __SUP_COUT__ << "activeVersions[\"" << groupAliasesTableName
7570  << "\"]=" << activeVersions[groupAliasesTableName] << __E__;
7571  xmlOut.addTextElementToData("GroupAliasesTableName", groupAliasesTableName);
7572  xmlOut.addTextElementToData("GroupAliasesTableVersion",
7573  activeVersions[groupAliasesTableName].toString());
7574 
7575  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7576  cfgMgr->getNode(groupAliasesTableName).getChildren();
7577 
7578  const int numOfThreads = StringMacros::getConcurrencyCount() / 2;
7579  __SUP_COUT__ << " getConcurrencyCount " << StringMacros::getConcurrencyCount()
7580  << " ==> " << numOfThreads << " threads for alias group loads." << __E__;
7581 
7582  if(numOfThreads < 2) // no multi-threading
7583  {
7584  std::string groupName, groupKey, groupComment, groupAuthor, groupCreateTime,
7585  groupType;
7586  for(auto& aliasNodePair : aliasNodePairs)
7587  {
7588  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7589  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7590 
7591  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7592  xmlOut.addTextElementToData("GroupName", groupName);
7593  xmlOut.addTextElementToData("GroupKey", groupKey);
7594  xmlOut.addTextElementToData(
7595  "AliasComment",
7596  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7597  .getValueAsString());
7598 
7599  // get group comment
7600  groupComment =
7601  ConfigurationManager::UNKNOWN_INFO; // clear just in case failure
7602  groupType = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7603  try
7604  {
7605  cfgMgr->loadTableGroup(groupName,
7606  TableGroupKey(groupKey),
7607  false /* doActivate */,
7608  0 /* groupMembers */,
7609  0 /* progressBar */,
7610  0 /* accumulatedWarnings */,
7611  &groupComment,
7612  &groupAuthor,
7613  &groupCreateTime,
7614  true /*doNotLoadMembers*/,
7615  &groupType);
7616  }
7617  catch(...)
7618  {
7619  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
7620  << groupKey << ")' to extract group comment and type."
7621  << __E__;
7622  }
7623  xmlOut.addTextElementToData("GroupComment", groupComment);
7624  xmlOut.addTextElementToData("GroupType", groupType);
7625  } // end alias pair loop
7626  }
7627  else //multi-threading
7628  {
7629  int threadsLaunched = 0;
7630  int foundThreadIndex = 0;
7631  std::vector<std::shared_ptr<std::atomic<bool>>> threadDone;
7632  for(int i = 0; i < numOfThreads; ++i)
7633  threadDone.push_back(std::make_shared<std::atomic<bool>>(true));
7634 
7635  std::vector<std::shared_ptr<ots::GroupInfo>> sharedGroupInfoPtrs;
7636  std::string groupName, groupKey;
7637 
7638  for(auto& aliasNodePair : aliasNodePairs)
7639  {
7640  //make temporary group info for thread
7641  sharedGroupInfoPtrs.push_back(std::make_shared<ots::GroupInfo>());
7642 
7643  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7644  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7645 
7646  if(threadsLaunched >= numOfThreads)
7647  {
7648  //find availableThreadIndex
7649  foundThreadIndex = -1;
7650  while(foundThreadIndex == -1)
7651  {
7652  for(int i = 0; i < numOfThreads; ++i)
7653  if(*(threadDone[i]))
7654  {
7655  foundThreadIndex = i;
7656  break;
7657  }
7658  if(foundThreadIndex == -1)
7659  {
7660  __SUP_COUTT__ << "Waiting for available thread..." << __E__;
7661  usleep(10000);
7662  }
7663  } //end thread search loop
7664  threadsLaunched = numOfThreads - 1;
7665  }
7666  __SUP_COUTT__ << "Starting load group thread... " << groupName << "("
7667  << groupKey << ")" << __E__;
7668  *(threadDone[foundThreadIndex]) = false;
7669 
7670  std::thread(
7671  [](ConfigurationManagerRW* theCfgMgr,
7672  std::string theGroupName,
7673  ots::TableGroupKey theGroupKey,
7674  std::shared_ptr<ots::GroupInfo> theGroupInfo,
7675  std::shared_ptr<std::atomic<bool>> theThreadDone) {
7677  theGroupName,
7678  theGroupKey,
7679  theGroupInfo,
7680  theThreadDone);
7681  },
7682  cfgMgr,
7683  groupName,
7684  TableGroupKey(groupKey),
7685  sharedGroupInfoPtrs.back(),
7686  threadDone[foundThreadIndex])
7687  .detach();
7688 
7689  ++threadsLaunched;
7690  ++foundThreadIndex;
7691 
7692  } //end alias group thread loop
7693 
7694  //check for all threads done
7695  do
7696  {
7697  foundThreadIndex = -1;
7698  for(int i = 0; i < numOfThreads; ++i)
7699  if(!*(threadDone[i]))
7700  {
7701  foundThreadIndex = i;
7702  break;
7703  }
7704  if(foundThreadIndex != -1)
7705  {
7706  __SUP_COUTT__ << "Waiting for thread to finish... " << foundThreadIndex
7707  << __E__;
7708  usleep(10000);
7709  }
7710  } while(foundThreadIndex != -1); //end thread done search loop
7711 
7712  //threads done now, so copy group info
7713  size_t i = 0;
7714  for(auto& aliasNodePair : aliasNodePairs)
7715  {
7716  groupName = aliasNodePair.second.getNode("GroupName").getValueAsString();
7717  groupKey = aliasNodePair.second.getNode("GroupKey").getValueAsString();
7718 
7719  if(groupKey != sharedGroupInfoPtrs[i]->getLatestKey().toString())
7720  {
7721  __SUP_SS__ << "Error loading group information for the group alias '"
7722  << aliasNodePair.first << "' mapping to group '" << groupName
7723  << "(" << groupKey << ")" << __E__;
7724  __SUP_SS_THROW__;
7725  }
7726 
7727  xmlOut.addTextElementToData("GroupAlias", aliasNodePair.first);
7728  xmlOut.addTextElementToData("GroupName", groupName);
7729  xmlOut.addTextElementToData(
7730  "GroupKey", sharedGroupInfoPtrs[i]->getLatestKey().toString());
7731  xmlOut.addTextElementToData(
7732  "AliasComment",
7733  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7734  .getValueAsString());
7735 
7736  xmlOut.addTextElementToData(
7737  "GroupComment", sharedGroupInfoPtrs[i]->getLatestKeyGroupComment());
7738  xmlOut.addTextElementToData(
7739  "GroupAuthor", sharedGroupInfoPtrs[i]->getLatestKeyGroupAuthor());
7740  xmlOut.addTextElementToData(
7741  "GroupCreationTime",
7742  sharedGroupInfoPtrs[i]->getLatestKeyGroupCreationTime());
7743  xmlOut.addTextElementToData(
7744  "GroupType", sharedGroupInfoPtrs[i]->getLatestKeyGroupTypeString());
7745  // xmlOut.addTextElementToData("GroupMemberMap", sharedGroupInfoPtrs[i]->latestKeyMemberMap_);
7746  ++i;
7747  } //end copy group info loop
7748 
7749  } //end multi-thread handling
7750 } // end handleGroupAliasesXML
7751 
7752 //==============================================================================
7763 void ConfigurationGUISupervisor::handleVersionAliasesXML(HttpXmlDocument& xmlOut,
7764  ConfigurationManagerRW* cfgMgr)
7765 {
7766  cfgMgr->loadConfigurationBackbone();
7767  std::map<std::string, TableVersion> activeVersions = cfgMgr->getActiveVersions();
7768 
7769  std::string versionAliasesTableName =
7770  ConfigurationManager::VERSION_ALIASES_TABLE_NAME;
7771  if(activeVersions.find(versionAliasesTableName) == activeVersions.end())
7772  {
7773  __SUP_SS__ << "Active version of VersionAliases missing!"
7774  << "Make sure you have a valid active Backbone Group." << __E__;
7775  xmlOut.addTextElementToData("Error", ss.str());
7776  return;
7777  }
7778  __SUP_COUT__ << "activeVersions[\"" << versionAliasesTableName
7779  << "\"]=" << activeVersions[versionAliasesTableName] << __E__;
7780  xmlOut.addTextElementToData("VersionAliasesVersion",
7781  activeVersions[versionAliasesTableName].toString());
7782 
7783  std::vector<std::pair<std::string, ConfigurationTree>> aliasNodePairs =
7784  cfgMgr->getNode(versionAliasesTableName).getChildren();
7785 
7786  for(auto& aliasNodePair : aliasNodePairs)
7787  {
7788  // note : these are column names in the versionAliasesTableName table
7789  // VersionAlias, TableName, Version, CommentDescription
7790  xmlOut.addTextElementToData(
7791  "VersionAlias",
7792  aliasNodePair.second.getNode("VersionAlias").getValueAsString());
7793  xmlOut.addTextElementToData(
7794  "TableName", aliasNodePair.second.getNode("TableName").getValueAsString());
7795  xmlOut.addTextElementToData(
7796  "Version", aliasNodePair.second.getNode("Version").getValueAsString());
7797  xmlOut.addTextElementToData(
7798  "Comment",
7799  aliasNodePair.second.getNode(TableViewColumnInfo::COL_NAME_COMMENT)
7800  .getValueAsString());
7801  }
7802 } // end handleVersionAliasesXML()
7803 
7804 //==============================================================================
7810 void ConfigurationGUISupervisor::handleGetTableGroupTypeXML(
7811  HttpXmlDocument& xmlOut, ConfigurationManagerRW* cfgMgr, const std::string& tableList)
7812 {
7813  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
7814  std::string name, versionStr;
7815  auto c = tableList.find(',', 0);
7816  auto i = c;
7817  i = 0; // auto used to get proper index/length type
7818  while(c < tableList.length())
7819  {
7820  // add the table name and version pair to the map
7821  name = tableList.substr(i, c - i);
7822  i = c + 1;
7823  c = tableList.find(',', i);
7824  if(c == std::string::npos) // missing version list entry?!
7825  {
7826  __SUP_SS__ << "Incomplete Table Name-Version pair!" << __E__;
7827  __SUP_COUT_ERR__ << "\n" << ss.str();
7828  xmlOut.addTextElementToData("Error", ss.str());
7829  return;
7830  }
7831 
7832  versionStr = tableList.substr(i, c - i);
7833  i = c + 1;
7834  c = tableList.find(',', i);
7835 
7836  memberMap[name] = TableVersion(versionStr);
7837  }
7838 
7839  std::string groupTypeString = "";
7840  // try to determine type, dont report errors, just mark ots::GroupType::UNKNOWN_TYPE
7841  try
7842  {
7843  // determine the type of the table group
7844  groupTypeString = cfgMgr->getTypeNameOfGroup(memberMap);
7845  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7846  }
7847  catch(std::runtime_error& e)
7848  {
7849  __SUP_SS__ << "Table group has invalid type! " << e.what() << __E__;
7850  __SUP_COUT__ << "\n" << ss.str();
7851  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7852  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7853  }
7854  catch(...)
7855  {
7856  __SUP_SS__ << "Table group has invalid type! " << __E__;
7857  try
7858  {
7859  throw;
7860  } //one more try to printout extra info
7861  catch(const std::exception& e)
7862  {
7863  ss << "Exception message: " << e.what();
7864  }
7865  catch(...)
7866  {
7867  }
7868  __SUP_COUT__ << "\n" << ss.str();
7869  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
7870  xmlOut.addTextElementToData("TableGroupType", groupTypeString);
7871  }
7872 } //end handleGetTableGroupTypeXML()
7873 
7874 //==============================================================================
7890 void ConfigurationGUISupervisor::handleTableGroupsXML(HttpXmlDocument& xmlOut,
7891  ConfigurationManagerRW* cfgMgr,
7892  bool returnMembers)
7893 {
7894  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7895  // 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
7896  // xercesc::DOMElement* parentEl;
7897 
7898  // get all group info from cache (if no cache, get from interface)
7899 
7900  if(!cfgMgr->getAllGroupInfo().size() ||
7901  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() == "" ||
7902  cfgMgr->getAllGroupInfo().begin()->second.getLatestKeyGroupTypeString() ==
7903  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN ||
7904  ( //if active Context group type is not defined, then refresh
7905  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE) !=
7906  TableGroupKey::INVALID &&
7907  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7908  ConfigurationManager::GroupType::CONTEXT_TYPE)) !=
7909  cfgMgr->getAllGroupInfo().end() &&
7910  (cfgMgr->getAllGroupInfo()
7911  .at(cfgMgr->getActiveGroupName(
7912  ConfigurationManager::GroupType::CONTEXT_TYPE))
7913  .getLatestKeyGroupTypeString() == "" ||
7914  cfgMgr->getAllGroupInfo()
7915  .at(cfgMgr->getActiveGroupName(
7916  ConfigurationManager::GroupType::CONTEXT_TYPE))
7917  .getLatestKeyGroupTypeString() ==
7918  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)) ||
7919  ( //if active Config group type is not defined, then refresh
7920  cfgMgr->getActiveGroupKey(
7921  ConfigurationManager::GroupType::CONFIGURATION_TYPE) !=
7922  TableGroupKey::INVALID &&
7923  cfgMgr->getAllGroupInfo().find(cfgMgr->getActiveGroupName(
7924  ConfigurationManager::GroupType::CONFIGURATION_TYPE)) !=
7925  cfgMgr->getAllGroupInfo().end() &&
7926  (cfgMgr->getAllGroupInfo()
7927  .at(cfgMgr->getActiveGroupName(
7928  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7929  .getLatestKeyGroupTypeString() == "" ||
7930  cfgMgr->getAllGroupInfo()
7931  .at(cfgMgr->getActiveGroupName(
7932  ConfigurationManager::GroupType::CONFIGURATION_TYPE))
7933  .getLatestKeyGroupTypeString() ==
7934  ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN)))
7935  {
7936  __SUP_COUT__
7937  << "Group Info cache appears empty or stale. Attempting to regenerate..."
7938  << __E__;
7939  cfgMgr->getAllTableInfo(true /*refresh*/,
7940  0 /* accumulatedWarnings */,
7941  "" /* errorFilterName */,
7942  true /* getGroupKeys */,
7943  true /* getGroupInfo */,
7944  true /* initializeActiveGroups */);
7945  }
7946 
7947  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
7948 
7949  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
7950 
7951  std::string groupName;
7952  std::string groupString, groupTypeString, groupComment, groupCreationTime,
7953  groupAuthor;
7954  for(auto& groupInfo : allGroupInfo)
7955  {
7956  groupName = groupInfo.first;
7957 
7958  //get group info and force update of groupKeys from DB Interface cache if possible
7959  cfgMgr->getGroupInfo(groupName, true /* attemptToReloadKeys */);
7960 
7961  if(groupInfo.second.getKeys().size() == 0)
7962  {
7963  __SUP_COUT__ << "Group name '" << groupName
7964  << "' found, but no keys so ignoring." << __E__;
7965  continue;
7966  }
7967 
7968  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
7969  xmlOut.dataSs_ << "<TableGroupKey value='" << groupInfo.second.getLatestKey()
7970  << "'/>" << __E__;
7971 
7972  // trusting the cache!
7973  xmlOut.dataSs_ << "<TableGroupType value='"
7974  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
7975  << __E__;
7976  xmlOut.dataSs_ << "<TableGroupComment value='"
7978  groupInfo.second.getLatestKeyGroupComment(),
7979  true /* allowWhiteSpace */)
7980  << "'/>" << __E__;
7981  xmlOut.dataSs_ << "<TableGroupAuthor value='"
7982  << groupInfo.second.getLatestKeyGroupAuthor() << "'/>" << __E__;
7983  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
7984  << groupInfo.second.getLatestKeyGroupCreationTime() << "'/>"
7985  << __E__;
7986 
7987  if(returnMembers)
7988  {
7989  // parentEl = xmlOut.addTextElementToData("TableGroupMembers", "");
7990  xmlOut.dataSs_ << "<TableGroupMembers value=''>" << __E__;
7991 
7992  for(auto& memberPair : groupInfo.second.getLatestKeyMemberMap())
7993  {
7994  xmlOut.dataSs_ << "\t<MemberName value='" << memberPair.first << "'/>"
7995  << __E__;
7996  xmlOut.dataSs_ << "\t<MemberVersion value='" << memberPair.second << "'/>"
7997  << __E__;
7998 
7999  // xmlOut.addTextElementToParent("MemberName", memberPair.first, parentEl);
8000  // xmlOut.addTextElementToParent(
8001  // "MemberVersion", memberPair.second.toString(), parentEl);
8002  }
8003  xmlOut.dataSs_ << "</TableGroupMembers>" << __E__;
8004  } // end if returnMembers
8005 
8006  // add other group keys to xml for this group name
8007  // but just empty members (not displayed anyway)
8008  for(auto& keyInSet : groupInfo.second.getKeys())
8009  {
8010  if(keyInSet == groupInfo.second.getLatestKey())
8011  continue; // skip the lastest
8012 
8013  xmlOut.dataSs_ << "<TableGroupName value='" << groupName << "'/>" << __E__;
8014  xmlOut.dataSs_ << "<TableGroupKey value='" << keyInSet << "'/>" << __E__;
8015  // xmlOut.addTextElementToData("TableGroupName", groupName);
8016  // xmlOut.addTextElementToData("TableGroupKey", keyInSet.toString());
8017 
8018  // TODO -- make loadingHistoricalInfo an input parameter
8019  bool loadingHistoricalInfo = false;
8020  if(loadingHistoricalInfo)
8021  {
8022  groupComment = ""; // clear just in case failure
8023  try
8024  {
8025  cfgMgr->loadTableGroup(groupName,
8026  keyInSet,
8027  0,
8028  0,
8029  0,
8030  0,
8031  &groupComment,
8032  0,
8033  0, // mostly defaults
8034  true /*doNotLoadMembers*/,
8035  &groupTypeString);
8036  }
8037  catch(...)
8038  {
8039  groupTypeString = ConfigurationManager::GROUP_TYPE_NAME_UNKNOWN;
8040  __SUP_COUT_WARN__ << "Failed to load group '" << groupName << "("
8041  << keyInSet
8042  << ")' to extract group comment and type." << __E__;
8043  }
8044 
8045  xmlOut.dataSs_ << "<TableGroupType value='" << groupTypeString << "'/>"
8046  << __E__;
8047  xmlOut.dataSs_ << "<TableGroupComment value='"
8048  << StringMacros::escapeString(groupComment,
8049  true /* allowWhiteSpace */)
8050  << "'/>" << __E__;
8051  xmlOut.dataSs_ << "<TableGroupAuthor value='" << groupAuthor << "'/>"
8052  << __E__;
8053  xmlOut.dataSs_ << "<TableGroupCreationTime value='" << groupCreationTime
8054  << "'/>" << __E__;
8055  // xmlOut.addTextElementToData("TableGroupType", groupTypeString);
8056  // xmlOut.addTextElementToData("TableGroupComment", groupComment);
8057  // xmlOut.addTextElementToData("TableGroupAuthor", groupAuthor);
8058  // xmlOut.addTextElementToData("TableGroupCreationTime", groupCreationTime);
8059  }
8060  else
8061  {
8062  // just use guess that historical groups are of same type
8063  xmlOut.dataSs_ << "<TableGroupType value='"
8064  << groupInfo.second.getLatestKeyGroupTypeString() << "'/>"
8065  << __E__;
8066  //leave place holder comment,author,time for javascript parsing
8067  xmlOut.dataSs_ << "<TableGroupComment value='"
8068  << ""
8069  << "'/>" << __E__;
8070  xmlOut.dataSs_ << "<TableGroupAuthor value='"
8071  << ""
8072  << "'/>" << __E__;
8073  xmlOut.dataSs_ << "<TableGroupCreationTime value='"
8074  << ""
8075  << "'/>" << __E__;
8076  }
8077 
8078  if(returnMembers)
8079  {
8080  //need to add empty group members, event for historical groups, for easier Javascript extraction
8081  xmlOut.dataSs_ << "<TableGroupMembers/>" << __E__;
8082  // xmlOut.addTextElementToData("TableGroupMembers", "");
8083  }
8084 
8085  } // end other key loop
8086  __SUP_COUTT__ << groupName << " runtime=" << cfgMgr->runTimeSeconds() << __E__;
8087  } // end primary group loop
8088  __SUP_COUTT__ << "cfgMgr runtime=" << cfgMgr->runTimeSeconds() << __E__;
8089 } // end handleTableGroupsXML()
8090 
8091 //==============================================================================
8110 void ConfigurationGUISupervisor::handleTablesXML(HttpXmlDocument& xmlOut,
8111  ConfigurationManagerRW* cfgMgr,
8112  const std::string& filterStartTimeStr,
8113  const std::string& filterEndTimeStr,
8114  const std::string& filterMode)
8115 {
8116  time_t filterStartTime = 0;
8117  time_t filterEndTime = 0;
8118  if(filterStartTimeStr != "")
8119  {
8120  try
8121  {
8122  filterStartTime = static_cast<time_t>(std::stoll(filterStartTimeStr));
8123  }
8124  catch(const std::exception& e)
8125  {
8126  __SUP_SS__ << "Error parsing startTime parameter: " << e.what() << __E__;
8127  __SUP_COUT_ERR__ << "\n" << ss.str();
8128  xmlOut.addTextElementToData("Error", ss.str());
8129  return;
8130  }
8131  }
8132  if(filterEndTimeStr != "")
8133  {
8134  try
8135  {
8136  filterEndTime = static_cast<time_t>(std::stoll(filterEndTimeStr));
8137  }
8138  catch(const std::exception& e)
8139  {
8140  __SUP_SS__ << "Error parsing endTime parameter: " << e.what() << __E__;
8141  __SUP_COUT_ERR__ << "\n" << ss.str();
8142  xmlOut.addTextElementToData("Error", ss.str());
8143  return;
8144  }
8145  }
8146  if(filterStartTime != 0 && filterEndTime != 0 && filterStartTime > filterEndTime)
8147  {
8148  __SUP_SS__ << "Invalid time range: startTime (" << filterStartTime
8149  << ") must be <= endTime (" << filterEndTime << ")." << __E__;
8150  __SUP_SS_THROW__;
8151  }
8152  if(filterMode != "created" && filterMode != "loaded")
8153  {
8154  __SUP_SS__ << "Invalid filterMode parameter '" << filterMode
8155  << ".' Expected 'created' or 'loaded.'" << __E__;
8156  __SUP_COUT_ERR__ << "\n" << ss.str();
8157  xmlOut.addTextElementToData("Error", ss.str());
8158  return;
8159  }
8160  // diagnostics: track where the time is going
8161  const auto diagStartTime = std::chrono::steady_clock::now();
8162  auto diagElapsedSec = [](const std::chrono::steady_clock::time_point& start) {
8163  return std::chrono::duration<double>(std::chrono::steady_clock::now() - start)
8164  .count();
8165  };
8166 
8167  if(cfgMgr->getAllGroupInfo().size() == 0 || cfgMgr->getActiveVersions().size() == 0)
8168  {
8169  __SUP_COUT__ << "Table Info cache appears empty. Attempting to regenerate."
8170  << __E__;
8171  cfgMgr->getAllTableInfo(true /*refresh*/,
8172  0 /* accumulatedWarnings */,
8173  "" /* errorFilterName */,
8174  false /* getGroupKeys */,
8175  false /* getGroupInfo */,
8176  true /* initializeActiveGroups */);
8177  __SUP_COUT__ << "getAllTableInfo() regenerate took "
8178  << diagElapsedSec(diagStartTime) << " s" << __E__;
8179  }
8180 
8181  xercesc::DOMElement* parentEl;
8182  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
8183 
8184  // construct specially ordered table name set
8185  std::set<std::string, StringMacros::IgnoreCaseCompareStruct> orderedTableSet;
8186  for(const auto& tablePair : allTableInfo)
8187  orderedTableSet.emplace(tablePair.first);
8188 
8189  // std::map<std::string, TableInfo>::const_iterator it = allTableInfo.begin();
8190 
8191  __SUP_COUT__ << "# of tables to consider: " << allTableInfo.size() << __E__;
8192 
8193  const auto diagAliasStartTime = std::chrono::steady_clock::now();
8194  std::map<std::string, std::map<std::string, TableVersion>> versionAliases =
8195  cfgMgr->getVersionAliases();
8196 
8197  __SUP_COUT__ << "# of tables w/aliases: " << versionAliases.size() << " (took "
8198  << diagElapsedSec(diagAliasStartTime) << " s)" << __E__;
8199 
8200  if(filterStartTime != 0 && filterEndTime != 0 && filterMode == "created")
8201  {
8202  // parallel pre-warm of the process-wide creation time cache, so the
8203  // filter loop below gets immediate cache hits (only versions not yet in
8204  // the disk-persisted cache are loaded, so this is cheap after first use)
8205  const auto diagPreloadStartTime = std::chrono::steady_clock::now();
8206  cfgMgr->preloadVersionCreationTimes();
8207  __SUP_COUT__ << "Version creation time cache pre-warm took "
8208  << diagElapsedSec(diagPreloadStartTime) << " s" << __E__;
8209  }
8210 
8211  // diagnostics accumulated over the table loop
8212  size_t diagNumTablesFound = 0; //tables with at least one version listed
8213  size_t diagNumVersionsConsidered = 0;
8214  size_t diagNumVersionsMatched = 0;
8215  size_t diagNumTimeLookups = 0;
8216  double diagTimeLookupSec = 0; //cumulative time in creation/load time lookups
8217  double diagSlowestTableSec = 0;
8218  std::string diagSlowestTableName = "";
8219  size_t diagTableCount = 0;
8220 
8221  for(const auto& orderedTableName : orderedTableSet) // while(it !=
8222  // allTableInfo.end())
8223  {
8224  std::map<std::string, TableInfo>::const_iterator it =
8225  allTableInfo.find(orderedTableName);
8226  if(it == allTableInfo.end())
8227  {
8228  __SS__ << "Impossible missing table in map '" << orderedTableName << "'"
8229  << __E__;
8230  __SS_THROW__;
8231  }
8232 
8233  // for each table name
8234  // get existing version keys
8235 
8236  // add system table name
8237  xmlOut.addTextElementToData("TableName", it->first);
8238  parentEl = xmlOut.addTextElementToData("TableVersions", "");
8239 
8240  // include aliases for this table (if the versions exist)
8241  if(versionAliases.find(it->first) != versionAliases.end())
8242  for(auto& aliasVersion : versionAliases[it->first])
8243  if(it->second.versions_.find(aliasVersion.second) !=
8244  it->second.versions_
8245  .end()) //Note : scratch version is always an alias ==> ConfigurationManager::SCRATCH_VERSION_ALIAS)
8246  xmlOut.addTextElementToParent(
8247  "Version",
8248  ConfigurationManager::ALIAS_VERSION_PREAMBLE + aliasVersion.first,
8249  parentEl);
8250 
8251  // get all table versions for the current table
8252  // except skip scratch version
8253  // for speed, group versions into spans:
8254  //======
8256  auto vSpanToXML = [&diagNumVersionsConsidered,
8257  &diagNumVersionsMatched,
8258  &diagNumTimeLookups,
8259  &diagTimeLookupSec](auto const& sortedKeys,
8260  auto& xmlOut,
8261  auto& configEl,
8262  const std::string& tableName,
8263  ConfigurationManagerRW* cfgMgr,
8264  const time_t filterStartTime,
8265  const time_t filterEndTime,
8266  const std::string& filterMode) {
8267  //add lo and hi spans, instead of each individual value
8268  size_t lo = -1, hi = -1;
8269  bool allVersionsFiltered = true;
8270  for(auto& keyInOrder : sortedKeys)
8271  {
8272  //skip scratch version
8273  if(keyInOrder.isScratchVersion())
8274  continue;
8275 
8276  ++diagNumVersionsConsidered;
8277 
8278  if(filterStartTime != 0 && filterEndTime != 0)
8279  {
8280  const auto diagLookupStartTime = std::chrono::steady_clock::now();
8281  try
8282  {
8283  // Note: neither helper stamps the version's lastAccessTime
8284  // ("Last Load"), so filtering does not corrupt Last Load times.
8285  // - "created" times are immutable and cached, so each version is
8286  // loaded from the database at most once per process lifetime.
8287  // - "loaded" times are in-memory only; 0 (never loaded by this
8288  // process, or evicted from cache) falls outside any range.
8289  time_t tableVersionTime =
8290  filterMode == "loaded"
8291  ? cfgMgr->getVersionLastAccessTime(tableName, keyInOrder)
8292  : cfgMgr->getVersionCreationTime(tableName, keyInOrder);
8293 
8294  ++diagNumTimeLookups;
8295  diagTimeLookupSec +=
8296  std::chrono::duration<double>(
8297  std::chrono::steady_clock::now() - diagLookupStartTime)
8298  .count();
8299 
8300  if(tableVersionTime < filterStartTime ||
8301  tableVersionTime > filterEndTime)
8302  {
8303  //Note: trace-level to avoid log flooding (one line per
8304  // filtered version can be thousands of lines)
8305  __COUTT__ << "Table '" << tableName << "' version v"
8306  << keyInOrder << " " << filterMode
8307  << " time is outside the filter range, so "
8308  "skipping."
8309  << __E__;
8310  continue;
8311  }
8312  }
8313  catch(const std::runtime_error&)
8314  {
8315  ++diagNumTimeLookups;
8316  diagTimeLookupSec +=
8317  std::chrono::duration<double>(
8318  std::chrono::steady_clock::now() - diagLookupStartTime)
8319  .count();
8320  __COUT__ << "Failed to get " << filterMode << " time for table '"
8321  << tableName << "' version v" << keyInOrder
8322  << ", so skipping." << __E__;
8323  continue;
8324  }
8325  }
8326 
8327  allVersionsFiltered = false;
8328  ++diagNumVersionsMatched;
8329 
8330  if(lo == size_t(-1)) //establish start of potential span
8331  {
8332  hi = lo = keyInOrder.version();
8333  continue;
8334  }
8335  else if(hi + 1 == keyInOrder.version()) //span is growing
8336  {
8337  hi = keyInOrder.version();
8338  continue;
8339  }
8340  //else jump by more than one, so close out span
8341 
8342  if(lo == hi) //single value
8343  xmlOut.addNumberElementToParent("Version", lo, configEl);
8344  else //span
8345  xmlOut.addTextElementToParent(
8346  "Version",
8347  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8348  configEl);
8349  hi = lo = keyInOrder.version();
8350  }
8351 
8352  if(lo != size_t(-1)) //check if last one to do!
8353  {
8354  if(lo == hi) //single value
8355  xmlOut.addNumberElementToParent("Version", lo, configEl);
8356  else //span
8357  xmlOut.addTextElementToParent(
8358  "Version",
8359  "_" + std::to_string(lo) + "_" + std::to_string(hi),
8360  configEl);
8361  }
8362  return allVersionsFiltered;
8363  }; //end local lambda vSpanToXML()
8364 
8365  const auto diagTableStartTime = std::chrono::steady_clock::now();
8366  if(vSpanToXML(it->second.versions_,
8367  xmlOut,
8368  parentEl,
8369  it->first,
8370  cfgMgr,
8371  filterStartTime,
8372  filterEndTime,
8373  filterMode) &&
8374  filterStartTime != 0 && filterEndTime != 0)
8375  {
8376  // Only remove tables when a time filter is active; without a filter,
8377  // tables with no persistent versions (e.g. definition-only tables)
8378  // should still be listed, as in the unfiltered Table View.
8379  // Remove the pair we just added: TableVersions then TableName.
8380  unsigned int childCount = xmlOut.getChildrenCount();
8381  if(childCount >= 2)
8382  {
8383  xmlOut.removeDataElement(childCount - 1);
8384  xmlOut.removeDataElement(childCount - 2);
8385  }
8386  }
8387  else
8388  ++diagNumTablesFound;
8389 
8390  // diagnostics: report slow tables and periodic progress
8391  double diagTableSec = diagElapsedSec(diagTableStartTime);
8392  if(diagTableSec > diagSlowestTableSec)
8393  {
8394  diagSlowestTableSec = diagTableSec;
8395  diagSlowestTableName = it->first;
8396  }
8397  if(diagTableSec > 1.0)
8398  __SUP_COUT__ << "Slow table filter: '" << it->first << "' with "
8399  << it->second.versions_.size() << " versions took "
8400  << diagTableSec << " s" << __E__;
8401  ++diagTableCount;
8402  if(diagTableCount % 50 == 0)
8403  __SUP_COUT__ << "getTables filter progress: " << diagTableCount << " of "
8404  << orderedTableSet.size() << " tables in "
8405  << diagElapsedSec(diagStartTime) << " s (" << diagNumTimeLookups
8406  << " version time lookups taking " << diagTimeLookupSec
8407  << " s so far)" << __E__;
8408 
8409  } // end table loop
8410 
8411  // always return the table and version counts, even if everything was filtered out
8412  xmlOut.addTextElementToData("NumberOfTablesConsidered",
8413  std::to_string(allTableInfo.size()));
8414  xmlOut.addTextElementToData("NumberOfTablesFound",
8415  std::to_string(diagNumTablesFound));
8416  xmlOut.addTextElementToData("NumberOfVersionsConsidered",
8417  std::to_string(diagNumVersionsConsidered));
8418  xmlOut.addTextElementToData("NumberOfVersionsFound",
8419  std::to_string(diagNumVersionsMatched));
8420 
8421  __SUP_COUT__ << "getTables filter summary: mode=" << filterMode << " range=["
8422  << filterStartTime << "," << filterEndTime << "]"
8423  << " tables considered=" << allTableInfo.size()
8424  << " found=" << diagNumTablesFound
8425  << "; versions considered=" << diagNumVersionsConsidered
8426  << " matched=" << diagNumVersionsMatched << "; " << diagNumTimeLookups
8427  << " version time lookups took " << diagTimeLookupSec
8428  << " s; slowest table '" << diagSlowestTableName << "' took "
8429  << diagSlowestTableSec << " s; total " << diagElapsedSec(diagStartTime)
8430  << " s" << __E__;
8431 
8432 } // end handleTablesXML()
8433 
8434 //==============================================================================
8441 void ConfigurationGUISupervisor::handleGetArtdaqNodeRecordsXML(
8442  HttpXmlDocument& xmlOut,
8443  ConfigurationManagerRW* cfgMgr,
8444  const std::string& modifiedTables)
8445 {
8446  __COUT__ << "Retrieving artdaq nodes..." << __E__;
8447 
8448  // setup active tables based on active groups and modified tables
8449  setupActiveTablesXML(
8450  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8451 
8452  std::map<std::string /*type*/,
8453  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8454  nodeTypeToObjectMap;
8455  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8456  subsystemObjectMap;
8457 
8458  std::vector<std::string /*property*/> artdaqSupervisorInfo;
8459 
8460  std::string artdaqSupervisorName;
8461  const ARTDAQTableBase::ARTDAQInfo& info = ARTDAQTableBase::getARTDAQSystem(
8462  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap, artdaqSupervisorInfo);
8463 
8464  if(artdaqSupervisorInfo.size() != 4 /*expecting 4 artdaq Supervisor parameters*/)
8465  {
8466  __SUP_COUT__ << "No artdaq supervisor found." << __E__;
8467  return;
8468  }
8469 
8470  __SUP_COUT__ << "========== "
8471  << "Found " << info.subsystems.size() << " subsystems." << __E__;
8472 
8473  unsigned int paramIndex = 0; // start at first artdaq Supervisor parameter
8474 
8475  auto parentEl = xmlOut.addTextElementToData("artdaqSupervisor",
8476  artdaqSupervisorInfo[paramIndex++]);
8477 
8478  std::string typeString = "artdaqSupervisor";
8479 
8480  xmlOut.addTextElementToParent(
8481  typeString + "-status", artdaqSupervisorInfo[paramIndex++], parentEl);
8482  xmlOut.addTextElementToParent(
8483  typeString + "-contextAddress", artdaqSupervisorInfo[paramIndex++], parentEl);
8484  xmlOut.addTextElementToParent(
8485  typeString + "-contextPort", artdaqSupervisorInfo[paramIndex++], parentEl);
8486 
8487  for(auto& subsystem : info.subsystems)
8488  {
8489  typeString = "subsystem";
8490 
8491  __SUP_COUT__ << "\t\t"
8492  << "Found " << typeString << " " << subsystem.first << " \t := '"
8493  << subsystem.second.label << "'" << __E__;
8494 
8495  xmlOut.addTextElementToParent(typeString, subsystem.second.label, parentEl);
8496  xmlOut.addTextElementToParent(
8497  typeString + "-id", std::to_string(subsystem.first), parentEl);
8498 
8499  xmlOut.addTextElementToParent(typeString + "-sourcesCount",
8500  std::to_string(subsystem.second.sources.size()),
8501  parentEl);
8502 
8503  // destination
8504  xmlOut.addTextElementToParent(typeString + "-destination",
8505  std::to_string(subsystem.second.destination),
8506  parentEl);
8507 
8508  } // end subsystem handling
8509 
8510  __SUP_COUT__ << "========== "
8511  << "Found " << nodeTypeToObjectMap.size() << " process types." << __E__;
8512 
8513  for(auto& nameTypePair : nodeTypeToObjectMap)
8514  {
8515  typeString = nameTypePair.first;
8516 
8517  __SUP_COUT__ << "\t"
8518  << "Found " << nameTypePair.second.size() << " " << typeString
8519  << "(s)" << __E__;
8520 
8521  for(auto& artdaqNode : nameTypePair.second)
8522  {
8523  __SUP_COUT__ << "\t\t"
8524  << "Found '" << artdaqNode.first << "' " << typeString << __E__;
8525  __SUP_COUTV__(StringMacros::vectorToString(artdaqNode.second));
8526 
8527  if(artdaqNode.second.size() < 2)
8528  {
8529  __SUP_SS__ << "Impossible parameter size for node '" << artdaqNode.first
8530  << "' " << typeString << " - please notify admins!" << __E__;
8531  __SUP_SS_THROW__;
8532  }
8533 
8534  auto nodeEl =
8535  xmlOut.addTextElementToParent(typeString, artdaqNode.first, parentEl);
8536 
8537  paramIndex = 3; // start at 3 after subsystem parameter
8538  if(artdaqNode.second.size() > paramIndex)
8539  {
8540  __SUP_COUTT__ << "\t\t\t"
8541  << "-multinode: " << artdaqNode.second[paramIndex] << __E__;
8542  xmlOut.addTextElementToParent(
8543  typeString + "-multinode", artdaqNode.second[paramIndex++], nodeEl);
8544  }
8545  if(artdaqNode.second.size() > paramIndex)
8546  {
8547  __SUP_COUTT__ << "\t\t\t"
8548  << "-nodefixedwidth: " << artdaqNode.second[paramIndex]
8549  << __E__;
8550  xmlOut.addTextElementToParent(typeString + "-nodefixedwidth",
8551  artdaqNode.second[paramIndex++],
8552  nodeEl);
8553  }
8554  if(artdaqNode.second.size() > paramIndex)
8555  {
8556  __SUP_COUTT__ << "\t\t\t"
8557  << "-hostarray: " << artdaqNode.second[paramIndex] << __E__;
8558  xmlOut.addTextElementToParent(
8559  typeString + "-hostarray", artdaqNode.second[paramIndex++], nodeEl);
8560  }
8561  if(artdaqNode.second.size() > paramIndex)
8562  {
8563  __SUP_COUTT__ << "\t\t\t"
8564  << "-hostfixedwidth: " << artdaqNode.second[paramIndex]
8565  << __E__;
8566  xmlOut.addTextElementToParent(typeString + "-hostfixedwidth",
8567  artdaqNode.second[paramIndex++],
8568  nodeEl);
8569  }
8570 
8571  paramIndex = 0; // return to starting parameter
8572  __SUP_COUTT__ << "\t\t\t"
8573  << "-status: " << artdaqNode.second[paramIndex] << __E__;
8574  xmlOut.addTextElementToParent(
8575  typeString + "-status", artdaqNode.second[paramIndex++], parentEl);
8576  __SUP_COUTT__ << "\t\t\t"
8577  << "-hostname: " << artdaqNode.second[paramIndex] << __E__;
8578  xmlOut.addTextElementToParent(
8579  typeString + "-hostname", artdaqNode.second[paramIndex++], parentEl);
8580  __SUP_COUTT__ << "\t\t\t"
8581  << "-subsystem: " << artdaqNode.second[paramIndex] << __E__;
8582  xmlOut.addTextElementToParent(
8583  typeString + "-subsystem", artdaqNode.second[paramIndex], parentEl);
8584  }
8585  } // end processor type handling
8586 
8587  __SUP_COUT__ << "Done retrieving artdaq nodes." << __E__;
8588 
8589 } // end handleGetArtdaqNodeRecordsXML()
8590 
8591 //==============================================================================
8598 void ConfigurationGUISupervisor::handleSaveArtdaqNodeRecordsXML(
8599  const std::string& nodeString,
8600  const std::string& subsystemString,
8601  HttpXmlDocument& xmlOut,
8602  ConfigurationManagerRW* cfgMgr,
8603  const std::string& modifiedTables)
8604 {
8605  __SUP_COUT__ << "Saving artdaq nodes..." << __E__;
8606 
8607  // setup active tables based on active groups and modified tables
8608  setupActiveTablesXML(
8609  xmlOut, cfgMgr, "", TableGroupKey(-1), modifiedTables, false /* refreshAll */);
8610 
8611  // start node object extraction from nodeString
8612  std::map<std::string /*type*/,
8613  std::map<std::string /*record*/, std::vector<std::string /*property*/>>>
8614  nodeTypeToObjectMap;
8615  {
8616  // nodeString format:
8617  // <type>:<nodeName>=<originalName>,<hostname>,<subsystemName>;<nodeName>=<originalName>,<hostname>,<subsystemName>;
8618  // ... |<type>:...|
8619  // repeat | separated types
8620  std::map<std::string /*type*/, std::string /*typeRecordSetString*/>
8621  nodeTypeToStringMap;
8622  StringMacros::getMapFromString(nodeString, nodeTypeToStringMap, {'|'}, {':'});
8623 
8624  __SUP_COUTV__(StringMacros::mapToString(nodeTypeToStringMap));
8625 
8626  for(auto& typePair : nodeTypeToStringMap)
8627  {
8628  if(typePair.first == "")
8629  continue; // skip empty names
8630 
8631  __SUP_COUTV__(StringMacros::decodeURIComponent(typePair.first));
8632 
8633  nodeTypeToObjectMap.emplace(
8634  std::make_pair(StringMacros::decodeURIComponent(typePair.first),
8635  std::map<std::string /*record*/,
8636  std::vector<std::string /*property*/>>()));
8637 
8638  std::map<std::string /*node*/, std::string /*nodeRecordSetString*/>
8639  nodeRecordToStringMap;
8640 
8642  typePair.second, nodeRecordToStringMap, {';'}, {'='});
8643 
8644  __SUP_COUTV__(StringMacros::mapToString(nodeRecordToStringMap));
8645 
8646  for(auto& nodePair : nodeRecordToStringMap)
8647  {
8648  if(nodePair.first == "")
8649  continue; // skip empty names
8650 
8651  __SUP_COUTV__(StringMacros::decodeURIComponent(nodePair.first));
8652 
8653  std::vector<std::string /*property*/> nodePropertyVector;
8654 
8656  nodePair.second, nodePropertyVector, {','});
8657 
8658  __SUP_COUTV__(StringMacros::vectorToString(nodePropertyVector));
8659 
8660  // decode all properties
8661  for(unsigned int i = 0; i < nodePropertyVector.size(); ++i)
8662  {
8663  __SUP_COUTV__(
8664  StringMacros::decodeURIComponent(nodePropertyVector[i]));
8665 
8666  nodePropertyVector[i] =
8667  StringMacros::decodeURIComponent(nodePropertyVector[i]);
8668  }
8669 
8670  nodeTypeToObjectMap[typePair.first].emplace(
8671  std::make_pair(StringMacros::decodeURIComponent(nodePair.first),
8672  nodePropertyVector));
8673  }
8674  }
8675  } // end node object extraction from nodeString
8676 
8677  // start subsystem object extraction from subsystemString
8678  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8679  subsystemObjectMap;
8680  {
8681  // subsystemString format:
8682  // <name>:<destination>;<name>:<destination>; ...;
8683  // repeat ; separated subsystems
8684 
8685  std::map<std::string /*subsystemName*/, std::string /*destinationSubsystemName*/>
8686  tmpSubsystemObjectMap;
8688  subsystemString, tmpSubsystemObjectMap, {';'}, {':'});
8689 
8690  __SUP_COUTV__(StringMacros::mapToString(tmpSubsystemObjectMap));
8691 
8692  // decode all values (probably unnecessary, but more future proof)
8693  for(auto& subsystemPair : tmpSubsystemObjectMap)
8694  {
8695  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.first));
8696  __SUP_COUTV__(StringMacros::decodeURIComponent(subsystemPair.second));
8697 
8698  subsystemObjectMap.emplace(
8699  std::make_pair(StringMacros::decodeURIComponent(subsystemPair.first),
8700  StringMacros::decodeURIComponent(subsystemPair.second)));
8701  }
8702  } // end subsystem object extraction from subsystemString
8703 
8705  cfgMgr, nodeTypeToObjectMap, subsystemObjectMap);
8706 
8707  __SUP_COUT__ << "Done saving artdaq nodes." << __E__;
8708 } // end handleSaveArtdaqNodeRecordsXML()
8709 
8710 //==============================================================================
8717 void ConfigurationGUISupervisor::handleLoadArtdaqNodeLayoutXML(
8718  HttpXmlDocument& xmlOut,
8720  cfgMgr, //force read-only config manager to avoid requiring user-lock (i.e., not ConfigurationManagerRW)
8721  const std::string& contextGroupName /* = "" */,
8722  const TableGroupKey& contextGroupKey /* = INVALID */) const
8723 {
8724  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8725 
8726  //NOTE: must be same/similar code as otsdaq/otsdaq/TablePlugins/ARTDAQTableBase/ARTDAQTableBase.cc:2332
8727  const std::string& finalContextGroupName =
8728  usingActiveGroups
8729  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8730  : contextGroupName;
8731  const TableGroupKey& finalContextGroupKey =
8732  usingActiveGroups
8733  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8734  : contextGroupKey;
8735  const std::string& finalConfigGroupName =
8736  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8737  const TableGroupKey& finalConfigGroupKey =
8738  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8739 
8740  FILE* fp = nullptr;
8741  //first try context+config name only
8742  {
8743  std::stringstream layoutPath;
8744  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8745  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8746  << finalConfigGroupKey << ".dat";
8747 
8748  fp = fopen(layoutPath.str().c_str(), "r");
8749  if(!fp)
8750  {
8751  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8752  << finalContextGroupKey << ") + " << finalConfigGroupName << "("
8753  << finalConfigGroupKey << ")': " << layoutPath.str() << __E__;
8754  // return; //try context only!
8755  }
8756  else
8757  __SUP_COUTV__(layoutPath.str());
8758  }
8759  //last try context name only
8760  if(!fp)
8761  {
8762  std::stringstream layoutPath;
8763  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8764  << "_" << finalContextGroupKey << ".dat";
8765  __SUP_COUTV__(layoutPath.str());
8766 
8767  fp = fopen(layoutPath.str().c_str(), "r");
8768  if(!fp)
8769  {
8770  __SUP_COUT__ << "Layout file not found for '" << finalContextGroupName << "("
8771  << finalContextGroupKey << ")': " << layoutPath.str() << __E__;
8772  return;
8773  }
8774  else
8775  __SUP_COUTV__(layoutPath.str());
8776  }
8777 
8778  // file format is line by line
8779  // line 0 -- grid: <rows> <cols>
8780  // line 1-N -- node: <type> <name> <x-grid> <y-grid>
8781 
8782  const size_t maxLineSz = 1000;
8783  char line[maxLineSz];
8784  if(!fgets(line, maxLineSz, fp))
8785  {
8786  fclose(fp);
8787  return;
8788  }
8789  else
8790  {
8791  // extract grid
8792 
8793  unsigned int rows, cols;
8794 
8795  sscanf(line, "%u %u", &rows, &cols);
8796 
8797  __COUT__ << "Grid rows,cols = " << rows << "," << cols << __E__;
8798 
8799  xmlOut.addTextElementToData("grid-rows", std::to_string(rows));
8800  xmlOut.addTextElementToData("grid-cols", std::to_string(cols));
8801  }
8802 
8803  char name[maxLineSz];
8804  char type[maxLineSz];
8805  unsigned int x, y;
8806  while(fgets(line, maxLineSz, fp))
8807  {
8808  // extract node
8809  sscanf(line, "%s %s %u %u", type, name, &x, &y);
8810 
8811  xmlOut.addTextElementToData("node-type", type);
8812  xmlOut.addTextElementToData("node-name", name);
8813  xmlOut.addTextElementToData("node-x", std::to_string(x));
8814  xmlOut.addTextElementToData("node-y", std::to_string(y));
8815  } // end node extraction loop
8816 
8817  fclose(fp);
8818 
8819 } // end handleLoadArtdaqNodeLayoutXML()
8820 
8821 //==============================================================================
8828 void ConfigurationGUISupervisor::handleSaveArtdaqNodeLayoutXML(
8829  HttpXmlDocument& /*xmlOut*/,
8830  ConfigurationManagerRW* cfgMgr,
8831  const std::string& layoutString,
8832  const std::string& contextGroupName,
8833  const TableGroupKey& contextGroupKey)
8834 {
8835  bool usingActiveGroups = (contextGroupName == "" || contextGroupKey.isInvalid());
8836 
8837  const std::string& finalContextGroupName =
8838  usingActiveGroups
8839  ? cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONTEXT_TYPE)
8840  : contextGroupName;
8841  const TableGroupKey& finalContextGroupKey =
8842  usingActiveGroups
8843  ? cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONTEXT_TYPE)
8844  : contextGroupKey;
8845  const std::string& finalConfigGroupName =
8846  cfgMgr->getActiveGroupName(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8847  const TableGroupKey& finalConfigGroupKey =
8848  cfgMgr->getActiveGroupKey(ConfigurationManager::GroupType::CONFIGURATION_TYPE);
8849 
8850  __SUP_COUTV__(layoutString);
8851 
8852  std::stringstream layoutPath;
8853  layoutPath << ARTDAQTableBase::ARTDAQ_CONFIG_LAYOUTS_PATH << finalContextGroupName
8854  << "_" << finalContextGroupKey << "." << finalConfigGroupName << "_"
8855  << finalConfigGroupKey << ".dat";
8856  __SUP_COUTV__(layoutPath.str());
8857 
8858  std::vector<std::string> fields = StringMacros::getVectorFromString(layoutString);
8859  __SUP_COUTV__(StringMacros::vectorToString(fields));
8860 
8861  if(fields.size() < 2 || (fields.size() - 2) % 4 != 0)
8862  {
8863  __SUP_SS__ << "Invalid layout string fields size of " << fields.size() << __E__;
8864  __SUP_SS_THROW__;
8865  }
8866 
8867  FILE* fp = fopen(layoutPath.str().c_str(), "w");
8868  if(!fp)
8869  {
8870  __SUP_SS__ << "Could not open layout file for writing for '"
8871  << finalContextGroupName << "(" << finalContextGroupKey << ") + "
8872  << finalConfigGroupName << "(" << finalConfigGroupKey
8873  << ")': " << layoutPath.str() << __E__;
8874  __SUP_SS_THROW__;
8875  }
8876 
8877  // match load code at ::handleLoadArtdaqNodeLayoutXML()
8878 
8879  // write grid
8880  fprintf(fp, "%s %s\n", fields[0].c_str(), fields[1].c_str());
8881 
8882  // write nodes
8883  for(unsigned int i = 2; i < fields.size(); i += 4)
8884  fprintf(fp,
8885  "%s %s %s %s\n",
8886  fields[i + 0].c_str(),
8887  fields[i + 1].c_str(),
8888  fields[i + 2].c_str(),
8889  fields[i + 3].c_str());
8890 
8891  fclose(fp);
8892 
8893 } // end handleSaveArtdaqNodeLayoutXML()
8894 
8895 //==============================================================================
8897 void ConfigurationGUISupervisor::handleOtherSubsystemActiveGroups(
8898  HttpXmlDocument& xmlOut,
8899  ConfigurationManagerRW* cfgMgr,
8900  bool getFullList,
8901  std::string targetSubsystem /* = "" */)
8902 try
8903 {
8904  try
8905  {
8906  ConfigurationTree node =
8907  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8908  auto children = node.getChildren();
8909 
8910  for(auto subsystem : children)
8911  {
8912  __SUP_COUTV__(subsystem.first);
8913  __SUP_COUTV__(
8914  StringMacros::vectorToString(subsystem.second.getChildrenNames()));
8915 
8916  std::string userPath =
8917  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8918  __SUP_COUTV__(userPath);
8919  }
8920  }
8921  catch(const std::runtime_error& e)
8922  {
8923  __SUP_COUT__ << "Ignoring errors in handling other subsystem active groups "
8924  "(assuming the subsystem information map is not setup in "
8925  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
8926  << ") -- here is the error: \n"
8927  << e.what() << __E__;
8928  return; //ignore errors if subsystems not defined
8929  }
8930 
8931  //else subsystems are defined, so do not ignore errors!
8932 
8933  ConfigurationTree node =
8934  cfgMgr->getNode(ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE);
8935  auto children = node.getChildren();
8936  for(auto subsystem : children)
8937  {
8938  if(targetSubsystem != "" && targetSubsystem != subsystem.first)
8939  continue; //skip non-target subsystem
8940 
8941  xercesc::DOMElement* parent =
8942  xmlOut.addTextElementToData("SubsystemName", subsystem.first);
8943 
8944  if(!getFullList)
8945  continue;
8946 
8947  std::string filename, userDataPath;
8948  std::string username, hostname;
8949 
8950  std::map<std::string /*groupType*/,
8951  std::pair<std::string /*groupName*/, TableGroupKey>>
8952  retMap = cfgMgr->getOtherSubsystemActiveTableGroups(
8953  subsystem.first, &userDataPath, &hostname, &username);
8954 
8955  for(const auto& retPair : retMap)
8956  {
8957  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupName",
8958  retPair.second.first,
8959  parent);
8960  xmlOut.addTextElementToParent("CurrentlyActive" + retPair.first + "GroupKey",
8961  retPair.second.second.toString(),
8962  parent);
8963  }
8964 
8965  std::vector<std::string> filenameTypes = {"Configured",
8966  "Started",
8967  "ActivatedConfig",
8968  "ActivatedContext",
8969  "ActivatedBackbone",
8970  "ActivatedIterator"};
8971 
8972  std::vector<std::string> filenames = {
8973  ConfigurationManager::LAST_CONFIGURED_CONFIG_GROUP_FILE,
8974  ConfigurationManager::LAST_STARTED_CONFIG_GROUP_FILE,
8975  ConfigurationManager::LAST_ACTIVATED_CONFIG_GROUP_FILE,
8976  ConfigurationManager::LAST_ACTIVATED_CONTEXT_GROUP_FILE,
8977  ConfigurationManager::LAST_ACTIVATED_BACKBONE_GROUP_FILE,
8978  ConfigurationManager::LAST_ACTIVATED_ITERATE_GROUP_FILE};
8979 
8980  std::string userPath =
8981  subsystem.second.getNode("SubsystemUserDataPath").getValue();
8982  auto splitPath = StringMacros::getVectorFromString(userPath, {':'});
8983  std::string cmdResult;
8984  for(unsigned int i = 0; i < filenames.size(); ++i)
8985  {
8986  filename = userDataPath + "/ServiceData/RunControlData/" + filenames[i];
8987  __SUP_COUTV__(filename);
8988 
8989  std::string tmpSubsystemFilename =
8990  ConfigurationManager::LAST_TABLE_GROUP_SAVE_PATH + "/" + filenames[i] +
8991  "." + subsystem.first;
8992  __SUP_COUTV__(tmpSubsystemFilename);
8993 
8994  if(splitPath.size() == 2) //must scp
8995  {
8996  if(username.size()) //has username
8997  cmdResult = StringMacros::exec(
8998  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + username +
8999  "@" + hostname + ":" + filename + " " + tmpSubsystemFilename +
9000  " 2>&1; cat " + tmpSubsystemFilename + " 2>&1")
9001  .c_str());
9002  else
9003  cmdResult = StringMacros::exec(
9004  ("rm " + tmpSubsystemFilename + " 2>/dev/null; scp " + hostname +
9005  ":" + filename + " " + tmpSubsystemFilename + " 2>&1; cat " +
9006  tmpSubsystemFilename + " 2>&1")
9007  .c_str());
9008  }
9009  else if(splitPath.size() == 1) //then can just directly access the file
9010  {
9011  cmdResult = StringMacros::exec(("rm " + tmpSubsystemFilename +
9012  " 2>/dev/null; cp " + filename + " " +
9013  tmpSubsystemFilename + " 2>&1; cat " +
9014  tmpSubsystemFilename + " 2>&1")
9015  .c_str());
9016  }
9017 
9018  __SUP_COUTV__(cmdResult);
9019  std::string timeString;
9020  std::pair<std::string /*group name*/, TableGroupKey> theGroup =
9022  filenames[i] + "." + subsystem.first, timeString);
9023 
9024  // fill return parameters
9025  xmlOut.addTextElementToParent(
9026  "Last" + filenameTypes[i] + "GroupName", theGroup.first, parent);
9027  xmlOut.addTextElementToParent("Last" + filenameTypes[i] + "GroupKey",
9028  theGroup.second.toString(),
9029  parent);
9030  xmlOut.addTextElementToParent(
9031  "Last" + filenameTypes[i] + "GroupTime", timeString, parent);
9032  } // end active/recent filename handling
9033 
9034  } //end subsystem loop
9035 } // end getSubsytemTableGroups()
9036 catch(const std::runtime_error& e)
9037 {
9038  __SUP_SS__
9039  << "An error occurred handling subsystem active groups (Please check the "
9040  "subsystem user data path information map setup in the Context group table "
9041  << ConfigurationManager::CONTEXT_SUBSYSTEM_OPTIONAL_TABLE
9042  << ") -- here is the error: \n"
9043  << e.what() << __E__;
9044  __SUP_SS_THROW__;
9045 } // end getSubsytemTableGroups() catch
9046 
9047 //==============================================================================
9049 void ConfigurationGUISupervisor::handleSearchFieldInGroupXML(
9050  HttpXmlDocument& xmlOut,
9051  ConfigurationManagerRW* cfgMgr,
9052  const std::string& searchText,
9053  const bool activeGroupsOnly,
9054  const std::string& groupType,
9055  const std::string& optionGroups,
9056  const std::string& versionsToCheck)
9057 {
9058  // Validate searchText: empty string matches every cell and would be extremely expensive
9059  if(searchText.empty() || searchText.find_first_not_of(" \t\r\n") == std::string::npos)
9060  {
9061  __COUT_WARN__ << "searchText is empty or whitespace; aborting search." << __E__;
9062  xmlOut.addTextElementToData("Error", "Search text must not be empty.");
9063  return;
9064  }
9065 
9066  bool searchAllGroups = false;
9067  if(optionGroups.size() == 0)
9068  {
9069  __COUT__ << "No optionGroups specified, searching all groups of type "
9070  << groupType << __E__;
9071  searchAllGroups = true;
9072  }
9073  std::vector<std::string> optionGroupsList =
9074  StringMacros::getVectorFromString(optionGroups, {','});
9075 
9076  const std::map<std::string, GroupInfo>& allGroupInfo = cfgMgr->getAllGroupInfo();
9077 
9078  int searchMinThreshold = 0;
9079  int searchMaxThreshold = 0;
9080  size_t dashPos = -1;
9081 
9082  // Parse versionsToCheck for version range
9083  if(versionsToCheck.empty())
9084  {
9085  searchMaxThreshold = INT_MAX;
9086  }
9087  else if(versionsToCheck.find('-') != std::string::npos)
9088  {
9089  __COUT__ << "Parsing versionsToCheck for range: " << versionsToCheck << __E__;
9090  dashPos = versionsToCheck.find('-');
9091 
9092  if(dashPos != 0)
9093  {
9094  // Format: "N-M" means version N to version M
9095  std::string minStr = versionsToCheck.substr(0, dashPos);
9096  std::string maxStr = versionsToCheck.substr(dashPos + 1);
9097 
9098  searchMinThreshold = atoi(minStr.c_str());
9099  searchMaxThreshold = atoi(maxStr.c_str());
9100  }
9101  }
9102  else if(versionsToCheck != "")
9103  {
9104  // Single version specified
9105  searchMinThreshold = atoi(versionsToCheck.c_str());
9106  searchMaxThreshold = INT_MAX; // effectively no upper limit
9107  }
9108  else
9109  {
9110  __COUT__ << "Unable to set min and max thresholds for versionsToCheck: "
9111  << versionsToCheck << __E__;
9112  xmlOut.addTextElementToData(
9113  "Error",
9114  "Unable to set min and max thresholds for versionsToCheck: " +
9115  versionsToCheck);
9116  }
9117  __SUP_COUT__ << "versionsToCheck range: " << searchMinThreshold << " to "
9118  << searchMaxThreshold << __E__;
9119 
9120  auto parentEl = xmlOut.addTextElementToData("SearchResults", "");
9121 
9122  for(auto& groupInfo : allGroupInfo)
9123  {
9124  __COUT__ << "Checking group: " << groupInfo.first
9125  << " group type : " << groupInfo.second.getLatestKeyGroupTypeString()
9126  << __E__;
9127 
9128  if(groupInfo.second.getLatestKeyGroupTypeString() != groupType)
9129  continue;
9130 
9131  // Check if groupInfo.first is in optionGroups
9132  if(!searchAllGroups &&
9133  std::find(optionGroupsList.begin(), optionGroupsList.end(), groupInfo.first) ==
9134  optionGroupsList.end())
9135  {
9136  __COUT__ << "\tSkipping group not in optionGroups list." << __E__;
9137  continue; // skip this group
9138  }
9139 
9140  std::set<TableGroupKey /*key*/> allGroupKeys = groupInfo.second.getKeys();
9141  // convert set to vector for easier string handling
9142  std::vector<TableGroupKey> groupKeysVec(allGroupKeys.begin(), allGroupKeys.end());
9143  // convert vector to string for output
9144  std::string groupKeysStr = StringMacros::vectorToString(groupKeysVec, {','});
9145  __COUT__ << "Group keys:" << groupKeysStr << __E__;
9146 
9147  // Dash is first position: "-N" means (maxVersion - N) to maxVersion
9148  if(dashPos == 0)
9149  {
9150  std::string offsetStr = versionsToCheck.substr(1);
9151  int offset =
9152  atoi(offsetStr.c_str()) - 1; // -1 to include the max version itself
9153 
9154  __SUP_COUT__ << "versionsToCheck offset from end: " << offset << __E__;
9155 
9156  // Find the max version from this group
9157  if(groupKeysVec.size() > 0)
9158  {
9159  TableGroupKey maxVersion = groupKeysVec.at(
9160  groupKeysVec.size() - 1); // get the last (max) version
9161  int maxVersionNum = std::stoi(maxVersion.toString());
9162  int minVersionNum = std::max(0, maxVersionNum - offset);
9163 
9164  searchMinThreshold = minVersionNum;
9165  searchMaxThreshold = maxVersionNum;
9166 
9167  __SUP_COUT__ << "Version range: " << searchMinThreshold << " to "
9168  << searchMaxThreshold << __E__;
9169  }
9170  }
9171 
9172  std::set<TableGroupKey /*key*/> groupKeys;
9173  // make set of single active key for easier handling below
9174  if(activeGroupsOnly)
9175  {
9176  std::set<TableGroupKey> singleKeySet;
9177  singleKeySet.insert(groupInfo.second.getLatestKey());
9178  groupKeys = std::move(singleKeySet);
9179  }
9180  else
9181  groupKeys = std::move(allGroupKeys);
9182 
9183  for(const auto& groupKey : groupKeys)
9184  {
9185  __COUT__ << "\tProcessing group: " << groupInfo.first << " v" << groupKey
9186  << __E__;
9187 
9188  if(!activeGroupsOnly &&
9189  (std::stoi(groupKey.toString()) < searchMinThreshold ||
9190  std::stoi(groupKey.toString()) > searchMaxThreshold))
9191  {
9192  __COUT__ << "\tSkipping group version outside of versionsToCheck range."
9193  << __E__;
9194  continue; // skip this group version
9195  }
9196 
9197  // Get the member map for this group
9198  std::map<std::string /*name*/, TableVersion /*version*/> memberMap;
9199  try
9200  {
9201  cfgMgr->loadTableGroup(groupInfo.first,
9202  groupKey,
9203  false /*doActivate*/,
9204  &memberMap,
9205  0 /*progressBar*/,
9206  0 /*accumulateErrors*/,
9207  0 /*groupComment*/,
9208  0 /*groupAuthor*/,
9209  0 /*groupCreationTime*/,
9210  true /*doNotLoadMembers*/);
9211 
9212  // Process each table in the group
9213  for(auto& tablePair : memberMap)
9214  {
9215  __COUT__ << "\t\tTable: " << tablePair.first << " v"
9216  << tablePair.second << __E__;
9217 
9218  // Load the table for this specific version and search that exact view
9219  try
9220  {
9221  TableBase* memberTable = cfgMgr->getVersionedTableByName(
9222  tablePair.first, tablePair.second);
9223 
9224  // Get the table view for the requested version
9225  const TableView& tableView = memberTable->getView();
9226 
9227  // Loop through all rows and columns to search for searchText
9228  for(unsigned int row = 0; row < tableView.getNumberOfRows();
9229  ++row)
9230  {
9231  for(unsigned int col = 0;
9232  col < tableView.getNumberOfColumns();
9233  ++col)
9234  {
9235  const std::string& cellValue =
9236  tableView.getDataView()[row][col];
9237 
9238  // Check if searchText is found in cell value
9239  if(cellValue.find(searchText) != std::string::npos)
9240  {
9241  __COUT__ << "\t\t\tMatch found in " << tablePair.first
9242  << " v" << tablePair.second << " row " << row
9243  << " column " << col
9244  << " value: " << cellValue << __E__;
9245  // Separate match element by group/table for better organization
9246  auto groupTableEl = xmlOut.addTextElementToParent(
9247  "GroupTableMatch",
9248  groupInfo.first + "/" + tablePair.first,
9249  parentEl);
9250  xmlOut.addTextElementToParent(
9251  "MatchRow", std::to_string(row), groupTableEl);
9252  xmlOut.addTextElementToParent(
9253  "MatchColumn",
9254  tableView.getColumnInfo(col).getName(),
9255  groupTableEl);
9256  xmlOut.addTextElementToParent("MatchGroupVersion",
9257  groupKey.toString(),
9258  groupTableEl);
9259  xmlOut.addTextElementToParent(
9260  "MatchTableVersion",
9261  tablePair.second.toString(),
9262  groupTableEl);
9263  }
9264  } // end columns
9265  } // end rows
9266  } // end try load versioned table
9267  catch(const std::runtime_error& e)
9268  {
9269  __COUT_WARN__ << "Failed to search table " << tablePair.first
9270  << " v" << tablePair.second << ": " << e.what()
9271  << __E__;
9272  }
9273  } // end try load table group members
9274  }
9275  catch(const std::runtime_error& e)
9276  {
9277  __COUT_WARN__ << "Failed to load group members for " << groupInfo.first
9278  << ": " << e.what() << __E__;
9279  }
9280  }
9281  } // end groupInfo loop
9282 } // end handleSearchFieldInGroupXML()
9283 
9284 //==============================================================================
9286 void ConfigurationGUISupervisor::handleSearchFieldInTableXML(
9287  HttpXmlDocument& xmlOut,
9288  ConfigurationManagerRW* cfgMgr,
9289  const std::string& searchText,
9290  const std::string& tableName,
9291  const std::string& searchVersionToCheck,
9292  bool activeTablesOnly)
9293 {
9294  // Validate searchText: empty string matches every cell and would be extremely expensive
9295  if(searchText.empty() || searchText.find_first_not_of(" \t\r\n") == std::string::npos)
9296  {
9297  __COUT_WARN__ << "searchText is empty or whitespace; aborting search." << __E__;
9298  xmlOut.addTextElementToData("Error", "Search text must not be empty.");
9299  return;
9300  }
9301 
9302  const std::map<std::string, TableInfo>& allTableInfo = cfgMgr->getAllTableInfo();
9303  std::map<std::string, TableVersion> allActivePairs = cfgMgr->getActiveVersions();
9304 
9305  int searchMinThreshold = 0;
9306  int searchMaxThreshold = 0;
9307 
9308  // Parse searchVersionToCheck for version range
9309  if(searchVersionToCheck.empty())
9310  {
9311  searchMaxThreshold = INT_MAX;
9312  }
9313  else if(searchVersionToCheck.find('-') != std::string::npos)
9314  {
9315  size_t dashPos = searchVersionToCheck.find('-');
9316 
9317  if(dashPos == 0)
9318  {
9319  // Dash is first position: "-N" means (maxVersion - N) to maxVersion
9320  std::string offsetStr = searchVersionToCheck.substr(1);
9321  int offset =
9322  atoi(offsetStr.c_str()) - 1; // -1 to include the max version itself
9323 
9324  __SUP_COUT__ << "searchVersionToCheck offset from end: " << offset << __E__;
9325 
9326  // Find the max version from allTableInfo
9327  if(allTableInfo.find(tableName) != allTableInfo.end())
9328  {
9329  const TableInfo& tableInfo = allTableInfo.at(tableName);
9330  if(tableInfo.versions_.size() > 0)
9331  {
9332  auto maxVersionIter = tableInfo.versions_.rbegin();
9333  int maxVersionNum = maxVersionIter->version();
9334  int minVersionNum = std::max(0, maxVersionNum - offset);
9335 
9336  searchMinThreshold = minVersionNum;
9337  searchMaxThreshold = maxVersionNum;
9338 
9339  __SUP_COUT__ << "Version range: " << searchMinThreshold << " to "
9340  << searchMaxThreshold << __E__;
9341  }
9342  }
9343  }
9344  else
9345  {
9346  // Format: "N-M" means version N to version M
9347  std::string minStr = searchVersionToCheck.substr(0, dashPos);
9348  std::string maxStr = searchVersionToCheck.substr(dashPos + 1);
9349 
9350  searchMinThreshold = atoi(minStr.c_str());
9351  searchMaxThreshold = atoi(maxStr.c_str());
9352  }
9353  }
9354  else if(searchVersionToCheck != "")
9355  {
9356  // Single version specified
9357  searchMinThreshold = atoi(searchVersionToCheck.c_str());
9358  searchMaxThreshold = INT_MAX; // effectively no upper limit
9359  }
9360  else
9361  {
9362  __COUT__ << "Unable to set min and max thresholds for searchVersionToCheck: "
9363  << searchVersionToCheck << __E__;
9364  xmlOut.addTextElementToData(
9365  "Error",
9366  "Unable to set min and max thresholds for searchVersionToCheck: " +
9367  searchVersionToCheck);
9368  }
9369  __SUP_COUT__ << "searchVersionToCheck range: " << searchMinThreshold << " to "
9370  << searchMaxThreshold << __E__;
9371 
9372  try
9373  {
9374  if(allTableInfo.find(tableName) == allTableInfo.end())
9375  {
9376  __SUP_SS__ << "Table '" << tableName << "' not found." << __E__;
9377  xmlOut.addTextElementToData("Error", ss.str());
9378  return;
9379  }
9380 
9381  const TableInfo& tableInfo = allTableInfo.at(tableName);
9382  __COUT__ << "tableInfo versions length " << tableInfo.versions_.size() << __E__;
9383 
9384  unsigned int matchCount = 0;
9385  bool allowIllegalColumns = true;
9386  // Create a parent element for search results
9387  auto parentEl = xmlOut.addTextElementToData("SearchResults", "");
9388 
9389  // Loop through all versions of the table
9390  for(const auto& version : tableInfo.versions_)
9391  {
9392  try
9393  {
9394  auto activeIt = allActivePairs.find(tableName);
9395  bool isActiveVersion =
9396  (activeIt != allActivePairs.end() && activeIt->second == version);
9397  __COUT__ << "Active check for " << tableName << " v" << version << " => "
9398  << (isActiveVersion ? "active" : "inactive") << __E__;
9399  if(!isActiveVersion && activeTablesOnly)
9400  continue; // Skip versions that are not active
9401 
9402  if((std::stoi(version.toString()) < searchMinThreshold ||
9403  std::stoi(version.toString()) > searchMaxThreshold) &&
9404  activeTablesOnly == false /*ignore version threshold if active only*/)
9405  {
9406  __COUT__ << "Skipping version " << version << " out of range "
9407  << searchMinThreshold << " to " << searchMaxThreshold
9408  << __E__;
9409  continue;
9410  }
9411  TableBase* table = cfgMgr->getTableByName(tableName);
9412 
9413  // get view pointer
9414  TableView* tableViewPtr;
9415  if(version.isInvalid()) // use mock-up
9416  {
9417  __COUT__ << "Using mock-up for table " << tableName << " version "
9418  << version << __E__;
9419  tableViewPtr = table->getMockupViewP();
9420  }
9421  else // use view version
9422  {
9423  try
9424  {
9425  // locally accumulate 'manageable' errors getting the version to avoid
9426  // reverting to mockup
9427  std::string localAccumulatedErrors = "";
9428  tableViewPtr =
9429  cfgMgr
9431  tableName,
9432  version,
9433  allowIllegalColumns /*looseColumnMatching*/,
9434  &localAccumulatedErrors,
9435  false /*getRawData*/)
9436  ->getViewP();
9437 
9438  if(localAccumulatedErrors != "")
9439  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9440  }
9441  catch(const std::runtime_error& e)
9442  {
9443  __COUT__ << "Error loading version " << version << " for table "
9444  << tableName << ": " << e.what() << __E__;
9445  __SUP_COUT_WARN__ << "Could not load version " << version
9446  << " for table " << tableName << ": "
9447  << e.what() << __E__;
9448  // fallback to mockup
9449  tableViewPtr = table->getMockupViewP();
9450  }
9451  }
9452 
9453  __COUT__ << "Getting table " << tableName << " version " << version
9454  << __E__;
9455 
9456  // Search through all rows and columns
9457  for(unsigned int row = 0; row < tableViewPtr->getNumberOfRows(); ++row)
9458  {
9459  for(unsigned int col = 0; col < tableViewPtr->getNumberOfColumns();
9460  ++col)
9461  {
9462  const std::string& cellValue =
9463  tableViewPtr->getDataView()[row][col];
9464  // Check if searchText is found in cell value
9465  if(cellValue.find(searchText) != std::string::npos)
9466  {
9467  __COUT__ << "Match found in row " << row << " column " << col
9468  << " value: " << cellValue << __E__;
9469  auto matchEl = xmlOut.addTextElementToParent(
9470  "Match", cellValue, parentEl);
9471  xmlOut.addTextElementToParent(
9472  "MatchVersion", version.toString(), matchEl);
9473  xmlOut.addTextElementToParent(
9474  "MatchRow", std::to_string(row), matchEl);
9475  xmlOut.addTextElementToParent(
9476  "MatchColumn",
9477  tableViewPtr->getColumnInfo(col).getName(),
9478  matchEl);
9479 
9480  ++matchCount;
9481  }
9482  }
9483  } // end for rows and columns
9484  }
9485  catch(const std::runtime_error& e)
9486  {
9487  __COUT__ << "Error searching in version " << version << " for table "
9488  << tableName << ": " << e.what() << __E__;
9489  __SUP_COUT_WARN__ << "Could not load version " << version << " for table "
9490  << tableName << ": " << e.what() << __E__;
9491  }
9492  } //emd for version loop
9493 
9494  xmlOut.addTextElementToData("MatchCount", std::to_string(matchCount));
9495  }
9496  catch(std::runtime_error& e)
9497  {
9498  __SUP_SS__ << "Error searching in table '" << tableName << "'!\n\n " << e.what()
9499  << __E__;
9500  __SUP_COUT_ERR__ << ss.str();
9501  xmlOut.addTextElementToData("Error", ss.str());
9502  }
9503  catch(...)
9504  {
9505  __SUP_SS__ << "Error searching in table '" << tableName << "'!\n\n " << __E__;
9506  try
9507  {
9508  throw;
9509  }
9510  catch(const std::exception& e)
9511  {
9512  ss << "Exception message: " << e.what();
9513  }
9514  catch(...)
9515  {
9516  }
9517  __SUP_COUT_ERR__ << ss.str();
9518  xmlOut.addTextElementToData("Error", ss.str());
9519  }
9520 } // end handleSearchFieldInTableXML()
9521 
9522 //==============================================================================
9524 void ConfigurationGUISupervisor::handleGroupDiff(
9525  HttpXmlDocument& xmlOut,
9526  ConfigurationManagerRW* cfgMgr,
9527  const std::string& groupName,
9528  const TableGroupKey& groupKey,
9529  const TableGroupKey& diffKey /* = TableGroupKey() */,
9530  const std::string& diffGroupNameInput /* = "" */)
9531 {
9532  //Steps:
9533  // - Get group type and load table map
9534  // - Get match type active group table map
9535  // - For each table, compare
9536  std::string diffGroupName;
9537 
9538  if(diffKey.isInvalid())
9539  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
9540  << ") with the active group." << __E__;
9541  else
9542  {
9543  if(diffGroupNameInput == "")
9544  diffGroupName = groupName;
9545  else
9546  diffGroupName = diffGroupNameInput;
9547 
9548  __SUP_COUT__ << "Differencing group " << groupName << "(" << groupKey
9549  << ") with group " << diffGroupName << "(" << diffKey << ")"
9550  << __E__;
9551  }
9552 
9553  try
9554  {
9555  std::map<std::string /*name*/, TableVersion /*version*/> memberMap, diffMemberMap;
9556  std::string groupType, accumulateErrors;
9557  std::stringstream diffReport;
9558  bool noDifference = true;
9559 
9560  cfgMgr->loadTableGroup(
9561  groupName,
9562  groupKey,
9563  false /*doActivate*/,
9564  &memberMap /*groupMembers*/,
9565  0 /*progressBar*/,
9566  &accumulateErrors /*accumulateErrors*/,
9567  0 /*groupComment*/,
9568  0 /*groupAuthor*/,
9569  0 /*groupCreationTime*/,
9570  false /*doNotLoadMember*/,
9571  (diffKey.isInvalid()
9572  ? &groupType
9573  : 0)); //for specified diff group (not active), do not need groupType
9574 
9575  __SUP_COUTV__(StringMacros::mapToString(memberMap));
9576 
9577  std::map<std::string /* groupType */, std::pair<std::string, TableGroupKey>>
9578  activeGroups;
9579  if(diffKey.isInvalid())
9580  {
9581  activeGroups = cfgMgr->getActiveTableGroups();
9582 
9583  __SUP_COUTV__(StringMacros::mapToString(activeGroups));
9584  __SUP_COUTV__(groupType);
9585 
9586  if(activeGroups.find(groupType) == activeGroups.end() ||
9587  activeGroups.at(groupType).first == "" ||
9588  activeGroups.at(groupType).second.isInvalid())
9589  {
9590  __SUP_SS__ << "Could not find an active group of type '" << groupType
9591  << ".' Please check the expected active configuration groups "
9592  "for errors (going to 'System View' of the Config App may "
9593  "reveal errors)."
9594  << __E__;
9595  __SUP_SS_THROW__;
9596  }
9597 
9598  __SUP_COUT__ << "active " << groupType << " group is "
9599  << activeGroups.at(groupType).first << "("
9600  << activeGroups.at(groupType).second << ")" << __E__;
9601 
9602  diffReport << "This difference report is between " << groupType
9603  << " group <b>'" << groupName << "(" << groupKey << ")'</b>"
9604  << " and active group <b>'" << activeGroups.at(groupType).first
9605  << "(" << activeGroups.at(groupType).second << ")'</b>." << __E__;
9606 
9607  cfgMgr->loadTableGroup(activeGroups.at(groupType).first,
9608  activeGroups.at(groupType).second,
9609  false /*doActivate*/,
9610  &diffMemberMap /*groupMembers*/,
9611  0 /*progressBar*/,
9612  &accumulateErrors /*accumulateErrors*/,
9613  0 /*groupComment*/,
9614  0 /*groupAuthor*/,
9615  0 /*groupCreationTime*/,
9616  false /*doNotLoadMember*/);
9617 
9618  diffReport << "\n\n"
9619  << "'" << groupName << "(" << groupKey << ")' has <b>"
9620  << memberMap.size() << " member tables</b>, and "
9621  << "'" << activeGroups.at(groupType).first << "("
9622  << activeGroups.at(groupType).second << ")' has <b>"
9623  << diffMemberMap.size() << " member tables</b>." << __E__;
9624  }
9625  else //specified diff group (not active), so do not need groupType
9626  {
9627  diffReport << "This difference report is between group <b>'" << groupName
9628  << "(" << groupKey << ")'</b>"
9629  << " and group <b>'" << diffGroupName << "(" << diffKey
9630  << ")'</b>." << __E__;
9631 
9632  cfgMgr->loadTableGroup(diffGroupName,
9633  diffKey,
9634  false /*doActivate*/,
9635  &diffMemberMap /*groupMembers*/,
9636  0 /*progressBar*/,
9637  &accumulateErrors /*accumulateErrors*/,
9638  0 /*groupComment*/,
9639  0 /*groupAuthor*/,
9640  0 /*groupCreationTime*/,
9641  false /*doNotLoadMember*/);
9642 
9643  diffReport << "\n\n"
9644  << "'" << groupName << "(" << groupKey << ")' has <b>"
9645  << memberMap.size() << " member tables</b>, and "
9646  << "'" << diffGroupName << "(" << diffKey << ")' has <b>"
9647  << diffMemberMap.size() << " member tables</b>." << __E__;
9648  }
9649 
9650  __SUP_COUTV__(StringMacros::mapToString(diffMemberMap));
9651 
9652  diffReport << "<INDENT><ol>";
9653 
9654  unsigned int tableDifferences = 0;
9655 
9656  for(auto& member : memberMap)
9657  {
9658  if(diffMemberMap.find(member.first) == diffMemberMap.end())
9659  {
9660  diffReport << "\n\n<li>"
9661  << "Table <b>" << member.first << "-v" << member.second
9662  << "</b> not found in active group."
9663  << "</li>" << __E__;
9664  noDifference = false;
9665  ++tableDifferences;
9666  continue;
9667  }
9668 
9669  __SUP_COUTT__ << "Comparing " << member.first << "-v" << member.second
9670  << " ... " << member.first << "-v"
9671  << diffMemberMap.at(member.first) << __E__;
9672 
9673  if(member.second == diffMemberMap.at(member.first))
9674  continue;
9675 
9676  diffReport << "\n\n<li>"
9677  << "Table <b>" << member.first << " v" << member.second
9678  << "</b> in " << groupName << "(" << groupKey << ")' ...vs... "
9679  << " <b>v" << diffMemberMap.at(member.first) << "</b> in "
9680  << diffGroupName << "(" << diffKey << ")':" << __E__;
9681 
9682  TableBase* table = cfgMgr->getTableByName(member.first);
9683 
9684  diffReport << "<ul>";
9685  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
9686  modifiedRecords; //useful for tree diff view display
9687  if(!table->diffTwoVersions(member.second,
9688  diffMemberMap.at(member.first),
9689  &diffReport,
9690  &modifiedRecords))
9691  {
9692  //difference found!
9693  noDifference = false;
9694  ++tableDifferences;
9695  auto parentEl =
9696  xmlOut.addTextElementToData("TableWithDiff", member.first);
9697  for(auto& modifiedRecord : modifiedRecords)
9698  {
9699  auto recordParentEl = xmlOut.addTextElementToParent(
9700  "RecordWithDiff", modifiedRecord.first, parentEl);
9701  for(auto& modifiedColumn : modifiedRecord.second)
9702  xmlOut.addTextElementToParent(
9703  "ColNameWithDiff", modifiedColumn, recordParentEl);
9704  }
9705  }
9706  diffReport << "</ul></li>";
9707 
9708  } //end member table comparison loop
9709 
9710  for(auto& diffMember : diffMemberMap)
9711  {
9712  if(memberMap.find(diffMember.first) == memberMap.end())
9713  {
9714  if(diffKey.isInvalid())
9715  diffReport << "\n\n<li>"
9716  << "Active Group Table <b>" << diffMember.first << "-v"
9717  << diffMember.second << "</b> not found in '" << groupName
9718  << "(" << groupKey << ")'."
9719  << "</li>" << __E__;
9720  else
9721  diffReport << "\n\n<li>" << diffGroupName << "(" << diffKey
9722  << ") Table <b>" << diffMember.first << "-v"
9723  << diffMember.second << "</b> not found in '" << groupName
9724  << "(" << groupKey << ")'."
9725  << "</li>" << __E__;
9726 
9727  noDifference = false;
9728  ++tableDifferences;
9729  continue;
9730  }
9731  }
9732  diffReport << "\n</ol></INDENT>";
9733 
9734  if(diffKey.isInvalid())
9735  {
9736  if(noDifference)
9737  diffReport << "\n\nNo difference found between "
9738  << "<b>'" << groupName << "(" << groupKey
9739  << ")'</b> and active group "
9740  << "<b>'" << activeGroups.at(groupType).first << "("
9741  << activeGroups.at(groupType).second << ")'</b>." << __E__;
9742  else
9743  diffReport << "\n\n<b>" << tableDifferences
9744  << "</b> member table differences identified between "
9745  << "<b>'" << groupName << "(" << groupKey
9746  << ")'</b> and active group "
9747  << "<b>'" << activeGroups.at(groupType).first << "("
9748  << activeGroups.at(groupType).second << ")'</b>." << __E__;
9749  }
9750  else
9751  {
9752  if(noDifference)
9753  diffReport << "\n\nNo difference found between "
9754  << "<b>'" << groupName << "(" << groupKey
9755  << ")'</b> and group "
9756  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
9757  << __E__;
9758  else
9759  diffReport << "\n\n<b>" << tableDifferences
9760  << "</b> member table differences identified between "
9761  << "<b>'" << groupName << "(" << groupKey
9762  << ")'</b> and group "
9763  << "<b>'" << diffGroupName << "(" << diffKey << ")'</b>."
9764  << __E__;
9765  }
9766 
9767  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
9768  xmlOut.addTextElementToData("DiffReport", diffReport.str());
9769  }
9770  catch(const std::runtime_error& e)
9771  {
9772  __SUP_COUT_ERR__ << "Caught error while differencing group " << groupName << "("
9773  << groupKey << ") with group " << diffGroupName << "(" << diffKey
9774  << ")" << __E__ << e.what() << __E__;
9775  throw; //rethrow
9776  }
9777 } // end handleGroupDiff()
9778 
9779 //==============================================================================
9781 void ConfigurationGUISupervisor::handleTableDiff(HttpXmlDocument& xmlOut,
9782  ConfigurationManagerRW* cfgMgr,
9783  const std::string& tableName,
9784  const TableVersion& vA,
9785  const TableVersion& vB)
9786 {
9787  __SUP_COUT__ << "Differencing tableName " << tableName << " v" << vA << " with v"
9788  << vB << __E__;
9789 
9790  //first make sure tables are loaded
9791  TableBase* table = cfgMgr->getTableByName(tableName);
9792 
9793  try
9794  {
9795  // locally accumulate 'manageable' errors getting the version to avoid
9796  // reverting to mockup
9797  std::string localAccumulatedErrors = "";
9798  cfgMgr->getVersionedTableByName(tableName,
9799  vA,
9800  false /*looseColumnMatching*/,
9801  &localAccumulatedErrors,
9802  false /*getRawData*/);
9803 
9804  if(localAccumulatedErrors != "")
9805  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9806  }
9807  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
9808  {
9809  __SUP_SS__ << "Failed to get table " << tableName << " version " << vA;
9810  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
9811  __SUP_COUT_ERR__ << "\n" << ss.str();
9812 
9813  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9814  }
9815  catch(...) // default to mock-up for fail-safe in GUI editor
9816  {
9817  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vA << __E__;
9818  try
9819  {
9820  throw;
9821  } //one more try to printout extra info
9822  catch(const std::exception& e)
9823  {
9824  ss << "Exception message: " << e.what();
9825  }
9826  catch(...)
9827  {
9828  }
9829 
9830  __SUP_COUT_ERR__ << "\n" << ss.str();
9831  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9832  }
9833  try
9834  {
9835  // locally accumulate 'manageable' errors getting the version to avoid
9836  // reverting to mockup
9837  std::string localAccumulatedErrors = "";
9838  cfgMgr->getVersionedTableByName(tableName,
9839  vB,
9840  false /*looseColumnMatching*/,
9841  &localAccumulatedErrors,
9842  false /*getRawData*/);
9843 
9844  if(localAccumulatedErrors != "")
9845  xmlOut.addTextElementToData("Error", localAccumulatedErrors);
9846  }
9847  catch(std::runtime_error& e) // default to mock-up for fail-safe in GUI editor
9848  {
9849  __SUP_SS__ << "Failed to get table " << tableName << " version " << vB;
9850  ss << "\n\n...Here is why it failed:\n\n" << e.what() << __E__;
9851  __SUP_COUT_ERR__ << "\n" << ss.str();
9852 
9853  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9854  }
9855  catch(...) // default to mock-up for fail-safe in GUI editor
9856  {
9857  __SUP_SS__ << "Failed to get table " << tableName << " version: " << vB << __E__;
9858  try
9859  {
9860  throw;
9861  } //one more try to printout extra info
9862  catch(const std::exception& e)
9863  {
9864  ss << "Exception message: " << e.what();
9865  }
9866  catch(...)
9867  {
9868  }
9869 
9870  __SUP_COUT_ERR__ << "\n" << ss.str();
9871  xmlOut.addTextElementToData("Error", "Error getting view! " + ss.str());
9872  }
9873 
9874  bool noDifference = true;
9875  std::stringstream diffReport;
9876 
9877  diffReport << "This difference report is between table " << tableName << " v" << vA
9878  << " and v" << vB << "</b>." << __E__;
9879 
9880  diffReport << "<INDENT>";
9881  diffReport << "<ul>";
9882  std::map<std::string /* uid */, std::vector<std::string /* colName */>>
9883  modifiedRecords; //useful for tree diff view display
9884  if(!table->diffTwoVersions(vA, vB, &diffReport))
9885  noDifference = false; //difference found!
9886  diffReport << "</ul></INDENT>";
9887 
9888  xmlOut.addTextElementToData("NoDifference", noDifference ? "1" : "0");
9889  xmlOut.addTextElementToData("DiffReport", diffReport.str());
9890 } // end handleTableDiff()
9891 
9892 //==============================================================================
9895 void ConfigurationGUISupervisor::testXDAQContext()
9896 {
9897  if(0) //keep for debugging
9898  {
9899  __COUT_INFO__ << "Hello0!";
9900  ConfigurationManagerRW cfgMgrInst("ExampleUser");
9901  __COUT_INFO__ << "Hello1!";
9902  ConfigurationManagerRW* cfgMgr = &cfgMgrInst;
9903  __COUT_INFO__ << "Hello2!";
9904  cfgMgr->testXDAQContext();
9905  __COUT_INFO__ << "Hello3!";
9906  return;
9907  }
9908 
9909  try
9910  {
9911  __SUP_COUT_INFO__ << "Attempting test activation of the context group." << __E__;
9913  cfgMgr; // create instance to activate saved context and backbone groups (not config group)
9914  }
9915  catch(const std::runtime_error& e)
9916  {
9917  __SUP_COUT_WARN__
9918  << "The test activation of the context group failed. Ignoring error: \n"
9919  << e.what() << __E__;
9920  return;
9921  }
9922  catch(...)
9923  {
9924  __SUP_COUT_WARN__ << "The test activation of the context group failed. Ignoring."
9925  << __E__;
9926  return;
9927  }
9928 
9929  __SUP_COUT_INFO__ << "Completed test activation of the context group." << __E__;
9930  return;
9931 
9933  // below has been used for debugging.
9934 
9935  // behave like a user
9936  // start with top level xdaq context
9937  // then add and delete rows proof-of-concept
9938  // export xml xdaq table file
9939 
9942  // behave like a new user
9943  //
9944  // ConfigurationManagerRW cfgMgrInst("ExampleUser");
9945 
9946  // ConfigurationManagerRW* cfgMgr =& cfgMgrInst;
9947 
9948  // // std::map<std::string, TableVersion> groupMembers;
9949  // // groupMembers["DesktopIcon"] = TableVersion(2);
9950  // // cfgMgr->saveNewTableGroup("test",
9951  // // groupMembers, "test comment");
9952 
9953  // //
9954  // const std::map<std::string, TableInfo>& allTableInfo =
9955  // cfgMgr->getAllTableInfo(true /* refresh*/);
9956  // __SUP_COUT__ << "allTableInfo.size() = " << allTableInfo.size() << __E__;
9957  // for(auto& mapIt : allTableInfo)
9958  // {
9959  // __SUP_COUT__ << "Table Name: " << mapIt.first << __E__;
9960  // __SUP_COUT__ << "\t\tExisting Versions: " << mapIt.second.versions_.size()
9961  // <<
9962  // __E__;
9963 
9964  // //get version key for the current system table key
9965  // for (auto& v:mapIt.second.versions_)
9966  // {
9967  // __SUP_COUT__ << "\t\t" << v << __E__;
9968  // }
9969  // }
9970  // __SUP_COUTT__ << "Group Info end runtime=" << cfgMgr->runTimeSeconds() << __E__;
9971  // testXDAQContext just a test bed for navigating the new config tree
9972  // cfgMgr->testXDAQContext();
9973 
9976 } // 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)
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)
bool isChildLink(void) const
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)