Skip to content

Commit d647fdb

Browse files
committed
- Convert UserDismissedPublication from Boolean to Date to allow deferred re-search after dismissal
- Re-search NCBI after deferral expires; clear dismissal if different publication found - Add publication search frequency (default 3 months) to admin UI and settings form - Update tests to match new log messages and Date-based dismissal
1 parent 754ec40 commit d647fdb

9 files changed

Lines changed: 137 additions & 27 deletions

File tree

panoramapublic/resources/schemas/dbscripts/postgresql/panoramapublic-25.003-25.004.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
ALTER TABLE panoramapublic.DatasetStatus ADD COLUMN PotentialPublicationId VARCHAR(255);
44
ALTER TABLE panoramapublic.DatasetStatus ADD COLUMN PublicationType VARCHAR(50);
55
ALTER TABLE panoramapublic.DatasetStatus ADD COLUMN PublicationMatchInfo TEXT;
6-
ALTER TABLE panoramapublic.DatasetStatus ADD COLUMN UserDismissedPublication BOOLEAN DEFAULT FALSE;
6+
ALTER TABLE panoramapublic.DatasetStatus ADD COLUMN UserDismissedPublication TIMESTAMP;

panoramapublic/resources/schemas/panoramapublic.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@
874874
<description>Information about which fields (e.g. PXD, Panorama link, title etc.) matched</description>
875875
</column>
876876
<column columnName="UserDismissedPublication">
877-
<description>User indicated the found publication is not related to their data</description>
877+
<description>Date when the user dismissed the publication suggestion for this dataset</description>
878878
</column>
879879
</columns>
880880
</table>

