Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

909
LINES

< > BotCompany Repo | #1022531 // DynGazelleWebServer

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (32571L/193K).

1  
// Note: use module, not cmodule - we want our own structure() because of DynamicObject
2  
3  
set flag DynModule.
4  
5  
abstract sclass DynGazelleWebServer > DynPrintLogAndEnabled {
6  
  new Set<S> authedCookies;
7  
  bool allowVisitors = true;
8  
  int httpPort = 80;
9  
  transient int absoluteMaxResults = 1000, defaultMaxResults = 100;
10  
  
11  
  transient MyHTTPD server;
12  
  transient new GazelleContextCache_v2 contextCache;
13  
14  
  transient LS searchTypes = ll("", "Rule", "Line");
15  
  
16  
  S switchableFields() { ret "allowVisitors httpPort"; }
17  
  
18  
  transient new HTTPSpamBlocker1 spamBlocker;
19  
  transient double spammerSleep = 60.0;
20  
21  
  void start() ctex {
22  
    super.start();
23  
    if (!enabled) ret;
24  
    
25  
    vm_cleanPrints();
26  
    dm_useLocalMechListCopies();
27  
    
28  
    ownResource(contextCache.listenToMessages());
29  
    contextCache.alwaysFullRefresh();
30  
    
31  
    server = new MyHTTPD(httpPort);
32  
    server.serveFunction = func(S uri, SS parms) {
33  
      serve(uri, parms)
34  
    };
35  
    server.start();
36  
    print("HTTP server started on port " + server.getPort());
37  
  }
38  
  
39  
  void cleanMeUp {
40  
    server.stop();
41  
    server = null;
42  
  }
43  
  
44  
  O serve(S uri, SS params) enter {
45  
    print exceptions {
46  
      ret newRequest().serve(uri, params);
47  
    }
48  
  }
49  
  
50  
  Request newRequest() {
51  
    ret new Request;
52  
  }
53  
  
54  
  S lineURL(long msgID) { ret msgID == 0 ? null : "/line/" + msgID; }
55  
  S lineURL(S context) { ret empty(context) ? null : "/line/" + urlencode(context); }
56  
  S ruleURL(S ruleID) { ret "/rule/" + urlencode(ruleID); }
57  
58  
  long contextToMsgID(S s) {
59  
    new Matches m;
60  
    if "discord msg *" if (isInteger($1)) ret parseLong($1);
61  
    ret 0;
62  
  }
63  
  
64  
  class Request {
65  
    bool authed;
66  
    S uri;
67  
    SS params;
68  
    O[] bodyParams;
69  
    Pair<Int, Bool> spamCheck;
70  
71  
    O spamTest() {    
72  
      spamCheck = spamBlocker.checkRequest(uri, clientIP());
73  
      if (spamCheck.b) {
74  
        sleepSeconds(spammerSleep);
75  
        ret print("go away");
76  
      }
77  
      
78  
      null;
79  
    }
80  
81  
    O serve(S uri, SS params) {
82  
      this.uri = uri;
83  
      this.params = params;
84  
      new Matches m;
85  
      
86  
      try object spamTest();
87  
      
88  
      S domain = serveHttp_domainName();
89  
      for (S a, S b : parseDoubleArrowLinkedHashMap(loadTextFile(javaxDataDir("gazelle-redirects.txt")))) {
90  
        if (swic(domain, a))
91  
          ret hrefresh(doubleToSingleSlashesInURL(b + uri));
92  
      }
93  
      
94  
      S cookie = serveHttp_cookieHandling();
95  
      authed = nempty(cookie) && syncContains(authedCookies, cookie);
96  
      print("Cookie: " + cookie + ", authed: " + authed + ", spam count: " + pairA(spamCheck));
97  
      S master = trim(loadTextFile(javaxSecretDir("gazelle-master-pw")));
98  
      S attempt = params.get('_pass);
99  
      if (nempty(attempt) && nempty(cookie) && nempty(master)) {
100  
        if (eq(attempt, master)) {
101  
          print("Login SUCCEEDED");
102  
          syncAdd(authedCookies, cookie);
103  
          change();
104  
          authed = true;
105  
        } else
106  
          print("Login FAILED");
107  
      }
108  
      
109  
      bodyParams = litobjectarray(style := "font-family: Roboto Mono; " + (authed ? "background-image: url(\"" + imageServerLink(#1101575) + "\")" : "background-color: #FFFF88"));
110  
      
111  
      if (eq(uri, "/logout")) {
112  
        authedCookies.remove(cookie);
113  
        ret "OK, logged out";
114  
      }
115  
      
116  
      if (eq(uri, "/login"))
117  
        ret hframe("Login",
118  
          hfullcenter(h3(htmlEncode2(gazelle_name()) + " Login")
119  
          + (nempty(master) ? hpostform(hpasswordfield('_pass), action := or(params.get('uri), "/")) : "No master PW")));
120  
      
121  
      if (eq(uri, "/favicon.ico"))
122  
        ret serveFile(loadLibrary(gazelle_faviconID()), faviconMimeType());
123  
        
124  
      if (!allowVisitors && !authed)
125  
        ret hrefresh("/login" + hquery(+uri));
126  
        
127  
      ret serve2();
128  
    }
129  
    
130  
    O serve2() {
131  
      new Matches m;
132  
      
133  
      if (eq(uri, "/tests"))
134  
        ret serveTests();
135  
        
136  
      if (swic(uri, "/texts/", m)) {
137  
        S textID = m.rest();
138  
        if (!possibleMD5(textID)) ret serve404();
139  
        ret serveTextFileAsUTF8(javaxDataDir("Gazelle Texts/" + textID));
140  
      }
141  
      
142  
      if (eq(uri, "/testInput"))
143  
        ret serveTestInput();
144  
      
145  
      if (eq(uri, "/lastOutput")) {
146  
        L<GazelleLine> lines = reversed(takeLast(10, dm_discord_allBotLines()));
147  
        ret hframe("Last Output", hcenter(hheading("Last Output")
148  
          + htmlTable2(
149  
            map(lines, line -> {
150  
              L applications = dm_gazelle_applicationsForMsgID(line.msgID);
151  
              //print("Got " + n2(applications, "application") + " for " + line.msgID + " from " + dm_gazelle_longFeedbackCRUD());
152  
              ret litorderedmap(
153  
                "Date" := spanTitle("Message ID: " + line.msgID, htmlencode2(localDateWithMinutes(line.timestamp))),
154  
                "Gazelle said:" := ahref(lineURL(line.msgID), htmlencode2(line.text)),
155  
                "Applied rules" := joinWithSpace(map(f<S, S> ruleLink, collect ruleID(applications)))
156  
              );
157  
            }), htmlEncode := false, tableParams := litparams(cellpadding := 5))
158  
        ));
159  
      }
160  
        
161  
      if (eq(uri, "/json/rawInputLines"))
162  
        ret serveText(jsonEncode(collect text(dm_discord_allLines())));
163  
        
164  
      if (eq(uri, "/channels"))
165  
        ret serveJSON(collectMulti_notNulls(ll('channelID, 'name), dm_discord_channels()));
166  
167  
      if (eq(uri, "/allDistinctInputs"))
168  
        ret serveJSON(uniquify(dm_discord_allTexts()));
169  
        
170  
      if (eq(uri, "/allLines")) {
171  
        LS fields = tok_splitAtComma_emptyOnEmpty(params.get('fields));
172  
        if (empty(fields)) ret serve500("Need fields param");
173  
        // TODO: restrict fields? seems OK though, it's just fields of GazelleLine
174  
        ret serveJSON(collectMulti_notNulls(fields, selectLines(params)));
175  
      }
176  
        
177  
      if (eq(uri, "/lastInput")) {
178  
        L<GazelleLine> lines = reversed(takeLast(30, dm_discord_allLines()));
179  
        ret hframe("Last Input", hcenter(hheading("Last Input")
180  
          + htmlTable2(
181  
            map(lines, line -> {
182  
              //L applications = dm_gazelle_applicationsForMsgID(line.msgID);
183  
              ret litorderedmap(
184  
                "Date" := spanTitle("Message ID: " + line.msgID, htmlencode2(localDateWithMinutes(line.timestamp))),
185  
                "Author" := htmlencode2(line.user),
186  
                "Bot?" := htmlencode2(line.bot ? "yes" : ""),
187  
                "Line" := ahref(lineURL(line.msgID), htmlencode2(escapeNewLines(line.text))),
188  
                //"Applied rules" := joinWithSpace(map(f<S, S> ruleLink, collect ruleID(applications))),
189  
                "More" := moreForMsgID(line.msgID)
190  
              );
191  
            }), htmlEncode := false, tableParams := litparams(cellpadding := 5))
192  
        ));
193  
      }
194  
      
195  
      if (eq(uri, "/commentPatterns"))
196  
        ret hframeAndHeading("Comment patterns that mean something",
197  
          htmlTable2(listToMapsWithSingleKey("Pattern", gazelle_commentPatterns())));
198  
    
199  
      if (eq(uri, "/ruleComments"))
200  
        ret serveRuleComments();
201  
      
202  
      if (swic(uri, "/revisit/", m)) {
203  
        long msgID = assertNotZero(parseLong(m.rest()));
204  
        GazelleLine line = dm_discord_lineForMsgID(msgID);
205  
        if (line == null) ret "Message not found: " + msgID;
206  
        long startTime = sysNow();
207  
        L<virtual GazelleTree> tree = evalWithTimeoutOrNull(30.0, func -> L<virtual GazelleTree> {
208  
          /*dm_gazelle_revisitChatLine(msgID, contextMaker := contextCache, skipBad := false)*/
209  
          (L) dm_call('gazelleLineRevisiter, 'revisitChatLine, msgID, skipBad := false)
210  
        });
211  
        long endTime = sysNow();
212  
        
213  
        S tv = lines(map(f<O, S> gazelleTreeToHTML, tree));
214  
        
215  
        ret hframe("Revisiting msg " + msgID,
216  
          hheading("Revisiting message " + msgID + ": " + htmlEncode2(line.text))
217  
          + nlToBr_withIndents(tv)
218  
          + p((endTime-startTime) + " ms"));
219  
      }
220  
      
221  
      if (swic(uri, "/rule/", m)) {
222  
        S ruleID = assertGlobalID(m.rest());
223  
        ret serveRule(ruleID);
224  
      }
225  
      
226  
      if (swic(uri, "/ruleApplication/", m)) {
227  
        S globalID = assertGlobalID(m.rest());
228  
        ret serveRuleApplication(globalID);
229  
      }
230  
      
231  
      if (swic(uri, "/rawReasoning/", m)) {
232  
        S context = urldecode(m.rest());
233  
        File f = gazelle_discord_reasoningFileForContext(context);
234  
        ret serveText(loadGZippedTextFile(f));
235  
      }
236  
      
237  
      if (eq(uri, "/createRule")) ret createRule();
238  
      
239  
      if (swic(uri, "/line/", m)) {
240  
        ret serveLine(urldecode(m.rest()));
241  
      }
242  
      
243  
      if (eq(uri, "/search") || eq(uri, "/") && nemptyAfterTrim(params.get('q)))
244  
        ret search(params);
245  
      
246  
      if (eq(uri, "/wikipedia"))
247  
        ret serveWikipedia();
248  
      
249  
      if (eq(uri, "/commands"))
250  
        ret hframe("Commands",
251  
          linesLL(hheading("Commands"),
252  
            h3("!eval"),
253  
          
254  
            p([[You can evaluate Java code directly through Discord.
255  
            Unless you are specifically authorized, only a ]] + ahref(rawSelfLink("safeIdentifiers"), "safe subset of identifiers") + " is allowed."),
256  
            
257  
            p("Example: " + tt("!eval 1+2")),
258  
            p("In rare cases " + tt("!eval") + " may fail and you need to type " + tt("!real-eval") + " instead (which invokes an actual Java compiler).")));
259  
  
260  
      if (eq(uri, "/safeIdentifiers"))
261  
        ret hframe("Safe Java Identifiers | Gazelle",
262  
          linesLL(
263  
            hheading("Safe Java(X) identifiers for !eval"),
264  
            hpre(lines(sortedIC(codeAnalysis_allSafeIdentifiers())))));
265  
            
266  
      if (!eq(uri, "/"))
267  
        ret serve404();
268  
      
269  
      //final Map<S, Int> feedbackStats = dm_gazelle_feedbackStats();
270  
      final Map<S, Int> feedbackStats2 = unnull(dm_gazelle_feedbackStatsByJudgement());
271  
      final Map<S, Int> feedbackStats3 = unnull(dm_gazelle_feedbackStatsForNonJudged());
272  
      
273  
      // Main Page / Rules List
274  
      
275  
      S filterByPurpose = params.get('purpose);
276  
      L<T3<S>> rules = empty(filterByPurpose)
277  
        ? dm_allRulesFromRulesModuleWithCommentsAndIDs()
278  
        : dm_gazelle_allRulesWithPurpose(filterByPurpose);
279  
        // dm_gazelle_allRulesWithComment("discord");
280  
        
281  
      bool showAll = eq("1", params.get('showAll));
282  
        
283  
      L<Map> mapped = mapReversed(rules,
284  
        func(T3<S> t) -> SS {
285  
          S ruleID = t.c;
286  
          ret litorderedmap(
287  
            "Rule ID" := !showAll ? null : htmlEncode2(ruleID),
288  
            "Rule Text" := ahref(ruleURL(ruleID), htmlEncode_nlToBr(t.a)),
289  
            "Comments" := !showAll ? null : nlToBr(
290  
              mapEachLine(/*withoutLine("discord",*/ t.b/*)*/, func(S s) -> S {
291  
                new Matches m;
292  
                if "use helper table mech list *"
293  
                  ret "use helper table mech list " +
294  
                    ahref(neatMechListURL($1), m.get(0));
295  
                ret htmlEncode2(s);
296  
              })),
297  
            "# of applications" := strOr(feedbackStats3.get(ruleID), "-"),
298  
            "Feedback" := ahref(rawSelfLink("rule/" + ruleID),
299  
              "+" + toInt(feedbackStats2.get(ruleID + "/good"))
300  
                /*+ " / " +
301  
              toInt(feedbackStats3.get(ruleID))*/
302  
                + " / " + 
303  
              "-" + toInt(feedbackStats2.get(ruleID + "/bad"))));
304  
        });
305  
      
306  
      S sortBy = params.get('sortBy);
307  
      bool desc = eq("1", params.get('desc));
308  
      if (nempty(sortBy))
309  
        if (eq(sortBy, "Feedback"))
310  
          mapped = sortByCalculatedField(mapped, func(Map map) -> Int {
311  
            S s = cast map.get("Feedback");
312  
            ret parseFirstInt(s) - parseSecondInt(s);
313  
          });
314  
        else if (eq(sortBy, "# of applications"))
315  
          mapped = sortByMapKeyAlphaNum(mapped, sortBy);
316  
        else
317  
          mapped = sortByMapKey(mapped, sortBy);
318  
      if (desc) reverseInPlace(mapped);
319  
      
320  
      int start = parseInt(params.get("start")), step = 50;
321  
      S nav = pageNav2(hquery(mapMinus(params, 'start)), l(mapped), start, step, 'start,
322  
        /*leftArrow := span(htmlencode2(unicode_leftPointingTriangle()), style := "font-family: Lucida Sans"),
323  
        rightArrow := span(htmlencode2(unicode_rightPointingTriangle()), style := "font-family: Lucida Sans")*/);
324  
      mapped = subList(mapped, start, start+step);
325  
        
326  
      ret hhtml_head_title_body(gazelle_name() + " - Next-Gen Chat Bot",
327  
        hprelude() +
328  
        hopeningTag link(rel :="icon", href := "/favicon.ico?v=2") +
329  
        hcenter(
330  
          p(b(ahref_unstyled("http://gazelle.botcompany.de", htmlEncode2(gazelle_fullName())), style := "font-size: 5em")) +
331  
          p(hsnippetimg(gazelle_imageID(), height := 150, title := gazelle_name()))
332  
          + greeting()
333  
          + navLine()
334  
335  
          + h3("Rules " + (nempty(filterByPurpose) ? " for purpose " + quote(filterByPurpose) : "") + " (" + l(rules) + ")")
336  
          + p("By purpose: " + joinWithVBar(map(listPlus_inFront(gazelle_allRulePurposes_cached(), 'default), purpose ->
337  
            ahref(hquery(mapPlus(params, +purpose)), htmlEncode2(purpose)))))
338  
          + nav
339  
        + htmlTable2(mapped,
340  
            htmlEncode := false,
341  
            paramsByColName := litmap(
342  
              "Feedback" := litobjectarray(align := 'center),
343  
              "# of applications" := litobjectarray(align := 'center)),
344  
            replaceHeaders := litmap(
345  
              "Rule Text" := "Rule (input + more input => output)",
346  
              "# of applications" := sortLink(params, "# of applications"),
347  
              "Feedback" := sortLink(params, "Feedback")),
348  
            tdParams := litobjectarray(valign := 'top)
349  
            )
350  
            + p(ahref(hquery(mapPlus(params, showAll := 1)), "Show technical stuff"))
351  
            ), bodyParams);
352  
    }
353  
  
354  
    S ruleLink(S ruleID) {
355  
      if (!isGlobalID(ruleID)) ret htmlEncode2(ruleID);
356  
      ret ahref("/rule/" + ruleID, ruleID);
357  
    }
358  
    
359  
    S commentToHTML(S s) {
360  
      new Matches m;
361  
      LS tok = javaTok(s);
362  
      S t = nextToLast(tok);
363  
      S html = null;
364  
      if (dm_gazelle_ruleExists(t))
365  
        html = htmlEncode2(joinSubList(tok, 0, l(tok)-2)) + ruleLink(t);
366  
      else if (matchX("... list *", s, m) && isQuoted(m.get(0)))
367  
        html = htmlEncode2(joinSubList(tok, 0, l(tok)-2)) + ahref(neatMechListURL($1), t);
368  
      if (html == null) html = htmlEncode2(s);
369  
      ret html;
370  
    }
371  
372  
    S gazelleTreeToHTML(virtual GazelleTree tree) {
373  
      if (tree == null) ret "*";
374  
      ret (getBool isSplitNode(tree) ? "[split] " : "")
375  
        + htmlEncode2(getString line(tree)) + appendSquareBracketed(
376  
        joinWithComma(listPlus(
377  
          map htmlEncode2((LS) call(tree, "renderQualityElements")),
378  
          ruleLink((S) call(tree, "ruleID")))));
379  
    }
380  
    
381  
    S moreForMsgID(long msgID) {
382  
      ret ahref(rawSelfLink("revisit/" + msgID), "revisit", rel := "nofollow");
383  
    }
384  
    
385  
    S search(SS params) {
386  
      S q = trim(params.get('q));
387  
      Set<S> types = singletonCISetIfNempty(params.get('type));
388  
      int maxResults = min(absoluteMaxResults, toIntOr(params.get('max), defaultMaxResults));
389  
      L results = empty(q) ? null : dm_gazelle_fullSearch(q, +types, maxResults := maxResults+1);
390  
      S title = empty(q) ? "Search" : "Search results for: " + q;
391  
      ret hframe(title,
392  
        hheading(htmlEncode2(title)) +
393  
        hform("Terms: " + hinputfield('q, value := q, autofocus := true)
394  
          + " Type: " + hselect_list(searchTypes, params.get('type), name := 'type)
395  
          + " " + hsubmit("Search"), action := "/search") +
396  
        (empty(results) ?
397  
          (empty(q) ? "" : "No results") : p(
398  
            l(results) > maxResults ? maxResults + "+ results" : n2(results, "result"))
399  
            + htmlTable2(map(takeFirst_lazy(maxResults, results), o -> {
400  
          S type = shortClassName(o);
401  
          bool isLine = eq(type, "Line");
402  
          long msgID = isLine ? getLong msgID(o) : 0;
403  
          Map map = litorderedmap("Type" :=
404  
            isLine ? spanTitle("Message ID: " + msgID, type) : type);
405  
          S text = htmlEncode2OrNull(getStringOpt text(o));
406  
          if (isLine)
407  
   {
408  
            map.put("Date" := localDateWithMinutes(getLong created(o)));
409  
            map.put("By" := (S) call(o, 'userName));
410  
          }
411  
          if (text != null) {
412  
            if (eq(type, "Rule"))
413  
              text = ahref("/rule/" + getString globalID(o), text);
414  
            else if (isLine)
415  
              text = ahref(lineURL(msgID), text);
416  
            mapPut(map, "Text", text);
417  
          }
418  
          if (isLine)
419  
            map.put("More" := moreForMsgID(msgID));
420  
          ret map;
421  
        }), htmlEncode := false)));
422  
    }
423  
424  
    S serveLine(S contextOrMsgID) {
425  
      GazelleLine l = isInteger(contextOrMsgID)
426  
        ? dm_discord_lineForMsgID(parseLong(contextOrMsgID))
427  
        : gazelle_lineForContext(contextOrMsgID);
428  
        
429  
      if (l == null) ret "Line " + contextOrMsgID + " not found";
430  
      L context = dm_discord_contextAroundLine(l.msgID, -3, 3);
431  
      L applications = 
432  
        nempty(l.context)
433  
          ? gazelle_applicationsForContext(l.context)
434  
          : dm_gazelle_applicationsForMsgID(l.msgID);
435  
      saveFeedback(applications);
436  
      L<GazelleFulfillment> fulfillments = dm_gazelle_fulfillmentsForMsg(l.msgID);
437  
      
438  
      File reasoningFile = gazelle_discord_reasoningFileForContext(l.context);
439  
      bool hasRawReasoning = fileExists(reasoningFile);
440  
441  
      ret hframe("Line " + l.context,
442  
        hheading("Line @ " + localDateWithMinutes(l.timestamp) + ": " + htmlEncode2("<" + l.user + "> " + l.text))
443  
        + p(moreForMsgID(l.msgID))
444  
        + (empty(context) ? "" :
445  
          h3("Context")
446  
          + nlToBr(lines(map(context, line2 -> {
447  
            GazelleLine line = dm_discord_importLine(line2);
448  
            S s = localDateWithMinutes(line.timestamp) + ": " + ahref(line.msgID == l.msgID ? null : lineURL(line.msgID),
449  
              htmlEncode2("<" + line.user + "> " + line.text));
450  
            ret line.msgID == l.msgID ? b(s) : s;
451  
          }))))
452  
        + (hasRawReasoning ? p(ahref("/rawReasoning/" + urlencode(l.context), "Raw reasoning.")) : "")
453  
        + (empty(applications) ? "" : h3("Reasoning")
454  
        + htmlTable2(map(applications, app -> {
455  
            S ruleID = getString ruleID(app);
456  
            Map map = new LinkedHashMap;
457  
            if (nempty(ruleID)) {
458  
              S ruleText = dm_textForRule(ruleID);
459  
              map.put("Rule" := ahref("/rule/" + urlencode(ruleID),
460  
                empty(ruleText) ? "?" : htmlEncode2_nlToBr(ruleText)));
461  
            }
462  
            map.putAll(applicationToMap(app, null, null));
463  
            map.remove("Context");
464  
            map.remove("Generated Output");
465  
            ret map;
466  
          }),
467  
          tdParams := litobjectarray(align := 'center, valign := 'top), htmlEncode := false))
468  
        + (empty(fulfillments) ? "" : h3("Fulfillments")
469  
        + htmlTable2(map(fulfillments, f -> {
470  
            new LinkedHashMap map;
471  
            if (neq(f.line, l.text)) map.put("Originally seen line" := f.line);
472  
            mapPut(map, "Rule" := f.rule);
473  
            mapPut(map, "Judgement" := f.judgement);
474  
            mapPut(map, "Mapping" := dropPrefix("cimap", f.varMap));
475  
            ret map;
476  
          }), tdParams := litobjectarray(align := 'center, valign := 'top), htmlEncode := false))
477  
      );
478  
    }
479  
    
480  
    S createRule() {
481  
      S text = trim(params.get('text)), comments = params.get('comments);
482  
      if (nempty(text)) {
483  
        LS _comments = tlft(comments);
484  
        _comments = map unSquareBracket(_comments);
485  
        if (!authed)
486  
          _comments = map(_comments, s -> "[anon] " + s);
487  
        text = gazelle_processSquareBracketAnnotations(text, _comments);
488  
        S ruleID = pairA(dm_gazelle_addRuleWithComment(text, lines_rtrim(_comments)));
489  
        ret hredirect(ruleURL(ruleID));
490  
      }
491  
      
492  
      S copyFrom = params.get('copyFrom);
493  
      PairS p = empty(copyFrom) ? pair("in => out", "")
494  
        : dm_textAndCommentForRule(copyFrom);
495  
      if (nempty(p.b))
496  
        p.b = appendWithNewLine(withoutLinesStartingWithIC("copied from rule ", p.b),
497  
        "copied from rule " + copyFrom);
498  
      
499  
      ret hframeAndHeading("Create Rule",
500  
        hpostform(
501  
          h3("Rule")
502  
          + htextarea(p.a,
503  
            name := 'text, cols := 80, rows := 10) + "<br><br>" +
504  
          h3("Comments")
505  
          + htextarea(p.b, name := 'comments, cols := 80, rows := 10) +
506  
          "<br><br>" +
507  
          hsubmit("Create Rule")
508  
        )
509  
        + p(targetBlank("/commentPatterns", "Comment patterns.")));
510  
    }
511  
    
512  
    O serveRuleApplication(S globalID) {
513  
      O app = gazelle_applicationForID(globalID);
514  
      if (app == null) ret serve404();
515  
      S title = "Rule Application " + globalID;
516  
      S mrStruct = getString matchedRuleStruct(app);
517  
      O mr = safeUnstruct(mrStruct);
518  
      ret hframe(title,
519  
        hheading(title) +
520  
        htmlTable2(map(objectToMap(mr), func(S field, O value) -> Map {
521  
          litorderedmap("Field" := field, "Value" := sfu(value))
522  
        })));
523  
    }
524  
  
525  
    S serveRule(S ruleID) {
526  
      // add rule comment
527  
      
528  
      S comment = trim(params.get("comment"));
529  
      if (nempty(comment)) {
530  
        if (!authed) comment = "[anon] " + comment;
531  
        dm_gazelle_addRuleComments_verbose(ruleID, ll(comment));
532  
      }
533  
      
534  
      // delete rule comment
535  
      
536  
      S del = params.get("deleteComment");
537  
      if (authed && nempty(del))
538  
        gazelle_removeCommentFromRule(ruleID, del);
539  
  
540  
      PairS textAndComment = unnull(dm_textAndCommentForRule(ruleID));
541  
      L feedback = dm_gazelle_feedbackForRule(ruleID);
542  
      L applications = dm_gazelle_applicationsForRule(ruleID);
543  
      Map<S, O> feedbackByContext = indexByFieldNotNull context(feedback);
544  
      L<GazelleFulfillment> fulfillments = dm_gazelle_fulfillmentsForRule_imported(ruleID);
545  
      L list = cloneList(feedback);
546  
      for (O o : applications)
547  
        if (!containsKey(feedbackByContext, getString context(o)))
548  
          list.add(o);
549  
          
550  
      // add feedback comments + judgement
551  
      
552  
      for (S applicationID, S text : subMapStartingWith_dropPrefix(params, "comment_")) {
553  
        continue if empty(text = trim(text));
554  
        if (!authed) text = "[anon] " + text;
555  
        O app = findByField(list, globalID := applicationID);
556  
        print("Processing comment for " + applicationID + " - " + app);
557  
        if (app == null) continue;
558  
        Set<S> comments = asSet(tlft(getString comments(app)));
559  
        if (!contains(comments, text))
560  
          call(app, '_setField, comments := appendWithNewLine(getString comments(app), text));
561  
      }
562  
      
563  
      saveFeedback(list);
564  
      
565  
      // change rule text
566  
      
567  
      S newRuleText = params.get('ruleText);
568  
      if (authed && nempty(newRuleText)) {
569  
        LS _comments = tlft(textAndComment.b);
570  
        newRuleText = gazelle_processSquareBracketAnnotations(newRuleText, _comments);
571  
        gazelle_setRuleText(ruleID, newRuleText);
572  
        gazelle_setRuleComment(ruleID, lines_rtrim(_comments));
573  
        textAndComment = unnull(dm_textAndCommentForRule(ruleID));
574  
      }
575  
      
576  
      if (authed && nempty(params.get('expandMultiRule))) {
577  
        gazelle_splitMultiRule(ruleID);
578  
        textAndComment = unnull(dm_textAndCommentForRule(ruleID));
579  
      }
580  
581  
      // Serve Rule & Applications & Feedback
582  
      
583  
      GazelleEvalContext ctx = dm_gazelle_evalContextForSingleRule(ruleID);
584  
      GazellePredictor predictor = dm_gazelle_standardPredictor();
585  
      
586  
      L<Map> mapped = map(list, app -> applicationToMap(app, ctx, predictor));
587  
      
588  
      S sortBy = params.get('sortBy);
589  
      if (nempty(sortBy))
590  
        mapped = sortByMapKey(mapped, sortBy);
591  
        
592  
      MultiSet<S> statements = dm_gazelle_statementsForRule_multiSet(ruleID);
593  
      
594  
      LS comments = tlft(textAndComment.b);
595  
      
596  
      // warnings
597  
      Collection<S> warnings = null;
598  
      pcall {
599  
        warnings = (Collection<S>) dm_call('gazelleThoughtHelper, 'autoTestRule, ruleID);
600  
      }
601  
  
602  
      // comment suggestions
603  
      Collection<S> suggestedComments = null;
604  
      pcall {
605  
        //suggestedComments = gazelle_suggestRuleComments(TextCommentsID(textAndComment.a, textAndComment.b, ruleID));
606  
        suggestedComments = (Collection<S>) dm_findAndCall("#1022585/GRuleCommentSuggester", 'suggestComments, textAndComment.a, textAndComment.b, ruleID);
607  
        suggestedComments = listMinusSet(suggestedComments, comments);
608  
      }
609  
      
610  
      S title = "Rule " + ruleLink(ruleID) + ": " + htmlEncode2(newLinesToSpaces_trim(textAndComment.a));
611  
      ret hframe(htmldecode_dropAllTags(title),
612  
        form(p(searchBox2(), style := "text-align: right"), action := "/search")
613  
        + hheading(title)
614  
        + hpostform(
615  
          h3("Rule")
616  
        + div(hblock(textAndComment.a), id := 'ruleText)
617  
        + (authed ? "<br>" + hbuttonOnClick_returnFalse("Edit",
618  
          [[
619  
            $(this).text('Save');
620  
            $(this).attr('onclick', '');
621  
            $('#ruleText').html(]] + jsQuote(htextarea(textAndComment.a, name := 'ruleText, rows := 5, cols := 80)) + [[);
622  
            $('#ruleText textarea').focus();]]) : "")
623  
            
624  
        // Render comments
625  
        
626  
        + (nempty(textAndComment.b) ? h3("Comments") + nlToBr(mapEachLine(textAndComment.b, s -> {
627  
          S html = commentToHTML(s);
628  
          if (authed)
629  
            html += " " + ahref(ruleURL(ruleID) + hquery(deleteComment := s), "[x]");
630  
          ret html;
631  
        })) : "")
632  
        + "<br><br>"
633  
        + "Add comment: " + htextfield("comment") + " " + hsubmit())
634  
        
635  
        // Render warnings
636  
        
637  
        + (empty(warnings) ? ""
638  
          : h3("Warnings") +
639  
          nlToBr(lines(map(f<S, S> commentToHTML, warnings))))
640  
        
641  
        // Render suggested comments
642  
        
643  
        + (empty(suggestedComments) ? ""
644  
          : h3("Suggested comments") +
645  
            nlToBr(lines(map(suggestedComments, c ->
646  
              htmlEncode2(c) + " " + ahref(ruleURL(ruleID) + hquery(comment := c), "[accept]")))))
647  
648  
        + p(ahref("/createRule" + hquery(copyFrom := ruleID), "Duplicate rule.")
649  
          + " " + targetBlank("/commentPatterns", "Comment patterns."))
650  
        
651  
        + (authed && cic(comments, "multi-rule")
652  
          ? hpostform(hsubmit("Deploy Multi-Rule") + hhidden(expandMultiRule := 1))
653  
          : "")
654  
        
655  
        // Serve Feedback
656  
        
657  
        + (empty(mapped) ? "" : h3("Feedback")
658  
        + htmlTable2(mapped,
659  
          tdParams := litobjectarray(align := 'center, valign := 'top),
660  
          htmlEncode := false)
661  
)
662  
        // Serve Fulfillments
663  
        
664  
        + (empty(fulfillments) ? "" : h3("Fulfillments")
665  
        + htmlTable2(map(fulfillments, f -> {
666  
            new LinkedHashMap map;
667  
            mapPut(map, "Line" := ahref(lineURL(contextToMsgID(f.context)), htmlEncode2_nlToBr(f.line)));
668  
            mapPut(map, "Judgement" := f.judgement);
669  
            mapPut(map, "Mapping" := dropPrefix("cimap", f.varMap));
670  
            mapPut(map, "Context" := f.context);
671  
            ret map;
672  
          }),
673  
          tdParams := litobjectarray(align := 'center, valign := 'top),
674  
          paramsByColName := litmap("Line" := litparams(align := 'left)),
675  
          htmlEncode := false))
676  
        + (empty(statements) ? "" : h3("Analysis 1")
677  
        + hblock(multiSetToLines(statements))
678  
)
679  
        + (empty(statements) ? "" : h3("Analysis 2")
680  
        + hblock(sfuLines(mapWithout(objectToMap(
681  
          ai_gazelle_analyzeStatementsForRule_multiSet(statements)), 'statements)))));
682  
    }
683  
    
684  
    S serveWikipedia() {
685  
      /*hheading("All Simple English Wikipedia Topics")
686  
        + htmlTable2(map(simpleWikipediaTopics_cached(), topic -> litorderedmap("Topic" := topic))));*/
687  
      ret hframe("Wikipedia",
688  
        hheading("Retrieved Simple Wikipedia Pages")
689  
        + htmlTable2(map(simpleWikipedia_allCachedTopics(), topic -> litorderedmap("Topic" := topic))));
690  
691  
    }
692  
693  
    Map applicationToMap(O o, GazelleEvalContext ctx, GazellePredictor predictor) {
694  
      bool showStruct = eq("1", params.get('struct));
695  
      S prediction = null;
696  
      if (ctx != null && predictor != null) {
697  
        prediction = "?";
698  
        pcall {
699  
          RuleEngine2_MatchedRule mr = cast unstruct(getString matchedRuleStruct(o));
700  
          if (mr != null)
701  
            prediction = or(predictor.get(ctx, mr), prediction);
702  
        }
703  
      }
704  
      
705  
      S judgement = getString judgement(o);
706  
      S globalID = getString globalID(o);
707  
      S context = getString context(o);
708  
709  
      ret litorderedmap(
710  
        "Judgement" := nempty(judgement) || !authed ? htmlEncode2(judgement) :
711  
          ahref(uri + hqueryWithoutNanoHTTPDStuff(mapPlus(params, "judgeFeedback_" + globalID, 'good)), "mark good") + " " +
712  
          ahref(uri + hqueryWithoutNanoHTTPDStuff(mapPlus(params, "judgeFeedback_" + globalID, 'bad)), "mark bad"),
713  
        "Prediction" := prediction,
714  
        "Generated Output" := getString_htmlEncode outText(o),
715  
        "Rewritten Rule" := ahref("/ruleApplication/" + globalID, getString_htmlEncode modifiedRule(o)),
716  
        "Mapping" := dropPrefix("cimap", getString_htmlEncode varMap(o)),
717  
        "Context" := ahref(lineURL(context), htmlEncode2(context)),
718  
        "Comments" := hform(
719  
          appendIfNempty(nlToBr(rtrim(getString_htmlEncode comments(o))), "<br>")
720  
          + htextfield("comment_" + globalID)),
721  
        "Struct" := showStruct ? getString matchedRuleStruct(o) : null);
722  
    }
723  
    
724  
    S serveRuleComments() {
725  
      ret hframe("All Rule Comments",
726  
        hheading("All Rule Comments")
727  
        + htmlTable2(map(dm_gazelle_allRuleComments(), comment -> litorderedmap("Comment" := comment))));
728  
    }
729  
    
730  
    S hframeAndHeading(S title, O body) {
731  
      ret hframe(title, hheading(htmlEncode2(title)) + body);
732  
    }
733  
734  
    // html-encoded title
735  
    S hframeAndHeading2(S title, O body) {
736  
      ret hframe(htmldecode_dropAllTags(title),
737  
        hheading(title) + body);
738  
    }
739  
740  
    S hframe(S title, O body) {
741  
      //print("Params: " + sfu(bodyParams));
742  
      ret hhtml_head_title_body(title + " | " + htmlEncode2(gazelle_name()),
743  
        loadJQuery() +
744  
        hprelude() +
745  
        body,
746  
        bodyParams);
747  
    }
748  
    
749  
    S searchBox() {
750  
      ret hinputfield('q, style := "width: 120px", autofocus := true)
751  
        + " " + hselect_list(searchTypes, "Rule", name := 'type)
752  
        + " " + hsubmit("Search");
753  
    }
754  
    
755  
    S searchBox2() {
756  
      ret "[search] " + hinputfield('q, style := "width: 120px")
757  
        + " " + hselect_list(searchTypes, "Rule", name := 'type);
758  
    }
759  
    
760  
    void saveFeedback(L list) {
761  
      if (authed) for (S applicationID, S judgement : subMapStartingWith_dropPrefix(params, "judgeFeedback_")) {
762  
        // TODO: copy to feedback module
763  
        continue if !eqOneOf(judgement, 'good, 'bad);
764  
        O app = findByField(list, globalID := applicationID);
765  
        if (dm_isConceptOfModule(app, dm_gazelle_longFeedbackCRUD())) {
766  
          print("Migrating to feedback CRUD");
767  
          app = dm_call(dm_gazelle_feedbackCRUD(), 'uniqConcept, (O) getFieldsAsParams(app, conceptFields_gen(app)));
768  
        }
769  
        print("Judging feedback for " + applicationID + " - " + app);
770  
        call(app, '_setField, +judgement);
771  
      }  
772  
    }
773  
    
774  
    S serveTestInput() {
775  
      S input = params.get('input);
776  
      L<virtual GazelleTree> out = null;
777  
      new L<virtual GInterpretable> interpretables;
778  
      
779  
      if (nempty(input)) {
780  
        // Hopefully safe!
781  
        
782  
        O inputTester = vmBus_query('gazelleInputTester);
783  
        if (inputTester != null)
784  
          out = (L) dm_call(inputTester, 'reason, input,
785  
            interpretables_out := listCollector(interpretables),
786  
            noEvals := true);
787  
788  
        /*out = gazelle_reason_repeat(input,
789  
          interpretables_out := listCollector(interpretables),
790  
          noEvals := true,
791  
          contextMaker := contextCache);*/
792  
      }
793  
        
794  
      ret hframeAndHeading("Test Input",
795  
        hform("Input: " + htextfield('input)
796  
        + " " + hsubmit("Test"))
797  
        + (empty(input) ? "" : p("Processed input: " + b(htmlEncode2(input)))
798  
        + (empty(interpretables) ? "" :
799  
          h3("Results of preprocessing")
800  
          + htmlTable2(
801  
            map(interpretables, func(virtual GInterpretable intp) -> Map {
802  
              litorderedmap(
803  
                "Text" := htmlEncode2(getString text(intp)),
804  
                "Rule" := ruleLink(getString ruleID(intp)))
805  
            }), htmlEncode := false))
806  
        + h3("Output")
807  
        + htmlTable2(
808  
          map(out, func(virtual GazelleTree t) -> Map {
809  
            litorderedmap(
810  
              "Output" := htmlEncode2(getString line(t)),
811  
              "Rule" := ruleLink(getString ruleID(t)),
812  
              "Quality" := htmlEncode2(joinWithComma((LS) call(t, 'renderQualityElements))))
813  
          }
814  
        ), htmlEncode := false)));
815  
    }
816  
817  
    S greeting() {
818  
      ret p(span("<b>Hello! I am a next-generation chat bot in training.</b>", style := "font-size: 1.4em")
819  
        + (!gazelle_isOriginal() ? "" : "<br>"
820  
          + span(targetBlank("https://BotCompany.de", "Maker.") + " "
821  
          + targetBlank("https://slides.com/stefanreich/how-about-thinking-machines/", "Slides.")
822  
          + " "
823  
          + targetBlank("https://discordapp.com/invite/SEAjPqk", b("Join my Discord server!")), style := "font-size: 1.2em")));
824  
    }
825  
    
826  
    S navLine(O... _) {
827  
      bool showAll = eq("1", params.get('showAll));
828  
        
829  
      ret hform(p(ahref(rawSelfLink("commands"), "Commands.")
830  
      + " " + ahref(rawSelfLink("lastInput"), "Last input.")
831  
      + " " + ahref(rawSelfLink("lastOutput"), "Last output.")
832  
      + " " + ahref(rawSelfLink("testInput"), "Test input.")
833  
      + " " + ahref("/tests", "Tests.")
834  
      + stringParOrEmpty more(_)
835  
      + (authed ? " " + ahref(rawSelfLink("createRule"), "Create rule.") : "")
836  
      + (!showAll ? "" : " " + ahref(rawSelfLink("ruleComments"), "Rule comments."))
837  
      + " | " + searchBox()), action := "/search");
838  
    }
839  
    
840  
    S testsModule() { ret gazelle_testsModule(); }
841  
    
842  
    O serveTests() {
843  
      L tests = cast dm_call(testsModule(), 'concepts);
844  
      
845  
      S show = params.get('test);
846  
      S runTest = params.get('runTest);
847  
      if (nempty(runTest)) {
848  
        show = runTest;
849  
        dm_call(testsModule(), 'runTestAndWait, runTest);
850  
      }
851  
      
852  
      O showing = empty(show) ? null : firstWhere(tests, name := show);
853  
      S showingName = getString name(showing);
854  
      
855  
      Pair<Int> stats = cast quickImport(dm_callOpt(testsModule(), 'countAndSuccesses));
856  
      
857  
      bool running = isTrue(dm_getOpt(testsModule(), 'running));
858  
      ret hframeAndHeading2(ahref("/tests", "Tests")
859  
        + (empty(showingName) ? "" : htmlEncode2(" => ") + ahref("/tests" + hquery(test := showingName), htmlEncode2(showingName))),
860  
        (running ? p(tag('blink, "TESTS RUNNING")) : "")
861  
        + (stats == null ? "" : p(n2(stats.a, "test") + ", " + stats.b + " OK"))
862  
        + hSingleRowTable_withSpacing(50, 
863  
            htmlTable2(map(tests, test -> litorderedmap(
864  
            "Test" := ahref(hquery(test := getString name(test)), htmlEncode2(getString name(test))),
865  
            "Last Run" := localDateWithMinutes(getLong lastRun(test)),
866  
            //"Duration" := getLong runtime(test) + " ms",
867  
            "Success" := yesNo2(getBool success(test)),
868  
            "Any Success Ever" := yesNo2(getBool anySuccess(test)))
869  
          ), htmlEncode := false),
870  
          
871  
          (showing == null ? null :
872  
            h3(htmlEncode2("Test " + quote(showingName) + " [" + (getBool success(showing) ? "OK" : "FAILED") + "]"))
873  
          + hpostform(hsubmit("Run Test"), action := hquery(runTest := showingName))
874  
          + htmlEncode_nlToBr(getString contents(showing)))
875  
      ));
876  
    }
877  
    
878  
    // end of Request
879  
  }
880  
} // end of class
881  
882  
sS rawSelfLink(S uri) { ret addSlashPrefix(uri); }
883  
884  
sS hheading(O contents) {
885  
  ret h2(ahref(rawSelfLink(""), htmlEncode2(gazelle_name())) + " | " + contents);
886  
}
887  
888  
sS hprelude() {
889  
  ret [[<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">]] +
890  
    hmobilefix();
891  
}
892  
893  
sS hblock(S s) {
894  
  ret htmlEncode_nlToBr(s);
895  
}
896  
897  
sS sortLink(SS params, S key) {
898  
  params = cloneMap(params);
899  
  mapPutOrRemove(params, 'desc,
900  
    eq(params.get('sortBy), key) && !eq("1", params.get('desc)) ? "1" : null);
901  
  params.put(sortBy := key);
902  
  ret ahref(hquery(params), htmlEncode2(key));
903  
}
904  
905  
static L<GazelleLine> selectLines(SS params) {
906  
  long modifiedAfter = getLong modifiedAfter(params);
907  
  if (modifiedAfter != 0) ret dm_discord_linesModifiedAfter(modifiedAfter);
908  
  ret dm_discord_allLines();
909  
}

Author comment

Began life as a copy of #1021679

download  show line numbers  debug dex  old transpilations   

Travelled to 10 computer(s): bhatertpkbcr, cfunsshuasjs, gwrvuhgaqvyk, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, whxojlpjdney, xrpafgyirdlv

No comments. add comment

Snippet ID: #1022531
Snippet name: DynGazelleWebServer
Eternal ID of this version: #1022531/71
Text MD5: 89da73adad5c94b5016b81f07d57a3b0
Transpilation MD5: a7b233a676dfa9bebea22fe836cd2d46
Author: stefan
Category: javax / stefan's os / a.i. / web
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2020-05-19 17:33:46
Source code size: 37312 bytes / 909 lines
Pitched / IR pitched: No / No
Views / Downloads: 370 / 1299
Version history: 70 change(s)
Referenced in: [show references]