olivier.massot il y a 7 ans
commit
d0d6b8a4ba
73 fichiers modifiés avec 2754 ajouts et 0 suppressions
  1. 7 0
      .gitignore
  2. 4 0
      .settings/org.eclipse.core.resources.prefs
  3. 117 0
      2d_dichotomy.py
  4. BIN
      Hermes/MCD.mwb
  5. BIN
      Hermes/MCD.mwb.bak
  6. BIN
      Hermes/Projet.docx
  7. 24 0
      Hermes/a faire.TXT
  8. BIN
      Hermes/developpez-votre-site-web-avec-le-framework-django.pdf
  9. BIN
      Hermes/exemple_cahier_charges.docx
  10. 1 0
      Hermes/hermes
  11. BIN
      Hermes/mockups.bmpr
  12. BIN
      Hermes/mockups/ecran_tel_0.bmpr
  13. BIN
      Hermes/mockups/ecran_tel_0.png
  14. BIN
      Hermes/~$Projet.docx
  15. 182 0
      barry/ffile.py
  16. 37 0
      barry/gui.py
  17. BIN
      barry/logo256.png
  18. BIN
      barry/logo32.png
  19. 20 0
      barry/os_.py
  20. 1 0
      barry/thanks.txt
  21. 4 0
      bot/.settings/org.eclipse.core.resources.prefs
  22. 13 0
      bot/cli.py
  23. 48 0
      bot/config.py
  24. 0 0
      bot/core/__init__.py
  25. 314 0
      bot/core/glossary.py
  26. 20 0
      bot/core/spacy_helper.py
  27. 8 0
      bot/core/speedup.py
  28. 228 0
      bot/debug.log
  29. 37 0
      bot/logging.yaml
  30. 0 0
      bot/memory/__init__.py
  31. 1 0
      bot/memory/data.json
  32. 22 0
      bot/memory/question.py
  33. 250 0
      bot/old/bot_1.py
  34. 46 0
      bot/old/bot_2.py
  35. 39 0
      bot/old/pos_tags.txt
  36. 81 0
      bot/old/test.py
  37. 206 0
      bot/pos_tags.html
  38. 2 0
      bot/requirements.txt
  39. 43 0
      bot/results.txt
  40. 62 0
      bot/sbot.py
  41. BIN
      bot/wheels/cymem-1.31.2-cp36-cp36m-win32.whl
  42. BIN
      bot/wheels/cytoolz-0.9.0-cp36-cp36m-win32.whl
  43. BIN
      bot/wheels/msgpack-0.5.1-cp36-cp36m-win32.whl
  44. BIN
      bot/wheels/murmurhash-0.28.0-cp36-cp36m-win32.whl
  45. BIN
      bot/wheels/preshed-1.0.0-cp36-cp36m-win32.whl
  46. 10 0
      bot/wheels/requirements.txt
  47. BIN
      bot/wheels/spacy-2.0.5-cp36-cp36m-win32.whl
  48. BIN
      bot/wheels/thinc-6.10.2-cp36-cp36m-win32.whl
  49. BIN
      bot/wheels/ujson-1.35-cp36-cp36m-win32.whl
  50. 0 0
      dbsearch.py
  51. 39 0
      dichotomy.py
  52. 11 0
      hello_world/.gitignore
  53. 2 0
      hello_world/MANIFEST.in
  54. 32 0
      hello_world/README.md
  55. 5 0
      hello_world/lib/__init__.py
  56. 18 0
      hello_world/lib/core.py
  57. 7 0
      hello_world/nose2.cfg
  58. 3 0
      hello_world/requirements.txt
  59. 86 0
      hello_world/setup.py
  60. 14 0
      hello_world/test.py
  61. 460 0
      landing.py
  62. 7 0
      mon_appli.py
  63. 4 0
      mon_programme.py
  64. 8 0
      orion_bkp.py
  65. 48 0
      setup.py
  66. 11 0
      shortuid.py
  67. 19 0
      symlinks.py
  68. 10 0
      test.py
  69. 22 0
      test_profiling.py
  70. 19 0
      test_stringio.py
  71. 29 0
      uid.py
  72. 49 0
      whr.py
  73. 24 0
      yaml_.py

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+*.pyc
+.project
+.pydevproject
+Output/
+htmlcov/
+*.accdb
+*.laccdb

+ 4 - 0
.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//pardit_old/pdfform.py=UTF-8
+encoding//pypog/tools/gridviewer/qt_viewer.py=utf-8
+encoding/<project>=UTF-8

+ 117 - 0
2d_dichotomy.py

@@ -0,0 +1,117 @@
+import math
+import sys
+
+
+w, h = [int(i) for i in input().split()]
+# grid = [(x, y) for x in range(0, w) for y in range(0, h)]
+
+grid = [[1 for _ in range(w)] for _ in range(h)]
+NULL_ROW = [0 for _ in range(w)]
+
+
+n = int(input())  # maximum number of turns before game over.
+x0, y0 = [int(i) for i in input().split()]
+
+# handle special case
+if w + h <= 2:
+    input()
+    print("0 0")
+
+DETECT = {"COLDER":-1, "WARMER": 1, "SAME": 0}
+
+def distance(xa, ya, xb, yb):
+    return math.hypot((xb - xa), (ya - yb))
+
+def _centroid(data):
+    xs, ys = zip(*data)
+    return int(sum(xs) / len(xs)), int(sum(ys) / len(xs))
+
+def centroid(table):
+    yc = int(sum([y * sum(row) for y, row in enumerate(table)]) / sum(sum(table, [])))
+    xc = int(sum([x * v for x, v in enumerate(table[yc])]) / sum(table[yc]))
+    return xc, yc
+
+def search2d(grid, _send, _get):
+    # dichotomy on 3d list
+    icur, jcur = x0, y0
+    input()
+
+    while 1:
+        inew, jnew = centroid(grid)
+        while not grid[jnew][inew]:
+            inew = (inew + 1) % w
+            jnew = (jnew + 1) % h
+
+        # print("send new ", inew, jnew, file=sys.stderr)
+
+        _send(inew, jnew)
+
+        ddist = _get()
+        # print("ddist= ", ddist, file=sys.stderr)
+        if ddist > 0:
+            grid = [[int(v == 1 and distance(i, j, inew, jnew) < distance(i, j, icur, jcur)) \
+                    for i, v in enumerate(row)]  for j, row in enumerate(grid)]
+        elif ddist < 0:
+            grid = [[int(v == 1 and distance(i, j, inew, jnew) > distance(i, j, icur, jcur)) \
+                    for i, v in enumerate(row)]  for j, row in enumerate(grid)]
+        else:
+            grid = [[int(v == 1 and distance(i, j, inew, jnew) == distance(i, j, icur, jcur)) \
+                    for i, v in enumerate(row)]  for j, row in enumerate(grid)]
+
+        # print("grid : ", grid, file=sys.stderr)
+        if len(grid) == 1:
+            return grid.pop()
+
+        # remove the current cell
+        grid[jcur][icur] = 0
+        icur, jcur = inew, jnew
+
+
+def old_search2d(grid, _send, _get):
+    # dichotomy on 3d list
+    x, y = x0, y0
+    xnew, ynew = x, y
+    input()
+    if len(grid) == 1:
+        return 0, 0
+
+    while 1:
+        imid = int(len(grid) / 2)
+        # print("middle of ", grid, file=sys.stderr)
+
+        while (xnew, ynew) == (x, y):
+            try:
+                xnew, ynew = grid[imid]
+                imid = (imid + 1) % len(grid)
+            except IndexError:
+                pass
+
+        # print("send new ", xnew, ynew, file=sys.stderr)
+
+        _send(xnew, ynew)
+
+        ddist = _get()
+        # print("ddist= ", ddist, file=sys.stderr)
+        if ddist > 0:
+            grid = [(xc, yc) for xc, yc in grid if distance(xc, yc, xnew, ynew) < distance(xc, yc, x, y)]
+        elif ddist < 0:
+            grid = [(xc, yc) for xc, yc in grid if distance(xc, yc, xnew, ynew) > distance(xc, yc, x, y)]
+        else:
+            grid = [(xc, yc) for xc, yc in grid if distance(xc, yc, xnew, ynew) == distance(xc, yc, x, y)]
+
+        # print("grid : ", grid, file=sys.stderr)
+        if len(grid) == 1:
+            return grid.pop()
+
+        x, y = xnew, ynew
+
+def get_data():
+    return DETECT[input()]
+
+def send_data(x, y):
+    print("{} {}".format(x, y))
+
+x, y = search2d(grid, send_data, get_data)
+
+
+

BIN
Hermes/MCD.mwb


BIN
Hermes/MCD.mwb.bak


BIN
Hermes/Projet.docx


+ 24 - 0
Hermes/a faire.TXT

@@ -0,0 +1,24 @@
+# stockage de fichiers
+
+# hebergement
+
+# compatibilité tel/pc
+
+# cryptage, sécurité
+
+# sauvegardes
+
+# github opensource
+
+# MCD
+
+# développement
+
+
+Documents: notes, evenements, listes à-faire, fichiers, contacts, marques-pages ...
+
+Analyses: calendrier, synthèses, lecteur musique/video, affichage d'images
+
+CSS
+
+Traduction

BIN
Hermes/developpez-votre-site-web-avec-le-framework-django.pdf


BIN
Hermes/exemple_cahier_charges.docx


+ 1 - 0
Hermes/hermes

@@ -0,0 +1 @@
+Subproject commit 56687955342a78172585040601ada2033966d4ab

BIN
Hermes/mockups.bmpr


BIN
Hermes/mockups/ecran_tel_0.bmpr


BIN
Hermes/mockups/ecran_tel_0.png


BIN
Hermes/~$Projet.docx


+ 182 - 0
barry/ffile.py

@@ -0,0 +1,182 @@
+# python3X
+"""
+    Convenient methods for managing files and directories
+    @author: olivier.massot
+"""
+import os
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+
+
+__VERSION__ = "0.2"
+
+
+def fdir(path):
+    """return the parent directory of the file or directory
+    (no file existence control)"""
+    if path[-1] == os.path.sep:
+        path = os.path.dirname(path)
+    return os.path.normpath(os.path.dirname(path))
+
+def fname(path, with_ext=True):
+    """return the name of the file
+    with_ext: return the name with its extension
+    (no file existence control)"""
+    if not with_ext:
+        path, _ = os.path.splitext(path)
+    if path[-1] == os.path.sep:
+        path = os.path.dirname(path)
+    return os.path.basename(path)
+
+def fext(path):
+    """return the extension of the file
+    (no file existence control)"""
+    return os.path.splitext(path)[1]
+
+def fabs(path):
+    """ return the absolute expanded normalized path """
+    return os.path.abspath(os.path.expandvars(os.path.expanduser(path)))
+
+def fexists(path):
+    """test the existence of a file or directory"""
+    return os.path.exists(os.path.normpath(path))
+
+def fisurl(instr):
+    """return True if the 'instr' is a web url"""
+    return re.match(r"((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)", \
+                    instr)
+
+def fstart(path):
+    """run/open the required file, directory, webpage... with the default software"""
+    if not fisurl(path):
+        path = os.path.normpath(path)
+    if sys.platform == "win32":
+        os.startfile(path)
+    else:
+        opener = "open" if sys.platform == "darwin" else "xdg-open"
+        subprocess.call([opener, path])
+
+def flist(target_dir, files=True, dirs=False, recursive=True):
+    """list the subdirectories and/or the files 'relative paths inside the directory
+    """
+    if not os.path.isdir(target_dir):
+        raise NotADirectoryError("'{var}' is not an existing directory".format(var=target_dir))
+    result = []
+    for _root, _dirs, _files in os.walk(target_dir):
+        rel_dir = os.path.relpath(_root, target_dir)
+        if dirs:
+            result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _dirs)
+        if files:
+            result += map(lambda p: (os.path.join(rel_dir, p) if rel_dir != "." else p), _files)
+        if not recursive:
+            break
+    return result
+
+def ftree(dirpath):
+    """ return a set (unordered) of the paths of the sub-directories
+    """
+    result = set()
+    for sdpath in (os.path.join(dirpath, dirname) for dirname \
+                   in os.listdir(dirpath)):
+        if os.path.isdir(sdpath):
+            sdpath = os.path.normpath(sdpath)
+            result |= ({sdpath} | ftree(sdpath))
+    return result
+
+def fcopy(source, targetdir, rename_as="", erase=False):
+    """ copy the file to the target directory
+    > return the path of the newly created file
+    """
+    source = fabs(source)
+    targetdir = fabs(targetdir)
+
+    if not os.path.isdir(targetdir):
+        raise NotADirectoryError("'targetdir' has to be a directory (given: {})".format(targetdir))
+
+    target_name, target_ext = os.path.splitext(fname(source)) if not rename_as else os.path.splitext(rename_as)
+    targetpath = os.path.join(targetdir, '%s%s' % (target_name, target_ext))
+
+    if not erase and fexists(targetpath):
+        targetpath = next((os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext)) for counter in range(2, 100000000) \
+                           if not fexists(os.path.join(targetdir, '%s_%d%s' % (target_name, counter, target_ext)))))
+
+
+    shutil.copyfile(source, targetpath)
+    return targetpath
+
+def faccess(path, mode="r"):
+    """ return True if user has an access to the directory or file in parameter
+    """
+    path = os.path.normpath(path)
+
+    if os.path.isfile(path):
+        try:
+            with open(path, mode) as _:
+                pass
+            return True
+        except PermissionError:
+            return False
+        except FileNotFoundError:
+            # could be a directory
+            pass
+        except OSError:
+            pass
+
+    if os.path.isdir(path):
+        # try to explore as a directory
+        if mode[0] == "r":
+            try:
+                next(os.walk(path))
+            except StopIteration:
+                return False
+            return True
+
+        else:
+            try:
+                with tempfile.TemporaryFile(mode=mode, dir=path) as _:
+                    pass
+                return True
+            except PermissionError:
+                return False
+
+    raise FileNotFoundError("'{path}' does not exist or is unaccessible".format(path=path))
+
+def fjoin(*args):
+    """recursively normalize and join the paths in args"""
+    return os.path.join(*[os.path.normpath(p) for p in args])
+
+def fmkdir(dirpath):
+    """ create the directory if it do not exist """
+    if not os.path.exists(dirpath):
+        os.mkdir(dirpath)
+
+def fmktree(dirpath):
+    """ create the directory recursively """
+    if not os.path.exists(os.path.dirname(dirpath)):
+        fmktree(os.path.dirname(dirpath))
+    fmkdir(dirpath)
+
+def frmtree(dirpath):
+    """recursively delete the directory, even it is not empty"""
+    try:
+        shutil.rmtree(dirpath)
+    except FileNotFoundError:
+        pass
+
+def fhumansize(size):
+    """return a human readable size of file"""
+    suffixes = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB']
+    suffix_index = 0
+    while size >= 1024:
+        suffix_index += 1  # increment the index of the suffix
+        size /= 1024.0  # apply the division
+    return "{0:03.2f} {1}".format(size, suffixes[suffix_index]) if suffix_index > 0 \
+           else "{0} {1}".format(size, suffixes[suffix_index])
+
+def fsub(dir1, dir2):
+    """ return True if dir1 a sub-directory of dir2 """
+    dir1, dir2 = map(fabs, [dir1, dir2])
+    return os.path.commonprefix([dir1, dir2]) == dir2

