Пример #1
0
def inscription():	
	if request.method == 'POST':
		pseudo = request.form['pseudo']
		email = request.form['email']
		password = request.form['password']
		password_confirmation = request.form['password_confirmation']
		is_admin = ('admin' in request.form)

		existing_name = list(mongo.MongoLoad({'pseudo': pseudo}).retrieve('users_accounts'))
		existing_mail = list(mongo.MongoLoad({'email': email}).retrieve('users_accounts'))

		if existing_name:
			error = 'Ce pseudo est déjà utilisé, veuillez en choisir un autre.'
		elif existing_mail:
			error = 'Cette adresse mail est déjà utilisée, veuillez vous-connectez.'
		elif password != password_confirmation:
			error = 'Les deux mots de passes sont différents.'

		else:
			#Cookies
			session['username'] = pseudo
			session['admin?'] = is_admin   

			#Cryptage du mot de passe
			hashpass= bcrypt.hashpw(password.encode('utf-8'),bcrypt.gensalt())
			
			#Stockage dans mongoDB
			dic = {
					'pseudo': pseudo,
					'email': email,
					'password': hashpass
				  }
			if session['admin?']:
				dic['admin?'] = 'YES'
				db_tester(session['username']) #Création d'un profil testeur
			else:
				dic['admin?'] = 'NO'
			documents = mongo.MongoSave([dic])
			documents.storeindb('users_accounts',pseudo='A',email='A')

			#redirection vers la map
			return redirect(url_for('map'))  

		return render_template('inscription.html',error=error)

	elif 'username' in session:
		return redirect(url_for('map'))

	else:
		return render_template('inscription.html')
Пример #2
0
def get_list_version():
    """Crée la collection de stockage des versions du scraper si elle n'existe pas.
	Récupère les résultats de tests finaux et les traite pour générer de nouvelles
	règles. Une nouvelle version du scraper est alors créée pour utiliser ces règles. 
	Toutes les versions sont envoyées à la page de lancement du scrape et sont
	proposées à l'utilisateur.
	"""

    dbfinder = mongo.MongoLoad(proj={'search_version': 1, '_id': 0})

    if not dbfinder.mongocheck('Versions_Scrape'):
        version_doc = mongo.MongoSave([{
            'search_version': '1.00',
            'submissions_scraped': 0,
            'accuracy': 0
        }])
        version_doc.storeindb('Versions_Scrape', search_version='D')

    proc.create_rule()

    version_list = []
    for doc in dbfinder.retrieve('Versions_Scrape'):
        version_list.append(doc['search_version'])

    return jsonify(version_list)
Пример #3
0
def get_count():
    """Renvoie le nombre total de documents que le testeur en session doit tester, et
	les versions du scraper disponibles.
	"""

    dbcounter = mongo.MongoLoad({'user_id': session['username']}, {
        'code': 1,
        '_id': 0
    })
    test_code = next(dbcounter.retrieve('Testeurs'))['code']
    doc_number = dbcounter.mongocount(
        'Resultats_RGN', {'testers': {
            '$bitsAllSet': 2**test_code
        }})

    version_list = []
    dbcounter.reinit({'testers': {
        '$bitsAllSet': 2**test_code
    }}, {
        'search_version': 1,
        '_id': 0
    })

    for doc in dbcounter.retrieve('Resultats_RGN'):
        if doc['search_version'] not in version_list:
            version_list.append(doc['search_version'])

    return jsonify(nbtest=doc_number,
                   pseudo=session['username'],
                   versions=version_list)
