@datagrok/hit-triage 1.7.7 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # HitTriage changelog
2
2
 
3
+ ## 1.8.0 (2025-12-09)
4
+
5
+ * Improved collaboration support
6
+ * Automatic sygnaling of changes in campaigns between multiple users.
7
+
3
8
  ## 1.7.7 (2025-09-19)
4
9
 
5
10
  * Sortable campaigns table columns via doubleclick.
@@ -0,0 +1,20 @@
1
+ -- Simple lock table
2
+ CREATE TABLE hitdesign.campaign_locks (
3
+ app_name VARCHAR(100) NOT NULL,
4
+ campaign_id VARCHAR(255) NOT NULL,
5
+ expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
6
+ --optional: user who holds the lock
7
+ locked_by TEXT,
8
+ PRIMARY KEY (app_name, campaign_id)
9
+ );
10
+
11
+ CREATE TABLE hitdesign.update_logs (
12
+ app_name VARCHAR(100) NOT NULL,
13
+ campaign_id VARCHAR(255) NOT NULL,
14
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
15
+ PRIMARY KEY (app_name, campaign_id)
16
+ );
17
+
18
+ GRANT ALL PRIVILEGES ON SCHEMA hitdesign TO CURRENT_USER;
19
+ GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA hitdesign TO :LOGIN;
20
+ GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA hitdesign TO :LOGIN;
package/dist/package.js CHANGED
@@ -3658,6 +3658,7 @@ __webpack_require__.r(__webpack_exports__);
3658
3658
  /* harmony import */ var _package__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../package */ "./src/package.ts");
3659
3659
  /* harmony import */ var _consts__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./consts */ "./src/app/consts.ts");
3660
3660
  /* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./utils */ "./src/app/utils.ts");
3661
+ /* harmony import */ var _package_api__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../package-api */ "./src/package-api.ts");
3661
3662
  var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
3662
3663
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3663
3664
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -3671,6 +3672,7 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
3671
3672
 
3672
3673
 
3673
3674
 
3675
+
3674
3676
  class HitAppBase {
3675
3677
  get appName() { return this._appName; }
3676
3678
  // public layouts: Promise<DG.ViewLayout[]>;
@@ -3751,9 +3753,12 @@ class HitAppBase {
3751
3753
  const df1MolCol = (_b = (_a = df1.col(molColName1)) === null || _a === void 0 ? void 0 : _a.toList()) === null || _b === void 0 ? void 0 : _b.filter((it) => !!it).map((it) => datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.convert(it, datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.Notation.Unknown, datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.Notation.Smiles));
3752
3754
  const df2MolCol = (_d = (_c = df2.col(molColName2)) === null || _c === void 0 ? void 0 : _c.toList()) === null || _d === void 0 ? void 0 : _d.filter((it) => !!it).map((it) => datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.convert(it, datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.Notation.Unknown, datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.chem.Notation.Smiles));
3753
3755
  if (!df1MolCol || !df2MolCol)
3754
- throw new Error('Molecule column not found');
3756
+ throw new Error('Molecule column not found'); // should not happen, but just in case
3755
3757
  const df2MolMap = new Map();
3756
3758
  df2MolCol.forEach((it, i) => df2MolMap.set(it, i));
3759
+ // const existingVidValues = new Set<string>();
3760
+ // const vidCol2 = df2.col(ViDColName);
3761
+ // vidCol2?.categories?.forEach((v) => existingVidValues.add(v));
3757
3762
  this.isJoining = true;
3758
3763
  try {
3759
3764
  // first check that all columns are there
@@ -3774,11 +3779,13 @@ class HitAppBase {
3774
3779
  df2.col(col.name).set(df2.rowCount - 1, value, false);
3775
3780
  }
3776
3781
  }
3777
- else if (addedColNames.length > 0) {
3778
- const row = df2MolMap.get(df1MolCol[i]);
3779
- for (const colName of addedColNames) {
3780
- const value = df1.col(colName).get(i);
3781
- df2.col(colName).set(row, value, false);
3782
+ else {
3783
+ if (addedColNames.length > 0) {
3784
+ const row = df2MolMap.get(df1MolCol[i]);
3785
+ for (const colName of addedColNames) {
3786
+ const value = df1.col(colName).get(i);
3787
+ df2.col(colName).set(row, value, false);
3788
+ }
3782
3789
  }
3783
3790
  }
3784
3791
  }
@@ -3791,6 +3798,41 @@ class HitAppBase {
3791
3798
  }
3792
3799
  return df2;
3793
3800
  }
3801
+ aquireAndWaitCampaignLock(campaignId_1) {
3802
+ return __awaiter(this, arguments, void 0, function* (campaignId, maxWaitMs = 30000) {
3803
+ var _a;
3804
+ const startTime = Date.now();
3805
+ let errorCount = 0;
3806
+ let lastError = null;
3807
+ const curUser = datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.User.current();
3808
+ const curUserName = curUser ? curUser.friendlyName || curUser.name || 'unknown' : 'unknown';
3809
+ while (Date.now() - startTime < maxWaitMs && errorCount < 5) {
3810
+ try {
3811
+ const result = yield _package_api__WEBPACK_IMPORTED_MODULE_4__.queries.acquireCampaignLock(this.appName, campaignId, curUserName);
3812
+ if (result && result.rowCount > 0) {
3813
+ const res = { aquired: true, expires_at: (_a = result === null || result === void 0 ? void 0 : result.col('expires_at')) === null || _a === void 0 ? void 0 : _a.getString(0) };
3814
+ return res;
3815
+ }
3816
+ lastError = null;
3817
+ yield datagrok_api_dg__WEBPACK_IMPORTED_MODULE_0__.delay(200);
3818
+ }
3819
+ catch (err) {
3820
+ // console.error('Error acquiring campaign lock:', err);
3821
+ lastError = err;
3822
+ errorCount++;
3823
+ }
3824
+ }
3825
+ if (lastError)
3826
+ throw lastError;
3827
+ return { aquired: false, expires_at: null };
3828
+ });
3829
+ }
3830
+ releaseCampaignLock(campaignId) {
3831
+ return __awaiter(this, void 0, void 0, function* () {
3832
+ var _a, _b;
3833
+ return ((_b = (_a = (yield _package_api__WEBPACK_IMPORTED_MODULE_4__.queries.releaseCampaignLock(this.appName, campaignId))) === null || _a === void 0 ? void 0 : _a.col('updated_at')) === null || _b === void 0 ? void 0 : _b.getString(0)) || null;
3834
+ });
3835
+ }
3794
3836
  }
3795
3837
  HitAppBase.molFileExtReaders = ['sdf', 'mol', 'smi', 'mol2']
3796
3838
  .map((ext) => {
@@ -3835,6 +3877,7 @@ __webpack_require__.r(__webpack_exports__);
3835
3877
  /* harmony import */ var rxjs_operators__WEBPACK_IMPORTED_MODULE_14___default = /*#__PURE__*/__webpack_require__.n(rxjs_operators__WEBPACK_IMPORTED_MODULE_14__);
3836
3878
  /* harmony import */ var _dialogs_permissions_dialog__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./dialogs/permissions-dialog */ "./src/app/dialogs/permissions-dialog.ts");
3837
3879
  /* harmony import */ var _packageSettingsEditor__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ../packageSettingsEditor */ "./src/packageSettingsEditor.ts");
3880
+ /* harmony import */ var _package_api__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ../package-api */ "./src/package-api.ts");
3838
3881
  var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