+ 37 - 0
barry/gui.py

@@ -0,0 +1,37 @@
+'''
+Created on 1 mars 2017
+
+@author: olivier.massot
+'''
+from tkinter import ttk, Tk, Canvas, PhotoImage
+from tkinter.constants import CENTER
+
+
+class Application(ttk.Frame):
+    def __init__(self, master=None):
+        super().__init__(master)
+        self.pack()
+        self.create_widgets()
+
+    def create_widgets(self):
+
+        self.logo = PhotoImage(file="logo32.png")
+
+        self.canvas = Canvas(self, width=100, height=100)
+        self.canvas.create_image(20, 20, anchor=CENTER, image=self.logo)
+        self.canvas.pack()
+
+        self.hi_there = ttk.Button(self)
+        self.hi_there["text"] = "Hello World\n(click me)"
+        self.hi_there["command"] = self.say_hi
+        self.hi_there.pack()
+
+        self.quit = ttk.Button(self, text="QUIT", command=root.destroy)
+        self.quit.pack()
+
+    def say_hi(self):
+        print("hi there, everyone!")
+
+root = Tk()
+app = Application(master=root)
+app.mainloop()

BIN
barry/logo256.png


BIN
barry/logo32.png


+ 20 - 0
barry/os_.py

@@ -0,0 +1,20 @@
+'''
+Created on 1 mars 2017
+
+@author: olivier.massot
+'''
+import os
+import socket
+import uuid
+
+def iswin():
+    return "nt" in os.name
+
+def current_drive_letter():
+    return os.path.splitdrive(__file__)[0]
+
+def cpuname():
+    return socket.gethostname()
+
+def mac_adress():
+    return hex(uuid.getnode())

+ 1 - 0
barry/thanks.txt

@@ -0,0 +1 @@
+<div>Icons made by <a href="http://www.freepik.com" title="Freepik">Freepik</a> from <a href="http://www.flaticon.com" title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>

+ 4 - 0
bot/.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//core/glossary.py=utf8
+encoding//old/test.py=utf-8
+encoding/<project>=UTF-8

+ 13 - 0
bot/cli.py

@@ -0,0 +1,13 @@
+'''
+Created on 19 janv. 2018
+
+@author: olivier.massot
+'''
+from sbot import answer_to
+
+if __name__ == '__main__':
+    print("Oui monsieur?")
+    msg = input("> ")
+    while msg != ".":
+        print(answer_to(msg))
+        msg = input("> ")

+ 48 - 0
bot/config.py

@@ -0,0 +1,48 @@
+FILTER_WORDS = set([
+    "skank",
+    "wetback",
+    "bitch",
+    "cunt",
+    "dick",
+    "douchebag",
+    "dyke",
+    "fag",
+    "nigger",
+    "tranny",
+    "trannies",
+    "paki",
+    "pussy",
+    "retard",
+    "slut",
+    "titt",
+    "tits",
+    "wop",
+    "whore",
+    "chink",
+    "fatass",
+    "shemale",
+    "nigga",
+    "daygo",
+    "dego",
+    "dago",
+    "gook",
+    "kike",
+    "kraut",
+    "spic",
+    "twat",
+    "lesbo",
+    "homo",
+    "fatso",
+    "lardass",
+    "jap",
+    "biatch",
+    "tard",
+    "gimp",
+    "gyp",
+    "chinaman",
+    "chinamen",
+    "golliwog",
+    "crip",
+    "raghead",
+    "negro",
+"hooker"])

+ 0 - 0
bot/core/__init__.py


+ 314 - 0
bot/core/glossary.py

@@ -0,0 +1,314 @@
+# coding: utf8
+from __future__ import unicode_literals
+
+
+def explain(term):
+    """Get a description for a given POS tag, dependency label or entity type.
+
+    term (unicode): The term to explain.
+    RETURNS (unicode): The explanation, or `None` if not found in the glossary.
+
+    EXAMPLE:
+        >>> spacy.explain(u'NORP')
+        >>> doc = nlp(u'Hello world')
+        >>> print([w.text, w.tag_, spacy.explain(w.tag_) for w in doc])
+    """
+    if term in GLOSSARY:
+        return GLOSSARY[term]
+
+
+GLOSSARY = {
+    # POS tags
+    # Universal POS Tags
+    # http://universaldependencies.org/u/pos/
+
+    'ADJ':          'adjective',
+    'ADP':          'adposition',
+    'ADV':          'adverb',
+    'AUX':          'auxiliary',
+    'CONJ':         'conjunction',
+    'CCONJ':        'coordinating conjunction',
+    'DET':          'determiner',
+    'INTJ':         'interjection',
+    'NOUN':         'noun',
+    'NUM':          'numeral',
+    'PART':         'particle',
+    'PRON':         'pronoun',
+    'PROPN':        'proper noun',
+    'PUNCT':        'punctuation',
+    'SCONJ':        'subordinating conjunction',
+    'SYM':          'symbol',
+    'VERB':         'verb',
+    'X':            'other',
+    'EOL':          'end of line',
+    'SPACE':        'space',
+
+
+    # POS tags (English)
+    # OntoNotes 5 / Penn Treebank
+    # https://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html
+
+    '.':            'punctuation mark, sentence closer',
+    ',':            'punctuation mark, comma',
+    '-LRB-':        'left round bracket',
+    '-RRB-':        'right round bracket',
+    '``':           'opening quotation mark',
+    '""':           'closing quotation mark',
+    "''":           'closing quotation mark',
+    ':':            'punctuation mark, colon or ellipsis',
+    '$':            'symbol, currency',
+    '#':            'symbol, number sign',
+    'AFX':          'affix',
+    'CC':           'conjunction, coordinating',
+    'CD':           'cardinal number',
+    'DT':           'determiner',
+    'EX':           'existential there',
+    'FW':           'foreign word',
+    'HYPH':         'punctuation mark, hyphen',
+    'IN':           'conjunction, subordinating or preposition',
+    'JJ':           'adjective',
+    'JJR':          'adjective, comparative',
+    'JJS':          'adjective, superlative',
+    'LS':           'list item marker',
+    'MD':           'verb, modal auxiliary',
+    'NIL':          'missing tag',
+    'NN':           'noun, singular or mass',
+    'NNP':          'noun, proper singular',
+    'NNPS':         'noun, proper plural',
+    'NNS':          'noun, plural',
+    'PDT':          'predeterminer',
+    'POS':          'possessive ending',
+    'PRP':          'pronoun, personal',
+    'PRP$':         'pronoun, possessive',
+    'RB':           'adverb',
+    'RBR':          'adverb, comparative',
+    'RBS':          'adverb, superlative',
+    'RP':           'adverb, particle',
+    'TO':           'infinitival to',
+    'UH':           'interjection',
+    'VB':           'verb, base form',
+    'VBD':          'verb, past tense',
+    'VBG':          'verb, gerund or present participle',
+    'VBN':          'verb, past participle',
+    'VBP':          'verb, non-3rd person singular present',
+    'VBZ':          'verb, 3rd person singular present',
+    'WDT':          'wh-determiner',
+    'WP':           'wh-pronoun, personal',
+    'WP$':          'wh-pronoun, possessive',
+    'WRB':          'wh-adverb',
+    'SP':           'space',
+    'ADD':          'email',
+    'NFP':          'superfluous punctuation',
+    'GW':           'additional word in multi-word expression',
+    'XX':           'unknown',
+    'BES':          'auxiliary "be"',
+    'HVS':          'forms of "have"',
+
+
+    # POS Tags (German)
+    # TIGER Treebank
+    # http://www.ims.uni-stuttgart.de/forschung/ressourcen/korpora/TIGERCorpus/annotation/tiger_introduction.pdf
+
+    '$(':           'other sentence-internal punctuation mark',
+    '$,':           'comma',
+    '$.':           'sentence-final punctuation mark',
+    'ADJA':         'adjective, attributive',
+    'ADJD':         'adjective, adverbial or predicative',
+    'APPO':         'postposition',
+    'APRP':         'preposition; circumposition left',
+    'APPRART':      'preposition with article',
+    'APZR':         'circumposition right',
+    'ART':          'definite or indefinite article',
+    'CARD':         'cardinal number',
+    'FM':           'foreign language material',
+    'ITJ':          'interjection',
+    'KOKOM':        'comparative conjunction',
+    'KON':          'coordinate conjunction',
+    'KOUI':         'subordinate conjunction with "zu" and infinitive',
+    'KOUS':         'subordinate conjunction with sentence',
+    'NE':           'proper noun',
+    'NNE':          'proper noun',
+    'PAV':          'pronominal adverb',
+    'PROAV':        'pronominal adverb',
+    'PDAT':         'attributive demonstrative pronoun',
+    'PDS':          'substituting demonstrative pronoun',
+    'PIAT':         'attributive indefinite pronoun without determiner',
+    'PIDAT':        'attributive indefinite pronoun with determiner',
+    'PIS':          'substituting indefinite pronoun',
+    'PPER':         'non-reflexive personal pronoun',
+    'PPOSAT':       'attributive possessive pronoun',
+    'PPOSS':        'substituting possessive pronoun',
+    'PRELAT':       'attributive relative pronoun',
+    'PRELS':        'substituting relative pronoun',
+    'PRF':          'reflexive personal pronoun',
+    'PTKA':         'particle with adjective or adverb',
+    'PTKANT':       'answer particle',
+    'PTKNEG':       'negative particle',
+    'PTKVZ':        'separable verbal particle',
+    'PTKZU':        '"zu" before infinitive',
+    'PWAT':         'attributive interrogative pronoun',
+    'PWAV':         'adverbial interrogative or relative pronoun',
+    'PWS':          'substituting interrogative pronoun',
+    'TRUNC':        'word remnant',
+    'VAFIN':        'finite verb, auxiliary',
+    'VAIMP':        'imperative, auxiliary',
+    'VAINF':        'infinitive, auxiliary',
+    'VAPP':         'perfect participle, auxiliary',
+    'VMFIN':        'finite verb, modal',
+    'VMINF':        'infinitive, modal',
+    'VMPP':         'perfect participle, modal',
+    'VVFIN':        'finite verb, full',
+    'VVIMP':        'imperative, full',
+    'VVINF':        'infinitive, full',
+    'VVIZU':        'infinitive with "zu", full',
+    'VVPP':         'perfect participle, full',
+    'XY':           'non-word containing non-letter',
+
+
+    # Noun chunks
+
+    'NP':           'noun phrase',
+    'PP':           'prepositional phrase',
+    'VP':           'verb phrase',
+    'ADVP':         'adverb phrase',
+    'ADJP':         'adjective phrase',
+    'SBAR':         'subordinating conjunction',
+    'PRT':          'particle',
+    'PNP':          'prepositional noun phrase',
+
+
+    # Dependency Labels (English)
+    # ClearNLP / Universal Dependencies
+    # https://github.com/clir/clearnlp-guidelines/blob/master/md/specifications/dependency_labels.md
+
+    'acomp':        'adjectival complement',
+    'advcl':        'adverbial clause modifier',
+    'advmod':       'adverbial modifier',
+    'agent':        'agent',
+    'amod':         'adjectival modifier',
+    'appos':        'appositional modifier',
+    'attr':         'attribute',
+    'aux':          'auxiliary',
+    'auxpass':      'auxiliary (passive)',
+    'cc':           'coordinating conjunction',
+    'ccomp':        'clausal complement',
+    'complm':       'complementizer',
+    'conj':         'conjunct',
+    'cop':          'copula',
+    'csubj':        'clausal subject',
+    'csubjpass':    'clausal subject (passive)',
+    'dep':          'unclassified dependent',
+    'det':          'determiner',
+    'dobj':         'direct object',
+    'expl':         'expletive',
+    'hmod':         'modifier in hyphenation',
+    'hyph':         'hyphen',
+    'infmod':       'infinitival modifier',
+    'intj':         'interjection',
+    'iobj':         'indirect object',
+    'mark':         'marker',
+    'meta':         'meta modifier',
+    'neg':          'negation modifier',
+    'nmod':         'modifier of nominal',
+    'nn':           'noun compound modifier',
+    'npadvmod':     'noun phrase as adverbial modifier',
+    'nsubj':        'nominal subject',
+    'nsubjpass':    'nominal subject (passive)',
+    'num':          'number modifier',
+    'number':       'number compound modifier',
+    'oprd':         'object predicate',
+    'obj':          'object',
+    'obl':          'oblique nominal',
+    'parataxis':    'parataxis',
+    'partmod':      'participal modifier',
+    'pcomp':        'complement of preposition',
+    'pobj':         'object of preposition',
+    'poss':         'possession modifier',
+    'possessive':   'possessive modifier',
+    'preconj':      'pre-correlative conjunction',
+    'prep':         'prepositional modifier',
+    'prt':          'particle',
+    'punct':        'punctuation',
+    'quantmod':     'modifier of quantifier',
+    'rcmod':        'relative clause modifier',
+    'root':         'root',
+    'xcomp':        'open clausal complement',
+
+
+    # Dependency labels (German)
+    # TIGER Treebank
+    # http://www.ims.uni-stuttgart.de/forschung/ressourcen/korpora/TIGERCorpus/annotation/tiger_introduction.pdf
+    # currently missing: 'cc' (comparative complement) because of conflict
+    # with English labels
+
+    'ac':           'adpositional case marker',
+    'adc':          'adjective component',
+    'ag':           'genitive attribute',
+    'ams':          'measure argument of adjective',
+    'app':          'apposition',
+    'avc':          'adverbial phrase component',
+    'cd':           'coordinating conjunction',
+    'cj':           'conjunct',
+    'cm':           'comparative conjunction',
+    'cp':           'complementizer',
+    'cvc':          'collocational verb construction',
+    'da':           'dative',
+    'dh':           'discourse-level head',
+    'dm':           'discourse marker',
+    'ep':           'expletive es',
+    'hd':           'head',
+    'ju':           'junctor',
+    'mnr':          'postnominal modifier',
+    'mo':           'modifier',
+    'ng':           'negation',
+    'nk':           'noun kernel element',
+    'nmc':          'numerical component',
+    'oa':           'accusative object',
+    'oc':           'clausal object',
+    'og':           'genitive object',
+    'op':           'prepositional object',
+    'par':          'parenthetical element',
+    'pd':           'predicate',
+    'pg':           'phrasal genitive',
+    'ph':           'placeholder',
+    'pm':           'morphological particle',
+    'pnc':          'proper noun component',
+    'rc':           'relative clause',
+    're':           'repeated element',
+    'rs':           'reported speech',
+    'sb':           'subject',
+
+
+    # Named Entity Recognition
+    # OntoNotes 5
+    # https://catalog.ldc.upenn.edu/docs/LDC2013T19/OntoNotes-Release-5.0.pdf
+
+    'PERSON':       'People, including fictional',
+    'NORP':         'Nationalities or religious or political groups',
+    'FACILITY':     'Buildings, airports, highways, bridges, etc.',
+    'ORG':          'Companies, agencies, institutions, etc.',
+    'GPE':          'Countries, cities, states',
+    'LOC':          'Non-GPE locations, mountain ranges, bodies of water',
+    'PRODUCT':      'Objects, vehicles, foods, etc. (not services)',
+    'EVENT':        'Named hurricanes, battles, wars, sports events, etc.',
+    'WORK_OF_ART':  'Titles of books, songs, etc.',
+    'LAW':          'Named documents made into laws.',
+    'LANGUAGE':     'Any named language',
+    'DATE':         'Absolute or relative dates or periods',
+    'TIME':         'Times smaller than a day',
+    'PERCENT':      'Percentage, including "%"',
+    'MONEY':        'Monetary values, including unit',
+    'QUANTITY':     'Measurements, as of weight or distance',
+    'ORDINAL':      '"first", "second", etc.',
+    'CARDINAL':     'Numerals that do not fall under another type',
+
+
+    # Named Entity Recognition
+    # Wikipedia
+    # http://www.sciencedirect.com/science/article/pii/S0004370212000276
+    # https://pdfs.semanticscholar.org/5744/578cc243d92287f47448870bb426c66cc941.pdf
+
+    'PER':          'Named person or family.',
+    'MISC':         ('Miscellaneous entities, e.g. events, nationalities, '
+                     'products or works of art'),
+}