Пример #4
0
def connexion():
	if request.method == 'POST':
		pseudo_email = request.form['pseudo_email']
		password = request.form['password']
		
		#Chercher le compte en supposant que c'est le pseudo
		cmpt = mongo.MongoLoad({'pseudo': pseudo_email})
		compte = list(cmpt.retrieve('users_accounts',limit=1))

		#Si compte pas trouvé, chercher le compte en supposant que c'est le mail
		if not compte:
			cmpt.reinit({'email': pseudo_email})
			compte = list(cmpt.retrieve('users_accounts',limit=1))

		#Si compte trouvé	
		if compte:
			compte = compte[0]
			#Vérifier le mot de passe checkpw(password, hashed)
			if bcrypt.hashpw(password.encode('utf-8'),compte['password']) == compte['password']:

				#Cookies
				session['username'] = compte['pseudo']
				session['admin?'] = ( compte['admin?'] == "YES" )
				return redirect(url_for('map'))
					
		#Pseudo,email ou mot de passe invalide
		error = 'Le pseudo/email ou le mot de passe n\'est pas valide'
		return render_template('connexion.html',error=error)
			
	elif 'username' in session:
		return redirect(url_for('map'))
	
	else:
		return render_template('connexion.html')
Пример #5
0
def select_results(version, url_list):
    """Prépare le traitement des résultats de tests des documents dont tous les tests
	ont été effectués. Récupère les résultats dans la collection de résultats et les
	valeurs nécessaires dans les documents issus du scraping.
	Sélection des résultats de test définitifs à partir des choix majoritaires des
	testeurs.
	"""

    dbfinder = mongo.MongoLoad(
        {
            'img_url': {
                '$in': url_list
            },
            'search_version': version
        }, {
            'img_url': 1,
            'locations_selected': 1,
            'sufficient': 1,
            '_id': 0
        })

    group_results = bucket(dbfinder.retrieve('Resultats_Test_Expert_1'),
                           key=lambda x: x['img_url'])

    dbfinder.reinit({
        'img_url': {
            '$in': url_list
        },
        'search_version': version
    }, {
        'search_version': 1,
        'country': 1,
        'img_url': 1,
        'tag_list': 1,
        'location_list': 1,
        '_id': 0
    })

    final_results = []
    for doc in dbfinder.retrieve('Resultats_RGN'):
        result = list(
            group_results[doc['img_url']]
        )  #Conversion de l'itérateur car double parcours nécessaire
        doc['locations_selected'] = [
            True if comp.count(True) > len(comp) / 2 else False
            for comp in zip(*[res['locations_selected'] for res in result])
        ]

        doc['sufficient'] = True if sum(
            1 if b else -1
            for b in [res['sufficient'] for res in result]) > 0 else False
        doc['processed'] = False

        final_results.append(doc)

    return final_results
Пример #6
0
def get_results():
    """Extraction de documents à tester de la collection 'Résultats_RGN' (résultats du
	scraping) de la base de données, en fonction du testeur qui a lancé la requête, et
	renvoie en format JSON des résultats à la page de tests.
	"""

    result_value = request.args.get('value')
    version = request.args.get('version')
    limit = int(request.args.get('limit'))

    dbfinder = mongo.MongoLoad({'user_id': session['username']}, {
        'code': 1,
        '_id': 0
    })
    test_code = next(dbfinder.retrieve('Testeurs'))['code']

    dbfinder.reinit(
        {
            'search_version': version,
            'test_result': result_value,
            'testers': {
                '$bitsAllSet': 2**test_code
            }
        },  #Opérateur binaire
        {
            'text': 1,
            'search_version': 1,
            'img_url': 1,
            'tag_list': 1,
            'scraped_title': 1,
            'test_list': 1,
            'location_list': 1,
            'location': 1,
            'name': 1,
            'country_code': 1,
            '_id': 0
        })
    doc_list = list(dbfinder.retrieve('Resultats_RGN', limit=limit))
    print(doc_list[0])

    return jsonify(results=doc_list)
