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

873
LINES

< > BotCompany Repo | #1024288 // agi.blue source [adding slices, dev.]

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

Download Jar. Libraryless. Click here for Pure Java version (17406L/126K).

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

Author comment

Began life as a copy of #1024284

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: #1024288
Snippet name: agi.blue source [adding slices, dev.]
Eternal ID of this version: #1024288/60
Text MD5: 94034d028c6e722e81b59c7de83e40f5
Transpilation MD5: e9bf0b87da09144e9d08544afe548ed5
Author: stefan
Category: javax / agi.blue
Type: JavaX module (desktop)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-07-30 14:07:14
Source code size: 31456 bytes / 873 lines
Pitched / IR pitched: No / No
Views / Downloads: 304 / 1859
Version history: 59 change(s)
Referenced in: [show references]