+ 20 - 0
bot/core/spacy_helper.py

@@ -0,0 +1,20 @@
+'''
+Created on 18 janv. 2018
+
+@author: olivier.massot
+'''
+from core.glossary import explain
+
+
+def analyse(nlp, sentence):
+    print("> ", sentence)
+
+    doc = nlp(sentence)
+
+    print(doc.print_tree(light=True))
+
+    for t in doc:
+        print("#", t.text)
+        print("tag: ", t.tag_)
+        print("pos: ", t.pos_, explain(t.pos_))
+        print("dep: ", t.dep_, explain(t.dep_))

+ 8 - 0
bot/core/speedup.py

@@ -0,0 +1,8 @@
+'''
+Created on 18 janv. 2018
+
+@author: olivier.massot
+'''
+
+def speedup(nlp):
+    pass

+ 228 - 0
bot/debug.log

@@ -0,0 +1,228 @@
+2018-01-19 11:09:04,516 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:09:04,516 - sbot - INFO - > Ceci est une phrase de test.
+2018-01-19 11:09:04,541 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:09:04,542 - sbot - INFO - * Ceci est une phrase de test.
+2018-01-19 11:09:04,543 - sbot - INFO - ######
+
+2018-01-19 11:09:04,543 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:09:04,544 - sbot - INFO - * T<text=Ceci, lemma_=Ceci, pos_=PRON, tag_=PRON__Number=Sing|PronType=Dem, dep_=nsubj, shape_=Xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,544 - sbot - INFO - * T<text=est, lemma_=être, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=cop, shape_=xxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,545 - sbot - INFO - * T<text=une, lemma_=un, pos_=DET, tag_=DET__Definite=Ind|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,545 - sbot - INFO - * T<text=phrase, lemma_=phraser, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=ROOT, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,545 - sbot - INFO - * T<text=de, lemma_=de, pos_=ADP, tag_=ADP___, dep_=case, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,545 - sbot - INFO - * T<text=test, lemma_=test, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=nmod, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,545 - sbot - INFO - * T<text=., lemma_=., pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=., is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,546 - sbot - INFO - ######
+
+2018-01-19 11:09:04,546 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:09:04,546 - sbot - INFO - ######
+
+2018-01-19 11:09:04,546 - sbot - INFO - 
+
+> answered in 0.03000783920288086 s.
+2018-01-19 11:09:04,547 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:09:04,547 - sbot - INFO - > J'adore le film "la soupe aux choux"!
+2018-01-19 11:09:04,563 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:09:04,563 - sbot - INFO - * J'adore le film "la soupe aux choux"!
+2018-01-19 11:09:04,564 - sbot - INFO - ######
+
+2018-01-19 11:09:04,564 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:09:04,564 - sbot - INFO - * T<text=J', lemma_=J', pos_=PRON, tag_=PRON__Number=Sing|Person=1, dep_=nsubj, shape_=X', is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,564 - sbot - INFO - * T<text=adore, lemma_=adorer, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=ROOT, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,565 - sbot - INFO - * T<text=le, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Masc|Number=Sing|PronType=Art, dep_=det, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,565 - sbot - INFO - * T<text=film, lemma_=film, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=obj, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,565 - sbot - INFO - * T<text=", lemma_=", pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=", is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,565 - sbot - INFO - * T<text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,566 - sbot - INFO - * T<text=soupe, lemma_=souper, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=appos, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,566 - sbot - INFO - * T<text=aux, lemma_=aux, pos_=ADP, tag_=ADP___, dep_=aux, shape_=xxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,566 - sbot - INFO - * T<text=choux, lemma_=chou, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=xcomp, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,566 - sbot - INFO - * T<text=", lemma_=", pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=", is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,567 - sbot - INFO - * T<text=!, lemma_=!, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=!, is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,567 - sbot - INFO - ######
+
+2018-01-19 11:09:04,567 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:09:04,567 - sbot - INFO - ######
+
+2018-01-19 11:09:04,569 - sbot - INFO - 
+
+> answered in 0.022504329681396484 s.
+2018-01-19 11:09:04,569 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:09:04,569 - sbot - INFO - > Comment peut-on vraiment croire que la terre est plate?
+2018-01-19 11:09:04,587 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:09:04,587 - sbot - INFO - * Comment peut-on vraiment croire que la terre est plate?
+2018-01-19 11:09:04,587 - sbot - INFO - ######
+
+2018-01-19 11:09:04,588 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:09:04,588 - sbot - INFO - * T<text=Comment, lemma_=Comment, pos_=ADV, tag_=ADV__PronType=Int, dep_=advmod, shape_=Xxxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,588 - sbot - INFO - * T<text=peut, lemma_=pouvoir, pos_=VERB, tag_=VERB__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=aux, shape_=xxxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,588 - sbot - INFO - * T<text=-, lemma_=-, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=-, is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,589 - sbot - INFO - * T<text=on, lemma_=on, pos_=PRON, tag_=PRON__Number=Sing|Person=3, dep_=nsubj, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,589 - sbot - INFO - * T<text=vraiment, lemma_=vraiment, pos_=ADV, tag_=ADV___, dep_=advmod, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,589 - sbot - INFO - * T<text=croire, lemma_=croire, pos_=VERB, tag_=VERB__VerbForm=Inf, dep_=ROOT, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,589 - sbot - INFO - * T<text=que, lemma_=que, pos_=SCONJ, tag_=SCONJ___, dep_=mark, shape_=xxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,590 - sbot - INFO - * T<text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,590 - sbot - INFO - * T<text=terre, lemma_=terrer, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=nsubj, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,590 - sbot - INFO - * T<text=est, lemma_=être, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=cop, shape_=xxx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,590 - sbot - INFO - * T<text=plate, lemma_=plat, pos_=VERB, tag_=VERB__Gender=Masc|Number=Sing|Tense=Past|VerbForm=Part|Voice=Pass, dep_=ccomp, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,591 - sbot - INFO - * T<text=?, lemma_=?, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=?, is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,591 - sbot - INFO - ######
+
+2018-01-19 11:09:04,591 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:09:04,591 - sbot - INFO - ######
+
+2018-01-19 11:09:04,591 - sbot - INFO - 
+
+> answered in 0.02200460433959961 s.
+2018-01-19 11:09:04,592 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:09:04,592 - sbot - INFO - > La Commune de Paris a duré 72 jours, jusqu'à la semaine sanglante.
+2018-01-19 11:09:04,611 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:09:04,611 - sbot - INFO - * La Commune de Paris a duré 72 jours, jusqu'à la semaine sanglante.
+2018-01-19 11:09:04,612 - sbot - INFO - ######
+
+2018-01-19 11:09:04,612 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:09:04,612 - sbot - INFO - * T<text=La, lemma_=La, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=Xx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,612 - sbot - INFO - * T<text=Commune, lemma_=Commune, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=nsubj, shape_=Xxxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,612 - sbot - INFO - * T<text=de, lemma_=de, pos_=ADP, tag_=ADP___, dep_=case, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,613 - sbot - INFO - * T<text=Paris, lemma_=Paris, pos_=PROPN, tag_=PROPN__Gender=Masc|Number=Sing, dep_=nmod, shape_=Xxxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,613 - sbot - INFO - * T<text=a, lemma_=avoir, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=aux, shape_=x, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,613 - sbot - INFO - * T<text=duré, lemma_=durer, pos_=VERB, tag_=VERB__Gender=Masc|Number=Sing|Tense=Past|VerbForm=Part, dep_=ROOT, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,613 - sbot - INFO - * T<text=72, lemma_=72, pos_=NUM, tag_=NUM__NumType=Card, dep_=nummod, shape_=dd, is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,613 - sbot - INFO - * T<text=jours, lemma_=jour, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Plur, dep_=obj, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=,, lemma_=,, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=,, is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=jusqu', lemma_=jusque, pos_=ADP, tag_=ADP___, dep_=case, shape_=xxxx', is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=à, lemma_=à, pos_=ADP, tag_=ADP___, dep_=fixed, shape_=x, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, is_alpha=True,is_stop=True>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=semaine, lemma_=semaine, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=obl, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,614 - sbot - INFO - * T<text=sanglante, lemma_=sanglant, pos_=ADJ, tag_=ADJ__Gender=Fem|Number=Sing, dep_=amod, shape_=xxxx, is_alpha=True,is_stop=False>
+2018-01-19 11:09:04,615 - sbot - INFO - * T<text=., lemma_=., pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=., is_alpha=False,is_stop=False>
+2018-01-19 11:09:04,615 - sbot - INFO - ######
+
+2018-01-19 11:09:04,615 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:09:04,615 - sbot - INFO - * Ent<text=La Commune de Paris, start_char=0, end_char=19, label_=MISC>
+2018-01-19 11:09:04,615 - sbot - INFO - ######
+
+2018-01-19 11:09:04,615 - sbot - INFO - 
+
+> answered in 0.022997617721557617 s.
+2018-01-19 11:40:58,978 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:40:58,979 - sbot - INFO - > Ceci est une phrase de test.
+2018-01-19 11:40:58,994 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:40:58,994 - sbot - INFO - * Ceci est une phrase de test.
+2018-01-19 11:40:58,994 - sbot - INFO - ######
+
+2018-01-19 11:40:58,995 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:41:25,036 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:41:25,037 - sbot - INFO - > Ceci est une phrase de test.
+2018-01-19 11:41:25,052 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:41:25,052 - sbot - INFO - * Ceci est une phrase de test.
+2018-01-19 11:41:25,052 - sbot - INFO - ######
+
+2018-01-19 11:41:25,053 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:41:25,053 - sbot - INFO - * T<index=0text=Ceci, lemma_=Ceci, pos_=PRON, tag_=PRON__Number=Sing|PronType=Dem, dep_=nsubj, shape_=Xxxx, norm_=ceciis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,053 - sbot - INFO - * T<index=1text=est, lemma_=être, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=cop, shape_=xxx, norm_=estis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,053 - sbot - INFO - * T<index=2text=une, lemma_=un, pos_=DET, tag_=DET__Definite=Ind|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xxx, norm_=uneis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,053 - sbot - INFO - * T<index=3text=phrase, lemma_=phraser, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=ROOT, shape_=xxxx, norm_=phraseis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,054 - sbot - INFO - * T<index=4text=de, lemma_=de, pos_=ADP, tag_=ADP___, dep_=case, shape_=xx, norm_=deis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,054 - sbot - INFO - * T<index=5text=test, lemma_=test, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=nmod, shape_=xxxx, norm_=testis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,054 - sbot - INFO - * T<index=6text=., lemma_=., pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=., norm_=.is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,054 - sbot - INFO - ######
+
+2018-01-19 11:41:25,054 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:41:25,055 - sbot - INFO - ######
+
+2018-01-19 11:41:25,055 - sbot - INFO - 
+
+> answered in 0.018988370895385742 s.
+2018-01-19 11:41:25,055 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:41:25,055 - sbot - INFO - > J'adore le film "la soupe aux choux"!
+2018-01-19 11:41:25,072 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:41:25,072 - sbot - INFO - * J'adore le film "la soupe aux choux"!
+2018-01-19 11:41:25,072 - sbot - INFO - ######
+
+2018-01-19 11:41:25,072 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:41:25,073 - sbot - INFO - * T<index=0text=J', lemma_=J', pos_=PRON, tag_=PRON__Number=Sing|Person=1, dep_=nsubj, shape_=X', norm_=j'is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,073 - sbot - INFO - * T<index=1text=adore, lemma_=adorer, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=ROOT, shape_=xxxx, norm_=adoreis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,073 - sbot - INFO - * T<index=2text=le, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Masc|Number=Sing|PronType=Art, dep_=det, shape_=xx, norm_=leis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,073 - sbot - INFO - * T<index=3text=film, lemma_=film, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Sing, dep_=obj, shape_=xxxx, norm_=filmis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,073 - sbot - INFO - * T<index=4text=", lemma_=", pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=", norm_="is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,074 - sbot - INFO - * T<index=5text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, norm_=lais_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,074 - sbot - INFO - * T<index=6text=soupe, lemma_=souper, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=appos, shape_=xxxx, norm_=soupeis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,074 - sbot - INFO - * T<index=7text=aux, lemma_=aux, pos_=ADP, tag_=ADP___, dep_=aux, shape_=xxx, norm_=auxis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,074 - sbot - INFO - * T<index=8text=choux, lemma_=chou, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=xcomp, shape_=xxxx, norm_=chouxis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,075 - sbot - INFO - * T<index=9text=", lemma_=", pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=", norm_="is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,075 - sbot - INFO - * T<index=10text=!, lemma_=!, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=!, norm_=!is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,075 - sbot - INFO - ######
+
+2018-01-19 11:41:25,075 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:41:25,076 - sbot - INFO - ######
+
+2018-01-19 11:41:25,076 - sbot - INFO - 
+
+> answered in 0.021004915237426758 s.
+2018-01-19 11:41:25,076 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:41:25,076 - sbot - INFO - > Comment peut-on vraiment croire que la terre est plate?
+2018-01-19 11:41:25,095 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:41:25,096 - sbot - INFO - * Comment peut-on vraiment croire que la terre est plate?
+2018-01-19 11:41:25,096 - sbot - INFO - ######
+
+2018-01-19 11:41:25,097 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:41:25,097 - sbot - INFO - * T<index=0text=Comment, lemma_=Comment, pos_=ADV, tag_=ADV__PronType=Int, dep_=advmod, shape_=Xxxxx, norm_=commentis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,097 - sbot - INFO - * T<index=1text=peut, lemma_=pouvoir, pos_=VERB, tag_=VERB__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=aux, shape_=xxxx, norm_=peutis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,098 - sbot - INFO - * T<index=2text=-, lemma_=-, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=-, norm_=-is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,098 - sbot - INFO - * T<index=3text=on, lemma_=on, pos_=PRON, tag_=PRON__Number=Sing|Person=3, dep_=nsubj, shape_=xx, norm_=onis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,098 - sbot - INFO - * T<index=4text=vraiment, lemma_=vraiment, pos_=ADV, tag_=ADV___, dep_=advmod, shape_=xxxx, norm_=vraimentis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,099 - sbot - INFO - * T<index=5text=croire, lemma_=croire, pos_=VERB, tag_=VERB__VerbForm=Inf, dep_=ROOT, shape_=xxxx, norm_=croireis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,099 - sbot - INFO - * T<index=6text=que, lemma_=que, pos_=SCONJ, tag_=SCONJ___, dep_=mark, shape_=xxx, norm_=queis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,099 - sbot - INFO - * T<index=7text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, norm_=lais_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,100 - sbot - INFO - * T<index=8text=terre, lemma_=terrer, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=nsubj, shape_=xxxx, norm_=terreis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,100 - sbot - INFO - * T<index=9text=est, lemma_=être, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=cop, shape_=xxx, norm_=estis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,100 - sbot - INFO - * T<index=10text=plate, lemma_=plat, pos_=VERB, tag_=VERB__Gender=Masc|Number=Sing|Tense=Past|VerbForm=Part|Voice=Pass, dep_=ccomp, shape_=xxxx, norm_=plateis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,101 - sbot - INFO - * T<index=11text=?, lemma_=?, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=?, norm_=?is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,102 - sbot - INFO - ######
+
+2018-01-19 11:41:25,102 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:41:25,102 - sbot - INFO - ######
+
+2018-01-19 11:41:25,102 - sbot - INFO - 
+
+> answered in 0.025996685028076172 s.
+2018-01-19 11:41:25,102 - sbot - INFO -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+2018-01-19 11:41:25,102 - sbot - INFO - > La Commune de Paris a duré 72 jours, jusqu'à la semaine sanglante.
+2018-01-19 11:41:25,123 - sbot - INFO - ### PHRASES ###
+2018-01-19 11:41:25,123 - sbot - INFO - * La Commune de Paris a duré 72 jours, jusqu'à la semaine sanglante.
+2018-01-19 11:41:25,123 - sbot - INFO - ######
+
+2018-01-19 11:41:25,123 - sbot - INFO - ### TOKENS ### 
+2018-01-19 11:41:25,123 - sbot - INFO - * T<index=0text=La, lemma_=La, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=Xx, norm_=lais_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,123 - sbot - INFO - * T<index=1text=Commune, lemma_=Commune, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=nsubj, shape_=Xxxxx, norm_=communeis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,124 - sbot - INFO - * T<index=2text=de, lemma_=de, pos_=ADP, tag_=ADP___, dep_=case, shape_=xx, norm_=deis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,124 - sbot - INFO - * T<index=3text=Paris, lemma_=Paris, pos_=PROPN, tag_=PROPN__Gender=Masc|Number=Sing, dep_=nmod, shape_=Xxxxx, norm_=parisis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,124 - sbot - INFO - * T<index=4text=a, lemma_=avoir, pos_=AUX, tag_=AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin, dep_=aux, shape_=x, norm_=ais_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,124 - sbot - INFO - * T<index=5text=duré, lemma_=durer, pos_=VERB, tag_=VERB__Gender=Masc|Number=Sing|Tense=Past|VerbForm=Part, dep_=ROOT, shape_=xxxx, norm_=duréis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,124 - sbot - INFO - * T<index=6text=72, lemma_=72, pos_=NUM, tag_=NUM__NumType=Card, dep_=nummod, shape_=dd, norm_=72is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=7text=jours, lemma_=jour, pos_=NOUN, tag_=NOUN__Gender=Masc|Number=Plur, dep_=obj, shape_=xxxx, norm_=joursis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=8text=,, lemma_=,, pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=,, norm_=,is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=9text=jusqu', lemma_=jusque, pos_=ADP, tag_=ADP___, dep_=case, shape_=xxxx', norm_=jusqu'is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=10text=à, lemma_=à, pos_=ADP, tag_=ADP___, dep_=fixed, shape_=x, norm_=àis_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=11text=la, lemma_=le, pos_=DET, tag_=DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art, dep_=det, shape_=xx, norm_=lais_alpha=True,is_stop=True,sentiment=0.0>
+2018-01-19 11:41:25,125 - sbot - INFO - * T<index=12text=semaine, lemma_=semaine, pos_=NOUN, tag_=NOUN__Gender=Fem|Number=Sing, dep_=obl, shape_=xxxx, norm_=semaineis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,126 - sbot - INFO - * T<index=13text=sanglante, lemma_=sanglant, pos_=ADJ, tag_=ADJ__Gender=Fem|Number=Sing, dep_=amod, shape_=xxxx, norm_=sanglanteis_alpha=True,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,126 - sbot - INFO - * T<index=14text=., lemma_=., pos_=PUNCT, tag_=PUNCT___, dep_=punct, shape_=., norm_=.is_alpha=False,is_stop=False,sentiment=0.0>
+2018-01-19 11:41:25,126 - sbot - INFO - ######
+
+2018-01-19 11:41:25,126 - sbot - INFO - ### ENTITIES ### 
+2018-01-19 11:41:25,126 - sbot - INFO - * Ent<text=La Commune de Paris, start_char=0, end_char=19, label_=MISC>
+2018-01-19 11:41:25,127 - sbot - INFO - ######
+
+2018-01-19 11:41:25,127 - sbot - INFO - 
+
+> answered in 0.025015830993652344 s.