Пример #7
0
def location_finder(country, version, tags):
    """Recherche des lieux potentiels dans le titre de la soumission Reddit. La recherche de base
	considère que les noms propres voisins forment un lieu. Par-dessus cette recherche, les règles
	générées par l'analyse des résultats des tests utilisateurs fournissent des mots particuliers
	à prendre en compte (dotés de l'étiquette 'TAR') ou à ignorer (l'étiquette 'IGN').
	En plus des lieux potentiels, la liste résultat stocke le nombre de mots non sélectionnés
	séparant les lieux retenus.
	"""

    db = mongo.MongoLoad(
        {
            'country': country,
            'search_version': {
                '$lte': version
            }
        }, {
            'expr': 1,
            'pos': 1,
            'take': 1,
            '_id': 0
        })

    for rule in db.retrieve('Nouvelles_Regles'):
        expr_len = len(rule['pos'])
        expr_str = ''
        expr_tags = []

        for i in range(len(tags)):
            if i - expr_len + 1 >= 0:
                expr_str = ' '.join(tags[i][0]
                                    for i in range(i - expr_len + 1, i + 1))
                expr_tags = [
                    tags[i][1] for i in range(i - expr_len + 1, i + 1)
                ]

            if expr_str == rule['expr']:
                if all(expr_tags[i] == rule['pos'][i]
                       for i in range(expr_len)):
                    left = i - expr_len

                    if rule['take'] == 0:
                        for j in range(i - expr_len, i + 1):
                            if tags[j][1] != 'NP0':
                                tags[j] = (tags[j][0], 'TAR', tags[j][2])

                    elif rule['take'] == 'X':
                        for j in range(i - expr_len, i + 1):
                            tags[j] = (tags[j][0], 'IGN', tags[j][2])

                    elif rule['take'] == 1:
                        if i + 1 < len(tags) and tags[i + 1][1] != 'NP0':
                            tags[i + 1] = (tags[i + 1][0], 'TAR',
                                           tags[i + 1][2])

                    elif rule['take'] == 'R':
                        if i + 1 < len(tags):
                            tags[i + 1] = (tags[i + 1][0], 'IGN',
                                           tags[i + 1][2])

                    elif rule['take'] == -1:
                        if left >= 0 and tags[left][1] != 'NP0':
                            tags[left] = (tags[left][0], 'TAR', tags[left][2])

                    elif rule['take'] == 'L':
                        if left >= 0:
                            tags[left] = (tags[left][0], 'IGN', tags[left][2])

                    else:
                        if i+1 < len(tags) and tags[i+1][1] == 'NP0' \
                        and left >= 0 and tags[left][1] == 'NP0':
                            tags[i] = (tags[i][0], 'TAR', tags[i][2])

    loc_list = [
        ' '.join(t[0] for t in g) if k else len(list(g))
        for k, g in groupby(tags, key=lambda x: x[1] == 'NP0' or x[1] == 'TAR')
    ]

    return loc_list