3839
3882
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3840
3883
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -3851,6 +3894,8 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
3851
3894
 
3852
3895
 
3853
3896
 
3897
+ // @ts-ignore
3898
+
3854
3899
 
3855
3900
 
3856
3901
 
@@ -3873,14 +3918,20 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
3873
3918
  this._molColName = _consts__WEBPACK_IMPORTED_MODULE_4__.HitDesignMolColName;
3874
3919
  this.campaignProps = {};
3875
3920
  this.existingStatuses = [];
3921
+ this._refreshIconTooltip = 'Refresh Campaign';
3922
+ this._updateCheckIntervalId = null;
3923
+ this._needsRefresh = false;
3924
+ this._lastExpiresTimeString = null;
3925
+ this._refreshCampaignIcon = null;
3876
3926
  // some of the subs are on grid, layout application will detach the grid, thus we need to re-init those subs
3877
3927
  this._gridSubs = null;
3928
+ this._isSaving = false;
3878
3929
  this._infoView = infoViewConstructor(this);
3879
3930
  this.multiView = new datagrok_api_dg__WEBPACK_IMPORTED_MODULE_2__.MultiView({ viewFactories: { [this._infoView.name]: () => this._infoView } });
3880
- this.multiView.tabs.onTabChanged.subscribe((_) => {
3931
+ this.multiView.subs.push(this.multiView.tabs.onTabChanged.subscribe((_) => {
3881
3932
  if (this.multiView.currentView instanceof _base_view__WEBPACK_IMPORTED_MODULE_12__.HitBaseView)
3882
3933
  this.multiView.currentView.onActivated();
3883
- });
3934
+ }));
3884
3935
  this.multiView.parentCall = c;
3885
3936
  this.mainView = this.multiView;
3886
3937
  this._initViewSubs();
@@ -4098,6 +4149,21 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4098
4149
  yield this.saveCampaign(true);
4099
4150
  });
4100
4151
  }