+ 37 - 0
bot/logging.yaml

@@ -0,0 +1,37 @@
+version: 1
+disable_existing_loggers: no
+formatters:
+    simple:
+        format: "%(asctime)s - %(levelname)s - %(message)s"
+    complete:
+        format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+    short:
+        format: "%(levelname)s - %(message)s"
+    message_only:
+        format: "%(message)s"
+        
+handlers:
+    console:
+        class: logging.StreamHandler
+        level: INFO
+        formatter: message_only
+        stream: ext://sys.stdout
+    file:
+        class: logging.handlers.RotatingFileHandler
+        level: DEBUG
+        formatter: complete
+        filename: debug.log
+        maxBytes: 100000
+        backupCount: 1
+        encoding: utf8
+
+loggers:
+    sbot:
+        level: INFO
+        handlers: [console, file]
+        propagate: no
+
+root:
+    level: INFO
+    handlers: [console]
+    propagate: yes

+ 0 - 0
bot/memory/__init__.py


+ 1 - 0
bot/memory/data.json

@@ -0,0 +1 @@
+{"Salut!": {}, "Comment vas-tu?": {}, "Ca va?": {}, "Bonjour": {}, "Yo": {}, "Hello": {}, "Montes le son": {}}

+ 22 - 0
bot/memory/question.py

@@ -0,0 +1,22 @@
+'''
+Saisie de questions courantes, auxquelles des réponses adaptées sont attendues
+
+@author: olivier.massot, jan 2018
+'''
+import json
+
+
+instr = ""
+
+with open("data.json", encoding="utf-8") as f:
+    data = json.load(f)
+
+while not instr == ".":
+    instr = input("> ")
+    if instr:
+        if instr in data:
+            print("# Déjà en mémoire")
+        else:
+            data[instr] = {}
+            with open("data.json", "w+", encoding="utf-8") as f:
+                json.dump(data, f)

+ 250 - 0
bot/old/bot_1.py

