Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migration of timestamp #7671

Merged
merged 14 commits into from
May 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/main/java/org/jabref/gui/UpdateTimestampListener.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jabref.gui;

import org.jabref.model.entry.event.EntriesEventSource;
import org.jabref.model.entry.event.EntryChangedEvent;
import org.jabref.model.entry.field.StandardField;
import org.jabref.preferences.PreferencesService;
Expand All @@ -18,7 +19,9 @@ class UpdateTimestampListener {

@Subscribe
public void listen(EntryChangedEvent event) {
if (preferencesService.getTimestampPreferences().shouldAddModificationDate()) {
// The event source needs to be checked, since the timestamp is always updated on every change. The cleanup formatter is an exception to that behaviour,
// since it just should move the contents from the timestamp field to modificationdate or creationdate.
if (preferencesService.getTimestampPreferences().shouldAddModificationDate() && event.getEntriesEventSource() != EntriesEventSource.CLEANUP_TIMESTAMP) {
event.getBibEntry().setField(StandardField.MODIFICATIONDATE,
preferencesService.getTimestampPreferences().now());
}
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/jabref/gui/cleanup/CleanupAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ private void doCleanup(BibDatabaseContext databaseContext, CleanupPreset preset,
// Create and run cleaner
CleanupWorker cleaner = new CleanupWorker(
databaseContext,
preferences.getCleanupPreferences(Globals.journalAbbreviationRepository));
preferences.getCleanupPreferences(Globals.journalAbbreviationRepository),
preferences.getTimestampPreferences());

List<FieldChange> changes = cleaner.cleanup(preset, entry);

Expand Down
9 changes: 9 additions & 0 deletions src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import org.jabref.gui.commonfxcontrols.FieldFormatterCleanupsPanel?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.control.RadioButton?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" spacing="10.0" type="VBox" xmlns="http://javafx.com/javafx/8.0.121"
fx:controller="org.jabref.gui.cleanup.CleanupPresetPanel">
<fx:define>
<ToggleGroup fx:id="timestampCleanup"/>
</fx:define>

<CheckBox fx:id="cleanUpDOI" text="%Move DOIs from note and URL field to DOI field and remove http prefix"/>
<CheckBox fx:id="cleanUpEprint"
Expand All @@ -28,6 +33,10 @@
text="%Convert to biblatex format (for example, move the value of the 'journal' field to 'journaltitle')"/>
<CheckBox fx:id="cleanUpBibtex"
text="%Convert to BibTeX format (for example, move the value of the 'journaltitle' field to 'journal')"/>
<CheckBox fx:id="cleanUpTimestampToCreationDate"
text="%Convert timestamp field to field 'creationdate'"/>
<CheckBox fx:id="cleanUpTimestampToModificationDate"
text="%Convert timestamp field to field 'modificationdate'"/>

<FieldFormatterCleanupsPanel fx:id="formatterCleanupsPanel"/>
</fx:root>
36 changes: 35 additions & 1 deletion src/main/java/org/jabref/gui/cleanup/CleanupPresetPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class CleanupPresetPanel extends VBox {
@FXML private CheckBox cleanUpUpgradeExternalLinks;
@FXML private CheckBox cleanUpBiblatex;
@FXML private CheckBox cleanUpBibtex;
@FXML private CheckBox cleanUpTimestampToCreationDate;
@FXML private CheckBox cleanUpTimestampToModificationDate;
@FXML private FieldFormatterCleanupsPanel formatterCleanupsPanel;

public CleanupPresetPanel(BibDatabaseContext databaseContext, CleanupPreset cleanupPreset, FilePreferences filePreferences) {
Expand Down Expand Up @@ -69,7 +71,30 @@ private void init(CleanupPreset cleanupPreset, FilePreferences filePreferences)
.concat(": ")
.concat(filePreferences.getFileNamePattern());
cleanupRenamePDFLabel.setText(currentPattern);

cleanUpBibtex.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue) {
cleanUpBiblatex.selectedProperty().setValue(false);
}
});
cleanUpBiblatex.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue) {
cleanUpBibtex.selectedProperty().setValue(false);
}
});
cleanUpTimestampToCreationDate.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue) {
cleanUpTimestampToModificationDate.selectedProperty().setValue(false);
}
});
cleanUpTimestampToModificationDate.selectedProperty().addListener(
(observable, oldValue, newValue) -> {
if (newValue) {
cleanUpTimestampToCreationDate.selectedProperty().setValue(false);
}
});
updateDisplay(cleanupPreset);
}

