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

973
LINES

< > BotCompany Repo | #1024329 // agi.blue source [backup before Google log-in]

JavaX module (desktop) [tags: use-pretranspiled]

Download Jar. Libraryless. Click here for Pure Java version (18179L/132K).

1  
!7
2  
3  
// See #1023660 for the older 99 lines version
4  
5  
// Customizations:
6  
7  
sbool asyncSearch = false;
8  
sbool allowMultipleCasesInValues = true;
9  
sbool showSliceSelector = true;
10  
sbool makeAllValuesPages = true; 
11  
sbool legacy = true; // We still have slices without applied fixes
12  
13  
static int displayLength = 140;
14  
static int sideDisplayLength = 50; // when in side bar
15  
static int searchResultsToShow = 50;
16  
sS sideSearchType = 'literal;
17  
18  
// end of customizations
19  
20  
// in main DB
21  
concept Slice {
22  
  GlobalID globalID = aGlobalIDObject();
23  
  S caseID;
24  
  S name;
25  
  
26  
  S defaultCaseID() {
27  
    ret uniqueFileNameUsingMD5_80_v2(name + " " + globalID);
28  
  }
29  
}
30  
31  
// in any DB
32  
concept Page {
33  
  S globalID = aGlobalID();
34  
  S url, q;
35  
}
36  
37  
abstract concept AbstractEntry {
38  
  new Ref<Page> page;
39  
  int count;
40  
  S key;
41  
  S ip;
42  
  new Ref<Signer> signer;
43  
}
44  
45  
concept Entry > AbstractEntry {
46  
  S globalID = aGlobalID(); // TODO: migrate to object
47  
  S value;
48  
}
49  
50  
concept MultiLineEntry > AbstractEntry {
51  
  GlobalID globalID = aGlobalIDObject();
52  
  S value;
53  
}
54  
55  
concept BlobEntry > AbstractEntry {
56  
  GlobalID globalID = aGlobalIDObject();
57  
  long length;
58  
  S md5;
59  
}
60  
61  
// in main DB?
62  
concept Signer {
63  
  S globalID = aGlobalID();
64  
  S publicKey;
65  
  bool trusted;
66  
  S approvedBy;
67  
}
68  
69  
// in main DB
70  
concept Session {
71  
  S cookie;
72  
  S selectedSlice; // global ID
73  
  Page slicePage; // phasing out
74  
}
75  
76  
// singleton concept in every DB
77  
concept SliceInfo {
78  
  GlobalID globalID;
79  
  LS mandatoryBotVMBusIDs;
80  
}
81  
82  
static SS searchTypeToText = litmap(
83  
  leven := "Leven",
84  
  literal := "Literal",
85  
  scored := "Scored");
