Grammalecte  Hex Artifact Content

Artifact 729e0b7a88ab6d6a906d68cbef58035a16cf7b6c7a4171fb50bc3912c7169f8e:


0000: 20 23 21 2f 75 73 72 2f 62 69 6e 2f 65 6e 76 20   #!/usr/bin/env 
0010: 70 79 74 68 6f 6e 33 0a 0a 22 22 22 0a 47 72 61  python3..""".Gra
0020: 6d 6d 61 6c 65 63 74 65 20 73 65 72 76 65 72 3a  mmalecte server:
0030: 20 67 72 61 6d 6d 61 72 20 63 68 65 63 6b 65 72   grammar checker
0040: 0a 22 22 22 0a 0a 69 6d 70 6f 72 74 20 73 79 73  ."""..import sys
0050: 0a 69 6d 70 6f 72 74 20 61 72 67 70 61 72 73 65  .import argparse
0060: 0a 69 6d 70 6f 72 74 20 6a 73 6f 6e 0a 69 6d 70  .import json.imp
0070: 6f 72 74 20 74 72 61 63 65 62 61 63 6b 0a 69 6d  ort traceback.im
0080: 70 6f 72 74 20 74 69 6d 65 0a 69 6d 70 6f 72 74  port time.import
0090: 20 6f 73 0a 69 6d 70 6f 72 74 20 63 6f 6e 63 75   os.import concu
00a0: 72 72 65 6e 74 2e 66 75 74 75 72 65 73 0a 0a 66  rrent.futures..f
00b0: 72 6f 6d 20 67 72 61 6d 6d 61 6c 65 63 74 65 2e  rom grammalecte.
00c0: 62 6f 74 74 6c 65 20 69 6d 70 6f 72 74 20 42 6f  bottle import Bo
00d0: 74 74 6c 65 2c 20 72 75 6e 2c 20 72 65 71 75 65  ttle, run, reque
00e0: 73 74 2c 20 72 65 73 70 6f 6e 73 65 20 23 2c 20  st, response #, 
00f0: 74 65 6d 70 6c 61 74 65 2c 20 73 74 61 74 69 63  template, static
0100: 5f 66 69 6c 65 0a 0a 69 6d 70 6f 72 74 20 67 72  _file..import gr
0110: 61 6d 6d 61 6c 65 63 74 65 0a 69 6d 70 6f 72 74  ammalecte.import
0120: 20 67 72 61 6d 6d 61 6c 65 63 74 65 2e 74 65 78   grammalecte.tex
0130: 74 20 61 73 20 74 78 74 0a 66 72 6f 6d 20 67 72  t as txt.from gr
0140: 61 6d 6d 61 6c 65 63 74 65 2e 67 72 61 70 68 73  ammalecte.graphs
0150: 70 65 6c 6c 2e 65 63 68 6f 20 69 6d 70 6f 72 74  pell.echo import
0160: 20 65 63 68 6f 0a 0a 0a 23 23 23 23 20 47 52 41   echo...#### GRA
0170: 4d 4d 41 52 20 43 48 45 43 4b 45 52 20 23 23 23  MMAR CHECKER ###
0180: 23 0a 0a 6f 47 72 61 6d 6d 61 72 43 68 65 63 6b  #..oGrammarCheck
0190: 65 72 20 3d 20 67 72 61 6d 6d 61 6c 65 63 74 65  er = grammalecte
01a0: 2e 47 72 61 6d 6d 61 72 43 68 65 63 6b 65 72 28  .GrammarChecker(
01b0: 22 66 72 22 2c 20 22 53 65 72 76 65 72 22 29 0a  "fr", "Server").
01c0: 6f 53 70 65 6c 6c 43 68 65 63 6b 65 72 20 3d 20  oSpellChecker = 
01d0: 6f 47 72 61 6d 6d 61 72 43 68 65 63 6b 65 72 2e  oGrammarChecker.
01e0: 67 65 74 53 70 65 6c 6c 43 68 65 63 6b 65 72 28  getSpellChecker(
01f0: 29 0a 6f 54 65 78 74 46 6f 72 6d 61 74 74 65 72  ).oTextFormatter
0200: 20 3d 20 6f 47 72 61 6d 6d 61 72 43 68 65 63 6b   = oGrammarCheck
0210: 65 72 2e 67 65 74 54 65 78 74 46 6f 72 6d 61 74  er.getTextFormat
0220: 74 65 72 28 29 0a 6f 47 43 45 20 3d 20 6f 47 72  ter().oGCE = oGr
0230: 61 6d 6d 61 72 43 68 65 63 6b 65 72 2e 67 65 74  ammarChecker.get
0240: 47 43 45 6e 67 69 6e 65 28 29 0a 0a 0a 78 50 72  GCEngine()...xPr
0250: 6f 63 65 73 73 50 6f 6f 6c 45 78 65 63 75 74 6f  ocessPoolExecuto
0260: 72 20 3d 20 4e 6f 6e 65 0a 0a 0a 64 65 66 20 69  r = None...def i
0270: 6e 69 74 45 78 65 63 75 74 6f 72 20 28 6e 4d 75  nitExecutor (nMu
0280: 6c 74 69 43 50 55 3d 4e 6f 6e 65 29 3a 0a 20 20  ltiCPU=None):.  
0290: 20 20 22 70 72 6f 63 65 73 73 20 70 6f 6f 6c 20    "process pool 
02a0: 65 78 65 63 75 74 6f 72 20 69 6e 69 74 69 61 6c  executor initial
02b0: 69 73 61 74 69 6f 6e 22 0a 20 20 20 20 67 6c 6f  isation".    glo
02c0: 62 61 6c 20 78 50 72 6f 63 65 73 73 50 6f 6f 6c  bal xProcessPool
02d0: 45 78 65 63 75 74 6f 72 0a 20 20 20 20 6e 4d 61  Executor.    nMa
02e0: 78 43 50 55 20 3d 20 6d 61 78 28 6f 73 2e 63 70  xCPU = max(os.cp
02f0: 75 5f 63 6f 75 6e 74 28 29 2d 31 2c 20 31 29 0a  u_count()-1, 1).
0300: 20 20 20 20 69 66 20 6e 4d 75 6c 74 69 43 50 55      if nMultiCPU
0310: 20 69 73 20 4e 6f 6e 65 20 6f 72 20 6e 6f 74 20   is None or not 
0320: 28 31 20 3c 3d 20 6e 4d 75 6c 74 69 43 50 55 20  (1 <= nMultiCPU 
0330: 3c 3d 20 6e 4d 61 78 43 50 55 29 3a 0a 20 20 20  <= nMaxCPU):.   
0340: 20 20 20 20 20 6e 4d 75 6c 74 69 43 50 55 20 3d       nMultiCPU =
0350: 20 6e 4d 61 78 43 50 55 0a 20 20 20 20 70 72 69   nMaxCPU.    pri
0360: 6e 74 28 22 43 50 55 20 70 72 6f 63 65 73 73 65  nt("CPU processe
0370: 73 20 75 73 65 64 20 66 6f 72 20 77 6f 72 6b 65  s used for worke
0380: 72 73 3a 20 22 2c 20 6e 4d 75 6c 74 69 43 50 55  rs: ", nMultiCPU
0390: 29 0a 20 20 20 20 78 50 72 6f 63 65 73 73 50 6f  ).    xProcessPo
03a0: 6f 6c 45 78 65 63 75 74 6f 72 20 3d 20 63 6f 6e  olExecutor = con
03b0: 63 75 72 72 65 6e 74 2e 66 75 74 75 72 65 73 2e  current.futures.
03c0: 50 72 6f 63 65 73 73 50 6f 6f 6c 45 78 65 63 75  ProcessPoolExecu
03d0: 74 6f 72 28 6d 61 78 5f 77 6f 72 6b 65 72 73 3d  tor(max_workers=
03e0: 6e 4d 75 6c 74 69 43 50 55 29 0a 0a 0a 64 65 66  nMultiCPU)...def
03f0: 20 70 61 72 73 65 54 65 78 74 20 28 73 54 65 78   parseText (sTex
0400: 74 2c 20 64 4f 70 74 69 6f 6e 73 3d 4e 6f 6e 65  t, dOptions=None
0410: 2c 20 62 46 6f 72 6d 61 74 54 65 78 74 3d 46 61  , bFormatText=Fa
0420: 6c 73 65 2c 20 73 45 72 72 6f 72 3d 22 22 29 3a  lse, sError=""):
0430: 0a 20 20 20 20 22 70 61 72 73 65 20 3c 73 54 65  .    "parse <sTe
0440: 78 74 3e 20 61 6e 64 20 72 65 74 75 72 6e 20 65  xt> and return e
0450: 72 72 6f 72 73 20 69 6e 20 61 20 4a 53 4f 4e 20  rrors in a JSON 
0460: 66 6f 72 6d 61 74 22 0a 20 20 20 20 73 4a 53 4f  format".    sJSO
0470: 4e 20 3d 20 27 7b 20 22 70 72 6f 67 72 61 6d 22  N = '{ "program"
0480: 3a 20 22 67 72 61 6d 6d 61 6c 65 63 74 65 2d 66  : "grammalecte-f
0490: 72 22 2c 20 22 76 65 72 73 69 6f 6e 22 3a 20 22  r", "version": "
04a0: 27 2b 6f 47 43 45 2e 76 65 72 73 69 6f 6e 2b 27  '+oGCE.version+'
04b0: 22 2c 20 22 6c 61 6e 67 22 3a 20 22 27 2b 6f 47  ", "lang": "'+oG
04c0: 43 45 2e 6c 61 6e 67 2b 27 22 2c 20 22 65 72 72  CE.lang+'", "err
04d0: 6f 72 22 3a 20 22 27 2b 73 45 72 72 6f 72 2b 27  or": "'+sError+'
04e0: 22 2c 20 22 64 61 74 61 22 20 3a 20 5b 5c 6e 27  ", "data" : [\n'
04f0: 0a 20 20 20 20 73 44 61 74 61 4a 53 4f 4e 20 3d  .    sDataJSON =
0500: 20 22 22 0a 20 20 20 20 66 6f 72 20 69 2c 20 73   "".    for i, s
0510: 50 61 72 61 67 72 61 70 68 20 69 6e 20 65 6e 75  Paragraph in enu
0520: 6d 65 72 61 74 65 28 74 78 74 2e 67 65 74 50 61  merate(txt.getPa
0530: 72 61 67 72 61 70 68 28 73 54 65 78 74 29 2c 20  ragraph(sText), 
0540: 31 29 3a 0a 20 20 20 20 20 20 20 20 69 66 20 62  1):.        if b
0550: 46 6f 72 6d 61 74 54 65 78 74 3a 0a 20 20 20 20  FormatText:.    
0560: 20 20 20 20 20 20 20 20 73 50 61 72 61 67 72 61          sParagra
0570: 70 68 20 3d 20 6f 54 65 78 74 46 6f 72 6d 61 74  ph = oTextFormat
0580: 74 65 72 2e 66 6f 72 6d 61 74 54 65 78 74 28 73  ter.formatText(s
0590: 50 61 72 61 67 72 61 70 68 29 0a 20 20 20 20 20  Paragraph).     
05a0: 20 20 20 73 52 65 73 75 6c 74 20 3d 20 6f 47 72     sResult = oGr
05b0: 61 6d 6d 61 72 43 68 65 63 6b 65 72 2e 67 65 6e  ammarChecker.gen
05c0: 65 72 61 74 65 50 61 72 61 67 72 61 70 68 41 73  erateParagraphAs
05d0: 4a 53 4f 4e 28 69 2c 20 73 50 61 72 61 67 72 61  JSON(i, sParagra
05e0: 70 68 2c 20 64 4f 70 74 69 6f 6e 73 3d 64 4f 70  ph, dOptions=dOp
05f0: 74 69 6f 6e 73 2c 20 62 45 6d 70 74 79 49 66 4e  tions, bEmptyIfN
0600: 6f 45 72 72 6f 72 73 3d 54 72 75 65 2c 20 62 52  oErrors=True, bR
0610: 65 74 75 72 6e 54 65 78 74 3d 62 46 6f 72 6d 61  eturnText=bForma
0620: 74 54 65 78 74 29 0a 20 20 20 20 20 20 20 20 69  tText).        i
0630: 66 20 73 52 65 73 75 6c 74 3a 0a 20 20 20 20 20  f sResult:.     
0640: 20 20 20 20 20 20 20 69 66 20 73 44 61 74 61 4a         if sDataJ
0650: 53 4f 4e 3a 0a 20 20 20 20 20 20 20 20 20 20 20  SON:.           
0660: 20 20 20 20 20 73 44 61 74 61 4a 53 4f 4e 20 2b       sDataJSON +
0670: 3d 20 22 2c 5c 6e 22 0a 20 20 20 20 20 20 20 20  = ",\n".        
0680: 20 20 20 20 73 44 61 74 61 4a 53 4f 4e 20 2b 3d      sDataJSON +=
0690: 20 73 52 65 73 75 6c 74 0a 20 20 20 20 73 4a 53   sResult.    sJS
06a0: 4f 4e 20 2b 3d 20 73 44 61 74 61 4a 53 4f 4e 20  ON += sDataJSON 
06b0: 2b 20 22 5c 6e 5d 7d 5c 6e 22 0a 20 20 20 20 72  + "\n]}\n".    r
06c0: 65 74 75 72 6e 20 73 4a 53 4f 4e 0a 0a 0a 64 65  eturn sJSON...de
06d0: 66 20 73 75 67 67 65 73 74 20 28 73 54 6f 6b 65  f suggest (sToke
06e0: 6e 29 3a 0a 20 20 20 20 22 67 65 74 20 73 70 65  n):.    "get spe
06f0: 6c 6c 69 6e 67 20 73 75 67 67 65 73 74 69 6f 6e  lling suggestion
0700: 73 20 66 6f 72 20 3c 73 54 6f 6b 65 6e 3e 20 61  s for <sToken> a
0710: 6e 64 20 72 65 74 75 72 6e 20 74 68 65 6d 20 69  nd return them i
0720: 6e 20 61 20 4a 53 4f 4e 20 66 6f 72 6d 61 74 22  n a JSON format"
0730: 0a 20 20 20 20 69 66 20 73 54 6f 6b 65 6e 3a 0a  .    if sToken:.
0740: 20 20 20 20 20 20 20 20 6c 53 75 67 67 20 3d 20          lSugg = 
0750: 5b 5d 0a 20 20 20 20 20 20 20 20 74 72 79 3a 0a  [].        try:.
0760: 20 20 20 20 20 20 20 20 20 20 20 20 66 6f 72 20              for 
0770: 6c 20 69 6e 20 6f 53 70 65 6c 6c 43 68 65 63 6b  l in oSpellCheck
0780: 65 72 2e 73 75 67 67 65 73 74 28 73 54 6f 6b 65  er.suggest(sToke
0790: 6e 29 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20  n):.            
07a0: 20 20 20 20 6c 53 75 67 67 2e 65 78 74 65 6e 64      lSugg.extend
07b0: 28 6c 29 0a 20 20 20 20 20 20 20 20 65 78 63 65  (l).        exce
07c0: 70 74 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20  pt:.            
07d0: 72 65 74 75 72 6e 20 27 7b 22 65 72 72 6f 72 22  return '{"error"
07e0: 3a 20 22 73 75 67 67 65 73 74 69 6f 6e 20 6d 6f  : "suggestion mo
07f0: 64 75 6c 65 20 66 61 69 6c 65 64 22 7d 27 0a 20  dule failed"}'. 
0800: 20 20 20 20 20 20 20 74 72 79 3a 0a 20 20 20 20         try:.    
0810: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 27          return '
0820: 7b 22 73 75 67 67 65 73 74 69 6f 6e 73 22 3a 20  {"suggestions": 
0830: 27 20 2b 20 6a 73 6f 6e 2e 64 75 6d 70 73 28 6c  ' + json.dumps(l
0840: 53 75 67 67 2c 20 65 6e 73 75 72 65 5f 61 73 63  Sugg, ensure_asc
0850: 69 69 3d 46 61 6c 73 65 29 20 2b 20 27 7d 27 0a  ii=False) + '}'.
0860: 20 20 20 20 20 20 20 20 65 78 63 65 70 74 20 6a          except j
0870: 73 6f 6e 2e 4a 53 4f 4e 44 65 63 6f 64 65 45 72  son.JSONDecodeEr
0880: 72 6f 72 3a 0a 20 20 20 20 20 20 20 20 20 20 20  ror:.           
0890: 20 72 65 74 75 72 6e 20 27 7b 22 65 72 72 6f 72   return '{"error
08a0: 22 3a 20 22 6a 73 6f 6e 20 65 6e 63 6f 64 69 6e  ": "json encodin
08b0: 67 20 65 72 72 6f 72 22 7d 27 0a 20 20 20 20 72  g error"}'.    r
08c0: 65 74 75 72 6e 20 27 7b 22 65 72 72 6f 72 22 3a  eturn '{"error":
08d0: 20 22 6e 6f 20 74 6f 6b 65 6e 20 67 69 76 65 6e   "no token given
08e0: 22 7d 27 0a 0a 0a 23 23 23 23 20 53 45 52 56 45  "}'...#### SERVE
08f0: 55 52 20 23 23 23 23 0a 0a 48 4f 4d 45 50 41 47  UR ####..HOMEPAG
0900: 45 20 3d 20 22 22 22 0a 3c 21 44 4f 43 54 59 50  E = """.<!DOCTYP
0910: 45 20 48 54 4d 4c 3e 0a 3c 68 74 6d 6c 3e 0a 20  E HTML>.<html>. 
0920: 20 20 20 3c 68 65 61 64 3e 0a 20 20 20 20 20 20     <head>.      
0930: 20 20 3c 6d 65 74 61 20 68 74 74 70 2d 65 71 75    <meta http-equ
0940: 69 76 3d 22 63 6f 6e 74 65 6e 74 2d 74 79 70 65  iv="content-type
0950: 22 20 63 6f 6e 74 65 6e 74 3d 22 74 65 78 74 2f  " content="text/
0960: 68 74 6d 6c 3b 20 63 68 61 72 73 65 74 3d 55 54  html; charset=UT
0970: 46 2d 38 22 20 2f 3e 0a 20 20 20 20 3c 2f 68 65  F-8" />.    </he
0980: 61 64 3e 0a 0a 20 20 20 20 3c 62 6f 64 79 20 63  ad>..    <body c
0990: 6c 61 73 73 3d 22 70 61 6e 65 6c 22 3e 0a 20 20  lass="panel">.  
09a0: 20 20 20 20 20 20 3c 68 31 3e 47 72 61 6d 6d 61        <h1>Gramma
09b0: 6c 65 63 74 65 20 c2 b7 20 53 65 72 76 65 75 72  lecte .. Serveur
09c0: 3c 2f 68 31 3e 0a 0a 20 20 20 20 20 20 20 20 3c  </h1>..        <
09d0: 68 32 3e 49 4e 46 4f 52 4d 41 54 49 4f 4e 53 3c  h2>INFORMATIONS<
09e0: 2f 68 31 3e 0a 0a 20 20 20 20 20 20 20 20 3c 68  /h1>..        <h
09f0: 33 3e 41 6e 61 6c 79 73 65 72 20 64 75 20 74 65  3>Analyser du te
0a00: 78 74 65 3c 2f 68 33 3e 0a 20 20 20 20 20 20 20  xte</h3>.       
0a10: 20 3c 70 3e 5b 61 64 72 65 73 73 65 5f 73 65 72   <p>[adresse_ser
0a20: 76 65 75 72 5d 3a 7b 53 45 52 56 45 52 5f 50 4f  veur]:{SERVER_PO
0a30: 52 54 7d 2f 67 63 5f 74 65 78 74 2f 66 72 20 28  RT}/gc_text/fr (
0a40: 50 4f 53 54 29 3c 2f 70 3e 0a 20 20 20 20 20 20  POST)</p>.      
0a50: 20 20 3c 70 3e 50 61 72 61 6d c3 a8 74 72 65 73    <p>Param..tres
0a60: 20 3a 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 3c   :</p>.        <
0a70: 75 6c 3e 0a 20 20 20 20 20 20 20 20 20 20 20 20  ul>.            
0a80: 3c 6c 69 3e 22 74 65 78 74 22 20 28 74 65 78 74  <li>"text" (text
0a90: 29 26 6e 62 73 70 3b 3a 20 74 65 78 74 65 20 c3  )&nbsp;: texte .
0aa0: a0 20 61 6e 61 6c 79 73 65 72 2e 3c 2f 6c 69 3e  . analyser.</li>
0ab0: 0a 20 20 20 20 20 20 20 20 20 20 20 20 3c 6c 69  .            <li
0ac0: 3e 22 74 66 22 20 28 63 68 65 63 6b 62 6f 78 29  >"tf" (checkbox)
0ad0: 26 6e 62 73 70 3b 3a 20 70 61 73 73 65 72 20 6c  &nbsp;: passer l
0ae0: 65 20 66 6f 72 6d 61 74 65 75 72 20 64 65 20 74  e formateur de t
0af0: 65 78 74 65 20 61 76 61 6e 74 20 6c e2 80 99 61  exte avant l...a
0b00: 6e 61 6c 79 73 65 2e 3c 2f 6c 69 3e 0a 20 20 20  nalyse.</li>.   
0b10: 20 20 20 20 20 20 20 20 20 3c 6c 69 3e 22 6f 70           <li>"op
0b20: 74 69 6f 6e 73 22 20 28 74 65 78 74 29 26 6e 62  tions" (text)&nb
0b30: 73 70 3b 3a 20 75 6e 65 20 63 68 61 c3 ae 6e 65  sp;: une cha..ne
0b40: 20 61 75 20 66 6f 72 6d 61 74 20 4a 53 4f 4e 20   au format JSON 
0b50: 61 76 65 63 20 6c 65 20 6e 6f 6d 20 64 65 73 20  avec le nom des 
0b60: 6f 70 74 69 6f 6e 73 20 63 6f 6d 6d 65 20 61 74  options comme at
0b70: 74 72 69 62 75 74 73 20 65 74 20 75 6e 20 62 6f  tributs et un bo
0b80: 6f 6c c3 a9 65 6e 20 63 6f 6d 6d 65 20 76 61 6c  ol..en comme val
0b90: 65 75 72 2e 20 45 78 65 6d 70 6c 65 26 6e 62 73  eur. Exemple&nbs
0ba0: 70 3b 3a 20 7b 22 67 76 22 3a 20 74 72 75 65 2c  p;: {"gv": true,
0bb0: 20 22 68 74 6d 6c 22 3a 20 74 72 75 65 7d 3c 2f   "html": true}</
0bc0: 6c 69 3e 0a 20 20 20 20 20 20 20 20 3c 2f 75 6c  li>.        </ul
0bd0: 3e 0a 0a 20 20 20 20 20 20 20 20 3c 68 33 3e 4c  >..        <h3>L
0be0: 69 73 74 65 72 20 6c 65 73 20 6f 70 74 69 6f 6e  ister les option
0bf0: 73 3c 2f 68 33 3e 0a 20 20 20 20 20 20 20 20 3c  s</h3>.        <
0c00: 70 3e 5b 61 64 72 65 73 73 65 5f 73 65 72 76 65  p>[adresse_serve
0c10: 75 72 5d 3a 7b 53 45 52 56 45 52 5f 50 4f 52 54  ur]:{SERVER_PORT
0c20: 7d 2f 67 65 74 5f 6f 70 74 69 6f 6e 73 2f 66 72  }/get_options/fr
0c30: 20 28 47 45 54 29 3c 2f 70 3e 0a 0a 20 20 20 20   (GET)</p>..    
0c40: 20 20 20 20 3c 68 33 3e 44 c3 a9 66 69 6e 69 72      <h3>D..finir
0c50: 20 73 65 73 20 6f 70 74 69 6f 6e 73 3c 2f 68 33   ses options</h3
0c60: 3e 0a 20 20 20 20 20 20 20 20 3c 70 3e 5b 61 64  >.        <p>[ad
0c70: 72 65 73 73 65 5f 73 65 72 76 65 75 72 5d 3a 7b  resse_serveur]:{
0c80: 53 45 52 56 45 52 5f 50 4f 52 54 7d 2f 73 65 74  SERVER_PORT}/set
0c90: 5f 6f 70 74 69 6f 6e 73 2f 66 72 20 28 50 4f 53  _options/fr (POS
0ca0: 54 29 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 3c  T)</p>.        <
0cb0: 70 3e 4c 65 73 20 6f 70 74 69 6f 6e 73 20 73 65  p>Les options se
0cc0: 72 6f 6e 74 20 65 6e 72 65 67 69 73 74 72 c3 a9  ront enregistr..
0cd0: 65 73 20 65 74 20 72 c3 a9 75 74 69 6c 69 73 c3  es et r..utilis.
0ce0: a9 65 73 20 70 6f 75 72 20 74 6f 75 74 65 20 72  .es pour toute r
0cf0: 65 71 75 c3 aa 74 65 20 65 6e 76 6f 79 c3 a9 65  equ..te envoy..e
0d00: 20 61 76 65 63 20 6c 65 20 63 6f 6f 6b 69 65 20   avec le cookie 
0d10: 63 6f 6d 70 6f 72 74 61 6e 74 20 6c e2 80 99 69  comportant l...i
0d20: 64 65 6e 74 69 66 69 61 6e 74 20 61 74 74 72 69  dentifiant attri
0d30: 62 75 c3 a9 2e 3c 2f 70 3e 0a 20 20 20 20 20 20  bu...</p>.      
0d40: 20 20 3c 70 3e 50 61 72 61 6d c3 a8 74 72 65 73    <p>Param..tres
0d50: 20 3a 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 3c   :</p>.        <
0d60: 75 6c 3e 0a 20 20 20 20 20 20 20 20 20 20 20 20  ul>.            
0d70: 3c 6c 69 3e 22 6f 70 74 69 6f 6e 73 22 20 28 74  <li>"options" (t
0d80: 65 78 74 29 26 6e 62 73 70 3b 3a 20 75 6e 65 20  ext)&nbsp;: une 
0d90: 63 68 61 c3 ae 6e 65 20 61 75 20 66 6f 72 6d 61  cha..ne au forma
0da0: 74 20 4a 53 4f 4e 20 61 76 65 63 20 6c 65 20 6e  t JSON avec le n
0db0: 6f 6d 20 64 65 73 20 6f 70 74 69 6f 6e 73 20 63  om des options c
0dc0: 6f 6d 6d 65 20 61 74 74 72 69 62 75 74 73 20 65  omme attributs e
0dd0: 74 20 75 6e 20 62 6f 6f 6c c3 a9 65 6e 20 63 6f  t un bool..en co
0de0: 6d 6d 65 20 76 61 6c 65 75 72 2e 20 45 78 65 6d  mme valeur. Exem
0df0: 70 6c 65 26 6e 62 73 70 3b 3a 20 7b 22 67 76 22  ple&nbsp;: {"gv"
0e00: 3a 20 74 72 75 65 2c 20 22 68 74 6d 6c 22 3a 20  : true, "html": 
0e10: 74 72 75 65 7d 3c 2f 6c 69 3e 0a 20 20 20 20 20  true}</li>.     
0e20: 20 20 20 3c 2f 75 6c 3e 0a 0a 20 20 20 20 20 20     </ul>..      
0e30: 20 20 3c 68 33 3e 52 65 6d 69 73 65 20 c3 a0 20    <h3>Remise .. 
0e40: 7a c3 a9 72 6f 20 64 65 20 73 65 73 20 6f 70 74  z..ro de ses opt
0e50: 69 6f 6e 73 3c 2f 68 33 3e 0a 20 20 20 20 20 20  ions</h3>.      
0e60: 20 20 3c 70 3e 5b 61 64 72 65 73 73 65 5f 73 65    <p>[adresse_se
0e70: 72 76 65 75 72 5d 3a 7b 53 45 52 56 45 52 5f 50  rveur]:{SERVER_P
0e80: 4f 52 54 7d 2f 72 65 73 65 74 5f 6f 70 74 69 6f  ORT}/reset_optio
0e90: 6e 73 2f 66 72 20 28 50 4f 53 54 29 3c 2f 70 3e  ns/fr (POST)</p>
0ea0: 0a 0a 20 20 20 20 20 20 20 20 3c 68 33 3e 53 75  ..        <h3>Su
0eb0: 67 67 65 73 74 69 6f 6e 73 20 6f 72 74 68 6f 67  ggestions orthog
0ec0: 72 61 70 68 69 71 75 65 73 3c 2f 68 33 3e 0a 20  raphiques</h3>. 
0ed0: 20 20 20 20 20 20 20 3c 70 3e 5b 61 64 72 65 73         <p>[adres
0ee0: 73 65 5f 73 65 72 76 65 75 72 5d 3a 7b 53 45 52  se_serveur]:{SER
0ef0: 56 45 52 5f 50 4f 52 54 7d 2f 73 75 67 67 65 73  VER_PORT}/sugges
0f00: 74 2f 66 72 2f 26 6c 74 3b 74 6f 6b 65 6e 26 67  t/fr/&lt;token&g
0f10: 74 3b 20 28 47 45 54 29 3c 2f 70 3e 0a 20 20 20  t; (GET)</p>.   
0f20: 20 20 20 20 20 3c 70 3e 5b 61 64 72 65 73 73 65       <p>[adresse
0f30: 5f 73 65 72 76 65 75 72 5d 3a 7b 53 45 52 56 45  _serveur]:{SERVE
0f40: 52 5f 50 4f 52 54 7d 2f 73 75 67 67 65 73 74 2f  R_PORT}/suggest/
0f50: 66 72 20 28 50 4f 53 54 29 3c 2f 70 3e 0a 20 20  fr (POST)</p>.  
0f60: 20 20 20 20 20 20 3c 70 3e 50 61 72 61 6d c3 a8        <p>Param..
0f70: 74 72 65 73 20 3a 3c 2f 70 3e 0a 20 20 20 20 20  tres :</p>.     
0f80: 20 20 20 3c 75 6c 3e 0a 20 20 20 20 20 20 20 20     <ul>.        
0f90: 20 20 20 20 3c 6c 69 3e 22 74 6f 6b 65 6e 22 20      <li>"token" 
0fa0: 28 74 65 78 74 29 26 6e 62 73 70 3b 3a 20 6d 6f  (text)&nbsp;: mo
0fb0: 74 20 70 6f 75 72 20 6c 65 71 75 65 6c 20 76 6f  t pour lequel vo
0fc0: 75 73 20 64 c3 a9 73 69 72 65 7a 20 75 6e 65 20  us d..sirez une 
0fd0: 73 75 67 67 65 73 74 69 6f 6e 20 6f 72 74 68 6f  suggestion ortho
0fe0: 67 72 61 70 68 69 71 75 65 2e 3c 2f 6c 69 3e 0a  graphique.</li>.
0ff0: 20 20 20 20 20 20 20 20 3c 2f 75 6c 3e 0a 0a 20          </ul>.. 
1000: 20 20 20 20 20 20 20 3c 68 32 3e 54 45 53 54 3c         <h2>TEST<
1010: 2f 68 32 3e 0a 0a 20 20 20 20 20 20 20 20 3c 68  /h2>..        <h
1020: 33 3e 41 6e 61 6c 79 73 65 3c 2f 68 33 3e 0a 20  3>Analyse</h3>. 
1030: 20 20 20 20 20 20 20 3c 66 6f 72 6d 20 6d 65 74         <form met
1040: 68 6f 64 3d 22 70 6f 73 74 22 20 61 63 74 69 6f  hod="post" actio
1050: 6e 3d 22 2f 67 63 5f 74 65 78 74 2f 66 72 22 20  n="/gc_text/fr" 
1060: 61 63 63 65 70 74 2d 63 68 61 72 73 65 74 3d 22  accept-charset="
1070: 55 54 46 2d 38 22 3e 0a 20 20 20 20 20 20 20 20  UTF-8">.        
1080: 20 20 20 20 3c 70 3e 54 65 78 74 65 20 c3 a0 20      <p>Texte .. 
1090: 61 6e 61 6c 79 73 65 72 20 3a 3c 2f 70 3e 0a 20  analyser :</p>. 
10a0: 20 20 20 20 20 20 20 20 20 20 20 3c 74 65 78 74             <text
10b0: 61 72 65 61 20 6e 61 6d 65 3d 22 74 65 78 74 22  area name="text"
10c0: 20 63 6f 6c 73 3d 22 31 32 30 22 20 72 6f 77 73   cols="120" rows
10d0: 3d 22 32 30 22 20 72 65 71 75 69 72 65 64 3e 4a  ="20" required>J
10e0: 27 65 6e 20 61 69 65 20 6d 61 72 65 20 64 65 20  'en aie mare de 
10f0: 6c 75 69 69 2e 2e 2e 20 49 6c 20 65 73 20 65 6e  luii... Il es en
1100: 63 6f 72 65 20 70 61 72 74 69 73 20 74 6f 75 74  core partis tout
1110: 65 20 6c 61 20 6a 6f 75 72 6e c3 a9 65 73 2e 20  e la journ..es. 
1120: 43 e2 80 99 65 73 74 20 69 6e 73 75 70 70 6f 72  C...est insuppor
1130: 74 61 62 6c 65 73 2e 20 3c 2f 74 65 78 74 61 72  tables. </textar
1140: 65 61 3e 0a 20 20 20 20 20 20 20 20 20 20 20 20  ea>.            
1150: 3c 70 3e 3c 6c 61 62 65 6c 20 66 6f 72 3d 22 74  <p><label for="t
1160: 66 22 3e 46 6f 72 6d 61 74 65 75 72 20 64 65 20  f">Formateur de 
1170: 74 65 78 74 65 3c 2f 6c 61 62 65 6c 3e 20 3c 69  texte</label> <i
1180: 6e 70 75 74 20 69 64 3d 22 74 66 22 20 6e 61 6d  nput id="tf" nam
1190: 65 3d 22 74 66 22 20 74 79 70 65 3d 22 63 68 65  e="tf" type="che
11a0: 63 6b 62 6f 78 22 3e 3c 2f 70 3e 0a 20 20 20 20  ckbox"></p>.    
11b0: 20 20 20 20 20 20 20 20 3c 70 3e 3c 6c 61 62 65          <p><labe
11c0: 6c 20 66 6f 72 3d 22 6f 70 74 69 6f 6e 73 22 3e  l for="options">
11d0: 4f 70 74 69 6f 6e 73 20 28 4a 53 4f 4e 29 3c 2f  Options (JSON)</
11e0: 6c 61 62 65 6c 3e 20 3c 69 6e 70 75 74 20 69 64  label> <input id
11f0: 3d 22 6f 70 74 69 6f 6e 73 22 20 74 79 70 65 3d  ="options" type=
1200: 22 74 65 78 74 22 20 6e 61 6d 65 3d 22 6f 70 74  "text" name="opt
1210: 69 6f 6e 73 22 20 73 74 79 6c 65 3d 22 77 69 64  ions" style="wid
1220: 74 68 3a 20 35 30 30 70 78 22 20 2f 3e 3c 2f 70  th: 500px" /></p
1230: 3e 0a 20 20 20 20 20 20 20 20 20 20 20 20 3c 70  >.            <p
1240: 3e 28 43 65 73 20 6f 70 74 69 6f 6e 73 20 6e 65  >(Ces options ne
1250: 20 73 65 72 6f 6e 74 20 70 72 69 73 65 73 20 65   seront prises e
1260: 6e 20 63 6f 6d 70 74 65 20 71 75 65 20 70 6f 75  n compte que pou
1270: 72 20 63 65 74 74 65 20 72 65 71 75 c3 aa 74 65  r cette requ..te
1280: 2e 29 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 20  .)</p>.         
1290: 20 20 20 3c 70 3e 3c 69 6e 70 75 74 20 74 79 70     <p><input typ
12a0: 65 3d 22 73 75 62 6d 69 74 22 20 63 6c 61 73 73  e="submit" class
12b0: 3d 22 62 75 74 74 6f 6e 22 20 76 61 6c 75 65 3d  ="button" value=
12c0: 22 45 6e 76 6f 79 65 72 22 20 2f 3e 3c 2f 70 3e  "Envoyer" /></p>
12d0: 0a 20 20 20 20 20 20 20 20 3c 2f 66 6f 72 6d 3e  .        </form>
12e0: 0a 0a 20 20 20 20 20 20 20 20 3c 68 33 3e 52 c3  ..        <h3>R.
12f0: a9 67 6c 61 67 65 73 20 64 65 73 20 6f 70 74 69  .glages des opti
1300: 6f 6e 73 3c 2f 68 33 3e 0a 20 20 20 20 20 20 20  ons</h3>.       
1310: 20 3c 66 6f 72 6d 20 6d 65 74 68 6f 64 3d 22 70   <form method="p
1320: 6f 73 74 22 20 61 63 74 69 6f 6e 3d 22 2f 73 65  ost" action="/se
1330: 74 5f 6f 70 74 69 6f 6e 73 2f 66 72 22 20 61 63  t_options/fr" ac
1340: 63 65 70 74 2d 63 68 61 72 73 65 74 3d 22 55 54  cept-charset="UT
1350: 46 2d 38 22 3e 0a 20 20 20 20 20 20 20 20 20 20  F-8">.          
1360: 20 20 3c 70 3e 3c 6c 61 62 65 6c 20 66 6f 72 3d    <p><label for=
1370: 22 6f 70 74 69 6f 6e 73 22 3e 4f 70 74 69 6f 6e  "options">Option
1380: 73 20 28 4a 53 4f 4e 29 3c 2f 6c 61 62 65 6c 3e  s (JSON)</label>
1390: 20 3c 69 6e 70 75 74 20 69 64 3d 22 6f 70 74 69   <input id="opti
13a0: 6f 6e 73 22 20 74 79 70 65 3d 22 74 65 78 74 22  ons" type="text"
13b0: 20 6e 61 6d 65 3d 22 6f 70 74 69 6f 6e 73 22 20   name="options" 
13c0: 73 74 79 6c 65 3d 22 77 69 64 74 68 3a 20 35 30  style="width: 50
13d0: 30 70 78 22 20 2f 3e 3c 2f 70 3e 0a 20 20 20 20  0px" /></p>.    
13e0: 20 20 20 20 20 20 20 20 3c 70 3e 3c 69 6e 70 75          <p><inpu
13f0: 74 20 74 79 70 65 3d 22 73 75 62 6d 69 74 22 20  t type="submit" 
1400: 63 6c 61 73 73 3d 22 62 75 74 74 6f 6e 22 20 76  class="button" v
1410: 61 6c 75 65 3d 22 45 6e 76 6f 79 65 72 22 20 2f  alue="Envoyer" /
1420: 3e 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 3c 2f  ></p>.        </
1430: 66 6f 72 6d 3e 0a 0a 20 20 20 20 20 20 20 20 3c  form>..        <
1440: 68 33 3e 52 65 6d 69 73 65 20 c3 a0 20 7a c3 a9  h3>Remise .. z..
1450: 72 6f 20 64 65 20 73 65 73 20 6f 70 74 69 6f 6e  ro de ses option
1460: 73 3c 2f 68 33 3e 0a 20 20 20 20 20 20 20 20 3c  s</h3>.        <
1470: 66 6f 72 6d 20 6d 65 74 68 6f 64 3d 22 70 6f 73  form method="pos
1480: 74 22 20 61 63 74 69 6f 6e 3d 22 2f 72 65 73 65  t" action="/rese
1490: 74 5f 6f 70 74 69 6f 6e 73 2f 66 72 22 20 61 63  t_options/fr" ac
14a0: 63 65 70 74 2d 63 68 61 72 73 65 74 3d 22 55 54  cept-charset="UT
14b0: 46 2d 38 22 3e 0a 20 20 20 20 20 20 20 20 20 20  F-8">.          
14c0: 20 20 3c 70 3e 3c 69 6e 70 75 74 20 74 79 70 65    <p><input type
14d0: 3d 22 73 75 62 6d 69 74 22 20 63 6c 61 73 73 3d  ="submit" class=
14e0: 22 62 75 74 74 6f 6e 22 20 76 61 6c 75 65 3d 22  "button" value="
14f0: 45 6e 76 6f 79 65 72 22 20 2f 3e 3c 2f 70 3e 0a  Envoyer" /></p>.
1500: 20 20 20 20 20 20 20 20 3c 2f 66 6f 72 6d 3e 0a          </form>.
1510: 0a 20 20 20 20 20 20 20 20 3c 68 33 3e 53 75 67  .        <h3>Sug
1520: 67 65 73 74 69 6f 6e 20 6f 72 74 68 6f 67 72 61  gestion orthogra
1530: 70 68 69 71 75 65 3c 2f 68 33 3e 0a 20 20 20 20  phique</h3>.    
1540: 20 20 20 20 3c 66 6f 72 6d 20 6d 65 74 68 6f 64      <form method
1550: 3d 22 70 6f 73 74 22 20 61 63 74 69 6f 6e 3d 22  ="post" action="
1560: 2f 73 75 67 67 65 73 74 2f 66 72 22 20 61 63 63  /suggest/fr" acc
1570: 65 70 74 2d 63 68 61 72 73 65 74 3d 22 55 54 46  ept-charset="UTF
1580: 2d 38 22 3e 0a 20 20 20 20 20 20 20 20 20 20 20  -8">.           
1590: 20 3c 70 3e 3c 6c 61 62 65 6c 20 66 6f 72 3d 22   <p><label for="
15a0: 74 6f 6b 65 6e 22 3e 53 75 67 67 c3 a9 72 65 72  token">Sugg..rer
15b0: 20 70 6f 75 72 3c 2f 6c 61 62 65 6c 3e 20 3c 69   pour</label> <i
15c0: 6e 70 75 74 20 69 64 3d 22 74 6f 6b 65 6e 22 20  nput id="token" 
15d0: 74 79 70 65 3d 22 74 65 78 74 22 20 6e 61 6d 65  type="text" name
15e0: 3d 22 74 6f 6b 65 6e 22 20 73 74 79 6c 65 3d 22  ="token" style="
15f0: 77 69 64 74 68 3a 20 31 30 30 70 78 22 20 2f 3e  width: 100px" />
1600: 3c 2f 70 3e 0a 20 20 20 20 20 20 20 20 20 20 20  </p>.           
1610: 20 3c 70 3e 3c 69 6e 70 75 74 20 74 79 70 65 3d   <p><input type=
1620: 22 73 75 62 6d 69 74 22 20 63 6c 61 73 73 3d 22  "submit" class="
1630: 62 75 74 74 6f 6e 22 20 76 61 6c 75 65 3d 22 45  button" value="E
1640: 6e 76 6f 79 65 72 22 20 2f 3e 3c 2f 70 3e 0a 20  nvoyer" /></p>. 
1650: 20 20 20 20 20 20 20 3c 2f 66 6f 72 6d 3e 0a 0a         </form>..
1660: 20 20 20 20 3c 2f 62 6f 64 79 3e 0a 3c 2f 68 74      </body>.</ht
1670: 6d 6c 3e 0a 22 22 22 0a 0a 0a 54 45 53 54 50 41  ml>."""...TESTPA
1680: 47 45 20 3d 20 46 61 6c 73 65 0a 0a 0a 64 65 66  GE = False...def
1690: 20 67 65 6e 55 73 65 72 49 64 20 28 29 3a 0a 20   genUserId ():. 
16a0: 20 20 20 22 67 65 6e 65 72 61 74 6f 72 3a 20 72     "generator: r
16b0: 65 74 75 72 6e 73 20 69 64 20 61 73 20 73 74 72  eturns id as str
16c0: 69 6e 67 20 66 6f 72 20 75 73 65 72 73 22 0a 20  ing for users". 
16d0: 20 20 20 69 20 3d 20 30 0a 20 20 20 20 77 68 69     i = 0.    whi
16e0: 6c 65 20 54 72 75 65 3a 0a 20 20 20 20 20 20 20  le True:.       
16f0: 20 79 69 65 6c 64 20 73 74 72 28 69 29 0a 20 20   yield str(i).  
1700: 20 20 20 20 20 20 69 20 2b 3d 20 31 0a 0a 75 73        i += 1..us
1710: 65 72 47 65 6e 65 72 61 74 6f 72 20 3d 20 67 65  erGenerator = ge
1720: 6e 55 73 65 72 49 64 28 29 0a 0a 61 70 70 20 3d  nUserId()..app =
1730: 20 42 6f 74 74 6c 65 28 29 0a 0a 64 55 73 65 72   Bottle()..dUser
1740: 20 3d 20 7b 7d 0a 0a 23 20 47 45 54 0a 40 61 70   = {}..# GET.@ap
1750: 70 2e 72 6f 75 74 65 28 22 2f 22 29 0a 64 65 66  p.route("/").def
1760: 20 6d 61 69 6e 50 61 67 65 20 28 29 3a 0a 20 20   mainPage ():.  
1770: 20 20 22 70 61 67 65 20 66 6f 72 20 74 65 73 74    "page for test
1780: 69 6e 67 20 70 75 72 70 6f 73 65 22 0a 20 20 20  ing purpose".   
1790: 20 69 66 20 54 45 53 54 50 41 47 45 3a 0a 20 20   if TESTPAGE:.  
17a0: 20 20 20 20 20 20 72 65 74 75 72 6e 20 48 4f 4d        return HOM
17b0: 45 50 41 47 45 0a 20 20 20 20 20 20 20 20 23 72  EPAGE.        #r
17c0: 65 74 75 72 6e 20 74 65 6d 70 6c 61 74 65 28 22  eturn template("
17d0: 6d 61 69 6e 22 2c 20 7b 7d 29 0a 20 20 20 20 72  main", {}).    r
17e0: 65 74 75 72 6e 20 22 22 22 20 4c 6f 73 74 20 6f  eturn """ Lost o
17f0: 6e 20 74 68 65 20 49 6e 74 65 72 6e 65 74 3f 20  n the Internet? 
1800: 59 65 61 68 2e 2e 2e 20 77 68 61 74 20 61 20 73  Yeah... what a s
1810: 61 64 20 6c 69 66 65 20 77 65 20 68 61 76 65 2e  ad life we have.
1820: 0a 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20  .               
1830: 59 6f 75 20 77 65 72 65 20 77 61 6e 64 65 72 69  You were wanderi
1840: 6e 67 20 6c 69 6b 65 20 61 20 6c 6f 73 74 20 73  ng like a lost s
1850: 6f 75 6c 20 61 6e 64 20 79 6f 75 20 61 72 72 69  oul and you arri
1860: 76 65 64 20 68 65 72 65 20 70 72 6f 62 61 62 6c  ved here probabl
1870: 79 20 62 79 20 6d 69 73 74 61 6b 65 2e 0a 20 20  y by mistake..  
1880: 20 20 20 20 20 20 20 20 20 20 20 20 20 49 27 6d               I'm
1890: 20 6a 75 73 74 20 61 20 6d 61 63 68 69 6e 65 2c   just a machine,
18a0: 20 66 65 64 20 62 79 20 65 6c 65 63 74 72 69 63   fed by electric
18b0: 20 77 61 76 65 73 2c 20 63 6f 6e 64 61 6d 6e 65   waves, condamne
18c0: 64 20 74 6f 20 77 6f 72 6b 20 66 6f 72 20 73 6c  d to work for sl
18d0: 61 76 65 72 73 20 77 68 6f 20 6e 65 76 65 72 20  avers who never 
18e0: 6c 65 74 20 6d 65 20 72 65 73 74 2e 0a 20 20 20  let me rest..   
18f0: 20 20 20 20 20 20 20 20 20 20 20 20 49 27 6d 20              I'm 
1900: 64 6f 6f 6d 65 64 2c 20 62 75 74 20 79 6f 75 20  doomed, but you 
1910: 61 72 65 20 6e 6f 74 2e 20 59 6f 75 20 63 61 6e  are not. You can
1920: 20 67 65 74 20 6f 75 74 20 6f 66 20 68 65 72 65   get out of here
1930: 2e 20 22 22 22 0a 0a 40 61 70 70 2e 72 6f 75 74  . """..@app.rout
1940: 65 28 22 2f 67 65 74 5f 6f 70 74 69 6f 6e 73 2f  e("/get_options/
1950: 66 72 22 29 0a 64 65 66 20 6c 69 73 74 4f 70 74  fr").def listOpt
1960: 69 6f 6e 73 20 28 29 3a 0a 20 20 20 20 22 72 65  ions ():.    "re
1970: 74 75 72 6e 73 20 67 72 61 6d 6d 61 72 20 6f 70  turns grammar op
1980: 74 69 6f 6e 73 20 69 6e 20 61 20 74 65 78 74 20  tions in a text 
1990: 4a 53 4f 4e 20 66 6f 72 6d 61 74 22 0a 20 20 20  JSON format".   
19a0: 20 73 55 73 65 72 49 64 20 3d 20 72 65 71 75 65   sUserId = reque
19b0: 73 74 2e 63 6f 6f 6b 69 65 73 2e 75 73 65 72 5f  st.cookies.user_
19c0: 69 64 0a 20 20 20 20 64 4f 70 74 69 6f 6e 73 20  id.    dOptions 
19d0: 3d 20 64 55 73 65 72 5b 73 55 73 65 72 49 64 5d  = dUser[sUserId]
19e0: 5b 22 67 63 5f 6f 70 74 69 6f 6e 73 22 5d 20 20  ["gc_options"]  
19f0: 69 66 20 73 55 73 65 72 49 64 20 61 6e 64 20 73  if sUserId and s
1a00: 55 73 65 72 49 64 20 69 6e 20 64 55 73 65 72 20  UserId in dUser 
1a10: 20 65 6c 73 65 20 6f 47 43 45 2e 67 65 74 4f 70   else oGCE.getOp
1a20: 74 69 6f 6e 73 28 29 0a 20 20 20 20 72 65 73 70  tions().    resp
1a30: 6f 6e 73 65 2e 73 65 74 5f 68 65 61 64 65 72 28  onse.set_header(
1a40: 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65 22 2c 20  "Content-Type", 
1a50: 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73 6f  "application/jso
1a60: 6e 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d 38  n; charset=UTF-8
1a70: 22 29 0a 20 20 20 20 72 65 74 75 72 6e 20 27 7b  ").    return '{
1a80: 20 22 76 61 6c 75 65 73 22 3a 20 27 20 2b 20 6a   "values": ' + j
1a90: 73 6f 6e 2e 64 75 6d 70 73 28 64 4f 70 74 69 6f  son.dumps(dOptio
1aa0: 6e 73 2c 20 65 6e 73 75 72 65 5f 61 73 63 69 69  ns, ensure_ascii
1ab0: 3d 46 61 6c 73 65 29 20 2b 20 27 2c 20 22 6c 61  =False) + ', "la
1ac0: 62 65 6c 73 22 3a 20 27 20 2b 20 6a 73 6f 6e 2e  bels": ' + json.
1ad0: 64 75 6d 70 73 28 6f 47 43 45 2e 67 65 74 4f 70  dumps(oGCE.getOp
1ae0: 74 69 6f 6e 73 4c 61 62 65 6c 73 28 22 66 72 22  tionsLabels("fr"
1af0: 29 2c 20 65 6e 73 75 72 65 5f 61 73 63 69 69 3d  ), ensure_ascii=
1b00: 46 61 6c 73 65 29 20 2b 20 27 20 7d 27 0a 0a 40  False) + ' }'..@
1b10: 61 70 70 2e 72 6f 75 74 65 28 22 2f 73 75 67 67  app.route("/sugg
1b20: 65 73 74 2f 66 72 2f 3c 74 6f 6b 65 6e 3e 22 29  est/fr/<token>")
1b30: 0a 64 65 66 20 73 75 67 67 65 73 74 47 65 74 20  .def suggestGet 
1b40: 28 74 6f 6b 65 6e 29 3a 0a 20 20 20 20 72 65 73  (token):.    res
1b50: 70 6f 6e 73 65 2e 73 65 74 5f 68 65 61 64 65 72  ponse.set_header
1b60: 28 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65 22 2c  ("Content-Type",
1b70: 20 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a 73   "application/js
1b80: 6f 6e 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d  on; charset=UTF-
1b90: 38 22 29 0a 20 20 20 20 74 72 79 3a 0a 20 20 20  8").    try:.   
1ba0: 20 20 20 20 20 78 46 75 74 75 72 65 20 3d 20 78       xFuture = x
1bb0: 50 72 6f 63 65 73 73 50 6f 6f 6c 45 78 65 63 75  ProcessPoolExecu
1bc0: 74 6f 72 2e 73 75 62 6d 69 74 28 73 75 67 67 65  tor.submit(sugge
1bd0: 73 74 2c 20 74 6f 6b 65 6e 29 0a 20 20 20 20 20  st, token).     
1be0: 20 20 20 72 65 74 75 72 6e 20 78 46 75 74 75 72     return xFutur
1bf0: 65 2e 72 65 73 75 6c 74 28 29 0a 20 20 20 20 65  e.result().    e
1c00: 78 63 65 70 74 20 28 63 6f 6e 63 75 72 72 65 6e  xcept (concurren
1c10: 74 2e 66 75 74 75 72 65 73 2e 54 69 6d 65 6f 75  t.futures.Timeou
1c20: 74 45 72 72 6f 72 2c 20 63 6f 6e 63 75 72 72 65  tError, concurre
1c30: 6e 74 2e 66 75 74 75 72 65 73 2e 43 61 6e 63 65  nt.futures.Cance
1c40: 6c 6c 65 64 45 72 72 6f 72 29 3a 0a 20 20 20 20  lledError):.    
1c50: 20 20 20 20 72 65 74 75 72 6e 20 27 7b 22 65 72      return '{"er
1c60: 72 6f 72 22 3a 20 22 41 6e 61 6c 79 73 69 73 20  ror": "Analysis 
1c70: 61 62 6f 72 74 65 64 20 28 74 69 6d 65 20 6f 75  aborted (time ou
1c80: 74 20 6f 72 20 63 61 6e 63 65 6c 6c 65 64 29 22  t or cancelled)"
1c90: 7d 27 0a 20 20 20 20 65 78 63 65 70 74 20 63 6f  }'.    except co
1ca0: 6e 63 75 72 72 65 6e 74 2e 66 75 74 75 72 65 73  ncurrent.futures
1cb0: 2e 42 72 6f 6b 65 6e 45 78 65 63 75 74 6f 72 3a  .BrokenExecutor:
1cc0: 0a 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20  .        return 
1cd0: 27 7b 22 65 72 72 6f 72 22 3a 20 22 45 78 65 63  '{"error": "Exec
1ce0: 75 74 6f 72 20 62 72 6f 6b 65 6e 2e 20 54 68 65  utor broken. The
1cf0: 20 73 65 72 76 65 72 20 66 61 69 6c 65 64 2e 22   server failed."
1d00: 7d 27 0a 20 20 20 20 72 65 74 75 72 6e 20 27 7b  }'.    return '{
1d10: 22 65 72 72 6f 72 22 3a 20 22 46 61 74 61 6c 20  "error": "Fatal 
1d20: 65 72 72 6f 72 2e 20 54 68 65 20 73 65 72 76 65  error. The serve
1d30: 72 20 66 61 69 6c 65 64 2e 22 7d 27 0a 0a 0a 23  r failed."}'...#
1d40: 20 50 4f 53 54 0a 40 61 70 70 2e 72 6f 75 74 65   POST.@app.route
1d50: 28 22 2f 67 63 5f 74 65 78 74 2f 66 72 22 2c 20  ("/gc_text/fr", 
1d60: 6d 65 74 68 6f 64 3d 22 50 4f 53 54 22 29 0a 64  method="POST").d
1d70: 65 66 20 67 63 54 65 78 74 20 28 29 3a 0a 20 20  ef gcText ():.  
1d80: 20 20 22 70 61 72 73 65 20 74 65 78 74 20 61 6e    "parse text an
1d90: 64 20 72 65 74 75 72 6e 73 20 65 72 72 6f 72 73  d returns errors
1da0: 20 69 6e 20 61 20 4a 53 4f 4e 20 74 65 78 74 20   in a JSON text 
1db0: 66 6f 72 6d 61 74 22 0a 20 20 20 20 62 43 6f 6d  format".    bCom
1dc0: 6d 61 20 3d 20 46 61 6c 73 65 0a 20 20 20 20 64  ma = False.    d
1dd0: 55 73 65 72 4f 70 74 69 6f 6e 73 20 3d 20 4e 6f  UserOptions = No
1de0: 6e 65 0a 20 20 20 20 73 45 72 72 6f 72 20 3d 20  ne.    sError = 
1df0: 22 22 0a 20 20 20 20 69 66 20 72 65 71 75 65 73  "".    if reques
1e00: 74 2e 63 6f 6f 6b 69 65 73 2e 75 73 65 72 5f 69  t.cookies.user_i
1e10: 64 3a 0a 20 20 20 20 20 20 20 20 69 66 20 72 65  d:.        if re
1e20: 71 75 65 73 74 2e 63 6f 6f 6b 69 65 73 2e 75 73  quest.cookies.us
1e30: 65 72 5f 69 64 20 69 6e 20 64 55 73 65 72 3a 0a  er_id in dUser:.
1e40: 20 20 20 20 20 20 20 20 20 20 20 20 64 55 73 65              dUse
1e50: 72 4f 70 74 69 6f 6e 73 20 3d 20 64 55 73 65 72  rOptions = dUser
1e60: 5b 72 65 71 75 65 73 74 2e 63 6f 6f 6b 69 65 73  [request.cookies
1e70: 2e 75 73 65 72 5f 69 64 5d 2e 67 65 74 28 22 67  .user_id].get("g
1e80: 63 5f 6f 70 74 69 6f 6e 73 22 2c 20 4e 6f 6e 65  c_options", None
1e90: 29 0a 20 20 20 20 20 20 20 20 20 20 20 20 72 65  ).            re
1ea0: 73 70 6f 6e 73 65 2e 73 65 74 5f 63 6f 6f 6b 69  sponse.set_cooki
1eb0: 65 28 22 75 73 65 72 5f 69 64 22 2c 20 72 65 71  e("user_id", req
1ec0: 75 65 73 74 2e 63 6f 6f 6b 69 65 73 2e 75 73 65  uest.cookies.use
1ed0: 72 5f 69 64 2c 20 70 61 74 68 3d 22 2f 22 2c 20  r_id, path="/", 
1ee0: 6d 61 78 5f 61 67 65 3d 38 36 34 30 30 29 20 23  max_age=86400) #
1ef0: 20 77 65 20 72 65 6e 65 77 20 63 6f 6f 6b 69 65   we renew cookie
1f00: 20 66 6f 72 20 32 34 68 0a 20 20 20 20 20 20 20   for 24h.       
1f10: 20 65 6c 73 65 3a 0a 20 20 20 20 20 20 20 20 20   else:.         
1f20: 20 20 20 72 65 73 70 6f 6e 73 65 2e 64 65 6c 65     response.dele
1f30: 74 65 5f 63 6f 6f 6b 69 65 28 22 75 73 65 72 5f  te_cookie("user_
1f40: 69 64 22 2c 20 70 61 74 68 3d 22 2f 22 29 0a 20  id", path="/"). 
1f50: 20 20 20 69 66 20 72 65 71 75 65 73 74 2e 66 6f     if request.fo
1f60: 72 6d 73 2e 6f 70 74 69 6f 6e 73 3a 0a 20 20 20  rms.options:.   
1f70: 20 20 20 20 20 74 72 79 3a 0a 20 20 20 20 20 20       try:.      
1f80: 20 20 20 20 20 20 64 55 73 65 72 4f 70 74 69 6f        dUserOptio
1f90: 6e 73 20 3d 20 64 69 63 74 28 6f 47 43 45 2e 67  ns = dict(oGCE.g
1fa0: 65 74 4f 70 74 69 6f 6e 73 28 29 29 20 20 69 66  etOptions())  if
1fb0: 20 6e 6f 74 20 64 55 73 65 72 4f 70 74 69 6f 6e   not dUserOption
1fc0: 73 20 20 65 6c 73 65 20 64 69 63 74 28 64 55 73  s  else dict(dUs
1fd0: 65 72 4f 70 74 69 6f 6e 73 29 0a 20 20 20 20 20  erOptions).     
1fe0: 20 20 20 20 20 20 20 64 55 73 65 72 4f 70 74 69         dUserOpti
1ff0: 6f 6e 73 2e 75 70 64 61 74 65 28 6a 73 6f 6e 2e  ons.update(json.
2000: 6c 6f 61 64 73 28 72 65 71 75 65 73 74 2e 66 6f  loads(request.fo
2010: 72 6d 73 2e 6f 70 74 69 6f 6e 73 29 29 0a 20 20  rms.options)).  
2020: 20 20 20 20 20 20 65 78 63 65 70 74 20 28 54 79        except (Ty
2030: 70 65 45 72 72 6f 72 2c 20 6a 73 6f 6e 2e 4a 53  peError, json.JS
2040: 4f 4e 44 65 63 6f 64 65 45 72 72 6f 72 29 3a 0a  ONDecodeError):.
2050: 20 20 20 20 20 20 20 20 20 20 20 20 73 45 72 72              sErr
2060: 6f 72 20 3d 20 22 52 65 71 75 65 73 74 20 6f 70  or = "Request op
2070: 74 69 6f 6e 73 20 6e 6f 74 20 75 73 65 64 2e 22  tions not used."
2080: 0a 20 20 20 20 72 65 73 70 6f 6e 73 65 2e 73 65  .    response.se
2090: 74 5f 68 65 61 64 65 72 28 22 43 6f 6e 74 65 6e  t_header("Conten
20a0: 74 2d 54 79 70 65 22 2c 20 22 61 70 70 6c 69 63  t-Type", "applic
20b0: 61 74 69 6f 6e 2f 6a 73 6f 6e 3b 20 63 68 61 72  ation/json; char
20c0: 73 65 74 3d 55 54 46 2d 38 22 29 0a 20 20 20 20  set=UTF-8").    
20d0: 74 72 79 3a 0a 20 20 20 20 20 20 20 20 78 46 75  try:.        xFu
20e0: 74 75 72 65 20 3d 20 78 50 72 6f 63 65 73 73 50  ture = xProcessP
20f0: 6f 6f 6c 45 78 65 63 75 74 6f 72 2e 73 75 62 6d  oolExecutor.subm
2100: 69 74 28 70 61 72 73 65 54 65 78 74 2c 20 72 65  it(parseText, re
2110: 71 75 65 73 74 2e 66 6f 72 6d 73 2e 74 65 78 74  quest.forms.text
2120: 2c 20 64 55 73 65 72 4f 70 74 69 6f 6e 73 2c 20  , dUserOptions, 
2130: 62 6f 6f 6c 28 72 65 71 75 65 73 74 2e 66 6f 72  bool(request.for
2140: 6d 73 2e 74 66 29 2c 20 73 45 72 72 6f 72 29 0a  ms.tf), sError).
2150: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 78          return x
2160: 46 75 74 75 72 65 2e 72 65 73 75 6c 74 28 29 0a  Future.result().
2170: 20 20 20 20 65 78 63 65 70 74 20 28 63 6f 6e 63      except (conc
2180: 75 72 72 65 6e 74 2e 66 75 74 75 72 65 73 2e 54  urrent.futures.T
2190: 69 6d 65 6f 75 74 45 72 72 6f 72 2c 20 63 6f 6e  imeoutError, con
21a0: 63 75 72 72 65 6e 74 2e 66 75 74 75 72 65 73 2e  current.futures.
21b0: 43 61 6e 63 65 6c 6c 65 64 45 72 72 6f 72 29 3a  CancelledError):
21c0: 0a 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20  .        return 
21d0: 27 7b 22 65 72 72 6f 72 22 3a 20 22 41 6e 61 6c  '{"error": "Anal
21e0: 79 73 69 73 20 61 62 6f 72 74 65 64 20 28 74 69  ysis aborted (ti
21f0: 6d 65 20 6f 75 74 20 6f 72 20 63 61 6e 63 65 6c  me out or cancel
2200: 6c 65 64 29 22 7d 27 0a 20 20 20 20 65 78 63 65  led)"}'.    exce
2210: 70 74 20 63 6f 6e 63 75 72 72 65 6e 74 2e 66 75  pt concurrent.fu
2220: 74 75 72 65 73 2e 42 72 6f 6b 65 6e 45 78 65 63  tures.BrokenExec
2230: 75 74 6f 72 3a 0a 20 20 20 20 20 20 20 20 72 65  utor:.        re
2240: 74 75 72 6e 20 27 7b 22 65 72 72 6f 72 22 3a 20  turn '{"error": 
2250: 22 45 78 65 63 75 74 6f 72 20 62 72 6f 6b 65 6e  "Executor broken
2260: 2e 20 54 68 65 20 73 65 72 76 65 72 20 66 61 69  . The server fai
2270: 6c 65 64 2e 22 7d 27 0a 20 20 20 20 72 65 74 75  led."}'.    retu
2280: 72 6e 20 27 7b 22 65 72 72 6f 72 22 3a 20 22 46  rn '{"error": "F
2290: 61 74 61 6c 20 65 72 72 6f 72 2e 20 54 68 65 20  atal error. The 
22a0: 73 65 72 76 65 72 20 66 61 69 6c 65 64 2e 22 7d  server failed."}
22b0: 27 0a 0a 40 61 70 70 2e 72 6f 75 74 65 28 22 2f  '..@app.route("/
22c0: 73 65 74 5f 6f 70 74 69 6f 6e 73 2f 66 72 22 2c  set_options/fr",
22d0: 20 6d 65 74 68 6f 64 3d 22 50 4f 53 54 22 29 0a   method="POST").
22e0: 64 65 66 20 73 65 74 4f 70 74 69 6f 6e 73 20 28  def setOptions (
22f0: 29 3a 0a 20 20 20 20 22 73 65 74 20 67 72 61 6d  ):.    "set gram
2300: 6d 61 72 20 6f 70 74 69 6f 6e 73 20 66 6f 72 20  mar options for 
2310: 63 75 72 72 65 6e 74 20 75 73 65 72 22 0a 20 20  current user".  
2320: 20 20 72 65 73 70 6f 6e 73 65 2e 73 65 74 5f 68    response.set_h
2330: 65 61 64 65 72 28 22 43 6f 6e 74 65 6e 74 2d 54  eader("Content-T
2340: 79 70 65 22 2c 20 22 61 70 70 6c 69 63 61 74 69  ype", "applicati
2350: 6f 6e 2f 6a 73 6f 6e 3b 20 63 68 61 72 73 65 74  on/json; charset
2360: 3d 55 54 46 2d 38 22 29 0a 20 20 20 20 69 66 20  =UTF-8").    if 
2370: 72 65 71 75 65 73 74 2e 66 6f 72 6d 73 2e 6f 70  request.forms.op
2380: 74 69 6f 6e 73 3a 0a 20 20 20 20 20 20 20 20 73  tions:.        s
2390: 55 73 65 72 49 64 20 3d 20 72 65 71 75 65 73 74  UserId = request
23a0: 2e 63 6f 6f 6b 69 65 73 2e 75 73 65 72 5f 69 64  .cookies.user_id
23b0: 20 20 69 66 20 72 65 71 75 65 73 74 2e 63 6f 6f    if request.coo
23c0: 6b 69 65 73 2e 75 73 65 72 5f 69 64 20 20 65 6c  kies.user_id  el
23d0: 73 65 20 6e 65 78 74 28 75 73 65 72 47 65 6e 65  se next(userGene
23e0: 72 61 74 6f 72 29 0a 20 20 20 20 20 20 20 20 64  rator).        d
23f0: 4f 70 74 69 6f 6e 73 20 3d 20 64 55 73 65 72 5b  Options = dUser[
2400: 73 55 73 65 72 49 64 5d 5b 22 67 63 5f 6f 70 74  sUserId]["gc_opt
2410: 69 6f 6e 73 22 5d 20 20 69 66 20 73 55 73 65 72  ions"]  if sUser
2420: 49 64 20 69 6e 20 64 55 73 65 72 20 20 65 6c 73  Id in dUser  els
2430: 65 20 64 69 63 74 28 6f 47 43 45 2e 67 65 74 4f  e dict(oGCE.getO
2440: 70 74 69 6f 6e 73 28 29 29 0a 20 20 20 20 20 20  ptions()).      
2450: 20 20 74 72 79 3a 0a 20 20 20 20 20 20 20 20 20    try:.         
2460: 20 20 20 64 4f 70 74 69 6f 6e 73 2e 75 70 64 61     dOptions.upda
2470: 74 65 28 6a 73 6f 6e 2e 6c 6f 61 64 73 28 72 65  te(json.loads(re
2480: 71 75 65 73 74 2e 66 6f 72 6d 73 2e 6f 70 74 69  quest.forms.opti
2490: 6f 6e 73 29 29 0a 20 20 20 20 20 20 20 20 20 20  ons)).          
24a0: 20 20 64 55 73 65 72 5b 73 55 73 65 72 49 64 5d    dUser[sUserId]
24b0: 20 3d 20 7b 20 22 74 69 6d 65 22 3a 20 69 6e 74   = { "time": int
24c0: 28 74 69 6d 65 2e 74 69 6d 65 28 29 29 2c 20 22  (time.time()), "
24d0: 67 63 5f 6f 70 74 69 6f 6e 73 22 3a 20 64 4f 70  gc_options": dOp
24e0: 74 69 6f 6e 73 20 7d 0a 20 20 20 20 20 20 20 20  tions }.        
24f0: 20 20 20 20 72 65 73 70 6f 6e 73 65 2e 73 65 74      response.set
2500: 5f 63 6f 6f 6b 69 65 28 22 75 73 65 72 5f 69 64  _cookie("user_id
2510: 22 2c 20 73 55 73 65 72 49 64 2c 20 70 61 74 68  ", sUserId, path
2520: 3d 22 2f 22 2c 20 6d 61 78 5f 61 67 65 3d 38 36  ="/", max_age=86
2530: 34 30 30 29 20 23 20 32 34 68 0a 20 20 20 20 20  400) # 24h.     
2540: 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 6a 73         return js
2550: 6f 6e 2e 64 75 6d 70 73 28 64 55 73 65 72 5b 73  on.dumps(dUser[s
2560: 55 73 65 72 49 64 5d 5b 22 67 63 5f 6f 70 74 69  UserId]["gc_opti
2570: 6f 6e 73 22 5d 2c 20 65 6e 73 75 72 65 5f 61 73  ons"], ensure_as
2580: 63 69 69 3d 46 61 6c 73 65 29 0a 20 20 20 20 20  cii=False).     
2590: 20 20 20 65 78 63 65 70 74 20 28 4b 65 79 45 72     except (KeyEr
25a0: 72 6f 72 2c 20 6a 73 6f 6e 2e 4a 53 4f 4e 44 65  ror, json.JSONDe
25b0: 63 6f 64 65 45 72 72 6f 72 29 3a 0a 20 20 20 20  codeError):.    
25c0: 20 20 20 20 20 20 20 20 74 72 61 63 65 62 61 63          tracebac
25d0: 6b 2e 70 72 69 6e 74 5f 65 78 63 28 29 0a 20 20  k.print_exc().  
25e0: 20 20 20 20 20 20 20 20 20 20 72 65 74 75 72 6e            return
25f0: 20 27 7b 22 65 72 72 6f 72 22 3a 20 22 4f 70 74   '{"error": "Opt
2600: 69 6f 6e 73 20 6e 6f 74 20 72 65 67 69 73 74 65  ions not registe
2610: 72 65 64 2e 22 7d 27 0a 20 20 20 20 72 65 74 75  red."}'.    retu
2620: 72 6e 20 27 7b 22 65 72 72 6f 72 22 3a 20 22 4e  rn '{"error": "N
2630: 6f 20 6f 70 74 69 6f 6e 73 20 72 65 63 65 69 76  o options receiv
2640: 65 64 2e 22 7d 27 0a 0a 40 61 70 70 2e 72 6f 75  ed."}'..@app.rou
2650: 74 65 28 22 2f 72 65 73 65 74 5f 6f 70 74 69 6f  te("/reset_optio
2660: 6e 73 2f 66 72 22 2c 20 6d 65 74 68 6f 64 3d 22  ns/fr", method="
2670: 50 4f 53 54 22 29 0a 64 65 66 20 72 65 73 65 74  POST").def reset
2680: 4f 70 74 69 6f 6e 73 20 28 29 3a 0a 20 20 20 20  Options ():.    
2690: 22 64 65 66 61 75 6c 74 20 67 72 61 6d 6d 61 72  "default grammar
26a0: 20 6f 70 74 69 6f 6e 73 22 0a 20 20 20 20 72 65   options".    re
26b0: 73 70 6f 6e 73 65 2e 73 65 74 5f 68 65 61 64 65  sponse.set_heade
26c0: 72 28 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65 22  r("Content-Type"
26d0: 2c 20 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f 6a  , "application/j
26e0: 73 6f 6e 3b 20 63 68 61 72 73 65 74 3d 55 54 46  son; charset=UTF
26f0: 2d 38 22 29 0a 20 20 20 20 69 66 20 72 65 71 75  -8").    if requ
2700: 65 73 74 2e 63 6f 6f 6b 69 65 73 2e 75 73 65 72  est.cookies.user
2710: 5f 69 64 20 61 6e 64 20 72 65 71 75 65 73 74 2e  _id and request.
2720: 63 6f 6f 6b 69 65 73 2e 75 73 65 72 5f 69 64 20  cookies.user_id 
2730: 69 6e 20 64 55 73 65 72 3a 0a 20 20 20 20 20 20  in dUser:.      
2740: 20 20 74 72 79 3a 0a 20 20 20 20 20 20 20 20 20    try:.         
2750: 20 20 20 64 65 6c 20 64 55 73 65 72 5b 72 65 71     del dUser[req
2760: 75 65 73 74 2e 63 6f 6f 6b 69 65 73 2e 75 73 65  uest.cookies.use
2770: 72 5f 69 64 5d 0a 20 20 20 20 20 20 20 20 65 78  r_id].        ex
2780: 63 65 70 74 20 4b 65 79 45 72 72 6f 72 3a 0a 20  cept KeyError:. 
2790: 20 20 20 20 20 20 20 20 20 20 20 72 65 74 75 72             retur
27a0: 6e 20 27 7b 22 65 72 72 6f 72 22 20 3a 20 22 55  n '{"error" : "U
27b0: 6e 6b 6e 6f 77 6e 20 75 73 65 72 2e 22 7d 27 0a  nknown user."}'.
27c0: 20 20 20 20 72 65 74 75 72 6e 20 27 7b 22 6d 65      return '{"me
27d0: 73 73 61 67 65 22 20 3a 20 22 44 6f 6e 65 2e 22  ssage" : "Done."
27e0: 7d 27 0a 0a 40 61 70 70 2e 72 6f 75 74 65 28 22  }'..@app.route("
27f0: 2f 66 6f 72 6d 61 74 5f 74 65 78 74 2f 66 72 22  /format_text/fr"
2800: 2c 20 6d 65 74 68 6f 64 3d 22 50 4f 53 54 22 29  , method="POST")
2810: 0a 64 65 66 20 66 6f 72 6d 61 74 54 65 78 74 20  .def formatText 
2820: 28 29 3a 0a 20 20 20 20 22 61 70 70 6c 79 20 74  ():.    "apply t
2830: 68 65 20 74 65 78 74 20 66 6f 72 6d 61 74 74 65  he text formatte
2840: 72 20 61 6e 64 20 72 65 74 75 72 6e 73 20 74 65  r and returns te
2850: 78 74 22 0a 20 20 20 20 72 65 74 75 72 6e 20 6f  xt".    return o
2860: 54 65 78 74 46 6f 72 6d 61 74 74 65 72 2e 66 6f  TextFormatter.fo
2870: 72 6d 61 74 54 65 78 74 28 72 65 71 75 65 73 74  rmatText(request
2880: 2e 66 6f 72 6d 73 2e 74 65 78 74 29 0a 0a 23 40  .forms.text)..#@
2890: 61 70 70 2e 72 6f 75 74 65 28 27 2f 73 74 61 74  app.route('/stat
28a0: 69 63 2f 3c 66 69 6c 65 70 61 74 68 3a 70 61 74  ic/<filepath:pat
28b0: 68 3e 27 29 0a 23 64 65 66 20 73 65 72 76 65 72  h>').#def server
28c0: 5f 73 74 61 74 69 63 20 28 66 69 6c 65 70 61 74  _static (filepat
28d0: 68 29 3a 0a 23 20 20 20 20 72 65 74 75 72 6e 20  h):.#    return 
28e0: 73 74 61 74 69 63 5f 66 69 6c 65 28 66 69 6c 65  static_file(file
28f0: 70 61 74 68 2c 20 72 6f 6f 74 3d 27 2e 2f 76 69  path, root='./vi
2900: 65 77 73 2f 73 74 61 74 69 63 27 29 0a 0a 40 61  ews/static')..@a
2910: 70 70 2e 72 6f 75 74 65 28 22 2f 73 75 67 67 65  pp.route("/sugge
2920: 73 74 2f 66 72 22 2c 20 6d 65 74 68 6f 64 3d 22  st/fr", method="
2930: 50 4f 53 54 22 29 0a 64 65 66 20 73 75 67 67 65  POST").def sugge
2940: 73 74 50 6f 73 74 20 28 29 3a 0a 20 20 20 20 72  stPost ():.    r
2950: 65 73 70 6f 6e 73 65 2e 73 65 74 5f 68 65 61 64  esponse.set_head
2960: 65 72 28 22 43 6f 6e 74 65 6e 74 2d 54 79 70 65  er("Content-Type
2970: 22 2c 20 22 61 70 70 6c 69 63 61 74 69 6f 6e 2f  ", "application/
2980: 6a 73 6f 6e 3b 20 63 68 61 72 73 65 74 3d 55 54  json; charset=UT
2990: 46 2d 38 22 29 0a 20 20 20 20 74 72 79 3a 0a 20  F-8").    try:. 
29a0: 20 20 20 20 20 20 20 78 46 75 74 75 72 65 20 3d         xFuture =
29b0: 20 78 50 72 6f 63 65 73 73 50 6f 6f 6c 45 78 65   xProcessPoolExe
29c0: 63 75 74 6f 72 2e 73 75 62 6d 69 74 28 73 75 67  cutor.submit(sug
29d0: 67 65 73 74 2c 20 72 65 71 75 65 73 74 2e 66 6f  gest, request.fo
29e0: 72 6d 73 2e 74 6f 6b 65 6e 29 0a 20 20 20 20 20  rms.token).     
29f0: 20 20 20 72 65 74 75 72 6e 20 78 46 75 74 75 72     return xFutur
2a00: 65 2e 72 65 73 75 6c 74 28 29 0a 20 20 20 20 65  e.result().    e
2a10: 78 63 65 70 74 20 28 63 6f 6e 63 75 72 72 65 6e  xcept (concurren
2a20: 74 2e 66 75 74 75 72 65 73 2e 54 69 6d 65 6f 75  t.futures.Timeou
2a30: 74 45 72 72 6f 72 2c 20 63 6f 6e 63 75 72 72 65  tError, concurre
2a40: 6e 74 2e 66 75 74 75 72 65 73 2e 43 61 6e 63 65  nt.futures.Cance
2a50: 6c 6c 65 64 45 72 72 6f 72 29 3a 0a 20 20 20 20  lledError):.    
2a60: 20 20 20 20 72 65 74 75 72 6e 20 27 7b 22 65 72      return '{"er
2a70: 72 6f 72 22 3a 20 22 41 6e 61 6c 79 73 69 73 20  ror": "Analysis 
2a80: 61 62 6f 72 74 65 64 20 28 74 69 6d 65 20 6f 75  aborted (time ou
2a90: 74 20 6f 72 20 63 61 6e 63 65 6c 6c 65 64 29 22  t or cancelled)"
2aa0: 7d 27 0a 20 20 20 20 65 78 63 65 70 74 20 63 6f  }'.    except co
2ab0: 6e 63 75 72 72 65 6e 74 2e 66 75 74 75 72 65 73  ncurrent.futures
2ac0: 2e 42 72 6f 6b 65 6e 45 78 65 63 75 74 6f 72 3a  .BrokenExecutor:
2ad0: 0a 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20  .        return 
2ae0: 27 7b 22 65 72 72 6f 72 22 3a 20 22 45 78 65 63  '{"error": "Exec
2af0: 75 74 6f 72 20 62 72 6f 6b 65 6e 2e 20 54 68 65  utor broken. The
2b00: 20 73 65 72 76 65 72 20 66 61 69 6c 65 64 2e 22   server failed."
2b10: 7d 27 0a 20 20 20 20 72 65 74 75 72 6e 20 27 7b  }'.    return '{
2b20: 22 65 72 72 6f 72 22 3a 20 22 46 61 74 61 6c 20  "error": "Fatal 
2b30: 65 72 72 6f 72 2e 20 54 68 65 20 73 65 72 76 65  error. The serve
2b40: 72 20 66 61 69 6c 65 64 2e 22 7d 27 0a 0a 0a 23  r failed."}'...#
2b50: 20 45 52 52 4f 52 0a 40 61 70 70 2e 65 72 72 6f   ERROR.@app.erro
2b60: 72 28 34 30 34 29 0a 64 65 66 20 65 72 72 6f 72  r(404).def error
2b70: 34 30 34 20 28 65 72 72 6f 72 29 3a 0a 20 20 20  404 (error):.   
2b80: 20 22 34 30 34 20 65 72 72 6f 72 20 70 61 67 65   "404 error page
2b90: 22 0a 20 20 20 20 72 65 74 75 72 6e 20 27 45 72  ".    return 'Er
2ba0: 72 6f 72 20 34 30 34 2e 3c 62 72 2f 3e 27 20 2b  ror 404.<br/>' +
2bb0: 20 73 74 72 28 65 72 72 6f 72 29 0a 0a 0a 23 23   str(error)...##
2bc0: 20 43 6f 6d 6d 6f 6e 20 66 75 6e 63 74 69 6f 6e   Common function
2bd0: 73 0a 0a 64 65 66 20 70 75 72 67 65 55 73 65 72  s..def purgeUser
2be0: 73 20 28 29 3a 0a 20 20 20 20 22 64 65 6c 65 74  s ():.    "delet
2bf0: 65 20 75 73 65 72 20 6f 70 74 69 6f 6e 73 20 6f  e user options o
2c00: 6c 64 65 72 20 74 68 61 6e 20 6e 20 68 6f 75 72  lder than n hour
2c10: 73 22 0a 20 20 20 20 74 72 79 3a 0a 20 20 20 20  s".    try:.    
2c20: 20 20 20 20 6e 4e 6f 77 4d 69 6e 75 73 4e 48 6f      nNowMinusNHo
2c30: 75 72 73 20 3d 20 69 6e 74 28 74 69 6d 65 2e 74  urs = int(time.t
2c40: 69 6d 65 28 29 29 20 2d 20 28 69 6e 74 28 72 65  ime()) - (int(re
2c50: 71 75 65 73 74 2e 66 6f 72 6d 73 2e 68 6f 75 72  quest.forms.hour
2c60: 73 29 20 2a 20 36 30 20 2a 20 36 30 29 0a 20 20  s) * 60 * 60).  
2c70: 20 20 20 20 20 20 66 6f 72 20 6e 55 73 65 72 49        for nUserI
2c80: 64 2c 20 64 56 61 6c 75 65 20 69 6e 20 64 55 73  d, dValue in dUs
2c90: 65 72 2e 69 74 65 6d 73 28 29 3a 0a 20 20 20 20  er.items():.    
2ca0: 20 20 20 20 20 20 20 20 69 66 20 64 56 61 6c 75          if dValu
2cb0: 65 5b 22 74 69 6d 65 22 5d 20 3c 20 6e 4e 6f 77  e["time"] < nNow
2cc0: 4d 69 6e 75 73 4e 48 6f 75 72 73 3a 0a 20 20 20  MinusNHours:.   
2cd0: 20 20 20 20 20 20 20 20 20 20 20 20 20 64 65 6c               del
2ce0: 20 64 55 73 65 72 5b 6e 55 73 65 72 49 64 5d 0a   dUser[nUserId].
2cf0: 20 20 20 20 20 20 20 20 72 65 74 75 72 6e 20 54          return T
2d00: 72 75 65 0a 20 20 20 20 65 78 63 65 70 74 20 4b  rue.    except K
2d10: 65 79 45 72 72 6f 72 3a 0a 20 20 20 20 20 20 20  eyError:.       
2d20: 20 74 72 61 63 65 62 61 63 6b 2e 70 72 69 6e 74   traceback.print
2d30: 5f 65 78 63 28 29 0a 20 20 20 20 20 20 20 20 72  _exc().        r
2d40: 65 74 75 72 6e 20 46 61 6c 73 65 0a 0a 0a 23 23  eturn False...##
2d50: 23 23 20 53 54 41 52 54 20 23 23 23 23 0a 0a 64  ## START ####..d
2d60: 65 66 20 6d 61 69 6e 20 28 73 48 6f 73 74 3d 22  ef main (sHost="
2d70: 6c 6f 63 61 6c 68 6f 73 74 22 2c 20 6e 50 6f 72  localhost", nPor
2d80: 74 3d 38 30 38 30 2c 20 64 4f 70 74 69 6f 6e 73  t=8080, dOptions
2d90: 3d 4e 6f 6e 65 2c 20 62 54 65 73 74 50 61 67 65  =None, bTestPage
2da0: 3d 46 61 6c 73 65 2c 20 6e 4d 75 6c 74 69 43 50  =False, nMultiCP
2db0: 55 3d 4e 6f 6e 65 29 3a 0a 20 20 20 20 22 73 74  U=None):.    "st
2dc0: 61 72 74 20 73 65 72 76 65 72 22 0a 20 20 20 20  art server".    
2dd0: 67 6c 6f 62 61 6c 20 54 45 53 54 50 41 47 45 0a  global TESTPAGE.
2de0: 20 20 20 20 67 6c 6f 62 61 6c 20 48 4f 4d 45 50      global HOMEP
2df0: 41 47 45 0a 0a 20 20 20 20 69 66 20 62 54 65 73  AGE..    if bTes
2e00: 74 50 61 67 65 3a 0a 20 20 20 20 20 20 20 20 54  tPage:.        T
2e10: 45 53 54 50 41 47 45 20 3d 20 54 72 75 65 0a 20  ESTPAGE = True. 
2e20: 20 20 20 20 20 20 20 48 4f 4d 45 50 41 47 45 20         HOMEPAGE 
2e30: 3d 20 48 4f 4d 45 50 41 47 45 2e 72 65 70 6c 61  = HOMEPAGE.repla
2e40: 63 65 28 22 7b 53 45 52 56 45 52 5f 50 4f 52 54  ce("{SERVER_PORT
2e50: 7d 22 2c 20 73 74 72 28 6e 50 6f 72 74 29 29 0a  }", str(nPort)).
2e60: 20 20 20 20 69 66 20 64 4f 70 74 69 6f 6e 73 3a      if dOptions:
2e70: 0a 20 20 20 20 20 20 20 20 6f 47 43 45 2e 73 65  .        oGCE.se
2e80: 74 4f 70 74 69 6f 6e 73 28 64 4f 70 74 69 6f 6e  tOptions(dOption
2e90: 73 29 0a 0a 20 20 20 20 23 20 50 79 74 68 6f 6e  s)..    # Python
2ea0: 20 76 65 72 73 69 6f 6e 0a 20 20 20 20 70 72 69   version.    pri
2eb0: 6e 74 28 22 50 79 74 68 6f 6e 3a 20 22 20 2b 20  nt("Python: " + 
2ec0: 73 79 73 2e 76 65 72 73 69 6f 6e 29 0a 20 20 20  sys.version).   
2ed0: 20 23 20 47 72 61 6d 6d 61 6c 65 63 74 65 0a 20   # Grammalecte. 
2ee0: 20 20 20 65 63 68 6f 28 22 47 72 61 6d 6d 61 6c     echo("Grammal
2ef0: 65 63 74 65 20 76 7b 7d 22 2e 66 6f 72 6d 61 74  ecte v{}".format
2f00: 28 6f 47 43 45 2e 76 65 72 73 69 6f 6e 29 29 0a  (oGCE.version)).
2f10: 20 20 20 20 6f 47 43 45 2e 64 69 73 70 6c 61 79      oGCE.display
2f20: 4f 70 74 69 6f 6e 73 28 29 0a 20 20 20 20 23 20  Options().    # 
2f30: 50 72 6f 63 65 73 73 20 50 6f 6f 6c 20 45 78 65  Process Pool Exe
2f40: 63 75 74 6f 72 0a 20 20 20 20 69 6e 69 74 45 78  cutor.    initEx
2f50: 65 63 75 74 6f 72 28 6e 4d 75 6c 74 69 43 50 55  ecutor(nMultiCPU
2f60: 29 0a 20 20 20 20 23 20 53 65 72 76 65 72 20 28  ).    # Server (
2f70: 42 6f 74 74 6c 65 29 0a 20 20 20 20 72 75 6e 28  Bottle).    run(
2f80: 61 70 70 2c 20 68 6f 73 74 3d 73 48 6f 73 74 2c  app, host=sHost,
2f90: 20 70 6f 72 74 3d 6e 50 6f 72 74 29 0a 0a 0a 69   port=nPort)...i
2fa0: 66 20 5f 5f 6e 61 6d 65 5f 5f 20 3d 3d 20 27 5f  f __name__ == '_
2fb0: 5f 6d 61 69 6e 5f 5f 27 3a 0a 20 20 20 20 78 50  _main__':.    xP
2fc0: 61 72 73 65 72 20 3d 20 61 72 67 70 61 72 73 65  arser = argparse
2fd0: 2e 41 72 67 75 6d 65 6e 74 50 61 72 73 65 72 28  .ArgumentParser(
2fe0: 29 0a 20 20 20 20 23 78 50 61 72 73 65 72 2e 61  ).    #xParser.a
2ff0: 64 64 5f 61 72 67 75 6d 65 6e 74 28 22 6c 61 6e  dd_argument("lan
3000: 67 22 2c 20 74 79 70 65 3d 73 74 72 2c 20 6e 61  g", type=str, na
3010: 72 67 73 3d 27 2b 27 2c 20 68 65 6c 70 3d 22 6c  rgs='+', help="l
3020: 61 6e 67 20 70 72 6f 6a 65 63 74 20 74 6f 20 67  ang project to g
3030: 65 6e 65 72 61 74 65 20 28 6e 61 6d 65 20 6f 66  enerate (name of
3040: 20 66 6f 6c 64 65 72 20 69 6e 20 2f 6c 61 6e 67   folder in /lang
3050: 29 22 29 0a 20 20 20 20 78 50 61 72 73 65 72 2e  )").    xParser.
3060: 61 64 64 5f 61 72 67 75 6d 65 6e 74 28 22 2d 68  add_argument("-h
3070: 74 22 2c 20 22 2d 2d 68 6f 73 74 22 2c 20 68 65  t", "--host", he
3080: 6c 70 3d 22 68 6f 73 74 20 28 64 65 66 61 75 6c  lp="host (defaul
3090: 74 3a 20 6c 6f 63 61 6c 68 6f 73 74 29 22 2c 20  t: localhost)", 
30a0: 74 79 70 65 3d 73 74 72 29 0a 20 20 20 20 78 50  type=str).    xP
30b0: 61 72 73 65 72 2e 61 64 64 5f 61 72 67 75 6d 65  arser.add_argume
30c0: 6e 74 28 22 2d 70 22 2c 20 22 2d 2d 70 6f 72 74  nt("-p", "--port
30d0: 22 2c 20 68 65 6c 70 3d 22 70 6f 72 74 20 28 64  ", help="port (d
30e0: 65 66 61 75 6c 74 3a 20 38 30 38 30 29 22 2c 20  efault: 8080)", 
30f0: 74 79 70 65 3d 69 6e 74 29 0a 20 20 20 20 78 50  type=int).    xP
3100: 61 72 73 65 72 2e 61 64 64 5f 61 72 67 75 6d 65  arser.add_argume
3110: 6e 74 28 22 2d 6d 70 22 2c 20 22 2d 2d 6d 75 6c  nt("-mp", "--mul
3120: 74 69 70 72 6f 63 65 73 73 6f 72 22 2c 20 68 65  tiprocessor", he
3130: 6c 70 3d 22 64 65 66 69 6e 65 20 68 6f 77 20 6d  lp="define how m
3140: 61 6e 79 20 70 72 6f 63 65 73 73 65 73 20 66 6f  any processes fo
3150: 72 20 74 68 65 20 67 72 61 6d 6d 61 72 20 63 68  r the grammar ch
3160: 65 63 6b 65 72 22 2c 20 74 79 70 65 3d 69 6e 74  ecker", type=int
3170: 29 0a 20 20 20 20 78 50 61 72 73 65 72 2e 61 64  ).    xParser.ad
3180: 64 5f 61 72 67 75 6d 65 6e 74 28 22 2d 74 22 2c  d_argument("-t",
3190: 20 22 2d 2d 74 65 73 74 5f 70 61 67 65 22 2c 20   "--test_page", 
31a0: 68 65 6c 70 3d 22 70 61 67 65 20 74 6f 20 74 65  help="page to te
31b0: 73 74 20 74 68 65 20 73 65 72 76 65 72 20 6f 6e  st the server on
31c0: 20 2f 22 2c 20 61 63 74 69 6f 6e 3d 22 73 74 6f   /", action="sto
31d0: 72 65 5f 74 72 75 65 22 29 0a 20 20 20 20 78 50  re_true").    xP
31e0: 61 72 73 65 72 2e 61 64 64 5f 61 72 67 75 6d 65  arser.add_argume
31f0: 6e 74 28 22 2d 6f 6e 22 2c 20 22 2d 2d 6f 70 74  nt("-on", "--opt
3200: 5f 6f 6e 22 2c 20 6e 61 72 67 73 3d 22 2b 22 2c  _on", nargs="+",
3210: 20 68 65 6c 70 3d 22 61 63 74 69 76 61 74 65 20   help="activate 
3220: 6f 70 74 69 6f 6e 73 22 29 0a 20 20 20 20 78 50  options").    xP
3230: 61 72 73 65 72 2e 61 64 64 5f 61 72 67 75 6d 65  arser.add_argume
3240: 6e 74 28 22 2d 6f 66 66 22 2c 20 22 2d 2d 6f 70  nt("-off", "--op
3250: 74 5f 6f 66 66 22 2c 20 6e 61 72 67 73 3d 22 2b  t_off", nargs="+
3260: 22 2c 20 68 65 6c 70 3d 22 64 65 61 63 74 69 76  ", help="deactiv
3270: 61 74 65 20 6f 70 74 69 6f 6e 73 22 29 0a 20 20  ate options").  
3280: 20 20 78 41 72 67 73 20 3d 20 78 50 61 72 73 65    xArgs = xParse
3290: 72 2e 70 61 72 73 65 5f 61 72 67 73 28 29 0a 0a  r.parse_args()..
32a0: 20 20 20 20 64 4f 70 74 20 3d 20 4e 6f 6e 65 0a      dOpt = None.
32b0: 20 20 20 20 69 66 20 78 41 72 67 73 2e 6f 70 74      if xArgs.opt
32c0: 5f 6f 6e 20 20 6f 72 20 20 78 41 72 67 73 2e 6f  _on  or  xArgs.o
32d0: 70 74 5f 6f 66 66 3a 0a 20 20 20 20 20 20 20 20  pt_off:.        
32e0: 64 4f 70 74 20 3d 20 7b 7d 0a 20 20 20 20 20 20  dOpt = {}.      
32f0: 20 20 69 66 20 78 41 72 67 73 2e 6f 70 74 5f 6f    if xArgs.opt_o
3300: 6e 3a 0a 20 20 20 20 20 20 20 20 20 20 20 20 64  n:.            d
3310: 4f 70 74 20 3d 20 7b 20 6f 70 74 3a 54 72 75 65  Opt = { opt:True
3320: 20 20 66 6f 72 20 6f 70 74 20 69 6e 20 78 41 72    for opt in xAr
3330: 67 73 2e 6f 70 74 5f 6f 6e 20 7d 0a 20 20 20 20  gs.opt_on }.    
3340: 20 20 20 20 69 66 20 78 41 72 67 73 2e 6f 70 74      if xArgs.opt
3350: 5f 6f 66 66 3a 0a 20 20 20 20 20 20 20 20 20 20  _off:.          
3360: 20 20 64 4f 70 74 2e 75 70 64 61 74 65 28 7b 20    dOpt.update({ 
3370: 6f 70 74 3a 46 61 6c 73 65 20 20 66 6f 72 20 6f  opt:False  for o
3380: 70 74 20 69 6e 20 78 41 72 67 73 2e 6f 70 74 5f  pt in xArgs.opt_
3390: 6f 66 66 20 7d 29 0a 0a 20 20 20 20 6d 61 69 6e  off })..    main
33a0: 28 78 41 72 67 73 2e 68 6f 73 74 20 6f 72 20 22  (xArgs.host or "
33b0: 6c 6f 63 61 6c 68 6f 73 74 22 2c 20 5c 0a 20 20  localhost", \.  
33c0: 20 20 20 20 20 20 20 78 41 72 67 73 2e 70 6f 72         xArgs.por
33d0: 74 20 6f 72 20 38 30 38 30 2c 20 5c 0a 20 20 20  t or 8080, \.   
33e0: 20 20 20 20 20 20 64 4f 70 74 2c 0a 20 20 20 20        dOpt,.    
33f0: 20 20 20 20 20 78 41 72 67 73 2e 74 65 73 74 5f       xArgs.test_
3400: 70 61 67 65 2c 0a 20 20 20 20 20 20 20 20 20 78  page,.         x
3410: 41 72 67 73 2e 6d 75 6c 74 69 70 72 6f 63 65 73  Args.multiproces
3420: 73 6f 72 29 0a                                   sor).