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