86  
87  
static int sourceCodeLines;
88  
static ConceptsLoadedOnDemand fan;
89  
static Map<S, LoadedSlice> loadedSlices = syncMap();
90  
91  
sclass LoadedSlice {
92  
  S caseID;
93  
  Concepts cc;
94  
  Slice sliceConcept;
95  
  
96  
  ConceptFieldIndexDesc idx_latestEntries, idx_latestCreatedPages, idx_latestChangedPages;
97  
  
98  
  *(S *caseID, Concepts *cc) {
99  
    indexThings();
100  
    
101  
    if (makeAllValuesPages && legacy)
102  
      for (Entry e : list(cc, Entry))
103  
        pageFromQ(e.value);
104  
  }
105  
  
106  
  void initialSetup(GlobalID globalID) {
107  
    SliceInfo info = uniq(cc, SliceInfo);
108  
    cset(info, +globalID);
109  
  }
110  
  
111  
  // create if not there
112  
  Page pageFromQ(S q) {
113  
    ret main.pageFromQ(cc, q);
114  
  }
115  
116  
  void indexThings() {
117  
    indexConceptFieldsCI(cc, Page, 'url, Page, 'q, Entry, 'key, Entry, 'value);
118  
    indexConceptFields(cc, Signer, 'publicKey);
119  
    indexConceptFields(cc, Session, 'cookie);
120  
    indexSingletonConcept(cc, SliceInfo);
121  
    idx_latestCreatedPages = new ConceptFieldIndexDesc(cc, Page, 'created);
122  
    idx_latestChangedPages = new ConceptFieldIndexDesc(cc, Page, '_modified);
123  
    idx_latestEntries = new ConceptFieldIndexDesc(cc, Entry, 'created);
124  
  }
125  
  
126  
  bool isMainSlice() { ret empty(caseID); }
127  
}
128  
129  
p {
130  
  thinMyBackups();
131  
  fan = new ConceptsLoadedOnDemand;
132  
  fan.onCaseLoaded(voidfunc(S caseID, O globalID, Concepts concepts) {
133  
    print("onCaseLoaded " + quote(caseID));
134  
    LoadedSlice ls = new(caseID, concepts);
135  
    if (nempty(globalID)) ls.initialSetup(toGlobalIDObj((S) globalID));
136  
    loadedSlices.put(caseID, ls);
137  
  });
138  
  mainConcepts = fan.get("", null);
139  
  indexConceptFields(Slice, 'globalID, Slice, 'caseID);
140  
  cset(uniq_returnIfNew Slice(caseID := ""), name := "main slice");
141  
  loadedSlices.get("").initialSetup(conceptWhere Slice(caseID := "").globalID);
142  
143  
  // legacy conversions!
144  
  //deleteConcepts(Slice, globalID := GlobalID('wftlawbagrwprywn));
145  
146  
  // Approve this machine's key
147  
  PKIKeyPair machineKey = agiBot_trustedKeyForMachine();
148  
  if (machineKey != null) {
149  
    print("Approving this machine's key: " + machineKey.publicKey);
150  
    cset(uniq_sync(Signer, publicKey := machineKey.publicKey), trusted := true, approvedBy := "local");
151  
  }
152  
  
153  
  sourceCodeLines = countLines(mySource());
154  
}
155  
156  
// Serve page
157  
158  
set flag NoNanoHTTPD. html {
159  
  ret new Request().serve(uri, params);
160  
}
161  
162  
sclass WorksOnSlice {
163  
  Concepts cc;
164  
  LoadedSlice slice;
165  
  
166  
  *() {}
167  
  *(LoadedSlice *slice) { cc = slice.cc; }
168  
  
169  
  void setSlice(LoadedSlice slice) {
170  
    this.slice = slice;
171  
    cc = slice.cc;
172  
  }
173  
  
174  
  Page findPageFromParams(Map map) {
175  
    S q = getString q(map);
176  
    ret empty(q) ? null : findPageFromQ(q);
177  
  }
178  
179  
  Page findOrMakePageFromParams(Map map) {
180  
    ret pageFromQ(getString q(map));
181  
  }
182  
  
183  
  Page findPageFromQ(S q) {
184  
    ret conceptWhereCI(cc, Page, +q);
185  
  }
186  
  
187  
  Page pageFromQ(S q) {
188  
    ret slice.pageFromQ(q);
189  
  }
190  
  
191  
  Set<Page> pagesForKeyAndValue(S key, S value) {
192  
    L<Entry> entries = conceptsWhereIC(cc, Entry, +value, +key);
193  
    ret asSet(ccollect page(entries));
194  
  }
195  
  
196  
  Cl<S> keysForPageAndValue(S q, S value) {
197  
    Page page = findPageFromQ(q);
198  
    if (page == null) null;
199  
    ret collect key(conceptsWhereIC(cc, Entry, +page, +value));
200  
  }
201  
202  
  // DB functions
203  
204  
  bool hasPage(S q) { ret hasConceptWhereIC(Page, +q); }
205  
  S getValue(Page page, S key) {
206  
    ret page == null || empty(key) ? null : getString value(highestByField count(objectsWhereIC(findBackRefs(page, Entry), +key)));
207  
  }
208  
  S getValue(S page, S key) {
209  
    ret getValue(findPageFromQ(page), key);
210  
  }
211  
  S pageDisplayName(Page page) {
212  
    /*S name = getValue(page, "read as");
213  
    bool unnaturalName = nempty(name) && !eq(makeAGIDomain(name), page.url);
214  
    ret unnaturalName ? name + " " + squareBracketed(page.url) : or2(name, unpackAGIDomainOpt(page.url));*/
215  
    ret page.q;
216  
  }
217  
  
218  
  S renderThing(S s, bool forceURLDisplay) {
219  
    ret forceURLDisplay || isURL(s) || isAGIDomain(s)
220  
      ? ahref(fixAGILink(absoluteURL(s)), htmlencode2(shorten(displayLength, s)))
221  
      : ahref(agiBlue_linkForPhrase(s,
222  
          slice := slice.isMainSlice() ? null : slice.sliceConcept.globalID),
223  
        htmlencode2(shorten(displayLength, s)));
224  
  }
225  
226  
} // end of WorksOnSlice
227  
228  
sclass Request extends WorksOnSlice {
229  
  S cookie;
230  
  Session session;
231  
  S uri;
232  
  SS params;
233  
  long started = sysNow();
234  
  
235  
  // should also work for standalone
236  
  bool isHomeDomain() {
237  
    S domain = domain();
238  
    ret eqic(domain, "www.agi.blue") || !ewic(domain, ".agi.blue");
239  
  }
240  
  
241  
  O serve(S uri, SS params) {
242  
    this.uri = dropMultipleSlashes(uri);
243  
    this.params = params;
244  
    new Matches m;
245  
    
246  
    print(uri + " ? " + unnull(subBot_query()));
247  
    
248  
    // get cookie & session
249  
    
250  
    cookie = cookieFromUser();
251  
    if (cookie != null)
252  
      session = uniq_sync(Session, +cookie);
253  
    else
254  
      session = unlistedWithValues(Session);
255  
      
256  
    // check if user wants to change slices
257  
      
258  
    S selectSlice = params.get('slice);
259  
    if (isGlobalID(selectSlice))
260  
      cset(session, selectedSlice := selectSlice);
261  
262  
    // get case ID
263  
    
264  
    S caseID = "";
265  
    Slice sliceConcept = sliceConceptForGlobalID(session.selectedSlice);
266  
    print("Selected slice: " + session.selectedSlice + ", obj? " + (sliceConcept != null));
267  
    if (sliceConcept != null) caseID = sliceConcept.caseID;
268  
    print("caseID: " + caseID);
269  
    
270  
    // load slice
271  
    
272  
    slice = assertNotNull(loadSlice(caseID, session.selectedSlice));
273  
    cc = slice.cc;
274  
    slice.sliceConcept = sliceConcept;
275  
      
276  
    // Check for special URIs
277  
278  
    if (swic(uri, "/bot/")) ret serveBot();
279  
    
280  
    // eleu appends a slash to the URI if it's a single identifier, so we drop it again
281  
    S uri2 = dropTrailingSlash(uri);
282  
    
283  
    if (eqic(uri2, "/search")) ret serveScoredSearch();
284  
    if (eqic(uri2, "/literalSearch")) ret serveLiteralSearch();
285  
    if (eqic(uri2, "/levenSearch")) ret serveLevenSearch();
286  
    
287  
    if (eqic(uri2, "/query")) ret serveQueryPage();
288  
    if (eqic(uri2, "/createSlice")) ret serveCreateSlicePage();
289  
    
290  
    S q = params.get('q);
291  
    S domain = or2(params.get('domain), domain());
292  
    
293  
    S raw = firstKeyWithValue("", params); // agi.blue?something
294  
    if (nempty(raw) && empty(q)) q = raw;
295  
    /*if (nempty(q)) {
296  
      domain = makeAGIDomain(q);
297  
      if (l(domain) > maximumDomainPartLength()) // escape with "domain="
298  
        ret hrefresh(agiBlueURL() + hquery(+domain, key := "read as", value := q));
299  
      ret hrefresh("http://" + domain + (eq(q, domain) ? "" : "/" +  hquery(key := "read as", value := q)));
300  
      //uri = "/"; replaceMapWithParams(params, key := "read as", value := q);
301  
    }*/
302  
    S url = domain + dropTrailingSlash(uri);
303  
    
304  
    // domain to query
305  
    //if (empty(q)) q = url;
306  
    if (empty(q)) q = agiBlue_urlToQuery(url);
307  
    
308  
    Page page, bool newPage = unpair uniqCI2_sync(cc, Page, +q);
309  
    if (newPage) dbLog("New page", +q);
310  
    //printStructs(+params, +raw, +q);
311  
  
312  
    Set<S> get = asCISet(nempties(subBot_paramsAsMultiMap().get('get)));
313  
    
314  
    S top = nempty(get) ? "" : hcomment("cookie: " + takeFirst(4, session.cookie))
315  
      + hSilentComputatorWithFlag("agi.blue: " + q)
316  
      + p(ahref(sliceHomeURL(),
317  
        //hsnippetimg(#1101682, width := 565/5, height := 800/5, title := "Robot by Peerro @ DeviantArt")
318  
        //hsnippetimg(#1101778, width := 96, height := 96, title := "agi.blue - a database for everything")
319  
        hsnippetimg(#1101822, width := 314, height := 125, title := "agi.blue - Wikipedia for robots")
320  
      ))
321  
      + p(small(
322  
          b(agiBlueNameHTML())
323  
        + (agiBlue_isOriginal() ? "" : " " + targetBlank("http://agi.blue", "[original]"))
324  
        + " | " + targetBlank(progLink(), "source code") + " of this web site (" + nLines(sourceCodeLines) + ") | " + targetBlank("https://gitter.im/agi-blue/community", "sponsor https?") + " | by " + targetBlank("https://BotCompany.de", "BC") + " | " + targetBlank("http://fiverr.tinybrain.de/", "Fiverr") + " | " + targetBlank("https://discordapp.com/invite/SEAjPqk", "Discord") + " | " + targetBlank("https://www.youtube.com/watch?v=b6jtRdV3Ev8", "Video")
325  
        + " | " + targetBlank("http://code.botcompany.de/1024233", "Notes")
326  
        + " | " + ahref(agiBlueURL() + "/query", "Query")
327  
      ));
328  
    
329  
    if (empty(params.get('q)) && empty(raw) && isHomeDomain()) {
330  
      L<Page> pages = sortedByFieldDesc _modified(list(cc, Page)); // TODO: use index
331  
      int start = parseInt(params.get("start")), step = 100;
332  
      S nav = pageNav2("/", l(pages), start, step, "start");
333  
      S content = hform(b("GIVE ME INPUT: &nbsp; ") + htextinput('q, autofocus := true) + " " + hsubmit("Ask"))
334  
        + h1(sliceAsHTML() + " has " + nPages(countConcepts(cc, Page)) + " and " + nConnections(countConcepts(cc, Entry)))
335  
        + p(nav)
336  
        + p_nemptyLines(map(pageToHTMLLink(), subList(pages, start, start+step)));
337  
338  
      ret hhtml2(hhead_title("Slice " + sliceConcept().name + " | agi.blue") // SERVE SLICE HOME PAGE
339  
        + hbody(hfullcenterAndTopLeft(top + content
340  
          + footer(),
341  
          sliceSelector()
342  
        )));
343  
    }
344  
        
345  
    S key = trim(params.get('key)), value = trim(params.get('value));
346  
    L<Entry> entries = GetEntriesAndPost(cc).go(page, params).entries;
347  
    
348  
    //S get = params.get('get);
349  
    if (nempty(get))
350  
      ret serveJSON(collect value(llNotNulls(firstThat(entries, e -> get.contains(e.key)))));
351  
    
352  
    S key2 = key, value2 = value; if (nempty(key) && nempty(value)) key2 = value2 = ""; // input reset
353  
  
354  
    bool withHidden = eq(params.get('withHidden), "1");
355  
    new Set<Int> hide;
356  
    if (!withHidden) for (Entry e : entries) if (eqic(e.key, 'hide) && isSquareBracketedInt(e.value)) addAll(hide, e.count, parseInt(unSquareBracket(e.value)));
357  
    new MultiMap<Int, S> mmMeta;
358  
    for (Entry e : entries) if (isSquareBracketedInt(e.key)) mmMeta.put(parseInt(unSquareBracket(e.key)), e.value);
359  
    new MultiMap<S> mm;
360  
    for (Entry e : entries) mm.put(e.key, e.value);
361  
    
362  
    //S name = or2(/* ouch */ last(mm.get("read as")), /* end ouch */ unpackAGIDomain(page.url), page.url);
363  
    S name = page.q;
364  
    
365  
    // Find references
366  
    
367  
    L<Entry> refs = concatLists(conceptsWhereIC(cc, Entry, value := name),
368  
      conceptsWhereIC(cc, Entry, key := name));
369  
    Set<Page> refPages = asSet(ccollect page(refs));
370  
    refPages.remove(page); // don't list current page as reference
371  
    
372  
    // Search in page names (depending on default search type)
373  
    
374  
    L<Page> searchResults;
375  
    if (eq(sideSearchType, 'leven))
376  
      searchResults = levenSearch(page.q, max := 50);
377  
    else if (eq(sideSearchType, 'literal))
378  
      searchResults = literalSearch(page.q, max := 50);
379  
    else
380  
      searchResults = (L<Page>) dm_call('agiBlueSearch, 'search, page.q, maxResult := searchResultsToShow+1, agiBlueBotID := programID(), +cc);
381  
    searchResults.remove(page);
382  
383  
    S mainContents =
384  
        top + h1(ahref_unstyled(agiBlue_pageURLWithSlice(page), htmlEncode2(shorten(displayLength, name))) + (newPage ? " [huh????]" : ""))
385  
      + p_nemptyLines(map(entries, func(Entry e) -> S {
386  
        !withHidden && (hide.contains(e.count) || eqic(e.key, "read as") && eqic(e.value, name)) ? ""
387  
        : "[" + e.count + "] " +
388  
          renderThing(e.key, false) + ": " +
389  
          b(renderThing(e.value, cic(mmMeta.get(e.count), "is a URL")))
390  
        }))
391  
      + hpostform(h3("Add an entry")
392  
        + "Key: " + hinputfield(key := key2) + " Value: " + hinputfield(value := value2) + "<br><br>" + hsubmit("Add")
393  
        )
394  
        
395  
      + p(ahref(agiBlueURL() + "/literalSearch" + hquery(q := page.q), "[literal search]", title := "Search pages with a name containing this page's name literally")
396  
       + " " +
397  
       ahref(agiBlueURL() + "/levenSearch" + hquery(q := page.q), "[leven search 1]", title := "Search pages with a Levenshtein similarity of 1 containing this page's name literally")
398  
       + " " +
399  
       ahref(agiBlueURL() + "/search" + hquery(q := page.q), "[scored search]", title := "Search pages with ScoredSearch"));
400  
        
401  
    S sideContents =
402  
      hform(b("GIVE ME INPUT:") + " "
403  
        + htextinput('q) + " "
404  
        + hsubmit("Ask", onclick := "document.getElementById('newInputForm').target = '';") + " "
405  
        + hsubmit("+Tab", title := "Ask and show result in a new tab", onclick := "document.getElementById('newInputForm').target = '_blank';"),
406  
        id := 'newInputForm)
407  
      
408  
      + h3("References (" + l(refPages) + ")")
409  
      
410  
      + p_nemptyLines_showFirst(10, map(pageToHTMLLink(displayLength := sideDisplayLength), refPages))
411  
      
412  
      + h3(searchTypeToText.get(sideSearchType) + " search results (" + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+" : str(l(searchResults))) + ")")
413  
      
414  
      + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(displayLength := sideDisplayLength), searchResults))
415  
      
416  
      + hdiv("", id := 'extraStuff);
417  
      
418  
    // TODO: sync search delivery with WebSocket creation
419  
    if (asyncSearch)
420  
      doLater(6.0, r { dm_call('agiBlueSearch, 'searchAndPost, page.q, agiBlueBotID := programID(), +cc) });
421  
      
422  
    // serve a concept page
423  
    ret hhtml2(hhead_title(pageDisplayName(page)) + hbody(
424  
      tag('table,
425  
        tr(
426  
          td(sliceSelector(), valign := 'top)
427  
        + td(sideContents, align := 'right, valign := 'top, rowspan := 2))
428  
      + tr(td(mainContents + footer(), align := 'center, valign := 'top)),
429  
      width := "100%", height := "100%")));
430  
  }
431  
  
432  
  O servePagesToBot(Iterable<Page> pages) {
433  
    ret serveListToBot(map(pageToMap(wrapMapAsParams(params)), pages));
434  
  }
435  
436  
  O serveListToBot(Collection l) {
437  
    if (nempty(params.get('max)))
438  
      l = takeFirst(parseInt(params.get('max)), l);
439  
    ret serveJSON(l);
440  
  }
441  
442  
  // uri starts with "/bot/"
443  
  O serveBot() {
444  
    S q = params.get('q);
445  
    
446  
    if (eqic(uri, "/bot/hello")) ret serveJSON("hello");
447  
    if (eqic(uri, "/bot/hasPage")) ret serveJSON(hasPage(q));
448  
    if (eqic(uri, "/bot/randomPageContaining")) {
449  
      assertNempty(q);
450  
      ret servePageToBot(random(filter(list(cc, Page), p -> cic(p.q, q))), params);
451  
    }
452  
    if (eqic(uri, "/bot/allPages"))
453  
      ret servePagesToBot(list(cc, Page));
454  
    if (eqic(uri, "/bot/allPagesStartingWith")) {
455  
      assertNempty(q);
456  
      ret servePagesToBot(filter(list(cc, Page), p -> swic(p.q, q)));
457  
    }
458  
    if (eqic(uri, "/bot/allPagesEndingWith")) {
459  
      assertNempty(q);
460  
      ret servePagesToBot(filter(list(cc, Page), p -> ewic(p.q, q)));
461  
    }
462  
    if (eqic(uri, "/bot/allPagesContaining")) {
463  
      assertNempty(q);
464  
      ret servePagesToBot(filter(list(cc, Page), p -> cic(p.q, q)));
465  
    }
466  
    if (eqic(uri, "/bot/allPagesContainingRegexp")) {
467  
      assertNempty(q);
468  
      Pattern pat = regexpIC(q);
469  
      ret servePagesToBot(filter(list(cc, Page), p -> regexpFindIC(pat, p.q)));
470  
    }
471  
    
472  
    if (eqicOneOf(uri, "/bot/postSigned", /*"/bot/makePhysicalSlice",*/ "/bot/approveTrustRequest")) {
473  
      S text = rtrim(params.get('text));
474  
      S key = getSignerKey(text);
475  
      if (empty(key)) ret subBot_serve500("Please include your public key");
476  
      if (!isSignedWithKey(text, key)) ret subBot_serve500("Signature didn't verify");
477  
      text = dropLastTwoLines(text); // drop signer + sig line
478  
      
479  
      Signer signer = uniq_sync Signer(publicKey := key);
480  
      
481  
      /*if (eqic(uri, "/bot/makePhysicalSlice")) {
482  
        if (!signer.trusted) ret subBot_serve500("Untrusted signer");
483  
        Page page = findPageFromParams(jsonDecodeMap(text));
484  
        if (page == null) ret subBot_serve500("Page not found");
485  
        ret serveJSON(uniq2_sync(PhysicalSlice, slicePage := page).b ? "Slice made" : "Slice exists");
486  
      }*/
487  
    
488  
      if (eqic(uri, "/bot/postSigned")) {
489  
        new L out;
490  
        for (S line : tlft(text)) {
491  
          SS map = jsonDecodeMap(line);
492  
          GetEntriesAndPost x = new(db_mainConcepts());
493  
          x.signer = signer;
494  
          Page page = findOrMakePageFromParams(map);
495  
          if (page == null) continue with out.add("Invalid page reference");
496  
          x.go(page, map);
497  
          out.add(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
498  
        }
499  
        ret serveJSON(out);
500  
      }
501  
      
502  
      if (eqic(uri, "/bot/approveTrustRequest")) {
503  
        if (!signer.trusted) ret subBot_serve500("Untrusted signer");
504  
        Signer toApprove = conceptWhere Signer(publicKey := trim(text));
505  
        if (toApprove == null) ret subBot_serve500("Signer to approve not found");
506  
        cset(toApprove, trusted := true, approvedBy := signer.globalID);
507  
        ret serveJSON("Approved: " + trim(text));
508  
      }
509  
      
510  
      ret subBot_serve500("CONFUSION");
511  
    }
512  
    
513  
    if (eqic(uri, "/bot/post")) {
514  
      GetEntriesAndPost x = new(db_mainConcepts());
515  
      x.go(pageFromQ(q), params);
516  
      ret serveJSON(x.newEntry ? "Saved" : x.entry != null ? "Entry exists" : "Need key and value");
517  
    }
518  
    
519  
    if (eqic(uri, "/bot/entriesOnPage"))
520  
      ret serveJSON(map(entriesOnPage(findPageFromParams(params)), entryToMap(false)));
521  
  
522  
    if (eqic(uri, "/bot/lookup")) {
523  
      S key = params.get('key);
524  
      if (empty(key)) ret serveJSON("Need key");
525  
      S value = getValue(findPageFromParams(params), key);
526  
      ret serveJSON(empty(value) ? "" : litmap(+value));
527  
    }
528  
  
529  
    if (eqic(uri, "/bot/latestEntries"))
530  
      ret serveJSON(map(takeFirst(10, slice.idx_latestEntries.objectIterator()), entryToMap(true)));
531  
    if (eqic(uri, "/bot/latestPages"))
532  
      ret serveJSON(map(takeFirst(10, slice.idx_latestCreatedPages.objectIterator()), pageToMap()));
533  
    if (eqic(uri, "/bot/latestChangedPages"))
534  
      ret serveJSON(map(takeFirst(10, slice.idx_latestChangedPages.objectIterator()), pageToMap()));
535  
      
536  
    if (eqic(uri, "/bot/totalPageCount"))
537  
      ret serveJSON(countConcepts(Page));
538  
    /*if (eqic(uri, "/bot/pageWithoutPhysicalSliceCount"))
539  
      ret serveJSON(countConceptsWhere(Page, slice := null));
540  
    if (eqic(uri, "/bot/physicalSliceCount"))
541  
      ret serveJSON(countConcepts(PhysicalSlice));*/
542  
    if (eqic(uri, "/bot/trustedSignersCount"))
543  
      ret serveJSON(countConcepts(Signer, trusted := true));
544  
      
545  
    if (eqic(uri, "/bot/valueSearch")) {
546  
      S value = params.get('value);
547  
      L<Entry> entries = conceptsWhereIC(cc, Entry, +value);
548  
      ret serveJSON(map(takeFirst(100, entries), entryToMap(true)));
549  
    }
550  
      
551  
    if (eqic(uri, "/bot/keyAndValueSearch")) {
552  
      S key = params.get('key), value = params.get('value);
553  
      Cl<Page> pages = pagesForKeyAndValue(key, value);
554  
      ret servePagesToBot(pages);
555  
    }
556  
    
557  
    if (eqic(uri, "/bot/keyValuePairsByPopularity")) {
558  
      L<PairS> pairs = map(list(Entry), e -> pair(e.key, e.value));
559  
      LPair<S, Int> pairs2 = multiSetTopPairs(ciMultiSet(map pairToUglyStringForCIComparison(pairs)));
560  
      ret serveJSON(map(pairs2, p -> {
561  
        S key, value = unpair pairFromUglyString(p.a);
562  
        ret litorderedmap(n := p.b, +key, +value);
563  
      }));
564  
    }
565  
    
566  
    if (eqic(uri, "/bot/allKeys"))
567  
      ret serveListToBot(distinctCIFieldValuesOfConcepts(cc, Entry, 'key));
568  
      
569  
    if (eqic(uri, "/bot/allKeysByPopularity"))
570  
      ret serveListToBot(mapMultiSetByPopularity(distinctCIFieldValuesOfConcepts_multiSet(cc, Entry, 'key), (key, n) -> litorderedmap(+n, +key)));
571  
      
572  
    if (eqic(uri, "/bot/dbSize"))
573  
      ret serveJSON(l(conceptsFile()));
574  
      
575  
    if (eqic(uri, "/bot/query"))
576  
      ret serveBotQuery();
577  
578  
    if (eqic(uri, "/bot/createSlice")) {
579  
      Slice slice = createSlice(assertNempty(params.get('name)));
580  
      ret serveJSON(litorderedmap(id := str(slice.globalID), name := slice.name));
581  
    }
582  
    
583  
    // end of serveBot()
584  
  
585  
    ret subBot_serve404();
586  
  }
587  
  
588  
  O serveBotQuery() {
589  
    S query = params.get('query);
590  
    ret new Query(slice).process(query);
591  
  }
592  
  
593  
  O serveLiteralSearch() {
594  
    S q = params.get('q);
595  
    L<Page> searchResults = literalSearch(q);
596  
    ret serveSearchResults("literal search" , q, searchResultsToShow, searchResults);
597  
  }
598  
  
599  
  L<Page> literalSearch(S q, O... _) {
600  
    int searchResultsToShow = optPar max(_, 100);
601  
    
602  
    // quick search in random order
603  
    //L<Page> searchResults = takeFirst(searchResultsToShow+1, filterIterator(iterator(list(cc, Page)), p -> cic(p.q, q));
604  
    
605  
    // full search, order by length
606  
    ret takeFirst(searchResultsToShow+1, pagesSortedByLength(filter(list(cc, Page), p -> cic(p.q, q))));
607  
  }
608  
  
609  
  O serveScoredSearch() {
610  
    S q = params.get('q);
611  
    L<Page> searchResults = (L<Page>) dm_call('agiBlueSearch, 'search, q, +cc);
612  
    ret serveSearchResults("scored search" , q, searchResultsToShow, searchResults);
613  
  }
614  
  
615  
  O serveLevenSearch() {
616  
    S q = params.get('q);
617  
    L<Page> searchResults = levenSearch(q);
618  
    ret serveSearchResults("leven search with distance 1" , q, searchResultsToShow, searchResults);
619  
  }
620  
  
621  
  L<Page> levenSearch(S q, O... _) {
622  
    int searchResultsToShow = optPar max(_, 100);
623  
    int maxEditDistance = 1;
624  
    
625  
    new Map<Page, Int> map;
626  
    for (Page p : list(cc, Page)) {
627  
      int distance = leven_limitedIC(q, p.q, maxEditDistance+1);
628  
      if (distance <= maxEditDistance) map.put(p, distance);
629  
    }
630  
    ret takeFirst(searchResultsToShow+1, keysSortedByValue(map));
631  
  }
632  
  
633  
  O serveSearchResults(S searchType, S q, int searchResultsToShow, Collection<Page> searchResults) {
634  
    S title = "agi.blue " + searchType + " for " + htmlEncode2(quote(q)) + " (" + (l(searchResults) >= searchResultsToShow ? searchResultsToShow + "+ results" : nResults(l(searchResults))) + ")";
635  
    
636  
    ret hhtml2(hhead_title(htmldecode_dropAllTags(title))
637  
      + hbody(hfullcenter(//top +
638  
        h3(title)
639  
      + p_nemptyLines_showFirst(searchResultsToShow, map(pageToHTMLLink(), searchResults)))));
640  
  }
641  
  
642  
  O serveQueryPage() {
643  
    S query = params.get('query);
644  
    if (query == null) query = loadSnippet(#1024258);
645  
    S title = agiBlueNameHTML() + " | Execute a query script (" + targetBlank("http://code.botcompany.de/1024274", "ALQL") + ")";
646  
    ret hhtml2(hhead_title(htmldecode_dropAllTags(title))
647  
      + hbody(hfullcenter(
648  
        h3(title)
649  
      + form(
650  
        htextarea(query, name := 'query, cols := 80, rows := 10, autofocus := true)
651  
        + "<br><br>"
652  
        + hsubmit("Execute"), action := "/bot/query")
653  
      )));    
654  
  }
655  
  
656  
  S sliceHomeURL() {
657  
    ret slice == null ? agiBlueURL() : sliceHomeURL(slice.sliceConcept.globalID);
658  
  }
659  
660  
  S sliceHomeURL(GlobalID slice) {
661  
    ret agiBlueURL() + hquery(+slice);
662  
  }
663  
  
664  
  O serveCreateSlicePage() {
665  
    S sliceName = trim(params.get('sliceName));
666  
    if (eq(params.get('doIt), "1") && nempty(sliceName)) {
667  
      // TODO: check for existing name
668  
      Slice slice = createSlice(sliceName);
669  
      ret hrefresh(sliceHomeURL(slice.globalID));
670  
    }
671  
    
672  
    S title = agiBlueNameHTML() + " | Create slice";
673  
    ret hhtml2(hhead_title(htmldecode_dropAllTags(title))
674  
      + hbody(hfullcenter(
675  
        h3(title)
676  
      + form(
677  
          hhidden(doIt := 1)
678  
        + "Slice name: " + htextinput(+sliceName, autofocus := true)
679  
        + "<br><br>"
680  
        + hsubmit("Create slice"))
681  
      )));    
682  
  }
683  
  
684  
  F1<Page, S> pageToHTMLLink(O... _) {
685  
    optPar int displayLength = main.displayLength;
686  
    ret func(Page p) -> S {
687  
      S name = pageDisplayName(p);
688  
      ret ahref(agiBlue_pageURLWithSlice(p),
689  
        htmlEncode2(shorten(displayLength, name)),
690  
        title := name);
691  
    };
692  
  }
693  
  
694  
  S sliceSelector() {
695  
    ret !showSliceSelector ? "" : hform(
696  
        "Select reality slice: "
697  
      + hselect(availableSlices(), session.selectedSlice, name := 'slice, onchange := "this.form.submit()")
698  
      /*+ " " + hsubmit("Go")*/
699  
      + " &nbsp; " + ahref(agiBlueURL() + "/createSlice", "Create slice...")
700  
    );
701  
  }
702  
  
703  
  S sliceAsHTML() {
704  
    if (slice.isMainSlice()) ret htmlEncode2(agiBlueName() + "'s main slice");
705  
    if (slice.sliceConcept == null) ret "Slice ???";
706  
    ret htmlEncode2("Slice " + quote(slice.sliceConcept.name));
707  
  }
708  
  
709  
  Slice sliceConcept() { ret slice.sliceConcept; }
710  
  
711  
  S footer() {
712  
    ret p(small(elapsedMS_sysNow(started) + " ms");
713  
  }
714  
715  
} // end of Request
716  
717  
svoid dbLog(O... params) {
718  
  logStructure(programFile("db.log"), ll(params));
719  
}
720  
721  
static IF1<Page, Map> pageToMap(O... _) {
722  
  optPar bool withEntries;
723  
  
724  
  bool nameOnly = eqOneOf(optPar nameOnly(_), "1", true);
725  
  if (nameOnly) ret (IF1<Page, Map>) p -> litmap(q := p.q);
726  
  
727  
  ret (IF1<Page, Map>) p -> {
728  
    L<Entry> entries = findBackRefs(p, Entry);
729  
    ret litorderedmap(
730  
      q := p.q,
731  
      nEntries := l(entries),
732  
      created := p.created,
733  
      modified := p._modified,
734  
      entries := !withEntries ? null : map(entries, entryToMap(false)));
735  
  };
736  
}
737  
738  
static IF1<Entry, Map> entryToMap(bool withPage) {
739  
  ret (IF1<Entry, Map>) e -> litorderedmap(
740  
    created := e.created,
741  
    i := e.count,
742  
    key := e.key,
743  
    value := e.value,
744  
    q := withPage ? e.page->q : null,
745  
    signer := getString globalID(e.signer!));
746  
}
747  
748  
sO servePageToBot(Page page, SS params) {
749  
  if (page == null) ret serveJSON(null);
750  
  params = asCIMap(params);
751  
  Map map = pageToMap(withEntries := valueIs1 withEntries(params)).get(page);
752  
  ret serveJSON(map);
753  
}
754  
755  
sclass GetEntriesAndPost {
756  
  Concepts cc;
757  
  L<Entry> entries;
758  
  Entry entry;
759  
  bool newEntry;
760  
  Signer signer;
761  
  
762  
  *(Concepts *cc) {}
763  
  
764  
  GetEntriesAndPost go(Page page, SS params) {
765  
    S key = trim(params.get('key)), value = trim(params.get('value));
766  
    print("GetEntriesAndPost: " + quote(page.q) + ", " + quote(key) + " := " + quote(value));
767  
    withDBLock {
768  
      entries = findBackRefs(page, Entry);
769  
      if (nempty(key) && nempty(value)) {
770  
        S ip = subBot_clientIP();
771  
        entry = firstThat(e -> eqic(e.key, key) && eq_icIf(!allowMultipleCasesInValues, e.value, value) && eq(e.ip, ip), entries);
772  
        if (entry == null) {
773  
          print("SAVING");
774  
          Entry e = cnew(cc, Entry, +page, +key, +value, +ip, count := l(entries) + 1, +signer);
775  
          if (makeAllValuesPages) pageFromQ(cc, value);
776  
          page.change(); // bump modification date
777  
          entry = e;
778  
          newEntry = true;
779  
          entries.add(entry);
780  
          dbLog("New entry", page := page.q, globalID := e.globalID, count := e.count, +key, +value);
781  
        }
782  
      }
783  
    }
784  
785  
    sortByFieldInPlace created(entries);
786  
    numberEntriesInConceptField count(entries);
787  
    this;
788  
  }
789  
}
790  
791  
static SS availableSlices() {
792  
  ret mapToOrderedMap(s -> pair(str(s.globalID), s.name + " [ID: " + s.globalID + "]"), list(Slice));
793  
}
794  
795  
static L<Entry> entriesOnPage(Page p) {
796  
  ret p == null ? null : sortedByField count(findBackRefs(p, Entry));
797  
}
798  
799  
static L<Page> pagesSortedByLength(L<Page> l) {
800  
  ret sortedByCalculatedField(l, p -> l(p.q));
801  
}
802  
803  
sS hhtml2(S contents) {
804  
  ret hhtml(hAddToHead_fast(contents, 
805  
    hIncludeGoogleFont("Source Sans Pro")
806  
    + hmobilefix()
807  
    + hstylesheet("body { font-family: Source Sans Pro }")));
808  
}
809  
810  
sbool agiBlue_isOriginal() {
811  
  ret amProgram(#1023558);
812  
}
813  
814  
sS agiBlueURL() {
815  
  ret agiBlue_isOriginal() ? "https://agi.blue" :  "/" + psI(programID()) + "/raw";
816  
}
817  
818  
sS agiBlueName() {
819  
  ret agiBlue_isOriginal() ? "agi.blue" : "agi.blue clone " + programID();
820  
}
821  
822  
svoid cleanMeUp {
823  
  cleanUp(fan);
824  
}
825  
826  
static LoadedSlice loadSlice(Slice slice) {
827  
  ret loadSlice(slice.caseID, str(slice.globalID));
828  
}
829  
830  
static LoadedSlice loadSlice(S caseID, S globalID) {
831  
  caseID = unnull(caseID);
832  
  fan.get(caseID, globalID);
833  
  ret loadedSlices.get(caseID);
834  
}
835  
836  
static Slice sliceConceptForGlobalID(S globalID) {
837  
  ret sliceConceptForGlobalID(toGlobalIDObj(globalID));
838  
}
839  
840  
static Slice sliceConceptForGlobalID(GlobalID globalID) {
841  
  ret conceptWhere Slice(+globalID);
842  
}
843  
844  
static Slice sliceConceptForCaseID(S caseID) {
845  
  ret conceptWhere Slice(+caseID);
846  
}
847  
848  
sS agiBlueNameHTML() {
849  
  ret ahref(agiBlueURL(), htmlEncode2(agiBlueName()));
850  
}
851  
852  
static Slice createSlice(S name) {
853  
  Slice slice = cnew Slice(+name);
854  
  cset(slice, caseID := slice.defaultCaseID());
855  
  loadSlice(slice).initialSetup(slice.globalID);
856  
  ret slice;
857  
}
858  
859  
static Slice sliceForName(S name) {
860  
  ret conceptWhereCI Slice(+name);
861  
}
862  
863  
sclass Query extends WorksOnSlice {
864  
  SS vars = ciMap();
865  
    
866  
  *(LoadedSlice slice) { super(slice); }
867  
  
868  
  O process(S query) {
869  
    try object processLines(agiBlue_parseQueryScript(query));
870  
    ret serveJSON("No return statement");
871  
  }
872  
    
873  
  O processLines(L<ALQLLine> lines) {
874  
    for (ALQLLine line : lines) {
875  
      if (line cast ALQLSlice) {
876  
        Slice slice = sliceForName(line.slice);
877  
        if (slice == null) ret serveJSON("Slice not found: " + line.slice);
878  
        LoadedSlice oldSlice = Query.this.slice;
879  
        try {
880  
          setSlice(loadSlice(slice));
881  
          try object processLines(line.contents);
882  
        } finally {
883  
          setSlice(oldSlice);
884  
        }
885  
      } else if (line cast ALQLReturn)
886  
        ret serveJSON(ll(getOrKeep(vars, line.var)));
887  
      else if (line cast ALQLTriple) {
888  
        T3S t = line.triple;
889  
        t = tripleMap(t, s -> getOrKeep(vars, s));
890  
        Cl<Page> pages;
891  
        S var;
892  
        if (isDollarVar(t.c)) {
893  
          var = t.c;
894  
          if (isDollarVar(t.a)) {
895  
            if (isDollarVar(t.b)) todo(t);
896  
            Entry e = random(conceptsWhereCI(cc, Entry, key := t.b));
897  
            if (e == null) ret serveJSON("No results for " + var);
898  
            pages = pagesForKeyAndValue(t.b, t.c);
899  
            vars.put(t.a, e.page->q);
900  
            vars.put(t.c, e.value);
901  
            continue;
902  
          } else if (isDollarVar(t.b)) {
903  
            Page page = findPageFromQ(t.a);
904  
            Entry e = random(findBackRefs(page, Entry));
905  
            if (e == null) ret serveJSON("No results for " + var);
906  
            vars.put(t.b, e.key);
907  
            vars.put(t.c, e.value);
908  
            continue;
909  
          } else {
910  
            S val = getValue(t.a, t.b);
911  
            if (val == null) ret serveJSON("No results for " + var);
912  
            pages = ll(pageFromQ(val));
913  
          }
914  
        } else if (isDollarVar(t.b)) {
915  
          var = t.b;
916  
          if (isDollarVar(t.c)) todo(t);
917  
          if (isDollarVar(t.a)) {
918  
            L<Entry> entries = conceptsWhereCI(cc, Entry, value := t.c);
919  
            if (empty(entries)) ret serveJSON("No results for " + var);
920  
            Entry e = random(entries);
921  
            vars.put(t.a, e.page->q);
922  
            vars.put(t.b, e.key);
923  
            continue;
924  
          } else {
925  
            Cl<S> keys = keysForPageAndValue(t.a, t.c);
926  
            if (empty(keys)) ret serveJSON("No results for " + var);
927  
            pages = map(f<S, Page> pageFromQ, keys);
928  
          }
929  
        } else {
930  
          var = t.a;
931  
          if (!isDollarVar(t.a)) todo(t);
932  
          if (isDollarVar(t.b)) todo(t);
933  
          if (isDollarVar(t.c)) todo(t);
934  
          pages = pagesForKeyAndValue(t.b, t.c);
935  
        }
936  
        if (empty(pages)) ret serveJSON("No results for " + var);
937  
        vars.put(var, random(pages).q);
938  
      } else if (line cast ALQLPage) {
939  
        if (eq(line.matchMethod, 'eqic))
940  
          findPageFromQ(line.page);
941  
        else if (eq(line.matchMethod, 'flexMatchDollarVarsIC_first)) {
942  
          new L<SS> l;
943  
          for (Page p : list(cc, Page))
944  
            addIfNotNull(l, flexMatchDollarVarsIC_first(line.page, p.q));
945  
          if (empty(l)) ret serveJSON("No results for " + curly(line.page));
946  
          vars.putAll(random(l));
947  
        } else
948  
          fail("Unknown match method: " + line.matchMethod);
949  
      } else
950  
        fail("Can't interpret: " + line);
951  
    }
952  
    
953  
    null;
954  
  }
955  
}
956  
957  
static Page pageFromQ(Concepts cc, S q) {
958  
  ret empty(q) ? null : uniqCI_sync(cc, Page, +q);
959  
}
960  
961  
/*sS globalIDFromCaseID(S caseID) {
962  
  ret assertGlobalID(takeLast(globalIDLength(), caseID));
963  
}*/
964  
965  
sS agiBlue_pageURLWithSlice(Page p) {
966  
  ret p == null ? null : agiBlueURL() + hquery(slice := _get(sliceForPage(p), 'globalID), q := p.q);
967  
}
968  
969  
static Slice sliceForPage(Page p) {
970  
  if (p == null) null;
971  
  SliceInfo info = conceptWhere(p._concepts, SliceInfo);
972  
  ret info == null ? null : sliceConceptForGlobalID(info.globalID);
973  
}

Author comment

Began life as a copy of #1023558

download  show line numbers  debug dex  old transpilations   

Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1024329
Snippet name: agi.blue source [backup before Google log-in]
Eternal ID of this version: #1024329/1
Text MD5: 4454b9adbe73090e57430517446da23c
Transpilation MD5: 18c8d075d4384d208057e42c3e793d75
Author: stefan
Category: javax / html
Type: JavaX module (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-08-02 17:19:24
Source code size: 34278 bytes / 973 lines
Pitched / IR pitched: No / No
Views / Downloads: 140 / 508
Referenced in: [show references]