/** * Adds songs which did not get any rating data assigned for a long time (or never) to the * {proposedSongs} list. This ensures reaching regions in the music space which are not explored * yet (or have been forgotten about).<br> * The agents vote for these songs is set to {@value #LONG_NOT_RATED_SONG_VOTE}. * * @param proposedSongs The proposal list * @param agentVotes The votes list */ private void addLongTimeNotListenedSongProposals() { // Fetch long not rated songs List<BaseSong<BaseArtist, BaseAlbum>> longNotRatedSongs; try { longNotRatedSongs = statisticsProvider.getLongNotRatedSongs( LONG_NOT_RATED_SONG_COUNT, LONG_NOT_RATED_THRESHOLD); } catch (DataUnavailableException e) { // Just ignore the warning and do not add any songs Log.w(getTag(), e); longNotRatedSongs = new LinkedList<BaseSong<BaseArtist, BaseAlbum>>(); } // Prepare agent votes for songs Map<IAgent, Float> agentVotesForProposedSongs = new HashMap<IAgent, Float>(); for (IAgent agent : agentManager.getAgents()) { agentVotesForProposedSongs.put(agent, (float) LONG_NOT_RATED_SONG_VOTE); } // Add long not played songs as proposals for (BaseSong<BaseArtist, BaseAlbum> song : longNotRatedSongs) { proposedSongs.add(song); songVotes.put(song, LONG_NOT_RATED_SONG_VOTE); agentVotesBySong.put(song, agentVotesForProposedSongs); } }
@Override public void run() { try { throwIfAborted(); TimingLogger timingLogger = new TimingLogger(getTag(), "next"); // Begin an immediate transaction dbDataPortal.beginTransaction(); timingLogger.addSplit("Transaction start"); Set<IAgent> agents = agentManager.getAgents(); agentWeights = agentManager.getAgentWeights(); // Enter a fake rating entry to calculate the next song on the predicted future Integer meSongId = null; if (currentSong != null) { double fractionPlayed = (getCalculationCase() == Case.Positive) ? 0.66d : 0.33d; playLog.writeToPlayLog( new Date(), new PlaylistSong<BaseArtist, BaseAlbum>(currentSong, SongSource.SMART_SHUFFLE), true, (int) (currentSong.getDuration() * fractionPlayed)); try { meSongId = otherDataProvider.getMusicExplorerSongId(currentSong); } catch (DataUnavailableException e) { Log.w(getTag(), e); } } timingLogger.addSplit("Rating entry"); // Get the song proposals AgentsTiming proposalTimes = new AgentsTiming(); { proposedSongs = getProposedSongs(proposalTimes); } timingLogger.addSplit("Proposals"); // Get the agent votes for the proposals Map<IAgent, List<SongVote>> agentVotes; AgentsTiming voteTimes = new AgentsTiming(); { agentVotes = new HashMap<IAgent, List<SongVote>>(); songVotes = getVotesForProposed(proposedSongs, agentVotes, voteTimes); } timingLogger.addSplit("Votes"); // Fill the song->agentVotes map agentVotesBySong = new HashMap<BaseSong<BaseArtist, BaseAlbum>, Map<IAgent, Float>>(proposedSongs.size()); for (BaseSong<BaseArtist, BaseAlbum> song : proposedSongs) { agentVotesBySong.put(song, new HashMap<IAgent, Float>(agents.size())); } for (Map.Entry<IAgent, List<SongVote>> entry : agentVotes.entrySet()) { for (SongVote vote : entry.getValue()) { Map<IAgent, Float> agentVote = agentVotesBySong.get(vote.getSong()); agentVote.put(entry.getKey(), vote.getVote()); } } // Add long time not listened songs addLongTimeNotListenedSongProposals(); /*// Order the next songs by rating Collections.sort(proposedSongs, new Comparator<BaseSong<BaseArtist, BaseAlbum>>() { @Override public int compare(BaseSong<BaseArtist, BaseAlbum> left, BaseSong<BaseArtist, BaseAlbum> right) { Double leftVote = songVotes.get(left); Double rightVote = songVotes.get(right); return rightVote.compareTo(leftVote); } });*/ // Randomize the song proposals by their vote proposedSongs = getShuffledSongsAtWeightedRandom(songVotes); // FIXME @sämy: this is only for debugging StringBuffer proposalsSb = new StringBuffer(); proposalsSb.append("song ident:overall vote"); for (IAgent agent : agents) { proposalsSb.append(':'); proposalsSb.append(agent.getIdentifier()); proposalsSb.append(String.format(" (%.2f)", agentWeights.get(agent))); } proposalsSb.append('|'); for (BaseSong<BaseArtist, BaseAlbum> song : proposedSongs) { proposalsSb.append(song); // song ident proposalsSb.append(':'); proposalsSb.append(String.format("%.3f", songVotes.get(song))); // overall vote Map<IAgent, Float> agentVotes2 = agentVotesBySong.get(song); for (IAgent agent : agents) { proposalsSb.append(String.format(":%.3f", agentVotes2.get(agent))); } proposalsSb.append('|'); } Log.d(getTag(), "Proposals: " + proposalsSb.toString()); // end debug only // never ever call dbDataPortal.setTransactionSucessful() !! dbDataPortal .endTransaction(); // ROLLBACK the transaction. Things below that line get written // persistently to the db! // Write a log entry about this run NextSongCalculationLogEntry.Builder log = NextSongCalculationLogEntry.createInstance() .setPredictionCase(getCalculationCase()) .setCurrentSong((meSongId != null) ? meSongId : 0) .setProposalTimes(proposalTimes) .setVoteTimes(voteTimes); logManager.addLogEntry(log.build()); // Write the time used in the different parts to the debug log timingLogger.dumpToLog(); } catch (AbortException e) { // Just ignore it, we want to land here } finally { if (dbDataPortal.inTransaction()) { dbDataPortal.endTransaction(); // never ever call dbDataPortal.setTransactionSucessful() !! } } }