javaclojureclojure-java-interopclj-time

Passing a parameter into Duckling Clojure function from within Java application


I'm working with wit.ai's Duckling (https://duckling.wit.ai/), however I am depending on and calling Duckling from within my Java application. I have no Clojure experience...

I am able to run Duckling's parse method, however I can't figure out how to pass in the date/time to be used as context for the time and date resolution.

Here is the function:

(defn parse
  "Public API. Parses text using given module. If dims are provided as a list of
  keywords referencing token dimensions, only these dimensions are extracted.
  Context is a map with a :reference-time key. If not provided, the system
  current date and time is used."
  ([module text]
   (parse module text []))
  ([module text dims]
   (parse module text dims (default-context :now)))
  ([module text dims context]
   (->> (analyze text context module (map (fn [dim] {:dim dim :label dim}) dims) nil)
        :winners
        (map #(assoc % :value (engine/export-value % {})))
        (map #(select-keys % [:dim :body :value :start :end :latent])))))

In the testing corpus, it has the context date at the top of the file. This gets passed into the parse function while testing the corpus.

{:reference-time (time/t -2 2013 2 12 4 30 0)
   :min (time/t -2 1900)
   :max (time/t -2 2100)}

Here is my Java code:

public void extract(String input) {
    IFn require = Clojure.var("clojure.core", "require");
    require.invoke(Clojure.read("duckling.core"));
    Clojure.var("duckling.core", "load!").invoke();
    LazySeq o = (LazySeq) Clojure.var("duckling.core", "parse").invoke("en$core", input, dims);
}

My question is, how do I insert a specific date/time in as a parameter to the parse function?

EDIT 1 Looking at it some more, it looks like this is a datetime object. Duckling depends on clj-time 0.8.0, however I can't figure out how to create that same object in Java by calling out to clj-time.


Solution

  • Duckling has its own datetime helper function ('t') in the duckling.time.obj namespace, which drives HOW I get the same datetime object that it's expecting.

    private final Keyword REFERENCE_TIME = Keyword.intern("reference-time");
    private final Keyword MIN = Keyword.intern("min");
    private final Keyword MAX = Keyword.intern("max");
    
    public void extract(String input) {
    
        PersistentArrayMap datemap = (PersistentArrayMap) Clojure.var("duckling.time.obj", "t").invoke(-5, 2017, 2, 21, 23, 30, 0);
        PersistentArrayMap minMap = (PersistentArrayMap) Clojure.var("duckling.time.obj", "t").invoke(-5, 1900);
        PersistentArrayMap maxMap = (PersistentArrayMap) Clojure.var("duckling.time.obj", "t").invoke(-5, 2100);
        Object[] contextArr = new Object[6];
        contextArr[0] = REFERENCE_TIME;
        contextArr[1] = datemap;
        contextArr[2] = MIN;
        contextArr[3] = minMap;
        contextArr[4] = MAX;
        contextArr[5] = maxMap;
        PersistentArrayMap cljContextMap = PersistentArrayMap.createAsIfByAssoc(contextArr);
    
        LazySeq results = (LazySeq) Clojure.var("duckling.core", "parse").invoke("en$core", input, dims, cljContextMap);
    }
    

    Only thing left to do is create the datemap with dynamic values instead of hardcoded.