Expand All @@ -85,6 +110,9 @@ private void updateDisplay(CleanupPreset preset) {
cleanUpUpgradeExternalLinks.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_UPGRADE_EXTERNAL_LINKS));
cleanUpBiblatex.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBLATEX));
cleanUpBibtex.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX));
cleanUpTimestampToCreationDate.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE));
cleanUpTimestampToModificationDate.setSelected(preset.isActive(CleanupPreset.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE));
cleanUpTimestampToModificationDate.setSelected(preset.isActive(CleanupPreset.CleanupStep.DO_NOT_CONVERT_TIMESTAMP));
cleanUpISSN.setSelected(preset.isActive(CleanupPreset.CleanupStep.CLEAN_UP_ISSN));
formatterCleanupsPanel.cleanupsDisableProperty().setValue(!preset.getFormatterCleanups().isEnabled());
formatterCleanupsPanel.cleanupsProperty().setValue(FXCollections.observableArrayList(preset.getFormatterCleanups().getConfiguredActions()));
Expand Down Expand Up @@ -124,6 +152,12 @@ public CleanupPreset getCleanupPreset() {
if (cleanUpBibtex.isSelected()) {
activeJobs.add(CleanupPreset.CleanupStep.CONVERT_TO_BIBTEX);
}
if (cleanUpTimestampToCreationDate.isSelected()) {
activeJobs.add(CleanupPreset.CleanupStep.CONVERT_TIMESTAMP_TO_CREATIONDATE);
}
if (cleanUpTimestampToModificationDate.isSelected()) {
activeJobs.add(CleanupPreset.CleanupStep.CONVERT_TIMESTAMP_TO_MODIFICATIONDATE);
}

activeJobs.add(CleanupPreset.CleanupStep.FIX_FILE_LINKS);

Expand Down
3 changes: 3 additions & 0 deletions src/main/java/org/jabref/logic/cleanup/CleanupPreset.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ public enum CleanupStep {
* Converts to bibtex format
*/
CONVERT_TO_BIBTEX,
CONVERT_TIMESTAMP_TO_CREATIONDATE,
CONVERT_TIMESTAMP_TO_MODIFICATIONDATE,
DO_NOT_CONVERT_TIMESTAMP,
MOVE_PDF,
FIX_FILE_LINKS,
CLEAN_UP_ISSN
Expand Down
9 changes: 8 additions & 1 deletion src/main/java/org/jabref/logic/cleanup/CleanupWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;
import java.util.Objects;

import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.FieldChange;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.entry.BibEntry;
Expand All @@ -13,10 +14,12 @@ public class CleanupWorker {

private final BibDatabaseContext databaseContext;
private final FilePreferences filePreferences;
private final TimestampPreferences timestampPreferences;

public CleanupWorker(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences) {
public CleanupWorker(BibDatabaseContext databaseContext, CleanupPreferences cleanupPreferences, TimestampPreferences timestampPreferences) {
this.databaseContext = databaseContext;
this.filePreferences = cleanupPreferences.getFilePreferences();
this.timestampPreferences = timestampPreferences;
}

public List<FieldChange> cleanup(CleanupPreset preset, BibEntry entry) {
Expand Down Expand Up @@ -65,6 +68,10 @@ private CleanupJob toJob(CleanupPreset.CleanupStep action) {
return new ConvertToBiblatexCleanup();
case CONVERT_TO_BIBTEX:
return new ConvertToBibtexCleanup();
case CONVERT_TIMESTAMP_TO_CREATIONDATE:
return new TimeStampToCreationDate(timestampPreferences);
case CONVERT_TIMESTAMP_TO_MODIFICATIONDATE:
return new TimeStampToModificationDate(timestampPreferences);
case MOVE_PDF:
return new MoveFilesCleanup(databaseContext, filePreferences);
case FIX_FILE_LINKS:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.jabref.logic.cleanup;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.FieldChange;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.Date;
import org.jabref.model.entry.event.EntriesEventSource;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;

/**
* This class handles the migration from timestamp field to creationdate and modificationdate fields.
* <p>
* If the old updateTimestamp setting is enabled, the timestamp field for each entry are migrated to the date-modified field.
* Otherwise it is migrated to the date-added field.
*/
public class TimeStampToCreationDate implements CleanupJob {

private final Field timeStampField;

public TimeStampToCreationDate(TimestampPreferences timestampPreferences) {
timeStampField = timestampPreferences.getTimestampField();
}

/**
* Formats the time stamp into the local date and time format.
* If the existing timestamp could not be parsed, the day/month/year "1" is used.
* For the time portion 00:00:00 is used.
*/
private Optional<String> formatTimeStamp(String timeStamp) {
Optional<Date> parsedDate = Date.parse(timeStamp);
if (parsedDate.isEmpty()) {
// In case the given timestamp could not be parsed
return Optional.empty();
} else {
Date date = parsedDate.get();
int year = date.getYear().orElse(1);
int month = getMonth(date);
int day = date.getDay().orElse(1);
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, 0, 0);
// Remove any time unites smaller than seconds
localDateTime.truncatedTo(ChronoUnit.SECONDS);
return Optional.of(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}

/**
* Returns the month value of the passed date if available.
* Otherwise returns the current month.
*/
private int getMonth(Date date) {
if (date.getMonth().isPresent()) {
return date.getMonth().get().getNumber();
}
return 1;
}

@Override
public List<FieldChange> cleanup(BibEntry entry) {
// Query entries for their timestamp field entries
if (entry.getField(timeStampField).isPresent()) {
Optional<String> formattedTimeStamp = formatTimeStamp(entry.getField(timeStampField).get());
if (formattedTimeStamp.isEmpty()) {
// In case the timestamp could not be parsed, do nothing to not lose data
return Collections.emptyList();
}
// Setting the EventSource is necessary to circumvent the update of the modification date during timestamp migration
entry.clearField(timeStampField, EntriesEventSource.CLEANUP_TIMESTAMP);
List<FieldChange> changeList = new ArrayList<>();
FieldChange changeTo;
// Add removal of timestamp field
changeList.add(new FieldChange(entry, StandardField.TIMESTAMP, formattedTimeStamp.get(), ""));
entry.setField(StandardField.CREATIONDATE, formattedTimeStamp.get(), EntriesEventSource.CLEANUP_TIMESTAMP);
changeTo = new FieldChange(entry, StandardField.CREATIONDATE, entry.getField(StandardField.CREATIONDATE).orElse(""), formattedTimeStamp.get());
changeList.add(changeTo);
return changeList;
}
return Collections.emptyList();
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package org.jabref.migrations;
package org.jabref.logic.cleanup;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.preferences.TimestampPreferences;
import org.jabref.model.FieldChange;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.Date;
import org.jabref.model.entry.event.EntriesEventSource;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;

Expand All @@ -18,46 +22,24 @@
* If the old updateTimestamp setting is enabled, the timestamp field for each entry are migrated to the date-modified field.
* Otherwise it is migrated to the date-added field.
*/
public class TimeStampToDateAddAndModify implements PostOpenMigration {
public class TimeStampToModificationDate implements CleanupJob {

private final boolean interpretTimeStampAsModificationDate;
private final Field timeStampField;
private final TimestampPreferences timestampPreferences;

public TimeStampToDateAddAndModify(TimestampPreferences timestampPreferences) {
this.timestampPreferences = timestampPreferences;
interpretTimeStampAsModificationDate = timestampPreferences.shouldUpdateTimestamp();
public TimeStampToModificationDate(TimestampPreferences timestampPreferences) {
timeStampField = timestampPreferences.getTimestampField();
}

@Override
public void performMigration(ParserResult parserResult) {
parserResult.getDatabase().getEntries().forEach(this::migrateEntry);
}

private void migrateEntry(BibEntry entry) {
// Query entries for their timestamp field entries
entry.getField(timeStampField).ifPresent(timeStamp -> {
String formattedTimeStamp = formatTimeStamp(timeStamp);
if (interpretTimeStampAsModificationDate) {
entry.setField(StandardField.MODIFICATIONDATE, formattedTimeStamp);
} else {
entry.setField(StandardField.CREATIONDATE, formattedTimeStamp);
}
entry.clearField(timeStampField);
});
}

/**
* Formats the time stamp into the local date and time format.
* If the existing timestamp could not be parsed, the day/month/year "1" is used.
* For the time portion 00:00:00 is used.
*/
private String formatTimeStamp(String timeStamp) {
private Optional<String> formatTimeStamp(String timeStamp) {
Optional<Date> parsedDate = Date.parse(timeStamp);
if (parsedDate.isEmpty()) {
// What to do if the date cannot be parsed? Do we need the custom date format possibly?
return timestampPreferences.now();
// In case the given timestamp could not be parsed
return Optional.empty();
} else {
Date date = parsedDate.get();
int year = date.getYear().orElse(1);
Expand All @@ -66,7 +48,7 @@ private String formatTimeStamp(String timeStamp) {
LocalDateTime localDateTime = LocalDateTime.of(year, month, day, 0, 0);
// Remove any time unites smaller than seconds
localDateTime.truncatedTo(ChronoUnit.SECONDS);
return localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
return Optional.of(localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
}
}

Expand All @@ -80,4 +62,27 @@ private int getMonth(Date date) {
}
return 1;
}

@Override
public List<FieldChange> cleanup(BibEntry entry) {
// Query entries for their timestamp field entries
if (entry.getField(timeStampField).isPresent()) {
Optional<String> formattedTimeStamp = formatTimeStamp(entry.getField(timeStampField).get());
if (formattedTimeStamp.isEmpty()) {
// In case the timestamp could not be parsed, do nothing to not lose data
return Collections.emptyList();
}
// Setting the EventSource is necessary to circumvent the update of the modification date during timestamp migration
entry.clearField(timeStampField, EntriesEventSource.CLEANUP_TIMESTAMP);
List<FieldChange> changeList = new ArrayList<>();
FieldChange changeTo;
// Add removal of timestamp field
changeList.add(new FieldChange(entry, StandardField.TIMESTAMP, formattedTimeStamp.get(), ""));
entry.setField(StandardField.MODIFICATIONDATE, formattedTimeStamp.get(), EntriesEventSource.CLEANUP_TIMESTAMP);
changeTo = new FieldChange(entry, StandardField.MODIFICATIONDATE, entry.getField(StandardField.MODIFICATIONDATE).orElse(""), formattedTimeStamp.get());
changeList.add(changeTo);
return changeList;
}
return Collections.emptyList();
}
}
2 changes: 0 additions & 2 deletions src/main/java/org/jabref/logic/importer/OpenDatabase.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.jabref.migrations.ConvertMarkingToGroups;
import org.jabref.migrations.PostOpenMigration;
import org.jabref.migrations.SpecialFieldsToSeparateFields;
import org.jabref.migrations.TimeStampToDateAddAndModify;
import org.jabref.model.util.FileUpdateMonitor;

import org.slf4j.Logger;
Expand Down Expand Up @@ -73,7 +72,6 @@ private static void performLoadDatabaseMigrations(ParserResult parserResult, Tim
List<PostOpenMigration> postOpenMigrations = Arrays.asList(
new ConvertLegacyExplicitGroups(),
new ConvertMarkingToGroups(),
new TimeStampToDateAddAndModify(timestampPreferences),
new SpecialFieldsToSeparateFields(keywordDelimited)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public enum EntriesEventSource {
LOCAL,
SHARED,
UNDO,
CLEANUP_TIMESTAMP,
SAVE_ACTION
}
4 changes: 4 additions & 0 deletions src/main/resources/l10n/JabRef_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2296,3 +2296,7 @@ Use\ custom\ DOI\ base\ URI\ for\ article\ access=Use custom DOI base URI for ar

Unable\ to\ find\ valid\ certification\ path\ to\ requested\ target(%0),\ download\ anyway?=Unable to find valid certification path to requested target(%0), download anyway?
Download\ operation\ canceled.=Download operation canceled.

Convert\ timestamp\ field\ to\ field\ 'creationdate'=Convert timestamp field to field 'creationdate'
Convert\ timestamp\ field\ to\ field\ 'modificationdate'=Convert timestamp field to field 'modificationdate'

Loading