panoramapublic/src/org/labkey/panoramapublic/PanoramaPublicController.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10118,6 +10118,14 @@ else if (form.getExtensionLength() < 0)
1011810118
{
1011910119
errors.reject(ERROR_MSG, "Value for 'Extension duration' cannot be less than 0.");
1012010120
}
10121+
if (form.getPublicationSearchFrequency() == null)
10122+
{
10123+
errors.reject(ERROR_MSG, "Please enter a value for 'Publication search frequency'.");
10124+
}
10125+
else if (form.getPublicationSearchFrequency() < 1)
10126+
{
10127+
errors.reject(ERROR_MSG, "Value for 'Publication search frequency' must be at least 1.");
10128+
}
1012110129
if (form.getReminderTime() == null)
1012210130
{
1012310131
errors.reject(ERROR_MSG, "Please enter a value for 'Reminder time'.");
@@ -10141,6 +10149,7 @@ public ModelAndView getView(PrivateDataReminderSettingsForm form, boolean reshow
1014110149
form.setReminderFrequency(settings.getReminderFrequency());
1014210150
form.setExtensionLength(settings.getExtensionLength());
1014310151
form.setEnablePublicationSearch(settings.isEnablePublicationSearch());
10152+
form.setPublicationSearchFrequency(settings.getPublicationSearchFrequency());
1014410153
}
1014510154

1014610155
VBox view = new VBox();
@@ -10160,6 +10169,7 @@ public boolean handlePost(PrivateDataReminderSettingsForm form, BindException er
1016010169
settings.setReminderFrequency(form.getReminderFrequency());
1016110170
settings.setExtensionLength(form.getExtensionLength());
1016210171
settings.setEnablePublicationSearch(form.isEnablePublicationSearch());
10172+
settings.setPublicationSearchFrequency(form.getPublicationSearchFrequency());
1016310173
PrivateDataReminderSettings.save(settings);
1016410174

1016510175
PrivateDataMessageScheduler.getInstance().initialize(settings.isEnableReminders());
@@ -10198,6 +10208,7 @@ public static class PrivateDataReminderSettingsForm
1019810208
private Integer _reminderFrequency;
1019910209
private Integer _delayUntilFirstReminder;
1020010210
private boolean _enablePublicationSearch;
10211+
private Integer _publicationSearchFrequency;
1020110212

1020210213
public boolean isEnabled()
1020310214
{
@@ -10258,6 +10269,16 @@ public void setEnablePublicationSearch(boolean enablePublicationSearch)
1025810269
{
1025910270
_enablePublicationSearch = enablePublicationSearch;
1026010271
}
10272+
10273+
public Integer getPublicationSearchFrequency()
10274+
{
10275+
return _publicationSearchFrequency;
10276+
}
10277+
10278+
public void setPublicationSearchFrequency(Integer publicationSearchFrequency)
10279+
{
10280+
_publicationSearchFrequency = publicationSearchFrequency;
10281+
}
1026110282
}
1026210283

1026310284
@RequiresPermission(AdminOperationsPermission.class)
@@ -10678,7 +10699,7 @@ protected void doValidationForAction(Errors errors)
1067810699
{
1067910700
errors.reject(ERROR_MSG, "No publication suggestion exists for the data with short URL " + _exptAnnotations.getShortUrl().renderShortURL());
1068010701
}
10681-
else if (Boolean.TRUE.equals(_datasetStatus.getUserDismissedPublication()))
10702+
else if (_datasetStatus.getUserDismissedPublication() != null)
1068210703
{
1068310704
errors.reject(ERROR_MSG, "The publication suggestion for the data with short URL " + _exptAnnotations.getShortUrl().renderShortURL()
1068410705
+ " has already been dismissed");
@@ -10697,7 +10718,7 @@ else if (Boolean.TRUE.equals(_datasetStatus.getUserDismissedPublication()))
1069710718
@Override
1069810719
protected void updateDatasetStatus(DatasetStatus datasetStatus)
1069910720
{
10700-
datasetStatus.setUserDismissedPublication(true);
10721+
datasetStatus.setUserDismissedPublication(new Date());
1070110722
}
1070210723

1070310724
@Override
@@ -10753,7 +10774,7 @@ public ModelAndView getView(ExperimentIdForm form, BindException errors)
1075310774
// Check if any displayed match was dismissed by the user
1075410775
boolean showDismissedColumn = false;
1075510776
String dismissedPubId = null;
10756-
if (datasetStatus != null && Boolean.TRUE.equals(datasetStatus.getUserDismissedPublication())
10777+
if (datasetStatus != null && datasetStatus.getUserDismissedPublication() != null
1075710778
&& datasetStatus.getPotentialPublicationId() != null)
1075810779
{
1075910780
dismissedPubId = datasetStatus.getPotentialPublicationId();
@@ -10926,7 +10947,7 @@ public boolean handlePost(NotifySubmitterForm form, BindException errors) throws
1092610947

1092710948
// Check if the user has already dismissed this publication suggestion
1092810949
DatasetStatus datasetStatus = DatasetStatusManager.getForExperiment(exptAnnotations);
10929-
if (datasetStatus != null && Boolean.TRUE.equals(datasetStatus.getUserDismissedPublication())
10950+
if (datasetStatus != null && datasetStatus.getUserDismissedPublication() != null
1093010951
&& form.getPublicationId().equals(datasetStatus.getPotentialPublicationId()))
1093110952
{
1093210953
errors.reject(ERROR_MSG, "The user has already dismissed the publication suggestion "
@@ -10995,7 +11016,7 @@ journal, submission, exptAnnotations, submitter, getUser(), notifyUsers,
1099511016
datasetStatus.setPotentialPublicationId(form.getPublicationId());
1099611017
datasetStatus.setPublicationType(pubType.name());
1099711018
datasetStatus.setPublicationMatchInfo(form.getMatchInfo());
10998-
datasetStatus.setUserDismissedPublication(false);
11019+
datasetStatus.setUserDismissedPublication(null);
1099911020
datasetStatus.setLastReminderDate(new Date());
1100011021

1100111022
if (datasetStatus.getId() == 0)

panoramapublic/src/org/labkey/panoramapublic/message/PrivateDataReminderSettings.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ public class PrivateDataReminderSettings
2525
public static final String PROP_REMINDER_FREQUENCY = "Reminder frequency (months)";
2626
public static final String PROP_EXTENSION_LENGTH = "Extension duration (months)";
2727
public static final String PROP_ENABLE_PUBLICATION_SEARCH = "Enable publication search";
28+
public static final String PROP_PUBLICATION_SEARCH_FREQUENCY = "Publication search frequency (months)";
2829

2930
private static final boolean DEFAULT_ENABLE_REMINDERS = false;
3031
public static final String DEFAULT_REMINDER_TIME = "8:00 AM";
3132
private static final int DEFAULT_DELAY_UNTIL_FIRST_REMINDER = 12; // Send the first reminder after the data has been private for a year.
3233
private static final int DEFAULT_REMINDER_FREQUENCY = 1; // Send reminders once a month, unless extension or deletion was requested.
3334
private static final int DEFAULT_EXTENSION_LENGTH = 6; // Private status of a dataset can be extended by 6 months.
3435
private static final boolean DEFAULT_ENABLE_PUBLICATION_SEARCH = false;
36+
private static final int DEFAULT_PUBLICATION_SEARCH_FREQUENCY = 3; // Re-search every 3 months after dismissal
3537

3638
public static final String DATE_FORMAT_PATTERN = "MMMM d, yyyy";
3739
public static final String REMINDER_TIME_FORMAT = "h:mm a";
@@ -43,6 +45,7 @@ public class PrivateDataReminderSettings
4345
private int _reminderFrequency;
4446
private int _extensionLength;
4547
private boolean _enablePublicationSearch;
48+
private int _publicationSearchFrequency;
4649

4750
public static PrivateDataReminderSettings get()
4851
{
@@ -78,6 +81,11 @@ public static PrivateDataReminderSettings get()
7881
? DEFAULT_ENABLE_PUBLICATION_SEARCH
7982
: Boolean.valueOf(settingsMap.get(PROP_ENABLE_PUBLICATION_SEARCH));
8083
settings.setEnablePublicationSearch(enablePublicationCheck);
84+
85+
int publicationSearchFrequency = settingsMap.get(PROP_PUBLICATION_SEARCH_FREQUENCY) == null
86+
? DEFAULT_PUBLICATION_SEARCH_FREQUENCY
87+
: Integer.valueOf(settingsMap.get(PROP_PUBLICATION_SEARCH_FREQUENCY));
88+
settings.setPublicationSearchFrequency(publicationSearchFrequency);
8189
}
8290
else
8391
{
@@ -87,6 +95,7 @@ public static PrivateDataReminderSettings get()
8795
settings.setExtensionLength(DEFAULT_EXTENSION_LENGTH);
8896
settings.setReminderTime(parseReminderTime(DEFAULT_REMINDER_TIME));
8997
settings.setEnablePublicationSearch(DEFAULT_ENABLE_PUBLICATION_SEARCH);
98+
settings.setPublicationSearchFrequency(DEFAULT_PUBLICATION_SEARCH_FREQUENCY);
9099
}
91100

92101
return settings;
@@ -122,6 +131,7 @@ public static void save(PrivateDataReminderSettings settings)
122131
settingsMap.put(PROP_EXTENSION_LENGTH, String.valueOf(settings.getExtensionLength()));
123132
settingsMap.put(PROP_REMINDER_TIME, settings.getReminderTimeFormatted());
124133
settingsMap.put(PROP_ENABLE_PUBLICATION_SEARCH, String.valueOf(settings.isEnablePublicationSearch()));
134+
settingsMap.put(PROP_PUBLICATION_SEARCH_FREQUENCY, String.valueOf(settings.getPublicationSearchFrequency()));
125135
settingsMap.save();
126136
}
127137

@@ -190,6 +200,16 @@ public void setEnablePublicationSearch(boolean enablePublicationSearch)
190200
_enablePublicationSearch = enablePublicationSearch;
191201
}
192202

203+
public int getPublicationSearchFrequency()
204+
{
205+
return _publicationSearchFrequency;
206+
}
207+
208+
public void setPublicationSearchFrequency(int publicationSearchFrequency)
209+
{
210+
_publicationSearchFrequency = publicationSearchFrequency;
211+
}
212+
193213
public @Nullable Date getReminderValidUntilDate(@NotNull DatasetStatus status)
194214
{
195215
return status.getLastReminderDate() == null ? null : addMonths(status.getLastReminderDate(), getReminderFrequency());
@@ -210,6 +230,14 @@ public boolean isExtensionValid(@NotNull DatasetStatus status)
210230
return isDateInFuture(getExtensionValidUntilDate(status));
211231
}
212232

233+
public boolean isPublicationDismissalRecent(@NotNull DatasetStatus status)
234+
{
235+
Date dismissed = status.getUserDismissedPublication();
236+
if (dismissed == null) return false;
237+
Date searchDeferralEnd = addMonths(dismissed, getPublicationSearchFrequency());
238+
return isDateInFuture(searchDeferralEnd);
239+
}
240+
213241
public @Nullable String extensionValidUntilFormatted(@NotNull DatasetStatus status)
214242
{
215243
Date date = getExtensionValidUntilDate(status);

panoramapublic/src/org/labkey/panoramapublic/model/DatasetStatus.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class DatasetStatus extends DbEntity
1616
private String _potentialPublicationId;
1717
private String _publicationType;
1818
private String _publicationMatchInfo;
19-
private Boolean _userDismissedPublication;
19+
private Date _userDismissedPublication;
2020

2121
public int getExperimentAnnotationsId()
2222
{
@@ -115,12 +115,12 @@ public void setPublicationMatchInfo(String publicationMatchInfo)
115115
_publicationMatchInfo = publicationMatchInfo;
116116
}
117117

118-
public Boolean getUserDismissedPublication()
118+
public Date getUserDismissedPublication()
119119
{
120120
return _userDismissedPublication;
121121
}
122122

123-
public void setUserDismissedPublication(Boolean userDismissedPublication)
123+
public void setUserDismissedPublication(Date userDismissedPublication)
124124
{
125125
_userDismissedPublication = userDismissedPublication;
126126
}

panoramapublic/src/org/labkey/panoramapublic/pipeline/PrivateDataReminderJob.java

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,7 @@ public static ReminderDecision skip(String reason) {
172172
public boolean shouldPost() { return shouldPost; }
173173
public String getReason() { return reason; }
174174
}
175-
176-
/**
177-
* Container for publication search results used during reminder processing
178-
*/
175+
179176
/**
180177
* Checks for publications associated with the experiment if enabled in settings
181178
* @param expAnnotations The experiment to check
@@ -184,9 +181,10 @@ public static ReminderDecision skip(String reason) {
184181
* @param log Logger for diagnostic messages
185182
* @return NcbiArticleMatch with search result
186183
*/
187-
private static PublicationMatch searchForPublication(@NotNull ExperimentAnnotations expAnnotations,
184+
private PublicationMatch searchForPublication(@NotNull ExperimentAnnotations expAnnotations,
188185
@NotNull PrivateDataReminderSettings settings,
189186
boolean forceCheck,
187+
@NotNull User user,
190188
@NotNull Logger log)
191189
{
192190
// Check if publication checking is enabled (either globally or forced for this run)
@@ -200,11 +198,46 @@ private static PublicationMatch searchForPublication(@NotNull ExperimentAnnotati
200198
DatasetStatus datasetStatus = DatasetStatusManager.getForExperiment(expAnnotations);
201199
if (datasetStatus != null)
202200
{
203-
// If user has dismissed the publication suggestion, don't search again
204-
if (Boolean.TRUE.equals(datasetStatus.getUserDismissedPublication()))
201+
// If user has dismissed the publication suggestion, check search delay
202+
Date dismissedDate = datasetStatus.getUserDismissedPublication();
203+
if (dismissedDate != null)
205204
{
206-
log.info(String.format("User has dismissed publication suggestion for experiment %d; skipping search", expAnnotations.getId()));
207-
return null;
205+
if (settings.isPublicationDismissalRecent(datasetStatus))
206+
{
207+
log.info(String.format("User dismissed publication for experiment %d on %s; Publication search deferred (%d months)",
208+
expAnnotations.getId(), dismissedDate, settings.getPublicationSearchFrequency()));
209+
return null;
210+
}
211+
212+
// Search deferral expired — re-search NCBI
213+
log.info(String.format("Search deferral expired for experiment %d (dismissed %s); re-searching NCBI",
214+
expAnnotations.getId(), dismissedDate));
215+
try
216+
{
217+
PublicationMatch newMatch = NcbiPublicationSearchService.get().searchForPublication(expAnnotations, log);
218+
if (newMatch != null && !newMatch.getPublicationId().equals(datasetStatus.getPotentialPublicationId()))
219+
{
220+
// Different publication found — return it (caller will save and notify)
221+
log.info(String.format("New publication %s found for experiment %d (previously dismissed %s)",
222+
newMatch.getPublicationId(), expAnnotations.getId(), datasetStatus.getPotentialPublicationId()));
223+
return newMatch;
224+
}
225+
else
226+
{
227+
// Same publication or nothing found — update dismissal date to restart search delay
228+
log.info(String.format("No new publication for experiment %d; resetting search delay",
229+
expAnnotations.getId()));
230+
datasetStatus.setUserDismissedPublication(new Date());
231+
DatasetStatusManager.update(datasetStatus, user);
232+
return null;
233+
}
234+
}
235+
catch (Exception e)
236+
{
237+
log.error(String.format("Error re-searching publication for experiment %d: %s",
238+
expAnnotations.getId(), e.getMessage()), e);
239+
return null;
240+
}
208241
}
209242

210243
// If we already have a cached publication ID, use it
@@ -332,7 +365,7 @@ private void processExperiment(Integer experimentAnnotationsId, ProcessingContex
332365
}
333366

334367
// Check for publications if enabled
335-
PublicationMatch publicationResult = searchForPublication(expAnnotations, context.getSettings(), _forcePublicationCheck, processingResults._log);
368+
PublicationMatch publicationResult = searchForPublication(expAnnotations, context.getSettings(), _forcePublicationCheck, getUser(), processingResults._log);
336369

337370
if (!context.isTestMode())
338371
{
@@ -392,12 +425,17 @@ private void updateDatasetStatus(ExperimentAnnotations expAnnotations, @Nullable
392425
{
393426
datasetStatus.setLastReminderDate(new Date());
394427

395-
// Save publication search results if found and not already cached
396-
if (publicationResult != null && StringUtils.isBlank(datasetStatus.getPotentialPublicationId()))
428+
// Update publication search results
429+
if (publicationResult != null)
397430
{
398-
datasetStatus.setPotentialPublicationId(publicationResult.getPublicationId());
399-
datasetStatus.setPublicationType(publicationResult.getPublicationType().name());
400-
datasetStatus.setPublicationMatchInfo(publicationResult.getMatchInfo());
431+
// If this is a new/different publication, update it and clear dismissal
432+
if (!publicationResult.getPublicationId().equals(datasetStatus.getPotentialPublicationId()))
433+
{
434+
datasetStatus.setPotentialPublicationId(publicationResult.getPublicationId());
435+
datasetStatus.setPublicationType(publicationResult.getPublicationType().name());
436+
datasetStatus.setPublicationMatchInfo(publicationResult.getMatchInfo());
437+
datasetStatus.setUserDismissedPublication(null); // Clear dismissal for new publication
438+
}
401439
}
402440

403441
DatasetStatusManager.update(datasetStatus, getUser());

panoramapublic/src/org/labkey/panoramapublic/view/privateDataRemindersSettingsForm.jsp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,19 @@
157157
</div>
158158
</td>
159159
</tr>
160+
<tr>
161+
<td class="labkey-form-label">
162+
<span><%=h(PrivateDataReminderSettings.PROP_PUBLICATION_SEARCH_FREQUENCY)%></span>
163+
</td>
164+
<td>
165+
<input style="padding:0 10px 0 0;" type="text" name="publicationSearchFrequency" value="<%=form.getPublicationSearchFrequency()%>" />
166+
<div style="font-size: 0.9em; color: #4682B4; margin: 4px 0 6px 0;">
167+
Number of months to wait after a user dismisses a publication suggestion before re-searching.
168+
<br/>
169+
If a different publication is found after this delay, the submitter will be notified again.
170+
</div>
171+
</td>
172+
</tr>
160173
<tr><td colspan=2">
161174
<%=button("Save").submit(true)%>
162175
<%=button("Cancel").href(panoramaPublicAdminUrl)%>

0 commit comments

Comments
 (0)