@@ -0,0 +1,250 @@
+import logging
+import os
+import random
+
+from textblob import TextBlob
+
+from config import FILTER_WORDS
+
+
+# See: https://pythonhosted.org/chatbot/
+# See: https://www.codeproject.com/Articles/36106/Chatbot-Tutorial
+# See: https://www.smallsurething.com/implementing-the-famous-eliza-chatbot-in-python/
+os.environ['NLTK_DATA'] = os.getcwd() + '/nltk_data'
+
+logging.basicConfig()
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+# Sentences we'll respond with if the user greeted us
+GREETING_KEYWORDS = ("salut", "bonjour", "yo", "hello",)
+
+GREETING_RESPONSES = ["Bonjour monsieur."]
+
+def check_for_greeting(sentence):
+    """If any of the words in the user's input was a greeting, return a greeting response"""
+    for word in sentence.words:
+        if word.lower() in GREETING_KEYWORDS:
+            return random.choice(GREETING_RESPONSES)
+
+# Sentences we'll respond with if we have no idea what the user just said
+NONE_RESPONSES = [
+    "Excusez-moi, je ne comprend pas.",
+    "Je vous demande pardon?"
+]
+
+# If the user tries to tell us something about ourselves, use one of these responses
+COMMENTS_ABOUT_SELF = [
+    "Je ne suis qu'un homme. Attendez... non.",
+    "Je ne fais que mon travail Monsieur.",
+]
+
+class UnacceptableUtteranceException(Exception):
+    """Raise this (uncaught) exception if the response was going to trigger our blacklist"""
+    pass
+
+def starts_with_vowel(word):
+    """Check for pronoun compability -- 'a' vs. 'an'"""
+    return True if word[0] in 'aeiou' else False
+
+def answer_to(sentence):
+    """Main program loop: select a response for the input sentence and return it"""
+    logger.debug("> respond to %s", sentence)
+    resp = respond(sentence)
+    return resp
+
+def find_pronoun(sent):
+    """Given a sentence, find a preferred pronoun to respond with. Returns None if no candidate
+    pronoun is found in the input"""
+    pronoun = None
+
+    for word, part_of_speech in sent.pos_tags:
+        # Disambiguate pronouns
+        if part_of_speech == 'PRP' and word.lower() == 'you':
+            pronoun = 'I'
+        elif part_of_speech == 'PRP' and word == 'I':
+            # If the user mentioned themselves, then they will definitely be the pronoun
+            pronoun = 'You'
+    return pronoun
+
+def find_verb(sent):
+    """Pick a candidate verb for the sentence."""
+    verb = None
+    pos = None
+    for word, part_of_speech in sent.pos_tags:
+        if part_of_speech.startswith('VB'):  # This is a verb
+            verb = word
+            pos = part_of_speech
+            break
+    return verb, pos
+
+
+def find_noun(sent):
+    """Given a sentence, find the best candidate noun."""
+    noun = None
+
+    if not noun:
+        for w, p in sent.pos_tags:
+            if p == 'NN':  # This is a noun
+                noun = w
+                break
+    if noun:
+        logger.info("Found noun: %s", noun)
+
+    return noun
+
+def find_adjective(sent):
+    """Given a sentence, find the best candidate adjective."""
+    adj = None
+    for w, p in sent.pos_tags:
+        if p == 'JJ':  # This is an adjective
+            adj = w
+            break
+    return adj
+
+def construct_response(pronoun, noun, verb):
+    """No special cases matched, so we're going to try to construct a full sentence that uses as much
+    of the user's input as possible"""
+    resp = []
+
+    if pronoun:
+        resp.append(pronoun)
+
+    # We always respond in the present tense, and the pronoun will always either be a passthrough
+    # from the user, or 'you' or 'I', in which case we might need to change the tense for some
+    # irregular verbs.
+    if verb:
+        verb_word = verb[0]
+        if verb_word in ('être', 'am', 'is', "'m"):  # This would be an excellent place to use lemmas!
+            if pronoun.lower() == 'you':
+                resp.append("aren't really")
+            else:
+                resp.append(verb_word)
+    if noun:
+        pronoun = "an" if starts_with_vowel(noun) else "a"
+        resp.append(pronoun + " " + noun)
+
+    resp.append(random.choice(("tho", "bro", "lol", "bruh", "smh", "")))
+
+    return " ".join(resp)
+
+
+def check_for_comment_about_bot(pronoun, noun, adjective):
+    """Check if the user's input was about the bot itself, in which case try to fashion a response
+    that feels right based on their input. Returns the new best sentence, or None."""
+    resp = None
+    if pronoun == 'I' and (noun or adjective):
+        if noun:
+            if random.choice((True, False)):
+                resp = random.choice(SELF_VERBS_WITH_NOUN_CAPS_PLURAL).format(**{'noun': noun.pluralize().capitalize()})
+            else:
+                resp = random.choice(SELF_VERBS_WITH_NOUN_LOWER).format(**{'noun': noun})
+        else:
+            resp = random.choice(SELF_VERBS_WITH_ADJECTIVE).format(**{'adjective': adjective})
+    return resp
+
+# Template for responses that include a direct noun which is indefinite/uncountable
+SELF_VERBS_WITH_NOUN_CAPS_PLURAL = [
+    "My last startup totally crushed the {noun} vertical",
+    "Were you aware I was a serial entrepreneur in the {noun} sector?",
+    "My startup is Uber for {noun}",
+    "I really consider myself an expert on {noun}",
+]
+
+SELF_VERBS_WITH_NOUN_LOWER = [
+    "Yeah but I know a lot about {noun}",
+    "My bros always ask me about {noun}",
+]
+
+SELF_VERBS_WITH_ADJECTIVE = [
+    "I'm personally building the {adjective} Economy",
+    "I consider myself to be a {adjective}preneur",
+]
+# end
+
+def preprocess_text(sentence):
+    """Handle some weird edge cases in parsing, like 'i' needing to be capitalized
+    to be correctly identified as a pronoun"""
+    cleaned = []
+    words = sentence.split(' ')
+    for w in words:
+        if w == 'i':
+            w = 'I'
+        if w == "i'm":
+            w = "I'm"
+        cleaned.append(w)
+
+    return ' '.join(cleaned)
+
+# start:example-respond.py
+def respond(sentence):
+    """Parse the user's inbound sentence and find candidate terms that make up a best-fit response"""
+    cleaned = preprocess_text(sentence)
+    parsed = TextBlob(cleaned)
+
+    # Loop through all the sentences, if more than one. This will help extract the most relevant
+    # response text even across multiple sentences (for example if there was no obvious direct noun
+    # in one sentence
+    pronoun, noun, adjective, verb = find_candidate_parts_of_speech(parsed)
+
+    # If we said something about the bot and used some kind of direct noun, construct the
+    # sentence around that, discarding the other candidates
+    resp = check_for_comment_about_bot(pronoun, noun, adjective)
+
+    # If we just greeted the bot, we'll use a return greeting
+    if not resp:
+        resp = check_for_greeting(parsed)
+
+    if not resp:
+        # If we didn't override the final sentence, try to construct a new one:
+        if not pronoun:
+            resp = random.choice(NONE_RESPONSES)
+        elif pronoun == 'I' and not verb:
+            resp = random.choice(COMMENTS_ABOUT_SELF)
+        else:
+            resp = construct_response(pronoun, noun, verb)
+
+    # If we got through all that with nothing, use a random response
+    if not resp:
+        resp = random.choice(NONE_RESPONSES)
+
+    logger.info("Returning phrase '%s'", resp)
+    # Check that we're not going to say anything obviously offensive
+    filter_response(resp)
+
+    return resp
+
+def find_candidate_parts_of_speech(parsed):
+    """Given a parsed input, find the best pronoun, direct noun, adjective, and verb to match their input.
+    Returns a tuple of pronoun, noun, adjective, verb any of which may be None if there was no good match"""
+    pronoun = None
+    noun = None
+    adjective = None
+    verb = None
+    for sent in parsed.sentences:
+        pronoun = find_pronoun(sent)
+        noun = find_noun(sent)
+        adjective = find_adjective(sent)
+        verb = find_verb(sent)
+    logger.info("Pronoun=%s, noun=%s, adjective=%s, verb=%s", pronoun, noun, adjective, verb)
+    return pronoun, noun, adjective, verb
+
+
+def filter_response(resp):
+    """Don't allow any words to match our filter list"""
+    tokenized = resp.split(' ')
+    for word in tokenized:
+        if '@' in word or '#' in word or '!' in word:
+            raise UnacceptableUtteranceException()
+        for s in FILTER_WORDS:
+            if word.lower().startswith(s):
+                raise UnacceptableUtteranceException()
+# end
+
+if __name__ == '__main__':
+    msg = ""
+    print("Oui monsieur?")
+    while msg != ".":
+        msg = input("> ")
+        print(answer_to(msg))
+

+ 46 - 0
bot/old/bot_2.py

@@ -0,0 +1,46 @@
+"""
+# See: https://pythonhosted.org/chatbot/
+# See: https://www.codeproject.com/Articles/36106/Chatbot-Tutorial
+# See: https://www.smallsurething.com/implementing-the-famous-eliza-chatbot-in-python/
+"""
+import logging
+
+from textblob import TextBlob
+from textblob_fr import PatternTagger, PatternAnalyzer
+
+logging.basicConfig()
+logger = logging.getLogger()
+logger.setLevel(logging.DEBUG)
+
+def answer_to(sentence):
+    """Main program loop: select a response for the input sentence and return it"""
+    logger.debug("> received: %s", sentence)
+
+    blob = TextBlob(sentence, pos_tagger=PatternTagger(), analyzer=PatternAnalyzer())
+
+    print("tags", blob.tags)
+
+    print("phrases", blob.sentences)
+#     print("mots", blob.words)
+#     print("phrases nom.", blob.noun_phrases)
+
+    print("mot 6:", blob.words[6])
+    print("lemma:", blob.words[6].lemmatize())
+#     print("pluriel:", blob.words[3].pluralize())
+
+    print("mot 12:", blob.words[12])
+    print("lemma:", blob.words[12].lemmatize())
+#     print("singulier:", blob.words[12].singularize())
+
+
+if __name__ == '__main__':
+    msg = ""
+    answer_to("Ceci est une phrase de test. J'adore le film \"la soupe aux choux\"")
+#     print("Oui monsieur?")
+#     while msg != ".":
+#         msg = input("> ")
+#         print(answer_to(msg))
+
+#     answer_to("This is a test sentence.")
+
+

+ 39 - 0
bot/old/pos_tags.txt

@@ -0,0 +1,39 @@
+# http://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html
+
+	CC Coordinating conjunction
+	CD Cardinal number
+	DT Determiner
+	EX Existential there
+	FW Foreign word
+	IN Preposition or subordinating conjunction
+	JJ Adjective
+	JJR Adjective, comparative
+	JJS Adjective, superlative
+	LS List item marker
+	MD Modal
+	NN Noun, singular or mass
+	NNS Noun, plural
+	NNP Proper noun, singular
+	NNPS Proper noun, plural
+	PDT Predeterminer
+	POS Possessive ending
+	PRP Personal pronoun
+	PRP$ Possessive pronoun
+	RB Adverb
+	RBR Adverb, comparative
+	RBS Adverb, superlative
+	RP Particle
+	SYM Symbol
+	TO to
+	UH Interjection
+	VB Verb, base form
+	VBD Verb, past tense
+	VBG Verb, gerund or present participle
+	VBN Verb, past participle
+	VBP Verb, non­3rd person singular present
+	VBZ Verb, 3rd person singular present
+	WDT Wh­determiner
+	WP Wh­pronoun
+	WP$ Possessive wh­pronoun
+	WRB Wh­adverb
+

+ 81 - 0
bot/old/test.py

@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+
+import pytest
+import random
+
+from bot import *
+
+random.seed(0)
+
+def test_random_utterance():
+    """An utterance which is unparsable should return one of the random responses"""
+    sent = "abcd"  # Something unparseable
+    resp = broback(sent)
+    assert resp == NONE_RESPONSES[-2]
+
+def test_basic_greetings():
+    """The bot should respond sensibly to common greeting words"""
+    sent = "hello"
+    resp = broback(sent)
+    assert resp == GREETING_RESPONSES[1]
+
+
+def test_contains_reference_to_user():
+    """An utterance where the user mentions themselves generally should specifically return a phrase starting with 'You'"""
+    sent = "I went to dinner"
+    resp = broback(sent)
+    assert resp.startswith("You ")
+
+
+def test_negs_user():
+    """An utterance where the user says 'I am' <something> should specifically tell them they aren't that thing"""
+    sent = "I am good at Python programming"
+    resp = broback(sent)
+    assert resp.startswith("You aren't really")
+
+    sent = "I'm good at Python programming"
+    resp = broback(sent)
+    assert resp.startswith("You aren't really")
+
+    sent = "i'm good at Python programming"
+    resp = broback(sent)
+    assert resp.startswith("You aren't really")
+
+
+def test_contains_reference_to_bot():
+    """An utterance where the user directs something at the bot itself should return a canned response"""
+    sent = "You are lame"
+    resp = broback(sent)
+    assert 'lame' in resp
+
+
+def test_reuses_subject():
+    """If the user tells us about some kind of subject, we should mention it in our response"""
+    sent = "I am a capable programmer"
+    resp = broback(sent)
+    assert "programmer" in resp
+
+
+def test_strip_offensive_words():
+    """Don't allow the bot to respond with anything obviously offensive"""
+    # To avoid including an offensive word in the test set, add a harmless word temporarily
+    from config import FILTER_WORDS
+    FILTER_WORDS.add('snakeperson')
+    sent = "I am a snakeperson"
+    with pytest.raises(UnacceptableUtteranceException):
+        broback(sent)
+
+
+def test_strip_punctuation():
+    """Removing most punctuation is one way to ensure that the bot doesn't include hashtags or @-signs, which are potential vectors for harrassment"""
+    sent = "I am a #snakeperson"
+    with pytest.raises(UnacceptableUtteranceException):
+        broback(sent)
+
+    sent = "@you are funny"
+    with pytest.raises(UnacceptableUtteranceException):
+        broback(sent)
+
+def test_unicode():
+    """Bros love internationalization"""
+broback(u"☃")  # Unicode snowman

