!7 concept AIList { S name; S text; bool botFlag, mighty; long modified; S status; } p { dbIndexing(AIList, 'name); } html { if (!webAuthed(params)) ret redirectToWebAuth(); new Matches m; // API if (eq(uri, "/list-count")) ret serveText(countConcepts(AIList)); if (startsWith(uri, "/list-text/", m)) { bool opt = eq("1", params.get("opt")); S name = urldecode(m.get(0)); AIList l = conceptWhere(AIList, +name); cset(l, mighty := true); if (l == null) if (opt) ret serveText(jsonEncode(litmap("Error", ll("List not found", name)))); else ret serveText(jsonEncode(litmap("Text" := ""))); else ret serveText(jsonEncode(litmap("Name" := l.name, "Text" := l.text))); } if (eq(uri, "/list-names")) ret serveText(jsonEncode(sortedIC(collect(list(AIList), 'name)))); // Edit API if (eq(uri, "/bot-list-edit")) { S name = params.get("name"), text = params.get("text"); AIList l = getList(name); if (eq(l.text, text)) ret "No change"; cset(l, +text, botFlag := true, modified := now()); ret "Changed"; } // Edit/Append API if (eq(uri, "/bot-list-append")) { S name = params.get("name"), text = params.get("text"); AIList l = getList(name); cset(l, text := appendNewLineIfNempty(l.text) + text, botFlag := true, modified := now()); ret "Changed"; } // Show list if (startsWith(uri, "/list/", m)) { AIList l = conceptWhere(AIList, name := urldecode(m.get(0))); if (l == null) ret "List not found"; // Update text S text = params.get("human"); S status = params.get("status"); if (text != null) { printStruct(ll(+text, +status)); if (neqAny(text, l.text, status, unnull(l.status))) { logStructure("versions.log", litmap(id := l.id, +text, +status, date := now())); if (status != null) cset(l, +status); cset(l, +text, botFlag := false, modified := now()); printStruct("l.text=" + sfu(text)); } ret hrefresh(0, rawLink(uri) + "?random=" + randomID()); } ret nav() + htitle_h2(htmlencode(l.name)) + hpostform( "Status: " + htextinput('status, value := l.status) + h3("Contents (" + (l.botFlag ? "bot" : "human") + "-edited)") + p(hsubmit("Save")) + htextarea(l.text, name := 'human, cols := 80, rows := 30)); } // New list S newList = params.get('newList); if (nempty(newList)) { AIList l = getList(newList); ret hrefresh(rawLink("/list/" + urlencode(l.name))); } // Search results S q = params.get('q); if (nempty(q)) ret hmobilefix() + htitle_h3("mech.tinybrain.de: Search results for " + htmlencode(singleQuote(q))) + listLists(scoredSearch_AIList(q, list(AIList))); // Overview bool alphabetical = eq(uri, "/alphabetical"); L list = alphabetical ? sortedByFieldIC('name, list(AIList)) : sortedByFieldDesc('modified, list(AIList)); ret hmobilefix() + htitle_h3("mech.tinybrain.de [" + n2(l(list), "list") + "]") + htableRaw_singleRow(ll( hform("Search: " + htextinput('q, style := "width: 200px") + " " + hsubmit("Search")), hpostform("| New list: " + htextinput('newList) + " " + hsubmit("Create list")))) + hBoolSelector(alphabetical, "/", "By date", "/alphabetical", "Alphabetical") + listLists(list); } sS nav() { ret hmobilefix() + p(ahref(rawLink(), "<< back")); } static AIList getList(S name) { AIList l = uniq(AIList, +name); if (l.modified == 0) cset(l, modified := now()); ret l; } sS listLists(L list) { ret ul(map(list, func(AIList l) -> S { int n = countLines(l.text); new L status; if (l.mighty) status.add("USED"); addIfNempty(status, htmlencode(l.status)); if (n != 0) status.add(n2(n, "line")); ret ahref(rawLink("/list/" + urlencode(l.name)), htmlencode(l.name)) + appendBracketed(joinWithComma(status)); })); } static L scoredSearch_AIList(S query, Iterable data) { new Map scores; L prepared = scoredSearch_prepare(query); for (AIList l : data) putUnlessZero(scores, l, 3*scoredSearch_score(l.name, prepared) + 2*scoredSearch_score(l.status, prepared) + scoredSearch_score(l.text, prepared)); ret keysSortedByValuesDesc(scores); }