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

Fix for issue #10418 - Added the ability to focus on the most viewed entries #12077

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

ExrosZ
Copy link

@ExrosZ ExrosZ commented Oct 24, 2024

Describe the changes you have made here:

  • By enabling view tracking in the entry tab, JabRef will track how many times a user looks an an entry every hour.
  • This information is stored on the user's machine in an MVStore.

Tracking

  • This can then be used to create a group that shows the X most popular entries in Y timeframe, so long as each entry has atleast one view. If there are less entries with views than entries requested, it will just show the entries with 1 or more views.

ExampleAddgroup
Working

Closes #10418

Mandatory checks

  • I own the copyright of the code submitted and I licence it under the MIT license
  • Change in CHANGELOG.md described in a way that is understandable for the average user (if applicable)
  • Tests created for changes (if applicable)
  • Manually tested changed features in running JabRef (always required)
  • Screenshots added in PR description (for UI changes)
  • Checked developer's documentation: Is the information available and up to date? If not, I outlined it in this pull request.
  • Checked documentation: Is the information available and up to date? If not, I created an issue at https://github.com/JabRef/user-documentation/issues or, even better, I submitted a pull request to the documentation repository.

Issues with my implementation:

  • Fails checkstyle due to using some static variables in EntryTabViewModel
  • Fails respectLayeredArchitecture test because popularityGroup is being used to get an MVStore, this is the only way I could find to get it to work without causing locking issues.
  • Likely needs some reworking to fix the above issues, but I'm genuinely lost.

try {
Files.createDirectories(userDataDir);
} catch (IOException e) {
// Update this for more elegant error handling
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI generated. Pleasemwork on this

int currentCount = viewCounts.getOrDefault(uniqueKey, 0);
viewCounts.put(uniqueKey, currentCount + 1);
lastViewTimestamps.put(uniqueKey, currentTime);
mvStore.commit(); // Save changes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI comment. Delete.

String uniqueKey = PopularityGroup.getUniqueKeyForEntry(entry);
long lastViewTime = lastViewTimestamps.getOrDefault(uniqueKey, 0L);

if (currentTime - lastViewTime >= 3600000 && EntryTabViewModel.trackViewsProperty().get()) { // 3600000 milliseconds in one hour
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Introduce constant

Copy link
Member

@HoussemNasri HoussemNasri Nov 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use java.time.Duration.

@@ -7,6 +7,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this useful for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that, I was having issues with file locks earlier in development and forgot to remove the import and some variables I never used.

@ExrosZ ExrosZ requested a review from koppor October 25, 2024 13:31
Copy link
Member

@koppor koppor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The architecture of the functionality should be changed: MVStore life cycle should be handled by the tab (and passed down to the group). See AIIndexer or SearchIndex how it could work. -- You also persist data across sessions: Thus, you will have a simlar functionality.

I do not know why ""%Group containing entries cited in a given TeX file" sneaked in. I did not find functionality about that.

Comment on lines +1209 to +1211
/**
* Takes an entry and increments the number of times it has been viewed by 1.
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does more - there is a kind of if

Comment on lines +1215 to +1216
MVMap<String, Integer> viewCounts = mvStore.openMap("entryViewCounts");
MVMap<String, Long> lastViewTimestamps = mvStore.openMap("lastViewTimestamps");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nono - the map is a class variable - no need to open the map each time for writing.

You need to shut down the mvstore when the tab is closed.

The the lifecycle of the AiService or search for examples.

String uniqueKey = PopularityGroup.getUniqueKeyForEntry(entry);
long lastViewTime = lastViewTimestamps.getOrDefault(uniqueKey, 0L);

if (currentTime - lastViewTime >= ONE_HOUR_IN_MILLISECONDS && EntryTabViewModel.trackViewsProperty().get()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for the property is too late - everything doesn't need to be done if the property is false.

int currentCount = viewCounts.getOrDefault(uniqueKey, 0);
viewCounts.put(uniqueKey, currentCount + 1);
lastViewTimestamps.put(uniqueKey, currentTime);
mvStore.commit();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, the commit happens automatically.

*/
private void incrementViewCount(BibEntry entry) {
MVStore mvStore = PopularityGroup.getMVStore();
synchronized (mvStore) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the maps are opened after load, there is no need for synchronized (see below)

* @return The number of views for the BibEntry, or 0 if not found.
*/
public static int getEntryViewCount(BibEntry entry) {
MVMap<String, Integer> viewCounts = mvStore.openMap("entryViewCounts");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to make that a class variable. It is passed from the library tab on creation.

Comment on lines +91 to +97
public static String getUniqueKeyForEntry(BibEntry entry) {
return entry.getField(StandardField.DOI).orElse(
entry.getField(StandardField.KEY).orElse(
String.valueOf(entry.hashCode())
)
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove --> use org.jabref.model.entry.BibEntry#getId instead.

Comment on lines +99 to +105
public static synchronized MVStore getMVStore() {
if (mvStore == null) {
Path mvStorePath = getOtherDataDir().resolve("tracking.mv");
mvStore = new MVStore.Builder().fileName(mvStorePath.toString()).open();
}
return mvStore;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MVStore should be passed by caller.

Comment on lines +128 to +139
private long getTimeThreshold(long currentTime, String selectedTimePeriod) {
return switch (selectedTimePeriod) {
case "Last week" ->
currentTime - 7 * 24 * 60 * 60 * 1000L;
case "Last month" ->
currentTime - 30 * 24 * 60 * 60 * 1000L;
case "Last year" ->
currentTime - 365 * 24 * 60 * 60 * 1000L;
default -> // "All time"
0;
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The factor should be stored upon group creation (I think on change of group properties, the group is re-created)

Always reading the property seems to slow down things.




<CheckBox fx:id="enableViewTracking" text="%Enable user behaviour analysis"/>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enable user behaviour analysis --> wrong word - please try to use the same words "view tracking" below "popularity" is OK, but "bheavior analysis" is even more general. It should be consistent with src/main/java/org/jabref/gui/groups/GroupDialog.fxml.

@ExrosZ
Copy link
Author

ExrosZ commented Oct 26, 2024

Thanks for your review @koppor, I have exams coming up so i'll need to take a break from working on this for the next two-ish weeks, but im committed to getting this done :)

Sorry for all the mess, it's my first time contributing to a project like this.

@koppor koppor marked this pull request as draft October 26, 2024 14:35
@koppor koppor added the status: changes required Pull requests that are not yet complete label Oct 26, 2024
}

public List<BibEntry> processPopularityGroup(List<BibEntry> allEntries, String timePeriodCombo, Integer maxEntriesCombo) {
List<BibEntry> filteredEntries = new ArrayList<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this popularEntries for consistency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: changes required Pull requests that are not yet complete
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Automatically focus on frequently used entries
4 participants