User:OrphicBot/orphicbot.py
Jump to navigation
Jump to search
import datetime, threading, time, json, requests, sqlite3, itertools, math, random
import socketIO_client
from utils import flatten, flattenAll, selectT, select, hashListByKey, a2u, u2a, nfc, number, txtToFile, is_number, urlencode, progress, dictToList, histogram
from getpass import getpass
from threading import Lock
from matplotlib import pyplot as plt
# utils
def slices(N, xs) : return [xs[N*x:N*(1+x)] for x in list(range(0,math.ceil(len(xs)/N)))]
kill = False
def fmt(fs, xss, threads = 32, fKill = lambda : kill, rvs = None, fNItems = lambda x : 1, silent = False) :
tf, tLock, rvs = [0, 0, 0, [0 for x in range(0,threads)], False], threading.Lock(), [None for x in fs] if rvs == None else rvs
def fT(n,f,tf) :
tLock.acquire()
tf[0], tf[1], tf[2], = tf[0]+n, tf[1]+f, tf[2] - 1
tLock.release()
def fi(n, i_fxs, fT, tfk) :
for i_fx in i_fxs :
if not fKill() and not tf[4]:
i,f,x = i_fx[0], i_fx[1][0], i_fx[1][1]
try :
rvs[i] = f(*x) if rvs[i] == None else rvs[i]
except :
print("Error at i={}; quitting.".format(i))
tf[4] = True
tf[3][n] = tf[3][n] + fNItems(x)
if not silent:
progress(sum(tf[3]))
if fKill() or tf[4] : print("Thread {}: killed".format(n))
fT(len(i_fxs), 1, tfk)
ts = [threading.Thread(target = fi, args=(n, i_fx, fT, tf)) for (n,i_fx) in number(slices(int(1+len(fs)/threads), number(list(zip(fs,xss)))))]
try :
for t in ts:
while tf[2] >= threads and not fKill() and not tf[4]:
time.sleep(.01)
tf[2] = tf[2]+ 1
t.start()
while tf[1] < len(ts) and not fKill() and not tf[4]:
time.sleep(.01)
except :
tf[4] = True
print("fmt killed.")
return rvs
def plot(xs, lang='en') :
plt.plot(xs)
plt.ylabel('xs')
plt.show()
# mw api : login, save, load, loadtitles
db = sqlite3.connect("enwiktionary.db")
def strToDt(ts) : return datetime.datetime.strptime(ts, "%Y-%m-%dT%H:%M:%SZ")
def dtToStr(dt, sep=False) : return dt.strftime("%Y%m%d%H%M%S" if sep == False else "%Y-%m-%dT%H:%M:%SZ")
def login(u, p) :
url, params3 = 'https://en.wiktionary.org/w/api.php', '?format=json&action=query&meta=tokens&continue='
login_token, cookies = [(x.json()['query']['tokens']['logintoken'], x.cookies.copy()) for x in [requests.post(url, data={'action': 'query', 'format': 'json', 'utf8': '', 'meta': 'tokens', 'type': 'login'})]][0]
cookies = requests.post(url, data={'action': 'login', 'format': 'json', 'utf8': '', 'lgname': u, 'lgpassword': p, 'lgtoken': login_token}, cookies=cookies).cookies.copy()
edit_token = requests.get(url + params3, cookies=cookies).json()['query']['tokens']['csrftoken']
return [cookies, edit_token, u, p]
def http(CT, params, postdata = [], token = True) :
uri = 'https://en.wiktionary.org/w/api.php' + params
rv = requests.get(uri, cookies = CT[0]) if postdata == [] else requests.post(uri, dict(postdata, **({'token':CT[1]} if token else {})), cookies = CT[0])
js = json.loads(rv.text)
if 'error' in js and 'code' in js['error'] and (js['error']['code'] == 'notoken' or js['error']['code'] == 'badtoken'):
raise Exception("Difficultyf logging in.")
if len(CT) < 4 : raise Exception("No login tokens or credentials.")
CT = [*login(*(CT[2:4]))] + CT[2:]
print("{}: Logged in as {}.".format(datetime.datetime.utcnow(), CT[2]))
return http(CT, params, postdata)
return rv.text
def loadMW5c(CT, ws, cols = ['content', 'timestamp'], qtype = 'titles', revisions = 1) :
fC = lambda c : '*' if c == 'content' else c
ocols = flattenAll([c if c != 'ids' else ['pageid', 'revid', 'parentid'] for c in cols])
post = [('action','query'),('prop','revisions'),('rvprop','|'.join([c for c in cols if c != 'title'])),('format','json'),(qtype,'|'.join(ws))] + ([('rvlimit',str(revisions))] if revisions != 1 else [])
tbl = json.loads(http(CT, "", post, token = False))
norm = dict([(x['from'], x['to']) for x in (tbl['query']['normalized'] if 'normalized' in tbl['query'] else {})])
iw = dict([(str(1/(2+x[0])), x[1]) for x in number(tbl['query']['interwiki'] if 'interwiki' in tbl['query'] else [])])
odict = dict([(w,n) for (n,w) in number(ws)])
fCRX = lambda c,r,p : r[fC(c)] if 'revisions' in p[1] and fC(c) in r else (p[1]['title'] if (c == 'title' and 'title' in p[1]) else p[0] if c == 'pageid' and (float(p[0]) <= 0 or float(p[0]) >=1) else p[0] if float(p[0]) >= 0 else '')
wToRow = hashListByKey(flatten([[((p[1]['title'] if qtype == 'titles' else str(p[0]) if qtype == 'pageids' else str(r['revid']),) + tuple(fCRX(c,r,p) for c in ocols)) for r in (p[1]['revisions'] if 'revisions' in p[1] else ['']) ] for p in dictToList(dict(tbl['query']['pages'] if 'pages' in tbl['query'] else {}, **iw))]), lambda x: x[0], lambda x: x[1:])
rv = flatten([(wToRow[norm[w] if w in norm else w] if (norm[w] if w in norm else w) in wToRow else [tuple('' for x in ocols)]) for w in ws])
return rv
def loadMW(CT, ws, cols = ['content', 'timestamp'], qtype = 'titles', revisions = 1, threads = 32, silent = False, startcount = 0, rvs = None) : return flatten([x for x in fmt([lambda x: loadMW5c(CT, x, cols = cols, qtype = qtype, revisions = revisions) for y in range(0,math.ceil(len(ws)/500))], [(s,) for s in slices(500, ws)], fNItems = lambda x : len(x[0])+startcount, silent = silent, threads = threads, rvs = rvs) if x != None])
def load(w, qtype = 'titles') : return loadMW5c(('',''), [w], qtype = qtype)[0][0]
def loadDB(x) :
return '\n'.join([l[0] for l in selectT(db, "select line from enwiktionary where name == ?", (x,))])
def saveMW(CT,w,x,c, sts = None, bts = None) : # add md5
w, sts, bts = (w[0], w[1], w[1]) if type(w) is tuple else (w, sts, bts)
return http(CT, "", [('action','edit'),('assert','user'),('format','json'),('utf8',''),('text',x),('summary',c),('title',w),('token',CT[1]),('bot',''),('minor',''),('starttimestamp',sts),('basetimestamp',bts)])
def moveMW(CT, s, d, c, redirect = False) :
url = 'https://en.wiktionary.org/w/api.php'
return json.loads(http(CT, "", [('action','move'), ('from',s), ('to',d), ('reason',c), ('format','json'), ('movetalk', ''), ('token',CT[1]), ('noredirect','')][0:(7 if redirect else 8)]))
def loadRevisions(db, CT, ws) :
cols = ['title', 'timestamp', 'user', 'comment', 'size', 'ids']
revs = loadMW(CT, ws, cols = cols, threads = 8)
total, n, rids = 0 , 0, []
while all(int(r) != 0 for r in rids) and len(revs) > 0 :
rids, ws, fO, total = [str(r[-1]) for r in revs if r[-1] != 0 and r[-1] != ''], [x[0] for x in revs if x[-1] != 0 and x[-1] != ''], print if len(revs) > 10000 else progress, total + len(revs)
db.executemany("insert or ignore into revisions values (?,?,?,?,?,?,?)", [x for x in revs if x[-2] != ''])
fO("{}: {} ({})".format(n,total, len(rids)))
revs, n = loadMW(CT, rids, cols = cols, qtype = 'revids', order = ws, silent = True if fO == progress else False), n + 1
return total
def loadTitles(CT = None, begin = '!', end = None) :
# can run parallel adjacent requests using hardcoded spacing data
words = []
while begin and (end == None or begin < end):
omnia = json.loads(http(CT, '?action=query&list=allpages&apfrom={}{}&aplimit={}&format=json&apfilterredir=nonredirects'.format(urlencode(begin), ("&apto="+end) if end != None else "", 500 if CT == None else 5000), []))
begin = omnia['continue']['apcontinue'] if 'continue' in omnia else None
words.extend(q['title'] for q in omnia['query']['allpages'])
progress(len(words))
return words
def addRevsToDb1(revs) :
db.executemany("insert or ignore into revisions values (?,?,?,?,?,?,?)", revs)
db.commit()
db.close()
return revs[0]
# mw rc client
def fOnChange(self, change) :
cols = ['title', 'timestamp', 'user', 'comment', 'title', 'length', 'id', 'revision']
info = flattenAll([[("{}-old".format(c), str(change[c]['old'])), ("{}-new".format(c), str(change[c]['new']))] if (c in change and (c == 'length' or c == 'revision')) else (c,str(change[c])) if c in change else (c,'') for c in cols])
print(("change: ({} items)\n".format(len(info)) + '\n'.join([" {}: {}".format(c,i.encode('utf-8')) for (c,i) in info])))
class WikiNamespace(socketIO_client.BaseNamespace):
def on_change(self, change):
fOnChange(change)
def on_connect(self):
self.emit('subscribe', 'en.wiktionary.org')
print('CONNECTED')
def subscribeRC(seconds) :
socketIO = socketIO_client.SocketIO('stream.wikimedia.org', 80)
socketIO.define(WikiNamespace, '/rc')
socketIO.wait(seconds)
myrunnings = [True]
def doongxi1(db, CT, lex, lix, f, runnings, fS = lambda a,b,c : saveMW(CT, a, b, c)) :
dlang = dict([(x,y) for (x,[y]) in hashListByKey(lix, lambda x: x[0], lambda x: x[2]).items()])
talkpage = load('User talk:OrphicBot')
mylangs = unique([x[1] for x in dlang.items()])
langs = dict([(x,unique(y)) for (x,y) in hashListByKey([(w,dlang[l]) for l in lex for w in lex[l]] , lambda x: x[0], lambda x: x[1]).items()])
def fOnChangeL(self, change) :
if runnings[0] == False :
raise Exception("No longer running; quitting.")
user = "" if not 'user' in change else change['user']
ulangs, title = ([], None) if not 'title' in change or not change['title'].lower() in langs else (langs[change['title'].lower()], change['title'])
if len(ulangs) > 0 :
print('Checking {} for new {} section{}{}'.format(title, ', '.join(ulangs), 's' if len(ulangs) > 1 else '', '' if user == '' else ' (user: {})'.format(user) ).encode('utf-8') )
currOld = [x[0] for x in loadMW(CT, [title], cols = ['content'], qtype = 'titles', revisions = 2)]
if any(x in currOld[0] and (len(currOld)==1 or not x in currOld[1]) for x in [ "=={}==\n".format(l) for l in ulangs]):
if talkpage != load('User talk:OrphicBot') :
print("User talk page changed. No action taken on {}. Quitting.".format(title))
runnings[0] = False
raise Exception("User talk page changed.")
result = runAllM(CT, [title], f, fSave = fS)
for (w,n,e0,e1,c,err,rv) in result :
if c != None and not 'No change.' in c :
print("Processed at {}: {}!".format(datetime.datetime.utcnow(), w).encode('utf-8'))
WikiNamespace.on_change = fOnChangeL
def curre () :
while runnings[0] :
subscribeRC(3600)
#except : print('Error subscribing; trying again.')
print('OrphicBot daemon process elapsed.')
threading.Thread(target = curre).start()
#agenda :
# 1. factor server from function
# 2. exclude accented Old Norse words (correct lex as argument)
# doongxi1(db, CT, lex, lix, addRefs, myrunnings, mySave)
# myrunnings = [True]
# doongxi1(db, CT, lex, lix, addRefs, myrunnings)
def recentChanges(CT, start, end) :
cols = ['title', 'user', 'ids', 'timestamp']
payload = {'action': 'query', 'format': 'json', 'utf8': '', 'list' : 'recentchanges', 'rcprop' : '|'.join(cols), 'rclimit' : '500', 'rcstart' : dtToStr(start), 'rcend' : dtToStr(end)}
n, rc, rccontinue = 0, [], ""
while n >= 0 :
result = json.loads(requests.post('https://en.wiktionary.org/w/api.php', data=payload).text)
rc.extend(result['query']['recentchanges'])
if 'continue' in result :
progress("{}: {} ({})".format(n, len(rc), result['continue']['rccontinue']) )
payload['rccontinue'] = result['continue']['rccontinue']
n = n + 1
else : n = -1
return rc
def updateRC(db, quiet = False) :
ts = select(db, "select max(timestamp) from recentchanges")[0][0]
cs = recentChanges(CT, datetime.datetime.utcnow(), strToDt(ts))
rs = [(c['rcid'], c['revid'], c['title'], c['user'] if 'user' in c else None, c['timestamp'], False) for c in cs if 'user' in c]
db.executemany("insert or ignore into recentchanges values (?,?,?,?,?,?)", rs)
db.commit()
if not quiet: print("{} items".format(len(rs)))
return rs
def update(db, wset) :
titles = [x[0] for x in select(db, 'select distinct(title) from recentchanges where user != "OrphicBot"') if not x[0] in wset]
def dongxi() :
test = updateRC(db)
histo = histogram([x[3] for x in test])
print(str(sorted([(x,histo[x]) for x in histo], key = lambda x: x[1])).encode('utf-8'))
ts = strToDt(test[0][4]) - strToDt(test[-1][4])
print(ts)
cheese = sorted(dictToList(histo), key = lambda x: x[1])[-1][1]
print(cheese/max(1,ts.seconds))
re
# parsing/editing
def foliage(x,t) :
def fH(x) : return x.strip('=')
def p1(x) :
def ffq(w,bl,br,n) : return lambda x : (x[0:len(w)+n] == n*bl + w) and x[-n:] == n*br
fCat, fC, fCLN, fDS = ffq("Category:","[","]",2), ffq("C|","{","}",2), ffq("catlangname|", "{", "}", 2), ffq("DEFAULTSORT:", "{", "}", 2)
return (x == '----') or fCat(x) or fDS(x) or fCLN(x) or fC(x) or (':' in x and "{}]]".format(t)==x.split(':')[1]) or x in ps
def fD(x) : return [l if d%2 == 0 and len(x) > d+1 and x[:l] == x[-l:] else (1 if p1(x) else 0) for (d,l) in [(d,int(d/2)) for d in [len(x) - len(fH(x))]]][0]
def fA(g,n) : return fH(xs[[l[1] for l in ls if l[0] == g and l[1] < n][-1]])
xs, ps = x.split('\n'), set([y for y in x[0:str.index(x, '==')].split('\n') if len(y) > 0])
hs, ls = [fD(x) for x in xs], []
[ls.extend([(hs[i],i)]) if hs[i] != 0 else ls.extend([]) for i in range(0,len(hs))]
return [(b[0], b[1], p[1]-b[1], fH(xs[b[1]] if b[1] < len(xs) else ""), [fA(l,b[1]) for l in range(2,b[0])], xs[b[1]:p[1]] ) for (b,p) in zip(ls, ls[1:]+[(0,len(hs))])]
def wiki(f) : return '\n'.join(flatten([x[-1] for x in f])).strip('\n')
def fParsingError(txt,t) :
try : Q = foliage(txt, t)
except : return "Invalid layout: hierarchy."
if any([x > 0 and Q[x][0] != 1 and (Q[x-1][0]==1 and Q[x-1][-1][0] != '----') and (Q[x+1][0]==1 and Q[x+1][-1][0] != '----') for x in range(0,len(Q)-1)]) :
return "Invalid layout: category or interwiki."
return None
def mapSections(ls, t, L, bs, fB) :
#if len(bs) == 0 : print("error ({}): no blocks")
lsNew, c = ls, ""
for e in bs:
lsNew = foliage(wiki([l if l[1] != e[1] else (0,0,0,0,0, fB(lsNew, e[-1])) for l in ls]), t)
return foliage(wiki(lsNew),t)
ffSort = lambda k : lambda LS,B : sorted(B, key = k)
fAll, fFirst, fLast, fOnly = lambda xs : xs, lambda xs : [xs[0]] if len(xs) > 0 else xs, lambda xs : [xs[-1]] if len(xs) > 0 else xs, lambda xs : xs if len(xs) == 1 else []
L, G = 'Latin', 'Ancient Greek'
E, R, lR, lEL, P = ('External links', fAll), ('References', fAll), ('References', fLast), ('External links', fLast), ('Pronunciation', fAll)
def fWithRefs(ls,t,Ls) :
def fWithRefI(ls, t, L) :
ixL = [l for l in ls if len(l[4])>0 and l[4][0] == L][-1][1] # no error checking #[x[1] for x in ls if x[0] > 1][-1]
lsN = foliage(wiki([ls[n] if ls[n][1] != ixL else (ls[n][0], ls[n][1], ls[n][2], ls[n][3], ls[n][4], ls[n][5] + [('\n' if ls[n][5][-1] != '' else '') + '===References===', ''] ) for n in range(0,len(ls))]), t)
return ls if 'References' in [l[-1][0].strip('=') for l in ls if L in l[4]] else foliage(wiki(lsN), t)
ls0 = ls
for L in Ls: ls0 = fWithRefI(ls0, t, L)
return ls0
def fWithELs(ls,t,Ls) :
if any(l[3]=='References' for l in ls) :
raise Exception('Will not create External links with extant References section.')
def fWithELsI(ls, t, L) :
ixL = [l for l in ls if len(l[4])>0 and l[4][0] == L][-1][1] # no error checking #[x[1] for x in ls if x[0] > 1][-1]
lsN = foliage(wiki([ls[n] if ls[n][1] != ixL else (ls[n][0], ls[n][1], ls[n][2], ls[n][3], ls[n][4], ls[n][5] + [('\n' if ls[n][5][-1] != '' else '') + '===External links===', ''] ) for n in range(0,len(ls))]), t)
return ls if 'External links' in [l[-1][0].strip('=') for l in ls if L in l[4]] else foliage(wiki(lsN), t)
ls0 = ls
for L in Ls: ls0 = fWithRefI(ls0, t, L)
return ls0
def blocks(ls, L, f_ss) :
def sections(ls,L,n) : return [l for l in ls if l[3] == n and l[4][0] == L]
return [e for (s,f) in f_ss for e in f(sections(ls,L,s))]
def run(t, Ls, ls, ss_n_fs) :
summaryG, summaryL, c = "", "", ""
for L in Ls :
for (ss,(n,f)) in ss_n_fs :
old = wiki(ls)
bs = blocks(ls, L, ss)
ls = mapSections(ls, t, L, bs, f) if len(bs) > 0 else ls
summaryL += "" if wiki(ls) == old else n + " "
summaryG, summaryL = summaryG if summaryL == "" else "{} {}: {}".format(summaryG, L, summaryL), ""
return (wiki(ls), "No change." if summaryG == "" else summaryG.strip())
# running
def diff(e0, e1) :
ls0, ls1 = e0.split('\n'), e1.split('\n')
S,E = ([x for x in range(0,min(len(ls0), len(ls1))) if ls0[x] != ls1[x]]+[len(ls0)])[0], -([x for x in range(0,min(len(ls0), len(ls1))) if ls0[-x-1] != ls1[-x-1]]+[len(ls1)])[0]
S = ([0]+[x for x in range(0,min(len(ls0),S)) if len(ls0[x])>1 and ls0[x][0:2]=='=='])[-1]
E0,E1 = ([x for x in range(E,0) if len(ls0[x])>1 and ls0[x][0:2]=='==' or ls0[x][0:4]=='----']+[len(ls0)])[0]+1, ([x for x in range(E,0) if len(ls1[x])>1 and ls1[x][0:2]=='==' or ls1[x][0:4]=='----']+[len(ls1)])[0]+1
return ('\n'.join(ls0[S:E0]), '\n'.join(ls1[S:E1]))
def makeDiffs(rvs) :
return '<sou'+'rce>\n\n\n\n' + '\n\n\n\n\n'.join(["{}: {}\n\n{}".format(x[0], x[4] if x[4] != None else x[5], appose(*diff(x[2], x[3] if x[3] != None else x[2]))) for x in rvs]) + '\n\n\n\n</sou'+'rce>'
def tryExcept(f, g) :
try : val = (f(), "")
except : val = g()
return val
# runOne2 : word, index, wiktionaryUserTalkPage, wiktionaryUserTalkPageOld, fRewrite, fSave ->
def runOne2(w,n,rw,fS,fL,fPS1s) :
while random.random() < fPS1s() :
time.sleep(1)
w,e0,t,err = (w[0],w[1],w[2],"") if type(w) is tuple else fL(w) + ("",)
F, err = (None, err) if err != "" else tryExcept(lambda : foliage(e0,w), lambda : (None, "E1: Cannot parse"))
err = err if err != "" else "" if e0 == wiki(F) else "E2: Round trip failed: original entry" # e1, c:
_, err = reversed((err, None) if err != "" else tryExcept(lambda : "E3: Errant space in references." if '===References===' in e0 and any([y.strip()=='' for y in [x[5] for x in F if x[3] == 'References'][0][:-1]] ) else "", lambda : (None,"E4: Test for malformed References section failed")))
((e1, c), err) = ((None, None), err) if err != "" else tryExcept(lambda : rw(F,w), lambda : ((None, None), "E5: Rewrite failed"))
err = err if err != "" else "E6: Round trip failed: rewritten entry" if e1 != wiki(foliage(e1,w)) else ""
err = err if err != "" else "E7: Idempotency test failed." if e1 != wiki(foliage(wiki(foliage(e1,w)),w)) else ""
rv, err = (None,err) if err != "" else tryExcept(lambda : "" if e0 == e1 else fS((w,t) if type(w) is tuple else w,e1,c), lambda : (None, "E8: Error saving."))
return w, n, e0, e1, c, err, rv
# return (logFull, logDiff, logSummary, logErr)
def persistLogs(logErr, logFull, logDiff, logSummary, wU, rwName, tS, fSave) :
for (l,n,c) in [(logErr, "Err", 1), (logFull, "Full,", 4), (logDiff, "Diff", 4), (logSummary, "Summary", 1)] :
l = sorted(l, key = lambda x: int(x.strip().split('.')[0].split('/')[0]))
ws = sorted([x.strip().split(':')[0].split('.')[1].strip() for x in l if not '(no change)' in x and len(x) > 0 and x.strip()[0].isdigit() and '.' in x and ':' in x])
print(n, len(ws))
if len(ws)>0 :
wS, wE = ws[0], ws[-1]
if(len(l)) > 0 :
path = '{}/EditLogs/{}/{}/{}_items_{}_to_{}_{}.txt'.format(wU,tS.strftime("%d%B%Y %Hh%Mm%Ss"),rwName, len(l), wS, wE, n)
if n == "Diff" or n == "Summary" or n == "Err":
fSave(path, "<sou"+"rce>\n\n"+('\n'*c).join(l)+"\n\n</sou"+"rce>", "{} {}".format(rwName, n))
def fLag(CT) : return float(json.loads(requests.get('https://en.wiktionary.org/w/api.php?action=query&titles=MediaWiki&format=json&maxlag=-1', cookies = CT[0]).text)['error']['info'].split(' ')[3])
running = True
def fUserPageChanged(CT, fKill, fL = lambda x : None) :
pages = ['User talk:OrphicBot', 'User:OrphicBot/Sandbox/test']
old = [loadMW(CT, [p])[0][0] for p in pages]
new = [o for o in old]
same = True
while not fKill() and same:
new = [loadMW(CT, [p])[0][0] for p in pages]
time.sleep(1)
fL(fLag(CT))
json.loads(requests.get('https://en.wiktionary.org/w/api.php?action=query&titles=MediaWiki&format=json&maxlag=-1', cookies = CT[0]).text)
requests
same = all(a==b for (a,b) in zip(old,new))
if not same:
print("User talk page changed.")
return True
def fPDelay1s(lag, threads) : return 0 if lag <= 1 else 1 - 2**(-lag-math.log((1 if lag > 2 else (lag-1))*threads,2))
def runAllM(CT, ws, rw, fSave = lambda a,b,c: datetime.datetime.utcnow(), fLoad = loadMW, rwName = "update", rvs = None, threads = 32) :
running = [True, 0]
def fRunningSet(x,n) : running[n] = x
threading.Thread(target = lambda : fRunningSet(not fUserPageChanged(CT, lambda : not running[0], lambda x: fRunningSet(x,1)), 0)).start()
try : rvs = fmt([runOne2 for _ in ws], [((w,a,t), n, rw, fSave, lambda x : loadMW(CT,[x], cols=['title','content','timestamp'])[0], lambda : fPDelay1s(running[1], threads)) for (n,(w,a,t)) in number(list(fLoad(CT, ws, cols = ['title', 'content', 'timestamp'])))], rvs = rvs, fKill = lambda : not running[0], threads = threads )
except : print("runAllM killed.")
finally : running[0] = False
return rvs
# user page lexica information
def slice(xs) : return [xs[2**21*x:2**21*(1+x)] for x in list(range(0,math.ceil(len(xs)/2**21)))]
def splice(xs) : return ''.join(xs)
def loadParts(w) :
for n in itertools.count():
x = load('{}.part{}'.format(w,n+1))
if x != '' :
yield x
else : break
def txtToLexica(txt) : return hashListByKey([(w,d) for (w,d) in [l.split('\t') for l in txt.split('\n')]], lambda x: x[1], lambda x: nfc(x[0]))
def lexicaToTxt(L) : return '\n'.join(sorted(flatten([["{}\t{}".format(nfc(l),k) for l in L[k]] for k in L]), key = lambda x: '\t'.join(reversed(x.split('\t')))))
def lexicaToPage(CT,L,w) : return [saveMW(CT,"{}.part{}".format(w,n+1), x, ', '.join(L)) for (n,x) in number(slice(u2a(lexicaToTxt(L)))) ]
def pageToLexica(w) : return txtToLexica(a2u(splice([x for x in loadParts(w)])))
def tableToTxt(t) : return u2a('\n'.join(['\t'.join([str(x) for x in l]) for l in ixHws]))
def txtToTable(t) : return [[x if not is_number(x) else float(x) for x in l.split('\t')] for l in a2u(t).split('\n')]