+ 206 - 0
bot/pos_tags.html

@@ -0,0 +1,206 @@
+<html><head>
+<title>Penn Treebank P.O.S. Tags</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+
+<!-- http://www.ling.upenn.edu/courses/Fall_2003/ling001/penn_treebank_pos.html -->
+
+<body bgcolor="#FFFFFF">
+<h3>Alphabetical list of part-of-speech tags used in the Penn Treebank Project:</h3>
+<table cellspacing="2" cellpadding="2" border="0">
+  <tbody><tr bgcolor="#DFDFFF" align="none"> 
+    <td align="none"> 
+      <div align="left">Number</div>
+    </td>
+    <td> 
+      <div align="left">Tag</div>
+    </td>
+    <td> 
+      <div align="left">Description</div>
+    </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 1. </td>
+    <td>CC </td>
+    <td>Coordinating conjunction </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 2. </td>
+    <td>CD </td>
+    <td>Cardinal number </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 3. </td>
+    <td>DT </td>
+    <td>Determiner </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 4. </td>
+    <td>EX </td>
+    <td>Existential <i>there<i> </i></i></td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 5. </td>
+    <td>FW </td>
+    <td>Foreign word </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 6. </td>
+    <td>IN </td>
+    <td>Preposition or subordinating conjunction </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 7. </td>
+    <td>JJ </td>
+    <td>Adjective </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 8. </td>
+    <td>JJR </td>
+    <td>Adjective, comparative </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 9. </td>
+    <td>JJS </td>
+    <td>Adjective, superlative </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 10. </td>
+    <td>LS </td>
+    <td>List item marker </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 11. </td>
+    <td>MD </td>
+    <td>Modal </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 12. </td>
+    <td>NN </td>
+    <td>Noun, singular or mass </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 13. </td>
+    <td>NNS </td>
+    <td>Noun, plural </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 14. </td>
+    <td>NNP </td>
+    <td>Proper noun, singular </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 15. </td>
+    <td>NNPS </td>
+    <td>Proper noun, plural </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 16. </td>
+    <td>PDT </td>
+    <td>Predeterminer </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 17. </td>
+    <td>POS </td>
+    <td>Possessive ending </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 18. </td>
+    <td>PRP </td>
+    <td>Personal pronoun </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 19. </td>
+    <td>PRP$ </td>
+    <td>Possessive pronoun </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 20. </td>
+    <td>RB </td>
+    <td>Adverb </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 21. </td>
+    <td>RBR </td>
+    <td>Adverb, comparative </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 22. </td>
+    <td>RBS </td>
+    <td>Adverb, superlative </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 23. </td>
+    <td>RP </td>
+    <td>Particle </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 24. </td>
+    <td>SYM </td>
+    <td>Symbol </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 25. </td>
+    <td>TO </td>
+    <td><i>to</i> </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 26. </td>
+    <td>UH </td>
+    <td>Interjection </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 27. </td>
+    <td>VB </td>
+    <td>Verb, base form </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 28. </td>
+    <td>VBD </td>
+    <td>Verb, past tense </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 29. </td>
+    <td>VBG </td>
+    <td>Verb, gerund or present participle </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 30. </td>
+    <td>VBN </td>
+    <td>Verb, past participle </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 31. </td>
+    <td>VBP </td>
+    <td>Verb, non-3rd person singular present </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 32. </td>
+    <td>VBZ </td>
+    <td>Verb, 3rd person singular present </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 33. </td>
+    <td>WDT </td>
+    <td>Wh-determiner </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 34. </td>
+    <td>WP </td>
+    <td>Wh-pronoun </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 35. </td>
+    <td>WP$ </td>
+    <td>Possessive wh-pronoun </td>
+  </tr>
+  <tr bgcolor="#FFFFCA"> 
+    <td align="none"> 36. </td>
+    <td>WRB </td>
+    <td>Wh-adverb 
+</td></tr></tbody></table>
+
+
+
+
+</body></html>

+ 2 - 0
bot/requirements.txt

@@ -0,0 +1,2 @@
+textblob
+textblob-fr

+ 43 - 0
bot/results.txt

@@ -0,0 +1,43 @@
+[{
+	'word': 'rouge',
+	'lemma': 'rouge',
+	'NE': '',
+	'POS_fine': 'ADJ__Gender=Masc|Number=Sing',
+	'POS_coarse': 'ADJ',
+	'arc': 'ROOT',
+	'modifiers': [{
+			'word': 'voiture',
+			'lemma': 'voiturer',
+			'NE': '',
+			'POS_fine': 'NOUN__Gender=Fem|Number=Sing',
+			'POS_coarse': 'NOUN',
+			'arc': 'nsubj:pass',
+			'modifiers': [{
+					'word': 'La',
+					'lemma': 'La',
+					'NE': '',
+					'POS_fine': 'DET__Definite=Def|Gender=Fem|Number=Sing|PronType=Art',
+					'POS_coarse': 'DET',
+					'arc': 'det',
+					'modifiers': []
+				}
+			]
+		}, {
+			'word': 'est',
+			'lemma': 'être',
+			'NE': '',
+			'POS_fine': 'AUX__Mood=Ind|Number=Sing|Person=3|Tense=Pres|VerbForm=Fin',
+			'POS_coarse': 'AUX',
+			'arc': 'aux:pass',
+			'modifiers': []
+		}, {
+			'word': '.',
+			'lemma': '.',
+			'NE': '',
+			'POS_fine': 'PUNCT___',
+			'POS_coarse': 'PUNCT',
+			'arc': 'punct',
+			'modifiers': []
+		}
+	]
+}]

+ 62 - 0
bot/sbot.py

@@ -0,0 +1,62 @@
+"""
+
+# speed-up spacy: https://medium.com/@vishnups/speeding-up-spacy-f766e3dd033c
+"""
+
+import logging.config
+
+import fr_core_news_sm
+import yaml
+
+from core.spacy_helper import analyse
+from core.speedup import speedup
+
+
+# ## Logging
+with open('logging.yaml', 'rt') as f:
+    conf = yaml.load(f)
+logging.config.dictConfig(conf)
+logger = logging.getLogger("sbot")
+# ##
+
+nlp = fr_core_news_sm.load()
+speedup(nlp)
+
+def answer_to(sentence):
+    """Main program loop: select a response for the input sentence and return it"""
+    doc = nlp(sentence)
+
+    root = next((t for t in doc if t.dep_ == "ROOT"))
+
+    print("la racine est: ", root.text)
+
+    if root.pos_ == "VERB":
+        print("> root est un verbe")
+        print("> Infinitif: ", root.lemma_)
+
+        for t in root.children:
+            if t.dep_ == "nsubj":
+                print("son sujet est:", t.text)
+            if t.dep_ == "dobj":
+                print("son objet est:", t.text)
+
+    elif root.pos_ == "NOUN":
+        print("> root est un nom commun")
+
+    elif root.pos_ == "PROPN":
+        print("> root est un nom propre")
+        for t in root.children:
+            if t.dep_ == "amod":
+                print("son modificateur (adj):", t.text)
+
+            if t.dep_ == "nmod":
+                print("son objet est:", t.text)
+    else:
+        print("> root est un: ", root.pos_)
+
+    return doc.print_tree(light=True)
+
+if __name__ == '__main__':
+    msg = "Apple cherche à acheter une startup anglaise pour 1 milliard de dollars."
+
+    analyse(nlp, msg)

BIN
bot/wheels/cymem-1.31.2-cp36-cp36m-win32.whl


BIN
bot/wheels/cytoolz-0.9.0-cp36-cp36m-win32.whl


BIN
bot/wheels/msgpack-0.5.1-cp36-cp36m-win32.whl


BIN
bot/wheels/murmurhash-0.28.0-cp36-cp36m-win32.whl


BIN
bot/wheels/preshed-1.0.0-cp36-cp36m-win32.whl


+ 10 - 0
bot/wheels/requirements.txt

@@ -0,0 +1,10 @@
+murmurhash-0.28.0-cp36-cp36m-win32.whl
+cymem-1.31.2-cp36-cp36m-win32.whl
+preshed-1.0.0-cp36-cp36m-win32.whl
+msgpack-0.5.1-cp36-cp36m-win32.whl
+msgpack-numpy==0.4.1
+cytoolz-0.9.0-cp36-cp36m-win32.whl
+ujson-1.35-cp36-cp36m-win32.whl
+spacy-2.0.5-cp36-cp36m-win32.whl
+#python -m spacy download en
+#python -m spacy download fr

BIN
bot/wheels/spacy-2.0.5-cp36-cp36m-win32.whl


BIN
bot/wheels/thinc-6.10.2-cp36-cp36m-win32.whl


BIN
bot/wheels/ujson-1.35-cp36-cp36m-win32.whl


+ 0 - 0
dbsearch.py


+ 39 - 0
dichotomy.py

@@ -0,0 +1,39 @@
+import timeit
+
+def first(iterable, validation):
+    l, r = 0, len(iterable) - 1
+    if validation(iterable[l]):
+        return iterable[l]
+    # if not validation(iterable[r]):
+    #    return None
+    while l < r:
+        mid = (r + l) // 2
+        if validation(iterable[mid]):
+            r = mid
+        else:
+            l = mid + 1
+    if not validation(iterable[l]):
+        return None
+    return iterable[r]
+
+def last(iterable, validation):
+    l, r = 0, len(iterable) - 1
+    if validation(iterable[r]):
+        return iterable[r]
+    # if not validation(iterable[l]):
+    #    return None
+    while l < r:
+        mid = (r + l) // 2
+        if validation(iterable[mid]):
+            l = mid + 1
+        else:
+            r = mid
+    if not validation(iterable[r]):
+        return None
+    return iterable[r - 1]
+
+lst = [0, 1, 2, 3, 4, 5, 4, 3, 2, 1, 0]
+def test(i):
+    return i == 4
+
+print(first(lst, test))

+ 11 - 0
hello_world/.gitignore

@@ -0,0 +1,11 @@
+*.pyc
+
+# Eclipse pydev project
+.project
+.pydevproject
+.settings/
+
+# tests
+htmlcov/
+.coverage
+

+ 2 - 0
hello_world/MANIFEST.in

@@ -0,0 +1,2 @@
+include *.md
+recursive-include hello_world *.png *.jpg *.gif

+ 32 - 0
hello_world/README.md

@@ -0,0 +1,32 @@
+Un script python hello_world.
+
+`hello_world/hello_world.py` : Script python, instancie une fonction `main()` qui renvoie un message de salutations. 
+
+`hello_world/__init__.py` : Fichier qui peut rester vide, mais nécessaire pour que le répertoire soit reconu comme une librairie python.
+
+`test.py` : Contient les tests unitaires
+
+`nose2.cfg` : (facultatif) configuration de nose2
+
+`setup.py` : (facultatif) instruction de déploiement
+
+`README.md` : (facultatif) C'est moi.
+
+`MANIFEST.in` : (facultatif) Détaille les fichiers non-python à inclure dans la distribution.
+
+Pour déployer le code:
+
+	python setup.py install
+
+Pour exécuter les tests, ouvrir une console (invite de commande), et exécuter:
+
+Tests unitaires et couverture
+
+	nose2
+ Syntaxe
+
+	pylint
+
+
+
+ 

+ 5 - 0
hello_world/lib/__init__.py

@@ -0,0 +1,5 @@
+"""
+    Module Hello world
+"""
+
+__version__ = "0.0.1"

+ 18 - 0
hello_world/lib/core.py

@@ -0,0 +1,18 @@
+"""
+    Impl�mentation de Hello World
+
+    Usage:
+
+    >>> from hello_world import hello_message
+    >>> hello_message()
+"""
+
+# limit the available imports
+__all__ = ['hello_message']
+
+def hello_message():
+	""" retourne un emssage de salutations """
+	return "Hello World!"
+
+if __name__ == "__main__":
+	print(hello_message())

+ 7 - 0
hello_world/nose2.cfg

@@ -0,0 +1,7 @@
+[unittest]
+with-coverage = True
+
+[coverage]
+always-on = True
+coverage = .
+coverage-report = html

+ 3 - 0
hello_world/requirements.txt

@@ -0,0 +1,3 @@
+nose2
+cov-core
+coverage

+ 86 - 0
hello_world/setup.py

