package info.opencards.learnstrats.ltm;
import info.opencards.Utils;
import info.opencards.core.*;
import info.opencards.ui.AbstractLearnDialog;
import java.util.*;
import java.util.logging.Level;
/**
* A long-term learning (LTM) processing and scheduling controller.
*
* @author Holger Brandl
*/
public class LTMProcessManager extends LearnProcessManager {
int numScheduled;
private int numNewScheduled;
int numProcessed;
private int numNewProcessed;
/**
* If this flag is set all new items will be included into the schedule.
*/
private boolean includeAllNew;
private final Map<CardFile, List<Item>> skippedItems = new HashMap<CardFile, List<Item>>();
private final Map<CardFile, List<Item>> newButNotSchedItems = new HashMap<CardFile, List<Item>>();
public LTMProcessManager(ItemValuater itemValuater, LearnMethodFactory factory) {
super(itemValuater, factory);
}
public void setupSchedule(Collection<CardFile> curFiles) {
numScheduled = 0;
numNewScheduled = 0;
// note: we do NOT clear the skipped items here to make sure that session skipped items do not reapperar in
// in reused procManagers
scheduler.clear();
// Utils.log("preparing session");
int maxNumNew = includeAllNew ? Integer.MAX_VALUE : ScheduleUtils.getMaxCardToBeLearntToday();
int allNew = 0;
Map<CardFile, Integer> newCounts = new HashMap<CardFile, Integer>();
for (CardFile curFile : curFiles) {
LTMCollection ltmCollection = curFile.getFlashCards().getLTMItems();
ArrayList<Item> newItems = ScheduleUtils.getNewItems(ltmCollection);
newCounts.put(curFile, newItems.size());
allNew += newItems.size();
}
// now compute the new amount for each class based on the number maximal number of newItems
double newRatio = maxNumNew / (double) allNew;
if (newRatio < 1) {
for (CardFile file : curFiles) {
int adaptNumNew = (int) Math.round(newRatio * newCounts.get(file));
newCounts.put(file, adaptNumNew);
}
}
// now setup a schedule for today which includes all scheduled whereby the number of new cards may be restricted
// The possible amount of new cards is thereby distributed equally between all files (relative to their size)
final Map<CardFile, ArrayList<Item>> unsortScheduler = new HashMap<CardFile, ArrayList<Item>>();
for (CardFile curFile : curFiles) {
LTMCollection ltmCollection = curFile.getFlashCards().getLTMItems();
ArrayList<Item> unfilteredItems = ltmCollection.getScheduledItems();
int numNew = maxNumNew - newCounts.get(curFile) >= 0 ? newCounts.get(curFile) : maxNumNew;
ArrayList<Item> scheduledFileItems = ScheduleUtils.getNewReducedItCo(unfilteredItems, numNew);
// extract the not scheduled ones
newButNotSchedItems.put(curFile, getNewButNotScheduledItems(unfilteredItems, scheduledFileItems));
unsortScheduler.put(curFile, scheduledFileItems);
ArrayList<Item> scheduledNewItems = ScheduleUtils.getNewItems(scheduledFileItems);
maxNumNew -= scheduledNewItems.size();
numScheduled += scheduledFileItems.size();
numNewScheduled += scheduledNewItems.size();
}
// finally reorder files based on urgentness
List<CardFile> presortFiles = new ArrayList<CardFile>(unsortScheduler.keySet());
Collections.sort(presortFiles, new Comparator<CardFile>() {
public int compare(CardFile o1, CardFile o2) {
return (int) (ScheduleUtils.getUrgency(unsortScheduler.get(o1)) - ScheduleUtils.getUrgency(unsortScheduler.get(o2)));
}
});
for (CardFile presortFile : presortFiles) {
scheduler.put(presortFile, unsortScheduler.get(presortFile));
}
procIt = scheduler.keySet().iterator();
numProcessed = 0;
numNewProcessed = 0;
}
private List<Item> getNewButNotScheduledItems(ArrayList<Item> allItems, ArrayList<Item> scheduledFileItems) {
ArrayList<Item> notScheduledNewItems = new ArrayList<Item>(allItems);
notScheduledNewItems.removeAll(scheduledFileItems);
for (int i = 0; i < notScheduledNewItems.size(); i++) {
Item item = notScheduledNewItems.get(i);
if (!((LTMItem) item).isNew())
notScheduledNewItems.remove(item);
}
return notScheduledNewItems;
}
public Map<CardFile, List<Item>> getNewButNotScheduledItems() {
return newButNotSchedItems;
}
protected ItemCollection getItemCollection(CardFile cardFile) {
return cardFile.getFlashCards().getLTMItems();
}
public void processStatusInfo(String statusMsg, double completeness) {
if (itemValuater instanceof AbstractLearnDialog) {
// int completeness = (int) (100 * numProcessed / (double) numScheduled);
// ((AbstractLearnDialog) itemValuater).updateStatus(completeness, (numScheduled - numProcessed) + " " + Utils.getRB().getString("AbstractLearnDialog.statusbar.text"));
String msg = (numScheduled - numProcessed) + " " + Utils.getRB().getString("AbstractLearnDialog.statusbar.text");
((AbstractLearnDialog) itemValuater).updateStatus((int) (100 * numProcessed / (double) (numScheduled)), msg);
}
}
public void itemChanged(Item item, boolean stillOnSchedule, Integer feedback) {
Utils.log(Level.FINE, "itemChanged onschedule=" + stillOnSchedule);
LTMItem ltmItem = (LTMItem) item;
if (feedback.equals(LearnMethod.SKIP_UNTIL_NEXT) || feedback.equals(LearnMethod.SKIP_UNTIL_TOMORROW)) {
if (!skippedItems.containsKey(curFile))
skippedItems.put(curFile, new ArrayList<Item>());
skippedItems.get(curFile).add(item);
}
if (ltmItem.isScheduledForToday() && stillOnSchedule)
return;
numProcessed++;
if (ltmItem.getNumRepetition() == 1 && !ltmItem.isSkippedForToday()) { // was new item ?
numNewProcessed++;
ScheduleUtils.setNumLearntToday(ScheduleUtils.getNumLearntToday() + 1);
}
processStatusInfo(null, -1);
}
public boolean isScheduled(CardFile cardFile) {
return scheduler.containsKey(cardFile);
}
public void setIncludeAllNew(boolean includeAllNew) {
this.includeAllNew = includeAllNew;
}
public Map<CardFile, List<Item>> getSkippedItems() {
return skippedItems;
}
public void unschdeduleItem(CardFile cardFile, Item item) {
List<Item> scheduledItems = getScheduledItems(cardFile);
if (scheduledItems == null || scheduledItems.isEmpty())
return;
if (scheduledItems.contains(item)) {
scheduledItems.remove(item);
numScheduled--;
if (((LTMItem) item).isNew())
numNewScheduled--;
}
}
}