Пример #8
0
def scraping():
    """Si l'utilisateur n'a pas demandé un scraping, recherche de documents du pays sélectionné
	dans la base de données; ces documents et leurs liens vers les photos seront renvoyés.
	Si l'utilisateur a demandé un scraping, ou s'il n'y a pas ou pas assez de documents du pays
	sélectionné dans la base de données, configuration et lancement du scrape sur Reddit, puis
	étiquetage des titres des soumissions résultats par TreeTagger, et analyse des étiquettes
	pour obtenir une liste de lieux potentiels.
	Ces lieux sont recherchés sur geonames. Les résultats de cette dernière recherche sont chargés
	dans deux dictionnaires, l'un pour l'affichage des photos sur le site et l'autre pour stocker
	les résultats dans la base de données sur mongoDB.
	NB: le scraping tente toujours d'obtenir de nouvelles photos (absentes de mongoDB).
	"""

    #Configuration Geoscape
    geokey = current_app.config['GEOKEY']
    geoauth = current_app.config['GEOAUTH']

    #Paramètres de la requête Javascript
    rgnversion = request.args.get('search_version')
    country = request.args.get('country')
    country_code = request.args.get('country_code')
    limit = int(request.args.get('nombre_image'))
    scrape_requested = True if request.args.get(
        'scraping') == 'true' else False

    #Dico de résultats pour l'affichage sur le site
    search_res = geo.GeoQuery(geokey, geoauth, country, country_code, 'E')
    dic_results = {
        'head': {
            'total': 0,
            'country': {
                'name': country,
                'lng': search_res.result.lng,
                'lat': search_res.result.lat
            }
        },
        'results': []
    }
    #Liste de chargement pour la base de données
    database_list = []

    if scrape_requested:  #On ne charge que les img_url
        load_arg = {'img_url': 1, '_id': 0}
    else:  #On charge le document pour l'affichage
        load_arg = {
            'scraped_title': 0,
            'location_list': 0,
            'feature_class': 0,
            'testers': 0,
            '_id': 0
        }

    existing_urls = []
    check_db = mongo.Mongo.mongocheck('Resultats_RGN')

    #Initialisation de la collection des résultats si elle n'existe pas
    if not check_db:
        dbstart = mongo.MongoSave([{
            'key':
            'Initialisation de la collection Resultats_RGN.'
        }])
        dbstart.storeindb('Resultats_RGN', img_url='A', search_version='D')
        dbstart.nonunique_index('Resultats_RGN',
                                country='A',
                                search_version='D')

    #Les documents pris dans la base de données sont chargés dans le dictionnaire de résultats
    else:
        dbfinder = mongo.MongoLoad(
            {
                'search_version': rgnversion,
                'country': country
            }, load_arg)
        for doc in dbfinder.retrieve('Resultats_RGN', limit=limit):
            if not scrape_requested:
                dic_results['head']['total'] += 1
                dic_results['results'].append(doc)
            existing_urls.append('-url:' + doc['img_url'])

    if scrape_requested or dic_results['head']['total'] < limit:
        #Configuration recherche reddit, profil chargé depuis praw.ini
        reddit = praw.Reddit('current_user')

        target_sub = reddit.subreddit('EarthPorn')
        query = country if country != 'United States' else 'USA'
        print(
            '\033[92m' + target_sub.display_name + '\033[0m'
            '\nRésultats de recherche pour les soumissions reddit avec:',
            query, '\n')

        #Exclure les documents déjà récupérés
        user_limit = limit

        if len(query) + len(existing_urls) + sum(
                len(url) for url in existing_urls) <= 512:
            query += (' ' + ' '.join(existing_urls)).rstrip()
            limit -= dic_results['head']['total']
        else:  #512 caractères max dans une requête Reddit
            limit = 1000  #Max permis par Reddit

        existing_urls = [url[5:] for url in existing_urls]

        #Config TreeTagger. Le dossier Treetagger doit être dans le dossier d'où le programme est exécuté
        if sys.platform.startswith('linux'):
            reddit_tagger = TreeTagger(TAGLANG='en',
                                       TAGDIR=join(getcwd(), 'Treetagger',
                                                   'TreeTagger_unix'))
        elif sys.platform.startswith('win'):
            reddit_tagger = TreeTagger(TAGLANG='en',
                                       TAGDIR=join(getcwd(), 'Treetagger',
                                                   'TreeTagger_windows'))
        else:
            sys.exit('Système d\'exploitation non compatible avec Geoscape.')

        #Résultats de la recherche dans le subreddit
        test_posts = target_sub.search(query, limit=limit)

        for post in test_posts:
            try:
                attempt = post.url
            except prawcore.exceptions.NotFound:
                continue  #Problème avec la photo; éliminé

            if post.url in existing_urls:
                continue  #Déjà stocké dans la base de données; éliminé

            if search('\W' + country + '\W',
                      post.title):  #Pays comme mot distinct
                #Saute aux plus une fois des caractères entre [] ou () au début du texte et s'arrête au premier [ ou (
                res = search('^(?:[\[(].*[\])])?([^\[(]+)', post.title)
                if (res):
                    print(res.group(1))

                    #Tagging: génère une liste de triplets: (word=..., pos=..., lemma=...)
                    reddit_tags = make_tags(reddit_tagger.tag_text(
                        res.group(1)),
                                            exclude_nottags=True)

                    #Le nom du pays est exclu des lieux potentiels; rajouté seulement en dernier recours
                    country_split = country.casefold().split(' ')
                    size = len(country_split)
                    indexes = []
                    if size > 1:
                        name_tags = [t[0].casefold() for t in reddit_tags]
                        for window in enumerate(windowed(name_tags, size)):
                            if all(window[1][i] == country_split[i]
                                   for i in range(size)):
                                indexes.extend([
                                    i
                                    for i in range(window[0], window[0] + size)
                                ])

                    for index, tag in enumerate(reddit_tags):
                        if tag[1] == 'NP':  #Tag nom propre sous Windows
                            reddit_tags[index] = (tag[0], 'NP0', tag[2])
                        if tag[0].casefold() == country.casefold(
                        ) or index in indexes:
                            reddit_tags[index] = (tag[0], 'CTY', tag[2])
                    pprint(reddit_tags)

                    #Recherche des lieux potentiels, avec stocké entre les lieux le nombre de mots non choisis
                    location_list = location_finder(country, rgnversion,
                                                    reddit_tags)

                    print('Lieux trouvés:', end='')
                    print(location_list, '\n')

                    #Geonames
                    date = gmtime(post.created_utc)
                    dic_mongo = {
                        'link': 'https://www.reddit.com' + post.permalink,
                        'img_url': post.url,
                        'search_version': rgnversion,
                        'country': country,
                        'country_code': country_code,
                        'scraped_title': res.group(1).strip(),
                        'text': post.title,
                        'tag_list': reddit_tags,
                        'location_list': location_list,
                        'date': {
                            'year': date.tm_year,
                            'month': date.tm_mon,
                            'day': date.tm_mday,
                            'hour': date.tm_hour,
                            'min': date.tm_min,
                            'sec': date.tm_sec
                        }
                    }

                    try:
                        attempt = post.author.icon_img
                    except prawcore.exceptions.NotFound:
                        pass
                    else:
                        dic_mongo['author'] = {
                            'name':
                            post.author.name,
                            'icon':
                            post.author.icon_img,
                            'profile':
                            'https://www.reddit.com/user/' + post.author.name
                        }
                    """ R: recherche standard
						RF: recherche fuzzy
						E: recherche exacte
						EH: recherche exacte sur ensembles humains
						EN: recherche exacte sur ensembles naturels
					"""

                    placefinder = geo.LocationList(country_code, location_list)
                    geo_res = placefinder.geo_search(geokey, geoauth, 'EN EH',
                                                     'R')  #Objet GeoQuery

                    #En dernier recours, le pays lui-même s'il est dans le titre
                    if geo_res.result is None and country in res.group(1):
                        placefinder.reinit(country_code, [country])
                        geo_res = placefinder.geo_search(geokey, geoauth, 'E')

                    if geo_res.result is not None:
                        dic_results['head']['total'] += 1
                        print('Résultat GeoNames:',
                              geo_res.result.address,
                              end='')
                        print('. Après', placefinder.counter, 'requêtes.')

                        dic_mongo['name'] = geo_res.result.address  #Nom
                        dic_mongo['lng'] = geo_res.result.lng
                        dic_mongo['lat'] = geo_res.result.lat
                        dic_mongo[
                            'feature_class'] = geo_res.result.feature_class
                        dic_mongo['location'] = geo_res.location

                        dic_results['results'].append(dic_mongo)

                        dic_tostore = deepcopy(dic_mongo)
                        database_list.append(dic_tostore)

                        user_limit -= 1
                        if not user_limit:
                            break

                print('\n###############')

        #Chargement dans la base de données des documents générés par le scrape
        documents = mongo.MongoSave(database_list)
        documents.storeindb('Resultats_RGN')

    return jsonify(dic_results)