@@ -0,0 +1,86 @@
+# fichier et explications issues de: http://sametmax.com/creer-un-setup-py-et-mettre-sa-bibliotheque-python-en-ligne-sur-pypi/
+
+from setuptools import setup, find_packages
+
+import hello_world
+
+setup(
+
+    # le nom de votre bibliothèque, tel qu'il apparaitre sur pypi
+    name='hello_world',
+
+    # la version du code
+    version=hello_world.__version__,
+
+    # Liste les packages à insérer dans la distribution
+    # plutôt que de le faire à la main, on utilise la foncton
+    # find_packages() de setuptools qui va cherche tous les packages
+    # python recursivement dans le dossier courant.
+    # C'est pour cette raison que l'on a tout mis dans un seul dossier:
+    # on peut ainsi utiliser cette fonction facilement
+    packages=find_packages(),
+
+    # votre pti nom
+    author="olivier.massot",
+
+    # Votre email, sachant qu'il sera publique visible, avec tous les risques
+    # que ça implique.
+    author_email="olivier.massot@gmail.com",
+
+    # Une description courte
+    description="Hello world",
+
+    # Une description longue, sera affichée pour présenter la lib
+    # Généralement on dump le README ici
+    long_description=open('README.md').read(),
+
+    # Vous pouvez rajouter une liste de dépendances pour votre lib
+    # et même préciser une version. A l'installation, Python essayera de
+    # les télécharger et les installer.
+    #
+    # Ex: ["docopt", "docutils >= 0.3", "lxml==0.5a7"]
+    #
+    # install_requires= ,
+
+    # Active la prise en compte du fichier MANIFEST.in
+    include_package_data=True,
+
+    # Une url qui pointe vers la page officielle de votre lib
+    url='http://codebox/lab/Integration-continue',
+
+    # Il est d'usage de mettre quelques metadata à propos de sa lib
+    # Pour que les robots puissent facilement la classer.
+    # La liste des marqueurs autorisées est longue:
+    # https://pypi.python.org/pypi?%3Aaction=list_classifiers.
+    #
+    # Il n'y a pas vraiment de règle pour le contenu. Chacun fait un peu
+    # comme il le sent. Il y en a qui ne mettent rien.
+    classifiers=[
+        "Programming Language :: Python",
+        "Development Status :: 1 - Planning",
+        "License :: OSI Approved",
+        "Natural Language :: French",
+        "Operating System :: OS Independent",
+        "Programming Language :: Python :: 2.7",
+        "Topic :: Communications",
+    ],
+
+    # C'est un système de plugin, mais on s'en sert presque exclusivement
+    # Pour créer des commandes, comme "django-admin".
+    # Par exemple, si on veut créer la fabuleuse commande "hello-command", on
+    # va faire pointer ce nom vers la fonction proclamer(). La commande sera
+    # créé automatiquement.
+    # La syntaxe est "nom-de-commande-a-creer = package.module:fonction".
+    entry_points={
+        'console_scripts': [
+            'hello-command = hello_world.core:hello_message',
+        ],
+    },
+
+    # A fournir uniquement si votre licence n'est pas listée dans "classifiers"
+    # ce qui est notre cas
+    license="WTFPL",
+
+    # Et encore d'autres paramètes...
+
+)

+ 14 - 0
hello_world/test.py

@@ -0,0 +1,14 @@
+'''
+    Tests unitaires
+'''
+import unittest
+
+import hello_world
+
+class Test(unittest.TestCase):
+
+    def test_main(self):
+        self.assertEqual(hello_world.hello_message(), "Hello CI World!")
+
+if __name__ == "__main__":
+    unittest.main()

+ 460 - 0
landing.py

@@ -0,0 +1,460 @@
+'''
+Created on 2017-02-01
+@author: olivier.massot
+'''
+import math
+import sys
+
+
+G = -3.711
+
+# surface_n = int(input())  # the number of points used to draw the surface of Mars.
+# land = [ int(input().split()[1]) for _ in range(surface_n)]
+
+
+class V(tuple):
+    """ 2D vector (dx, dy)
+    where dx and dy are float """
+    def __new__(cls, dx, dy):
+        return tuple.__new__(cls, tuple([float(dx), float(dy)]))
+
+    @property
+    def dx(self):
+        return self.__getitem__(0)
+
+    @property
+    def dy(self):
+        return self.__getitem__(1)
+
+    @property
+    def angle(self):
+        return math.atan2(self.dy, self.dx)
+
+    @property
+    def amplitude(self):
+        return math.hypot(self.dx, self.dy)
+
+    def unit_vector(self):
+        return self / self.amplitude
+
+    def __repr__(self):
+        return "<Vector ({}, {})>".format(self.dx, self.dy)
+
+    def __getitem__(self, key):
+        return super(V, self).__getitem__(key)
+
+    def __add__(self, value):
+        if isinstance(value, self.__class__):
+            return V(self.dx + value.dx, self.dy + value.dy)
+        elif isinstance(value, int) or isinstance(value, float):
+            return V(self.dx + value, self.dy + value)
+        else:
+            raise TypeError("value has to be a V instance, or a numeric (given: {})".format(value))
+
+    def __sub__(self, value):
+        if isinstance(value, self.__class__):
+            return V(self.dx - value.dx, self.dy - value.dy)
+        elif isinstance(value, int) or isinstance(value, float):
+            return V(self.dx - value, self.dy - value)
+        else:
+            raise TypeError("value has to be a V instance, or a numeric (given: {})".format(value))
+
+    def __mul__(self, value) :
+        return V(self.dx * value, self.dy * value)
+
+    def __truediv__(self, value) :
+        return V(self.dx / value, self.dy / value)
+
+    @classmethod
+    def from_to(cls, point1, point2):
+        return cls(*point2) - cls(*point1)
+
+class Point(tuple):
+    """ 2D point """
+    def __new__(cls, x, y):
+        return tuple.__new__(cls, tuple([int(x), int(y)]))
+
+    def __repr__(self):
+        return "<Point ({}, {})>".format(self.x, self.y)
+
+    @property
+    def x(self):
+        return self.__getitem__(0)
+
+    @x.setter
+    def x(self, x):
+        return self.__setitem__(0, x)
+
+    @property
+    def y(self):
+        return self.__getitem__(1)
+
+    @y.setter
+    def y(self, y):
+        return self.__setitem__(1, y)
+
+    def distance_to(self, other_point):
+        return V(other_point.x - self.x, other_point.y - self.y).amplitude
+
+
+class Landscape(list):
+    def __init__(self, *args):
+        super(Landscape, self).__init__(*args)
+
+    @classmethod
+    def x_index(cls, x):
+        return int(x / 1000)
+
+    def y(self, x):
+        return self.__getitem__(self.x_index(x))
+
+    @property
+    def coordinates(self):
+        return [Point(index * 1000, y) for index, y in enumerate(self)]
+
+    def landing_zones(self):
+        """ "find the index of flat zones where rover can land
+         (index is the position of the left point of the zone in the 'land' list) """
+        return [ index for index in range(len(land) - 1) if land[index] == land[index + 1] ]
+
+    def nearest_landing_point(self, from_x):
+        """return the coordinates of the middle of the
+        nearest landing zone as a tuple (x, y)
+        for zone going from land[index] to land[index + 1] """
+        index = sorted(self.landing_zones(), key=lambda x: abs(1000 * x - from_x))[0]
+        return Point((1000 * index + 500), self.__getitem__(index))
+
+    def next_top(self, from_x, to_x):
+        """return the next peak as a tuple (x, y) """
+        if from_x > to_x:
+            from_x, to_x = to_x, from_x
+        try:
+            return Point(max([(1000 * index, y) for index, y in enumerate(self) \
+                        if to_x > (1000 * index) > from_x], key=lambda x: x[1]))
+        except ValueError:
+            return None
+
+class MarsLander(object):
+    max_h_landing_speed = 20.0
+    max_v_landing_speed = 40.0
+    minimal_flight_altitude = 60
+
+    def __init__(self):
+        self._x = 0
+        self._y = 0
+        self._h_speed = 0.0
+        self._v_speed = 0.0
+        self._power = 0
+        self._rotation = 0
+        self._fuel = 0
+
+    def __setattr__(self, attr, val):
+        """ override object __setattr__ method """
+        # check that the new value has the same type as the old one
+        try:
+            prev = type(self.__getattribute__(attr))
+            if not isinstance(val, prev):
+                raise TypeError("{attribute} has to be an {previous_type} (given: {value})\
+                ".format(attribute=attr, value=val, previous_type=prev))
+        except AttributeError:
+            # newly created attribute
+            pass
+        object.__setattr__(self, attr, val)
+
+
+    def __repr__(self):
+        return "<MarsLander: {}>".format("; ".join(sorted(["{} = {}".format(key, val) for key, val in self.__dict__.items()])))
+
+    @classmethod
+    def from_input(cls, instr):
+        """ load the parameters from the given input """
+        lander = cls()
+        lander.x, lander.y, h_speed, v_speed, lander.fuel, lander.rotation, lander.power = [int(i) for i in instr.split()]
+        lander.h_speed, lander.v_speed = float(h_speed), float(v_speed)
+        return lander
+
+    @property
+    def x(self):
+        return self._x
+
+    @x.setter
+    def x(self, val):
+        if not val in range(7000):
+            raise ValueError("x has to be an integer in range 0 to 6999 (given: {value})".format(value=val))
+        self._x = val
+
+    @property
+    def y(self):
+        return self._y
+
+    @y.setter
+    def y(self, val):
+        if not val in range(3000):
+            raise ValueError("y has to be an integer in range 0 to 2999 (given: {value})".format(value=val))
+        self._y = val
+
+    @property
+    def position(self):
+        return Point(self.x, self.y)
+
+    @property
+    def h_speed(self):
+        return self._h_speed
+
+    @h_speed.setter
+    def h_speed(self, val):
+        # if not val in range(-500, 500):
+        #    raise ValueError("h_speed has to be an integer in range -499 to 499 (given: {value})".format(value=val))
+        self._h_speed = val
+
+    @property
+    def v_speed(self):
+        return self._v_speed
+
+    @v_speed.setter
+    def v_speed(self, val):
+        # if not val in range(-500, 500):
+        #    raise ValueError("v_speed has to be an integer in range -499 to 499 (given: {value})".format(value=val))
+        self._v_speed = val
+
+    @property
+    def speed_vector(self):
+        return V(self.h_speed, self.v_speed)
+
+    @property
+    def power(self):
+        return self._power
+
+    @power.setter
+    def power(self, val):
+        if not val in range(5):
+            raise ValueError("power has to be an integer in range 0 to 4 (given: {value})".format(value=val))
+        self._power = val
+
+    @property
+    def rotation(self):
+        return self._rotation
+
+    @rotation.setter
+    def rotation(self, val):
+        if not val in range(-91, 91):
+            raise ValueError("rotation has to be an integer in range -90 to 90 (given: {value})".format(value=val))
+        self._rotation = val
+
+    @property
+    def fuel(self):
+        return self._fuel
+
+    @fuel.setter
+    def fuel(self, val):
+        if not val in range(2001):
+            raise ValueError("fuel has to be an integer in range 0 to 2000 (given: {value})".format(value=val))
+        self._fuel = val
+
+    @classmethod
+    def compute_acceleration(cls, power, rotation):
+        """ return the resulting acceleration as a 2d vector (ax, ay)
+        with that power / rotation configuration"""
+        return V(-1 * math.sin(math.radians(rotation)) * power, math.cos(math.radians(rotation)) * power + G)
+
+    @property
+    def acceleration(self):
+        """ return the current acceleration as a 2d vector (ax, ay) """
+        return self.compute_acceleration(self.power, self.rotation)
+
+    def speed_in(self, t):
+        """ compute the speed in 't' seconds with the same acceleration,
+        returns a 2d vector (vx, vy) """
+        if not isinstance(t, int):
+            raise TypeError("'t' has to be an integer (given:{})".format(t))
+        if not t >= 0:
+            raise ValueError("'t' has to be positive (given:{})".format(t))
+        ax, ay = self.acceleration
+        return V(self.h_speed + ax * t, self.v_speed + ay * t)
+
+    def update_speed(self, dt):
+        """ update the current speed, after 't' sec. with current acceleration """
+        self.h_speed, self.v_speed = self.speed_in(dt)
+
+    def configurations(self):
+        """return the available (power, rotation) depending on the current configuration"""
+        return [(p, 15 * r) for p in range(5) for r in range(-6, 7)]
+#                 if abs(p - self.power) <= 1 and abs(15 * r - self.rotation) <= 15]
+
+    def compute_trajectory(self, landscape):
+        """ return the route to follow to each the landing point
+        as a list of Points
+        The last point will be above the landing point of 'minimal_flight_altitude'
+        If that point has been reached, return an empty list"""
+        landing_vector = V.from_to(self.position, landscape.nearest_landing_point(self.x))
+        steps = []
+
+        if abs(landing_vector.dx) < 100 and abs(landing_vector.dy) <= self.minimal_flight_altitude:
+            # landing point has been reached
+            return []
+
+        # could be shorter, could be quicker, but robustness and clarity are needed
+        for point in landscape.coordinates:
+            top_vector = V.from_to(self.position, point) + V(0, self.minimal_flight_altitude)
+
+            if top_vector.dx * landing_vector.dx < 0:
+                # top is not in the landing point direction
+                continue
+
+            if abs(top_vector.dx) > abs(landing_vector.dx):
+                # too far
+                continue
+
+            if (landing_vector * top_vector.dx / abs(landing_vector.dx)).dy > top_vector.dy:
+                # top is under the trajectory
+                continue
+
+            steps.append(Point(*(V(*self.position) + top_vector)))
+
+        steps.sort(key=lambda p: p.x, reverse=(landing_vector.dx < 0))
+        last_step_vector = (V(*self.position) + landing_vector + V(0, self.minimal_flight_altitude))
+        steps.append(Point(*last_step_vector))
+
+        return steps
+
+    def land(self, landscape, callback=None, output=None):
+        """ (generator)
+            Mars lander will land on the given landscape
+        """
+        # callback function is called at each iteration
+        if not callback:
+            callback = (lambda x: x)
+        # output function is called to print lander's thoughts
+        if not output:
+            output = (lambda x: print(x))
+
+        t = 0
+        while 1:
+            t += 1
+            output("Hi, this is Mars Lander, and it is {} s.".format(t))
+            output("I am currently at ({} , {})".format(self.x, self.y))
+
+            trajectory = self.compute_trajectory(landscape)
+            output("My trajectory is {}".format(trajectory))
+
+            try:
+                target = trajectory[0]
+                output("For now, I try to reach {}".format(target))
+            except IndexError:
+                output("Right: I'm landing right now")
+                self.power = 4 if self.v_speed > 1 else 3
+                self.rotation = 0
+                callback(self)
+                yield t
+                continue
+
+            target_direction = V.from_to(self.position, target).unit_vector()
+            output("Direction unit vector is {}".format(target_direction))
+
+            # current speed vector
+            output("My current speed vector is {}".format(self.speed_vector))
+
+            # optimal speed vector
+            if abs(target_direction.dx * self.max_h_landing_speed) >= abs(target_direction.dy * self.max_v_landing_speed):
+                needed_speed_vector = target_direction * self.max_h_landing_speed
+            else:
+                needed_speed_vector = target_direction * self.max_v_landing_speed
+            output("I need a speed like: {}".format(needed_speed_vector))
+
+            # compute the new acceleration vector
+            new_acc_vector = needed_speed_vector - self.speed_vector
+            output("I can reach that with that acceleration: {}".format(new_acc_vector))
+
+            # look for the closest possible acceleration (p is set to 0.1 instead of 0 in order to give sense to vectors comparison)
+            configs = [(p, r) for p in (0.1, 1, 2, 3, 4) for r in range(-90, 91, 15)]
+            closest_config = min([(p, r, self.compute_acceleration(p, r)) for p, r in configs], \
+                                 key=lambda c: Point(*new_acc_vector).distance_to(Point(*c[2])))
+            power, rotation, new_acc_vector = closest_config
+            if power < 1:
+                power = 0
+            output("The closest acceleration I could reach is {} ms-2, with power {} and rotation {}".format(new_acc_vector, power, rotation))
+
+            # limits the changes if needed
+            if abs(power - self.power) > 1 or abs(rotation - self.rotation) > 15:
+                if power != self.power:
+                    power = self.power + int((power - self.power) / abs(power - self.power))
+                if rotation != self.rotation:
+                    rotation = self.rotation + 15 * int((rotation - self.rotation) / abs(rotation - self.rotation))
+                new_acc_vector = self.compute_acceleration(power, rotation)
+                output("Hmmph, the best I can reach is power {}, rotation {}, for an acceleration of {} ms-2".format(power, rotation, new_acc_vector))
+
+            # update the lander attributes
+            self.power = power
+            self.rotation = rotation
+            self.fuel -= power
+
+            # new position
+            new_x, new_y = V(*self.position) + self.speed_vector
+            self.x, self.y = int(new_x), int(new_y)
+
+            # new speed
+            self.h_speed, self.v_speed = self.speed_vector + new_acc_vector
+
+            callback(self)
+            yield t
+
+    def hyperspace(self):
+        raise NotImplementedError()
+
+    def distance_to(self, x, y):
+        return math.hypot((x - self.x), (y - self.y))
+
+    def angle_to(self, x, y):
+        return math.atan2((y - self.y), (x - self.x))
+
+def send_data(lander):
+    print("{rotation} {power}".format(rotation=lander.rotation, power=lander.power))
+    # input()
+
+def output(msg):
+    print(msg, file=sys.stderr)
+
+
+surface_n = 7
+land = [150, 500, 2000, 150, 150, 500, 2000]
+# land = [3000, 150, 150, 500, 3000, 600, 1000]
+
+landscape = Landscape(land)
+
+lander = MarsLander()
+lander.x = 3000
+lander.y = 2000
+lander.fuel = 1000
+gen = lander.land(landscape)
+
+for i in range(5):
+    t = next(gen)
+    print(t, lander)
+    input()
+
+# lander = MarsLander.from_input( input() )
+# landing_point_x, landing_point_y = nearest_landing_zone(lander.x)
+# target_x, target_y = landing_point_x, landing_point_y
+# #move_generator = lander.move_to(target_x, target_y, callback=send_data, landing=True, output=output, land_max_elevation=max(land))
+#
+# while True:
+#     target = next_peak(lander.x, landing_point_x)
+#     if target:
+#         if target != (target_x, target_y):
+#             target_x, target_y = target
+#             move_generator = lander.move_to(target_x, target_y + 100, callback=send_data, landing=False)
+#     else:
+#         move_generator = lander.move_to(landing_point_x, landing_point_y, callback=send_data, landing=True)
+#
+#     t = next(move_generator)
+#     print(t, lander, file=sys.stderr)
+#
+#     x, y, h_speed, v_speed, fuel, rotate, power = [int(i) for i in input().split()]
+#     #print(x, y, h_speed, v_speed, fuel, rotate, power, file=sys.stderr)
+#
+#
+#     # rotate power. rotate is the desired rotation angle. power is the desired thrust power.
+#     # print("{rotation} {power}".format(rotation=lander.rotation, power=lander.power))
+#
+#     #input() # get the unused input
+#     #t = next(move)

