!7

concept DetectorEntry {
  S task; // e.g. "English vs German n=1000"
  S expression; // posNgram|posNgram|...^negNgram|negNgram|...
  S result; // double (0..1)
}

cmodule LanguageDetectionNGramScorers > DynCRUD<DetectorEntry> {
  start {
    indexConceptFieldsCI(DetectorEntry, 'task, DetectorEntry, 'expression);
    crud.sorter = func(Cl<DetectorEntry> l) -> L<DetectorEntry> {
      sortByCalculatedFieldDesc(l, e -> pair(parseDoubleOpt(e.result), -l(e.expression)))
    };
    crud.renderer = func(DetectorEntry e) -> Map {
      litorderedmap(
        "Expression" := e.expression,
        "Result / Length" := e.result + " / " + l(e.expression),
        "Task" := e.task)
    };
  }
  
  // API
  
  void saveResult(S task, S expression, S result) {
    cset(uniqCI DetectorEntry(+expression, +task), +result);
  }
  
  // pairs of expression + result
  LPairS expressionsForTask(S task) {
    ret map(conceptsWhereCI DetectorEntry(+task),
      e -> pair(e.expression, e.result));
  }
  
  LS expressionsForTaskByScore(S task) {
    ret pairsA(sortedByCalculatedFieldDesc(expressionsForTask(task),
      p -> parseDoubleOpt(p.b)));
  }
}