Пример #9
0
def report():
    """Déclenchée par le signalement d'une image mal affichée sur la carte (mauvais
	lieu choisi). L'information est rajoutée au document dans la base de données, ce
	qui le rend disponible pour le test avancé.
	Sélection aléatoire de 3 testeurs à partir de la collection 'Testeurs', et stockage
	de leurs codes comme champ du document signalé.
	Ce champ 'testers' est de type BSON BinData: une chaîne en base 64 représentant un
	champ de bits de taille quelconque, ce qui permet de stocker des valeurs supérieures
	à 2^63-1.
	Le module pymongo convertit automatiquement un objet de type bytes en BinData.
	"""

    #La méthode POST renvoie des bytes: convertir en string JSON puis en dico python
    data = json.loads(request.data.decode('utf-8'))
    response = data['image']
    value = data['value']  #OK ou NOT_OK
    word_list = data['list_words']  #Vide si OK

    dbfinder = mongo.MongoLoad(
        {
            'search_version': response['search_version'],
            'img_url': response['img_url'],
            'test_result': {
                '$exists': True
            }
        }, {
            'img_url': 1,
            '_id': 0
        })

    try:
        next(dbfinder.retrieve('Resultats_RGN'))
    except StopIteration:  #L'image n'a pas été signalée auparavant
        update = mongo.MongoUpd(
            {
                'img_url': response['img_url'],
                'search_version': response['search_version']
            }, {'$set': {
                'test_result': value,
                'test_list': word_list
            }})
        update.singleval_upd('Resultats_RGN')

        dbfinder.reinit(proj={'code': 1, '_id': 0})
        tester_list = list(dbfinder.retrieve('Testeurs'))
        n = len(tester_list)

        if n >= 1:
            num_testers = min(n if n % 2 else n - 1, 9)

            seed()  #Sélection aléatoire dans la liste
            tester_sum = sum(2**t['code']
                             for t in sample(tester_list, num_testers))
            bytesize = floor(log2(tester_sum) /
                             8 if tester_sum else 0) + 1  #log2(0) non défini
            tester_sum = tester_sum.to_bytes(bytesize, byteorder='big')

            update.reinit(update={'$set': {'testers': tester_sum}})
            update.singleval_upd('Resultats_RGN')

    return jsonify(status='OK')