+ 7 - 0
mon_appli.py

@@ -0,0 +1,7 @@
+
+
+
+for i in range(10):
+	print(i)
+	
+input()

+ 4 - 0
mon_programme.py

@@ -0,0 +1,4 @@
+
+with open(r"C:\dev\python\bac_a_sable\histo.txt", "r") as f:
+    for line in f:
+        print(line)

+ 8 - 0
orion_bkp.py

@@ -0,0 +1,8 @@
+from path import Path
+
+
+ORION_PROGRAM_DIR = Path("/var/www/html/nextcloud")
+ORION_DATA_DIR = Path("/media/mem/orion")
+ORION_DB = "orion"
+
+BACKUP_DIR = Path("/media/bkp")

+ 48 - 0
setup.py

@@ -0,0 +1,48 @@
+import sys
+from cx_Freeze import setup, Executable
+
+name = "amap"
+version = "0.33"
+description = "AMAP - Des produits locaux et de saison"
+author = "olivier.massot[at]bas-rhin.fr"
+
+includefiles = []
+includes = []
+excludes = []
+packages = []
+optimize = 1
+silent = False
+icon = r".\rsc\amap.ico"
+
+# construction du dictionnaire des options
+options = {"path": sys.path,
+           "includes": includes,
+           "excludes": excludes,
+           "packages": packages,
+           "include_files": includefiles,
+           "optimize": optimize,
+           "silent": silent
+           }
+
+# pour inclure sous Windows les dll system de Windows necessaires
+if sys.platform == "win32":
+    options["include_msvcr"] = True
+
+target = Executable(
+    script="amap.py",
+    base=None,
+    icon=icon
+    )
+
+setup(
+    name=name,
+    version=version,
+    description=description,
+    author=author,
+    options={"build_exe": options},
+    executables=[target]
+    )
+
+
+
+

+ 11 - 0
shortuid.py

@@ -0,0 +1,11 @@
+import random
+import time
+
+
+def shortuid():
+    """ 15 cars hexadecimal uuid """
+    base = int(time.time()) << 32
+    rand = random.SystemRandom().getrandbits(32)
+    return hex((base + rand))[2:-1]
+
+print(shortuid())

+ 19 - 0
symlinks.py

@@ -0,0 +1,19 @@
+'''
+Created on 1 mars 2017
+
+@author: olivier.massot
+'''
+import os
+
+
+# Python 3+, Windows Vista et superieur
+# ! Requiert les droits admin (ou modifier la sécurité locale)
+
+src = r"c:\wrktmp\abc\abc"
+dst = r"c:\wrktmp\abc\symlink_abc"
+is_dir = True
+
+os.symlink(src, dst, is_dir)
+
+
+

+ 10 - 0
test.py

@@ -0,0 +1,10 @@
+from subprocess import call
+
+from path import Path
+
+
+importfile = Path(r"C:\dev\python\scripts_pde\work\gf2analytique\import.csv")
+
+print(importfile)
+
+call(["start", "", importfile.abspath()], shell=True)

+ 22 - 0
test_profiling.py

@@ -0,0 +1,22 @@
+'''
+'''
+import cProfile
+from math import sqrt
+
+
+def test():
+    result = []
+
+    for i in range(100000):
+        x = sqrt(i)
+        result.append(x)
+    return result
+
+def main():
+    result = test()
+    for val in result:
+        pass
+
+# cProfile.run('main()', 'results', 1)
+cProfile.run('main()', filename='my_result.txt', sort='nfl')
+cProfile.run('test()', filename='my_result.txt', sort='nfl')

+ 19 - 0
test_stringio.py

@@ -0,0 +1,19 @@
+import io
+import sys
+
+sysout = sys.stdout
+
+strio = io.StringIO()
+sys.stdout = strio
+
+for i in range(5):
+    print(5)
+
+input()
+
+result = strio.getvalue()
+
+sys.stdout = sysout
+print(result)
+
+strio.close()

+ 29 - 0
uid.py

@@ -0,0 +1,29 @@
+import time
+from random import randint
+
+def uid(prefixe = ""):
+    """construit un identifiant unique de 10 caracteres"""
+    #representation de la date (annee/mois/jour/heure/min/seconde)
+    base = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n", \
+        "o","p","q","r","s","t","u","v","w","x","y","z", \
+        "A","B","C","D","E","F","G","H","I","J","K","L", \
+        "M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z", \
+        "2","3","4","5","6","7","8","9"]
+    dat = time.gmtime()
+    a = dat[0] ; m = dat[1] ; j = dat[2] ; h = dat[3] ; mn = dat[4] ; s = dat[5]
+    a_, m_, j_, h_, mn_, s_ = base[(int(a)-2000)], base[int(m)], base[int(j)], base[int(h)], base[int(mn)], base[int(s)]
+    #ajout de 2 caracteres aleatoires
+    c1 = base[randint(0,59)] ; c2 = base[randint(0,59)]
+    #concatenation
+    if len(prefixe) >= 2:
+        p_ = prefixe[0:2]
+    else:
+        while len(prefixe) < 2:
+            prefixe += "-"
+        p_ = prefixe  
+    retour = "{}{}{}{}{}{}{}{}{}".format(p_, a_, m_, j_, h_, mn_, s_, c1, c2)
+    
+    return retour
+
+
+

+ 49 - 0
whr.py

@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#-*- coding:utf-8 -*-
+
+import BaseHTTPServer
+import sys
+import time
+import urlparse
+import json
+
+
+HOST_NAME = sys.argv[1]
+PORT_NUMBER = int(sys.argv[2])
+
+
+def handle_hook(payload):
+    pass
+
+
+class HookHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    server_version = "HookHandler/0.1"
+    def do_GET(s):
+        s.send_response(200)
+        s.wfile.write('Hello!')
+
+    def do_POST(s):
+        # Check that the IP is within the GH ranges
+##        if not any(s.client_address[0].startswith(IP)
+##                   for IP in ('192.30.252', '192.30.253', '192.30.254', '192.30.255')):
+##            s.send_error(403)
+
+        length = int(s.headers['Content-Length'])
+        post_data = urlparse.parse_qs(s.rfile.read(length).decode('utf-8'))
+        payload = json.loads(post_data['payload'][0])
+
+        handle_hook(payload)
+
+        s.send_response(200)
+
+
+if __name__ == '__main__':
+    server_class = BaseHTTPServer.HTTPServer
+    httpd = server_class((HOST_NAME, PORT_NUMBER), HookHandler)
+    print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)
+    try:
+        httpd.serve_forever()
+    except KeyboardInterrupt:
+        pass
+    httpd.server_close()
+print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)

+ 24 - 0
yaml_.py

@@ -0,0 +1,24 @@
+'''
+Created on 17 fevr. 2017
+
+@author: olivier.massot
+'''
+import json
+import os
+
+import yaml
+
+
+def json_to_yaml(path, new_path):
+    with open(path) as f:
+        data = json.load(f)
+
+    print(data)
+
+    with open(new_path, "w") as f2:
+        yaml.dump(data, f2, default_flow_style=False)
+
+
+# json_to_yaml(r"C:\dev\access\Factures\setup.json", r"C:\dev\access\Factures\.amap.yaml")
+with open(r"C:\dev\tests\test-amap-project\.amap.yaml") as f:
+    print(yaml.load(f.read()))