4152
+ refreshCampaign() {
4153
+ return __awaiter(this, void 0, void 0, function* () {
4154
+ if (!this._campaignId || !this.template) {
4155
+ datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.warning('No campaign to refresh');
4156
+ return; // should not happen
4157
+ }
4158
+ if (this._isSaving) {
4159
+ datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.info('Campaign is being saved. Please wait a moment and try again.');
4160
+ return;
4161
+ }
4162
+ clearInterval(this._updateCheckIntervalId);
4163
+ this._refreshIconTooltip = 'Refresh Campaign';
4164
+ yield this.setTemplate(this.template, this._campaignId);
4165
+ });
4166
+ }
4101
4167
  setTemplate(template, campaignId) {
4102
4168
  return __awaiter(this, void 0, void 0, function* () {
4103
4169
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
@@ -4137,7 +4203,9 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4137
4203
  //await this.dataFrame.meta.detectSemanticTypes();
4138
4204
  }
4139
4205
  if (!this.dataFrame) {
4140
- console.error('DataFrame is empty');
4206
+ datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.error('DataFrame is empty');
4207
+ clearInterval(this._updateCheckIntervalId);
4208
+ this._updateCheckIntervalId = null;
4141
4209
  return;
4142
4210
  }
4143
4211
  yield this.dataFrame.meta.detectSemanticTypes();
@@ -4156,6 +4224,53 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4156
4224
  (0,_utils__WEBPACK_IMPORTED_MODULE_8__.modifyUrl)(_consts__WEBPACK_IMPORTED_MODULE_4__.CampaignIdKey, (_l = (_j = this._campaignId) !== null && _j !== void 0 ? _j : (_k = this._campaign) === null || _k === void 0 ? void 0 : _k.name) !== null && _l !== void 0 ? _l : '');
4157
4225
  if (this.campaign)
4158
4226
  this.campaign.template = template;
4227
+ // get the last saved time
4228
+ _package_api__WEBPACK_IMPORTED_MODULE_17__.queries.getLastModified(this.appName, this._campaignId).then((timeDf) => {
4229
+ var _a;
4230
+ const timeStr = (_a = timeDf === null || timeDf === void 0 ? void 0 : timeDf.col('updated_at')) === null || _a === void 0 ? void 0 : _a.getString(0);
4231
+ this._lastExpiresTimeString = timeStr !== null && timeStr !== void 0 ? timeStr : null;
4232
+ this._needsRefresh = false;
4233
+ // add the interval to check for updates
4234
+ clearInterval(this._updateCheckIntervalId);
4235
+ let isChecking = false;
4236
+ this._updateCheckIntervalId = setInterval(() => __awaiter(this, void 0, void 0, function* () {
4237
+ var _a;
4238
+ if (isChecking || this._needsRefresh)
4239
+ return;
4240
+ isChecking = true;
4241
+ // first, check if this view is still active
4242
+ if (this._designView && !Array.from(datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.tableViews).some((tv) => tv === this._designView)) {
4243
+ clearInterval(this._updateCheckIntervalId);
4244
+ console.log('Clearing campaign update check interval');
4245
+ this._updateCheckIntervalId = null;
4246
+ isChecking = false;
4247
+ return;
4248
+ }
4249
+ if (!this._campaignId || !this.template || this._isSaving || this.isJoining || !this._designView || datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.v !== this._designView) {
4250
+ isChecking = false;
4251
+ return;
4252
+ }
4253
+ try {
4254
+ const timeDf = yield _package_api__WEBPACK_IMPORTED_MODULE_17__.queries.getLastModified(this.appName, this._campaignId);
4255
+ const timeStr = (_a = timeDf === null || timeDf === void 0 ? void 0 : timeDf.col('updated_at')) === null || _a === void 0 ? void 0 : _a.getString(0);
4256
+ if (!timeStr) {
4257
+ isChecking = false;
4258
+ return;
4259
+ }
4260
+ if (this._lastExpiresTimeString !== timeStr && this._refreshCampaignIcon) {
4261
+ this._lastExpiresTimeString = timeStr;
4262
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.hints.addHintIndicator(this._refreshCampaignIcon, true, 10000);
4263
+ this._refreshIconTooltip = 'New changes available. Click to refresh campaign.';
4264
+ this._lastExpiresTimeString = timeStr;
4265
+ this._needsRefresh = true;
4266
+ }
4267
+ }
4268
+ catch (e) {
4269
+ console.error(e);
4270
+ }
4271
+ isChecking = false;
4272
+ }), 3000);
4273
+ }).catch((e) => console.error(e));
4159
4274
  });
4160
4275
  }
4161
4276
  get campaignId() { return this._campaignId; }
@@ -4320,6 +4435,8 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4320
4435
  const calculateRibbon = datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.iconFA('wrench', getComputeDialog, 'Calculate additional properties');
4321
4436
  const addNewRowButton = datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.icons.add(() => { var _a; (_a = this.dataFrame) === null || _a === void 0 ? void 0 : _a.rows.addNew(null, true); }, 'Add new row');
4322
4437
  const applyLayoutButton = datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.iconSvg('view-layout', () => { this.applyTemplateLayout(view); }, `Apply template layout ${((_a = this.template) === null || _a === void 0 ? void 0 : _a.localLayoutPath) ? '(Loaded from mounted file storage)' : '(Static)'}`);