Пример #10
0
def send_results():
    """Réception des résultats du test-expert et stockage dans la base de données;
	décrémentation de la champ 'testers' des documents qui viennent d'être testés
	de la collection 'Resultats_RGN', et incrémentation du champ 'num_answers' du
	testeur dans la collection 'Testeurs'.
	Si tous les tests sur un document ont été réalisés, lance la sélection des
	résultats finaux à partir des choix des testeurs, puis stocke ce résultat final
	dans la collection 'Resultats_Final_Expert_1'.
	"""

    response = json.loads(request.data.decode('utf-8'))
    tester = session['username']
    version = response['search_version']
    test_results = response['results']
    url_list = response['img_url']

    result_docs = []
    for url, test_item in zip(url_list, test_results):
        if test_item[
                'lieux_choisis']:  #Si la liste est vide, le testeur n'a pas su répondre
            result_docs.append({
                'tester': tester,
                'search_version': version,
                'img_url': url,
                'locations_selected': test_item['lieux_choisis'],
                'sufficient': test_item['suffisant'],
                'geoname': test_item['geonames_chosen_result']
            })

    documents = mongo.MongoSave(result_docs)
    documents.storeindb('Resultats_Test_Expert_1',
                        tester='A',
                        img_url='A',
                        search_version='D')

    update = mongo.MongoUpd({'user_id': tester}, {'$inc': {'num_answers': 1}})
    update.singleval_upd('Testeurs')

    dbfinder = mongo.MongoLoad({'user_id': tester}, {'code': 1, '_id': 0})
    test_code = next(dbfinder.retrieve('Testeurs'))['code']

    dbfinder.reinit({
        'img_url': {
            '$in': url_list
        },
        'search_version': version
    }, {
        'testers': 1,
        '_id': 0
    })
    sum_list = []
    done_list = []
    for url, doc in zip(url_list, dbfinder.retrieve('Resultats_RGN')):
        tester_sum = int.from_bytes(
            doc['testers'],
            byteorder='big')  #classmethod, appelée sans instance
        tester_sum &= (~(1 << test_code))

        if tester_sum == 0:
            done_list.append(url)
            bytesize = 1
        else:
            bytesize = floor(log2(tester_sum) / 8) + 1
        tester_sum = tester_sum.to_bytes(bytesize, byteorder='big')
        sum_list.append(tester_sum)

    update.reinit({
        'img_url': '',
        'search_version': version
    }, {'$set': {
        'testers': ''
    }}, url_list, sum_list)
    update.multval_upd('Resultats_RGN', 'img_url')

    if done_list:
        final_list = proc.select_results(version, done_list)
        documents.reinit(final_list)
        documents.nonunique_index('Resultats_Final_Expert_1', processed='A')
        documents.storeindb('Resultats_Final_Expert_1',
                            img_url='A',
                            search_version='D')

    return jsonify(status='OK')