4438
+ this._refreshCampaignIcon = datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.icons.sync(() => __awaiter(this, void 0, void 0, function* () { this.refreshCampaign(); }));
4439
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.tooltip.bind(this._refreshCampaignIcon, () => this._refreshIconTooltip);
4323
4440
  const permissionsButton = datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.iconFA('share', () => __awaiter(this, void 0, void 0, function* () {
4324
4441
  var _a;
4325
4442
  yield (new _dialogs_permissions_dialog__WEBPACK_IMPORTED_MODULE_15__.PermissionsDialog((_a = this.campaign) === null || _a === void 0 ? void 0 : _a.permissions)).show((res) => {
@@ -4390,6 +4507,7 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4390
4507
  }
4391
4508
  ribbonButtons.unshift(calculateRibbon);
4392
4509
  ribbonButtons.unshift(addNewRowButton);
4510
+ ribbonButtons.unshift(this._refreshCampaignIcon);
4393
4511
  ribbons.push(ribbonButtons);
4394
4512
  // remove project save button from the ribbon
4395
4513
  ribbons.some((rg) => {
@@ -4721,8 +4839,31 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4721
4839
  }
4722
4840
  saveCampaign() {
4723
4841
  return __awaiter(this, arguments, void 0, function* (notify = true, isCreating = false, customProps) {
4724
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
4842
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
4843
+ this._isSaving = true;
4725
4844
  const campaignId = this.campaignId;
4845
+ const someoneElseEditingLoaderTimeout = setTimeout(() => {
4846
+ var _a;
4847
+ if ((_a = this._designView) === null || _a === void 0 ? void 0 : _a.grid)
4848
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.setUpdateIndicator(this._designView.grid.root, true, 'Someone else is saving the campaign. Please wait...');
4849
+ }, 500);
4850
+ try {
4851
+ const lock = yield this.aquireAndWaitCampaignLock(campaignId);
4852
+ if (!(lock === null || lock === void 0 ? void 0 : lock.aquired)) // could not acquire lock
4853
+ throw new Error('Could not acquire lock for campaign during 30 seconds');
4854
+ }
4855
+ catch (e) {
4856
+ clearTimeout(someoneElseEditingLoaderTimeout);
4857
+ if ((_a = this._designView) === null || _a === void 0 ? void 0 : _a.grid)
4858
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.setUpdateIndicator(this._designView.grid.root, false);
4859
+ datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.error('Failed to acquire campaign lock. Someone else might be editing the campaign. Please try again later.');
4860
+ this._isSaving = false;
4861
+ return this.campaign;
4862
+ }
4863
+ clearTimeout(someoneElseEditingLoaderTimeout);
4864
+ if ((_b = this._designView) === null || _b === void 0 ? void 0 : _b.grid)
4865
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.setUpdateIndicator(this._designView.grid.root, true, 'Saving Campaign...');
4866
+ // await DG.delay(3000); // artificial delay to simulate long save
4726
4867
  const templateName = this.template.name;
4727
4868
  const enrichedDf = this.dataFrame;
4728
4869
  const campaignName = campaignId;
@@ -4730,7 +4871,7 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4730
4871
  enrichedDf.columns.toList().forEach((col) => columnSemTypes[col.name] = col.semType);
4731
4872
  const colTypeMap = {};
4732
4873
  enrichedDf.columns.toList().forEach((col) => colTypeMap[col.name] = col.type);
4733
- const sketchStateString = (_b = (_a = this.campaign) === null || _a === void 0 ? void 0 : _a.tilesViewerFormSketch) !== null && _b !== void 0 ? _b : undefined;
4874
+ const sketchStateString = (_d = (_c = this.campaign) === null || _c === void 0 ? void 0 : _c.tilesViewerFormSketch) !== null && _d !== void 0 ? _d : undefined;
4734
4875
  const getDefaultPerms = () => __awaiter(this, void 0, void 0, function* () {
4735
4876
  try {
4736
4877
  const perms = yield (0,_packageSettingsEditor__WEBPACK_IMPORTED_MODULE_16__.getDefaultSharingSettings)();
@@ -4748,16 +4889,16 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4748
4889
  return _dialogs_permissions_dialog__WEBPACK_IMPORTED_MODULE_15__.defaultPermissions;
4749
4890
  });
4750
4891
  // if its first time save author as current user, else keep the same
4751
- const authorUserId = (_d = (_c = this.campaign) === null || _c === void 0 ? void 0 : _c.authorUserId) !== null && _d !== void 0 ? _d : datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.user.id;
4752
- const permissions = (_f = (_e = this.campaign) === null || _e === void 0 ? void 0 : _e.permissions) !== null && _f !== void 0 ? _f : yield getDefaultPerms();
4753
- const authorName = authorUserId ? (_h = (_g = this.campaign) === null || _g === void 0 ? void 0 : _g.authorUserFriendlyName) !== null && _h !== void 0 ? _h : (_j = (yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.dapi.users.find(authorUserId))) === null || _j === void 0 ? void 0 : _j.friendlyName : undefined;
4892
+ const authorUserId = (_f = (_e = this.campaign) === null || _e === void 0 ? void 0 : _e.authorUserId) !== null && _f !== void 0 ? _f : datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.user.id;
4893
+ const permissions = (_h = (_g = this.campaign) === null || _g === void 0 ? void 0 : _g.permissions) !== null && _h !== void 0 ? _h : yield getDefaultPerms();
4894
+ const authorName = authorUserId ? (_k = (_j = this.campaign) === null || _j === void 0 ? void 0 : _j.authorUserFriendlyName) !== null && _k !== void 0 ? _k : (_l = (yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.dapi.users.find(authorUserId))) === null || _l === void 0 ? void 0 : _l.friendlyName : undefined;
4754
4895
  const campaign = {
4755
4896
  name: campaignName,
4756
- friendlyName: (_m = (_l = (_k = this.campaign) === null || _k === void 0 ? void 0 : _k.friendlyName) !== null && _l !== void 0 ? _l : customProps === null || customProps === void 0 ? void 0 : customProps.friendlyName) !== null && _m !== void 0 ? _m : campaignName,
4897
+ friendlyName: (_p = (_o = (_m = this.campaign) === null || _m === void 0 ? void 0 : _m.friendlyName) !== null && _o !== void 0 ? _o : customProps === null || customProps === void 0 ? void 0 : customProps.friendlyName) !== null && _p !== void 0 ? _p : campaignName,
4757
4898
  templateName,
4758
- status: (_p = (_o = this.campaign) === null || _o === void 0 ? void 0 : _o.status) !== null && _p !== void 0 ? _p : 'In Progress',
4759
- createDate: (_r = (_q = this.campaign) === null || _q === void 0 ? void 0 : _q.createDate) !== null && _r !== void 0 ? _r : (0,_utils__WEBPACK_IMPORTED_MODULE_8__.toFormatedDateString)(new Date()),
4760
- campaignFields: (_t = (_s = this.campaign) === null || _s === void 0 ? void 0 : _s.campaignFields) !== null && _t !== void 0 ? _t : this.campaignProps,
4899
+ status: (_r = (_q = this.campaign) === null || _q === void 0 ? void 0 : _q.status) !== null && _r !== void 0 ? _r : 'In Progress',
4900
+ createDate: (_t = (_s = this.campaign) === null || _s === void 0 ? void 0 : _s.createDate) !== null && _t !== void 0 ? _t : (0,_utils__WEBPACK_IMPORTED_MODULE_8__.toFormatedDateString)(new Date()),
4901
+ campaignFields: (_v = (_u = this.campaign) === null || _u === void 0 ? void 0 : _u.campaignFields) !== null && _v !== void 0 ? _v : this.campaignProps,
4761
4902
  columnSemTypes,
4762
4903
  rowCount: enrichedDf.rowCount,
4763
4904
  filteredRowCount: enrichedDf.filter.trueCount,
@@ -4772,6 +4913,10 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4772
4913
  };
4773
4914
  if (!this.hasEditPermission) {
4774
4915
  datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.error('You do not have permission to modify this campaign');
4916
+ if ((_w = this._designView) === null || _w === void 0 ? void 0 : _w.grid)
4917
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.setUpdateIndicator(this._designView.grid.root, false);
4918
+ yield this.releaseCampaignLock(campaignId);
4919
+ this._isSaving = false;
4775
4920
  return campaign;
4776
4921
  }
4777
4922
  const campaignPath = `${this.appName}/campaigns/${campaignId}/${_consts__WEBPACK_IMPORTED_MODULE_4__.CampaignJsonName}`;
@@ -4779,8 +4924,8 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4779
4924
  let resDf = enrichedDf;
4780
4925
  if (yield _package__WEBPACK_IMPORTED_MODULE_7__._package.files.exists(campaignPath)) {
4781
4926
  const prevCamp = JSON.parse(yield _package__WEBPACK_IMPORTED_MODULE_7__._package.files.readAsText(campaignPath));
4782
- if (((_u = prevCamp.version) !== null && _u !== void 0 ? _u : 0) > this.version) {
4783
- campaign.version = Math.max(this.version, ((_v = prevCamp.version) !== null && _v !== void 0 ? _v : 0)) + 1;
4927
+ if (((_x = prevCamp.version) !== null && _x !== void 0 ? _x : 0) > this.version) {
4928
+ campaign.version = Math.max(this.version, ((_y = prevCamp.version) !== null && _y !== void 0 ? _y : 0)) + 1;
4784
4929
  if (yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.dapi.files.exists(this._filePath)) {
4785
4930
  try {
4786
4931
  const prevCampDf = yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.dapi.files.readCsv(this._filePath);
@@ -4805,7 +4950,13 @@ class HitDesignApp extends _hit_app_base__WEBPACK_IMPORTED_MODULE_11__.HitAppBas
4805
4950
  notify && datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.info('Campaign saved successfully.');
4806
4951
  !notify && isCreating && datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.shell.info('Campaign created successfully.');
4807
4952
  this.campaign = campaign;
4808
- this.designViewName = (_w = campaign.friendlyName) !== null && _w !== void 0 ? _w : campaign.name;
4953
+ this.designViewName = (_z = campaign.friendlyName) !== null && _z !== void 0 ? _z : campaign.name;
4954
+ const updatedAt = yield this.releaseCampaignLock(campaignId);
4955
+ this._lastExpiresTimeString = updatedAt;
4956
+ if ((_0 = this._designView) === null || _0 === void 0 ? void 0 : _0.grid)
4957
+ datagrok_api_ui__WEBPACK_IMPORTED_MODULE_1__.setUpdateIndicator(this._designView.grid.root, false);
4958
+ this._isSaving = false;
4959
+ this._refreshIconTooltip = 'Refresh Campaign';
4809
4960
  return campaign;
4810
4961
  });
4811
4962
  }
@@ -5456,10 +5607,11 @@ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _argume
5456
5607
 
5457
5608
 
5458
5609
 
5610
+ // @ts-ignore
5459
5611
 
5460
5612
 
5461
5613
  function getTilesViewDialog(app, getTableView) {
5462
- var _a, _b, _c, _d, _e, _f, _g, _h;
5614
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
5463
5615
  const tilesViewerSketchStateString = (_a = app.campaign) === null || _a === void 0 ? void 0 : _a.tilesViewerFormSketch;
5464
5616
  let sketchState = null;
5465
5617
  if (tilesViewerSketchStateString && tilesViewerSketchStateString.length > 0) {
@@ -5562,7 +5714,9 @@ function getTilesViewDialog(app, getTableView) {
5562
5714
  else
5563
5715
  hideModal();
5564
5716
  });
5717
+ const colVersion = (_h = (_g = app.dataFrame) === null || _g === void 0 ? void 0 : _g.col(_consts__WEBPACK_IMPORTED_MODULE_3__.TileCategoriesColName)) === null || _h === void 0 ? void 0 : _h.version;
5565
5718
  const closeSub = modal.onClose.subscribe(() => {
5719
+ var _a, _b;
5566
5720
  closeSub.unsubscribe();
5567
5721
  viewChangeSub.unsubscribe();
5568
5722
  stageEditorDialog === null || stageEditorDialog === void 0 ? void 0 : stageEditorDialog.close();
@@ -5580,12 +5734,14 @@ function getTilesViewDialog(app, getTableView) {
5580
5734
  console.error('Failed to save sketch state', e);
5581
5735
  }
5582
5736
  closeViewer();
5737
+ if (colVersion != undefined && ((_b = (_a = app.dataFrame) === null || _a === void 0 ? void 0 : _a.col(_consts__WEBPACK_IMPORTED_MODULE_3__.TileCategoriesColName)) === null || _b === void 0 ? void 0 : _b.version) !== colVersion)
5738
+ app.saveCampaign();
5583
5739
  });
5584
5740
  modal.showModal(true);
5585
- (_g = modal.getButton('CANCEL')) === null || _g === void 0 ? void 0 : _g.remove();
5741
+ (_j = modal.getButton('CANCEL')) === null || _j === void 0 ? void 0 : _j.remove();
5586
5742
  modal.onOK(() => { });
5587
5743
  // remove the modal background
5588
- (_h = document.querySelector('.d4-modal-background')) === null || _h === void 0 ? void 0 : _h.remove();
5744
+ (_k = document.querySelector('.d4-modal-background')) === null || _k === void 0 ? void 0 : _k.remove();
5589
5745
  }
5590
5746
 
5591
5747
 
@@ -7327,7 +7483,9 @@ __webpack_require__.r(__webpack_exports__);
7327
7483
  /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7328
7484
  /* harmony export */ calculateCellValues: () => (/* binding */ calculateCellValues),
7329
7485
  /* harmony export */ calculateColumns: () => (/* binding */ calculateColumns),
7330
- /* harmony export */ getNewVid: () => (/* binding */ getNewVid)
7486
+ /* harmony export */ getNewVid: () => (/* binding */ getNewVid),
7487
+ /* harmony export */ getNewVidFromArray: () => (/* binding */ getNewVidFromArray),
7488
+ /* harmony export */ getNextVid: () => (/* binding */ getNextVid)
7331
7489
  /* harmony export */ });
7332
7490
  /* harmony import */ var datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! datagrok-api/grok */ "datagrok-api/grok");
7333
7491
  /* harmony import */ var datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__);
@@ -7435,9 +7593,13 @@ function calculateCellValues(values_1, descriptors_1, functions_1) {
7435
7593
  }
7436
7594
  ;
7437
7595
  function getNewVid(vidCol) {
7596
+ var _a;
7597
+ return getNewVidFromArray((_a = vidCol.categories) !== null && _a !== void 0 ? _a : []);
7598
+ }
7599
+ function getNewVidFromArray(vidArray) {
7438
7600
  let maxId = 0;
7439
- if (vidCol.length > 1) {
7440
- for (const vid of vidCol.toList()) {
7601
+ if (vidArray.length > 1) {
7602
+ for (const vid of vidArray) {
7441
7603
  if (!vid || !vid.startsWith(_consts__WEBPACK_IMPORTED_MODULE_2__.ViDColFormat[0]) || vid.length !== _consts__WEBPACK_IMPORTED_MODULE_2__.ViDColFormat.length)
7442
7604
  continue;
7443
7605
  const num = parseInt(vid.substring(1));
@@ -7449,6 +7611,9 @@ function getNewVid(vidCol) {
7449
7611
  }
7450
7612
  return `V${''.concat(...new Array(_consts__WEBPACK_IMPORTED_MODULE_2__.ViDColFormat.length - 1 - (maxId + 1).toString().length).fill('0'))}${maxId + 1}`;
7451
7613
  }
7614
+ function getNextVid(maxVid) {
7615
+ return `V${''.concat(...new Array(_consts__WEBPACK_IMPORTED_MODULE_2__.ViDColFormat.length - 1 - (maxVid + 1).toString().length).fill('0'))}${maxVid + 1}`;
7616
+ }
7452
7617
  function calculateColumns(resultMap, dataFrame, molColName) {
7453
7618
  return __awaiter(this, void 0, void 0, function* () {
7454
7619
  var _a, _b;
@@ -7552,6 +7717,144 @@ function calculateColumns(resultMap, dataFrame, molColName) {
7552
7717
  }
7553
7718
 
7554
7719
 
7720
+ /***/ }),
7721
+
7722
+ /***/ "./src/package-api.ts":
7723
+ /*!****************************!*\
7724
+ !*** ./src/package-api.ts ***!
7725
+ \****************************/
7726
+ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
7727
+
7728
+ "use strict";
7729
+ __webpack_require__.r(__webpack_exports__);
7730
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
7731
+ /* harmony export */ funcs: () => (/* binding */ funcs),
7732
+ /* harmony export */ queries: () => (/* binding */ queries)
7733
+ /* harmony export */ });
7734
+ /* harmony import */ var datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! datagrok-api/grok */ "datagrok-api/grok");
7735
+ /* harmony import */ var datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__);
7736
+ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
7737
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7738
+ return new (P || (P = Promise))(function (resolve, reject) {
7739
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7740
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7741
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7742
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
7743
+ });
7744
+ };
7745
+ /**
7746
+ This file is auto-generated by the grok api command.
7747
+ If you notice any changes, please push them to the repository.
7748
+ Do not edit this file manually.
7749
+ */
7750
+
7751
+ var queries;
7752
+ (function (queries) {
7753
+ /**
7754
+ Acquire a lock for a campaign. If the lock is expired, it will be removed and a new lock will be created.
7755
+ */
7756
+ function acquireCampaignLock(appName, campaignId, lockedBy) {
7757
+ return __awaiter(this, void 0, void 0, function* () {
7758
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.data.query('HitTriage:AcquireCampaignLock', { appName, campaignId, lockedBy });
7759
+ });
7760
+ }
7761
+ queries.acquireCampaignLock = acquireCampaignLock;
7762
+ /**
7763
+ Release a lock for a campaign.
7764
+ */
7765
+ function releaseCampaignLock(appName, campaignId) {
7766
+ return __awaiter(this, void 0, void 0, function* () {
7767
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.data.query('HitTriage:ReleaseCampaignLock', { appName, campaignId });
7768
+ });
7769
+ }
7770
+ queries.releaseCampaignLock = releaseCampaignLock;
7771
+ /**
7772
+ Get the last modified time of a campaign lock release (same as save).
7773
+ */
7774
+ function getLastModified(appName, campaignId) {
7775
+ return __awaiter(this, void 0, void 0, function* () {
7776
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.data.query('HitTriage:GetLastModified', { appName, campaignId });
7777
+ });
7778
+ }
7779
+ queries.getLastModified = getLastModified;
7780
+ })(queries || (queries = {}));
7781
+ var funcs;
7782
+ (function (funcs) {
7783
+ function hitTriageAppTreeBrowser(treeNode) {
7784
+ return __awaiter(this, void 0, void 0, function* () {
7785
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:HitTriageAppTreeBrowser', { treeNode });
7786
+ });
7787
+ }
7788
+ funcs.hitTriageAppTreeBrowser = hitTriageAppTreeBrowser;
7789
+ function hitDesignAppTreeBrowser(treeNode) {
7790
+ return __awaiter(this, void 0, void 0, function* () {
7791
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:HitDesignAppTreeBrowser', { treeNode });
7792
+ });
7793
+ }
7794
+ funcs.hitDesignAppTreeBrowser = hitDesignAppTreeBrowser;
7795
+ function peptiHitAppTreeBrowser(treeNode) {
7796
+ return __awaiter(this, void 0, void 0, function* () {
7797
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:PeptiHitAppTreeBrowser', { treeNode });
7798
+ });
7799
+ }
7800
+ funcs.peptiHitAppTreeBrowser = peptiHitAppTreeBrowser;
7801
+ function hitTriageApp() {
7802
+ return __awaiter(this, void 0, void 0, function* () {
7803
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:HitTriageApp', {});
7804
+ });
7805
+ }
7806
+ funcs.hitTriageApp = hitTriageApp;
7807
+ function hitDesignApp() {
7808
+ return __awaiter(this, void 0, void 0, function* () {
7809
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:HitDesignApp', {});
7810
+ });
7811
+ }
7812
+ funcs.hitDesignApp = hitDesignApp;
7813
+ function peptiHitApp() {
7814
+ return __awaiter(this, void 0, void 0, function* () {
7815
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:PeptiHitApp', {});
7816
+ });
7817
+ }
7818
+ funcs.peptiHitApp = peptiHitApp;
7819
+ function demoFileIngest() {
7820
+ return __awaiter(this, void 0, void 0, function* () {
7821
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:DemoFileIngest', {});
7822
+ });
7823
+ }
7824
+ funcs.demoFileIngest = demoFileIngest;
7825
+ function demoFileIngest1() {
7826
+ return __awaiter(this, void 0, void 0, function* () {
7827
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:DemoFileIngest1', {});
7828
+ });
7829
+ }
7830
+ funcs.demoFileIngest1 = demoFileIngest1;
7831
+ function demoFileIngest2(numberOfMolecules) {
7832
+ return __awaiter(this, void 0, void 0, function* () {
7833
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:DemoFileIngest2', { numberOfMolecules });
7834
+ });
7835
+ }
7836
+ funcs.demoFileIngest2 = demoFileIngest2;
7837
+ function demoFileSubmit(df, molecules) {
7838
+ return __awaiter(this, void 0, void 0, function* () {
7839
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:DemoFileSubmit', { df, molecules });
7840
+ });
7841
+ }
7842
+ funcs.demoFileSubmit = demoFileSubmit;
7843
+ function gasteigerCellRenderer() {
7844
+ return __awaiter(this, void 0, void 0, function* () {
7845
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:GasteigerCellRenderer', {});
7846
+ });
7847
+ }
7848
+ funcs.gasteigerCellRenderer = gasteigerCellRenderer;
7849
+ function htPackageSettingEditor(propList) {
7850
+ return __awaiter(this, void 0, void 0, function* () {
7851
+ return yield datagrok_api_grok__WEBPACK_IMPORTED_MODULE_0__.functions.call('HitTriage:HtPackageSettingEditor', { propList });
7852
+ });
7853
+ }
7854
+ funcs.htPackageSettingEditor = htPackageSettingEditor;
7855
+ })(funcs || (funcs = {}));
7856
+
7857
+
7555
7858
  /***/ }),
7556
7859
 
7557
7860
  /***/ "./src/package.g.ts":
@@ -7652,7 +7955,7 @@ function demoFileIngest1() {
7652
7955
  }
7653
7956
  //name: Demo Molecules variable
7654
7957
  //tags: HitTriageDataSource
7655
- //input: double numberOfMolecules { description: Molecules counts; type: int }
7958
+ //input: int numberOfMolecules { description: Molecules counts }
7656
7959
  //output: dataframe result
7657
7960
  function demoFileIngest2(numberOfMolecules) {
7658
7961
  return __awaiter(this, void 0, void 0, function* () {
@@ -7661,8 +7964,8 @@ function demoFileIngest2(numberOfMolecules) {
7661
7964
  }
7662
7965
  //name: Demo File Submit
7663
7966
  //tags: HitTriageSubmitFunction
7664
- //input: dataframe df { description: Dataframe }
7665
- //input: string molecules { description: Molecules column name }
7967
+ //input: dataframe df { description: Dataframe }
7968
+ //input: string molecules { description: Molecules column name }
7666
7969
  function demoFileSubmit(df, molecules) {
7667
7970
  return __awaiter(this, void 0, void 0, function* () {
7668
7971
  yield _package__WEBPACK_IMPORTED_MODULE_0__.PackageFunctions.demoFileSubmit(df, molecules);
@@ -7678,7 +7981,7 @@ function gasteigerCellRenderer() {
7678
7981
  }
7679
7982
  //name: Hit Triage package settings editor
7680
7983
  //tags: packageSettingsEditor
7681
- //input: dynamic propList { type: object }
7984
+ //input: object propList
7682
7985
  //output: widget result
7683
7986
  function htPackageSettingEditor(properties) {
7684
7987
  return __awaiter(this, void 0, void 0, function* () {