Пример #11
0
def create_rule():
    """Recherche d'erreurs et création de nouvelles règles à partir de celles-ci.
	Récupère dans la base de données les résultats finaux des tests si le champ
	'sufficient' est vrai, c'est-à-dire s'il est possible de récupérer le lieu dans le
	titre.
	La liste des lieux potentiels est converti en liste de mots distincts correctement
	indiciés. Cette liste est ensuite fusionnée avec les listes de résultat du test et
	d'étiquettes TreeTagger.
	Sont extraites de cette liste de comparaison les sous-listes contenant les erreurs
	détectées et les noms propres voisins.
	Les nouvelles règles sont construites à partir de ces sous-listes.
	"""

    dbfinder = mongo.MongoLoad(proj={'search_version': 1, '_id': 0})
    max_version = max(doc['search_version']
                      for doc in dbfinder.retrieve('Versions_Scrape'))
    next_version = str(float(max_version) + 0.01)

    dbfinder.reinit({
        'processed': {
            '$eq': False
        },
        'sufficient': {
            '$eq': True
        }
    }, {
        'search_version': 0,
        '_id': 0
    })

    rule_list = []
    for doc in dbfinder.retrieve('Resultats_Final_Expert_1'):
        words = chain(
            *(loc.split(' ') if type(loc) == str else [0 for i in range(loc)]
              for loc in doc['location_list']))

        tags = doc['tag_list']
        comp_list = list(
            zip(doc['locations_selected'], words, (t[1] for t in tags),
                (t[0] for t in tags)))

        bad_results = []
        good_neighbors = []
        err = []
        i = 0

        while i < len(comp_list):
            if comp_list[i][0] and comp_list[i][0] == bool(comp_list[i][1]):
                good_neighbors.append(
                    list(takewhile(lambda x: x[0], comp_list[i:])))
                i += len(good_neighbors)

            if i < len(comp_list) and comp_list[i][0] != bool(comp_list[i][1]):
                errpos = len(good_neighbors)
                errtype = comp_list[i][0]
                err = list(
                    takewhile(lambda x: x[0] != bool(x[1]) and x[0] == errtype,
                              comp_list[i:]))
                badlen = len(err)

                err.extend(
                    list(takewhile(lambda x: x[0], comp_list[i + badlen:])))
                good_neighbors.extend(err)

                bad_results.append({
                    'errpos': errpos,
                    'errlen': badlen,
                    'errlist': good_neighbors,
                    'errtype': errtype
                })
                good_neighbors = []
                err = []
                i += badlen

            else:
                good_neighbors.clear()
                i += 1

        for res in bad_results:
            errpos = res['errpos']
            errlen = res['errlen']
            errlist = res['errlist']
            errtype = res['errtype']
            rule_vect = errlist[errpos:errpos + errlen]
            take = 0 if errtype else 'X'

            if len(errlist) - errlen > 0:
                take = 2 if errtype else 'X'

                if errpos == len(errlist) - 1:
                    rule_vect = errlist[:errpos]
                    take = 1 if errtype else 'R'

                elif errpos == 0:
                    rule_vect = errlist[errpos + errlen:]
                    take = -1 if errtype else 'L'

            new_rule = {
                'country': doc['country'],
                'expr': ' '.join(i[3] for i in rule_vect),
                'pos': [i[2] for i in rule_vect],
                'take': take,
                'search_version': next_version,
                'img_url': doc['img_url'],
                'verified': False
            }
            rule_list.append(new_rule)

    if rule_list:
        db = mongo.MongoSave(rule_list)
        db.nonunique_index('Nouvelles_Regles', country='A', search_version='D')
        db.storeindb('Nouvelles_Regles',
                     country='A',
                     expr='A',
                     pos='A',
                     take='A')

        db.reinit([{
            'search_version': next_version,
            'submissions_scraped': 0,
            'accuracy': 0
        }])
        db.storeindb('Versions_Scrape')

        upd = mongo.MongoUpd({'processed': {
            '$eq': False
        }}, {'$set': {
            'processed': True
        }})
        upd.singleval_upd('Resultats_Final_Expert_1')