commit
92628d076b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.log
|
||||
config.py
|
22
README.md
22
README.md
@ -134,9 +134,10 @@ _Внимание_! Только Python 2.7+ (на 3+ не тестировал)
|
||||
|
||||
## Настройка
|
||||
### Первоначальная настройка базы данных
|
||||
1. Настроим конфиг, для этого необходимо изменить параметры в Вашем wsgi-entrypoint (в моем случае _passenger_wsgi.py_):
|
||||
прописать параметры доступа к базе, демону Sphinx и путь, куда будут сохраняться данные Sphinx; по этому пути
|
||||
дополнительно необходимо создать 3 папки: log, run и data. Все доступные настройки можно увидеть в _aore/config/common.py_
|
||||
1. Настроим конфиг, для этого необходимо изменить параметры в Вашем wsgi-entrypoint (в моем случае
|
||||
[passenger_wsgi.py](passenger_wsgi.py)): в строке `from config import *` измените _config_ на имя Вашего
|
||||
конфигурационного файла (создается рядом с wsgi app), пример конфига находится в файле
|
||||
[config.example.py](config.example.py).
|
||||
2. Создадим базу:
|
||||
- из архива `sudo -u phias python manage.py -b create -s /tmp/fias_xml.rar`
|
||||
- из директории `sudo -u phias python manage.py -b create -s /tmp/fias_xml_unpacked`
|
||||
@ -144,15 +145,18 @@ _Внимание_! Только Python 2.7+ (на 3+ не тестировал)
|
||||
- Также, можно указать конкретную версию ФИАС _только_ при http загрузке, с ключом `--update-version <num>`, где num -
|
||||
номер версии ФИАС, все доступные версии можно получить, выполнив `manage.py -v`.
|
||||
|
||||
**Внимание**! Если Вы инициализируете БД из архива или директории, для последующего корректного обновления необходимо
|
||||
прописать номер версии ФИАС, которую Вы только что установили, в базе приложения, таблица CONFIG
|
||||
Примечание 1: Если Вы инициализируете БД из архива или директории, при создании или обновлении базы у Вас будет
|
||||
запрошен номер устанавливаемой версии ФИАС.
|
||||
|
||||
Примечание 2: У пользователя PostgreSql (postgres, либо созданного Вами) должны быть права на чтение из директории,
|
||||
указанной в `config.folders.temp`, иначе будет Permission denied при попытке bulk-import.
|
||||
3. Проиндексируем Sphinx:
|
||||
- Windows: `python manage.py -c -i C://sphinx//indexer.exe -o C://sphinx//sphinx.conf`
|
||||
- Windows: `python manage.py -c -i C:\sphinx\bin\indexer.exe -o C:\sphinx\sphinx.conf`
|
||||
- Debian: `sudo python manage.py -c -i indexer -o /usr/local/sphinx/etc/sphinx.conf`
|
||||
4. Затем запустим searchd:
|
||||
- Windows:
|
||||
- Устанавливаем службу: `C:\Sphinx\bin\searchd --install --config C:\Sphinx\sphinx.conf.in --servicename sphinxsearch`
|
||||
- Устанавливаем службу: `C:\Sphinx\bin\searchd --install --config C:\Sphinx\sphinx.conf --servicename sphinxsearch`
|
||||
- и запускаем: `net start sphinxsearch`
|
||||
- Debian: `sudo searchd --config /usr/local/sphinx/etc/sphinx.conf`
|
||||
5. Настроим WSGI server, я использую nginx + passenger (см. файл passenger_wsgi.py). Вы можете использовать любое
|
||||
приемлемое сочетание.
|
||||
5. Настроим WSGI server, я использую nginx + passenger (см. файл [passenger_wsgi.py](passenger_wsgi.py)). Вы можете
|
||||
использовать любое приемлемое сочетание.
|
24
aore/miscutils/bottlecl.py
Normal file
24
aore/miscutils/bottlecl.py
Normal file
@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from bottle import Bottle
|
||||
|
||||
|
||||
class BottleCL(object):
|
||||
def __init__(self):
|
||||
self._app = Bottle()
|
||||
self.init_routes()
|
||||
|
||||
def init_routes(self):
|
||||
pass
|
||||
|
||||
def add_route(self, route_path, handler):
|
||||
self._app.route(route_path, callback=handler)
|
||||
|
||||
def add_error(self, error_code, handler):
|
||||
if not self._app.error_handler:
|
||||
self._app.error_handler = {error_code: handler}
|
||||
else:
|
||||
self._app.error_handler[error_code] = handler
|
||||
|
||||
def start(self, **kwargs):
|
||||
self._app.run(**kwargs)
|
@ -21,6 +21,14 @@ class SphinxHelper:
|
||||
if not os.path.exists(folders.temp):
|
||||
os.makedirs(folders.temp)
|
||||
|
||||
# оздаем 3 папки для Сфинкса
|
||||
if not os.path.exists(sphinx_conf.var_dir+'/run'):
|
||||
os.makedirs(sphinx_conf.var_dir+'/run')
|
||||
if not os.path.exists(sphinx_conf.var_dir+'/log'):
|
||||
os.makedirs(sphinx_conf.var_dir+'/log')
|
||||
if not os.path.exists(sphinx_conf.var_dir+'/data'):
|
||||
os.makedirs(sphinx_conf.var_dir+'/data')
|
||||
|
||||
def configure_indexer(self, indexer_binary, config_filename):
|
||||
logging.info("Start configuring Sphinx...")
|
||||
self.index_binary = indexer_binary
|
||||
|
@ -2,44 +2,49 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from bottle import Bottle, response
|
||||
from bottle import response
|
||||
|
||||
from aore.search.fiasfactory import FiasFactory
|
||||
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
|
||||
app = Bottle()
|
||||
fias_factory = FiasFactory()
|
||||
from miscutils.bottlecl import BottleCL
|
||||
|
||||
|
||||
@app.route(r'/expand/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>')
|
||||
def expand(aoid):
|
||||
class App(BottleCL):
|
||||
def __init__(self, log_filename):
|
||||
super(App, self).__init__()
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG, filename=log_filename)
|
||||
|
||||
self._factory = FiasFactory()
|
||||
|
||||
def init_routes(self):
|
||||
self.add_route(r'/expand/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>', self.__expand)
|
||||
self.add_route(r'/normalize/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>', self.__normalize)
|
||||
self.add_route(r'/find/<text>', self.__find)
|
||||
self.add_route(r'/find/<text>/<strong>', self.__find)
|
||||
self.add_error(404, self.basic_error_handler)
|
||||
self.add_error(500, self.basic_error_handler)
|
||||
|
||||
def __expand(self, aoid):
|
||||
response.content_type = 'application/json'
|
||||
response.set_header('Access-Control-Allow-Origin', '*')
|
||||
|
||||
return json.dumps(fias_factory.expand(aoid))
|
||||
return json.dumps(self._factory.expand(aoid))
|
||||
|
||||
|
||||
@app.route(r'/normalize/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>')
|
||||
def normalize(aoid):
|
||||
def __normalize(self, aoid):
|
||||
response.content_type = 'application/json'
|
||||
response.set_header('Access-Control-Allow-Origin', '*')
|
||||
|
||||
return json.dumps(fias_factory.normalize(aoid))
|
||||
return json.dumps(self._factory.normalize(aoid))
|
||||
|
||||
|
||||
@app.route(r'/find/<text>')
|
||||
@app.route(r'/find/<text>/<strong>')
|
||||
def find(text, strong=False):
|
||||
def __find(self, text, strong=False):
|
||||
strong = (strong == "strong")
|
||||
response.content_type = 'application/json'
|
||||
response.set_header('Access-Control-Allow-Origin', '*')
|
||||
|
||||
return json.dumps(fias_factory.find(text, strong))
|
||||
return json.dumps(self._factory.find(text, strong))
|
||||
|
||||
|
||||
@app.error(404)
|
||||
def error404(error):
|
||||
@staticmethod
|
||||
def basic_error_handler(error):
|
||||
response.content_type = 'application/json'
|
||||
response.set_header('Access-Control-Allow-Origin', '*')
|
||||
|
||||
return json.dumps(dict(error="Page not found"))
|
||||
return json.dumps(dict(error=error.status))
|
||||
|
@ -1,14 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import re
|
||||
import urllib
|
||||
from uuid import UUID
|
||||
|
||||
import psycopg2
|
||||
import traceback
|
||||
from bottle import template
|
||||
|
||||
from aore.config import db_conf
|
||||
from aore.config import db_conf, basic
|
||||
from aore.dbutils.dbimpl import DBImpl
|
||||
from aore.search.search import SphinxSearch
|
||||
from search import SphinxSearch
|
||||
|
||||
|
||||
class FiasFactory:
|
||||
@ -19,7 +21,8 @@ class FiasFactory:
|
||||
self.normalize_templ = template('aore/templates/postgre/normalize_query.sql', aoid="//aoid")
|
||||
|
||||
# Проверка, что строка является действительым UUID v4
|
||||
def __check_uuid(self, guid):
|
||||
@staticmethod
|
||||
def __check_uuid(guid):
|
||||
try:
|
||||
UUID(guid)
|
||||
except ValueError:
|
||||
@ -54,6 +57,8 @@ class FiasFactory:
|
||||
|
||||
results = self.searcher.find(text, strong)
|
||||
except Exception, err:
|
||||
if basic.logging:
|
||||
logging.error(traceback.format_exc(err))
|
||||
return dict(error=err.args[0])
|
||||
|
||||
return results
|
||||
@ -66,6 +71,8 @@ class FiasFactory:
|
||||
sql_query = self.normalize_templ.replace("//aoid", aoid_guid)
|
||||
rows = self.db.get_rows(sql_query, True)
|
||||
except Exception, err:
|
||||
if basic.logging:
|
||||
logging.error(traceback.format_exc(err))
|
||||
return dict(error=err.args[0])
|
||||
|
||||
if len(rows) == 0:
|
||||
@ -85,6 +92,8 @@ class FiasFactory:
|
||||
sql_query = self.expand_templ.replace("//aoid", normalized_id)
|
||||
rows = self.db.get_rows(sql_query, True)
|
||||
except Exception, err:
|
||||
if basic.logging:
|
||||
logging.error(traceback.format_exc(err))
|
||||
return dict(error=err.args[0])
|
||||
|
||||
return rows
|
||||
|
@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
@ -7,9 +8,10 @@ import sphinxapi
|
||||
|
||||
from aore.config import basic
|
||||
from aore.config import sphinx_conf
|
||||
from aore.search.wordentry import WordEntry
|
||||
from aore.search.wordvariation import VariationType
|
||||
from aore.miscutils.exceptions import FiasException
|
||||
from aore.miscutils.trigram import trigram
|
||||
from wordentry import WordEntry
|
||||
from wordvariation import VariationType
|
||||
|
||||
|
||||
class SphinxSearch:
|
||||
@ -28,15 +30,19 @@ class SphinxSearch:
|
||||
|
||||
sphinx_host = sphinx_conf.listen
|
||||
sphinx_port = None
|
||||
|
||||
# Получаем строку подключения для Sphinx
|
||||
if ":" in sphinx_conf.listen and "unix:/" not in sphinx_conf.listen:
|
||||
sphinx_host, sphinx_port = sphinx_conf.listen.split(":")
|
||||
sphinx_port = int(sphinx_port)
|
||||
|
||||
# Настраиваем подключение для подсказок
|
||||
self.client_sugg = sphinxapi.SphinxClient()
|
||||
self.client_sugg.SetServer(sphinx_host, sphinx_port)
|
||||
self.client_sugg.SetLimits(0, self.max_result)
|
||||
self.client_sugg.SetConnectTimeout(3.0)
|
||||
|
||||
# Настраиваем подключение для поиска адреса
|
||||
self.client_show = sphinxapi.SphinxClient()
|
||||
self.client_show.SetServer(sphinx_host, sphinx_port)
|
||||
self.client_show.SetLimits(0, self.max_result)
|
||||
@ -139,8 +145,11 @@ class SphinxSearch:
|
||||
rs = self.client_show.RunQueries()
|
||||
elapsed_t = time.time() - start_t
|
||||
|
||||
if rs is None:
|
||||
raise FiasException("Cannot find sentence.")
|
||||
|
||||
if basic.logging:
|
||||
print(elapsed_t)
|
||||
logging.info("Sphinx time for {} = {}".format(text, elapsed_t))
|
||||
|
||||
results = []
|
||||
parsed_ids = []
|
||||
|
@ -61,3 +61,4 @@ class AoRar:
|
||||
logging.warning("Cannot delete %s, do it manually", self.fname)
|
||||
else:
|
||||
logging.error("No file specified or not exists")
|
||||
raise FiasException("No DB archive specified.")
|
||||
|
@ -55,11 +55,27 @@ class Updater:
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# Получает верию ФИАС с клавиатуры (если мы берем базу из папки или локального архива и не можем определить,
|
||||
# что это за версия
|
||||
@staticmethod
|
||||
def __get_update_version_from_console():
|
||||
mode = None
|
||||
while not mode:
|
||||
try:
|
||||
mode = int(raw_input('Enter FIAS update version (3 digit):'))
|
||||
except ValueError:
|
||||
print "Not a valid fias version, try again."
|
||||
|
||||
return mode
|
||||
|
||||
def __get_updates_from_folder(self, foldername):
|
||||
# TODO: Вычислять версию, если берем данные из каталога
|
||||
yield dict(intver=0, textver="Unknown", delta_url=foldername, complete_url=foldername)
|
||||
yield dict(intver=self.__get_update_version_from_console(),
|
||||
textver="Unknown", delta_url=foldername,
|
||||
complete_url=foldername)
|
||||
|
||||
def __get_updates_from_rar(self, url):
|
||||
@staticmethod
|
||||
def __get_updates_from_rar(url):
|
||||
aorar = AoRar()
|
||||
|
||||
if url.startswith("http://") or url.startswith("https://"):
|
||||
|
@ -15,7 +15,7 @@ class XMLParser:
|
||||
elem.clear()
|
||||
# Also eliminate now-empty references from the root node to elem
|
||||
for ancestor in elem.xpath('ancestor-or-self::*'):
|
||||
while ancestor.getprevious():
|
||||
while ancestor.getprevious() is not None:
|
||||
del ancestor.getparent()[0]
|
||||
del context
|
||||
|
||||
|
17
config.example.py
Normal file
17
config.example.py
Normal file
@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from aore import config
|
||||
|
||||
# Config section
|
||||
config.sphinx_conf.listen = "127.0.0.1:9312"
|
||||
config.sphinx_conf.var_dir = "C:\\Sphinx"
|
||||
|
||||
config.db_conf.database = "pyfias"
|
||||
config.db_conf.host = "192.168.0.1"
|
||||
config.db_conf.port = 5432
|
||||
config.db_conf.user = "postgres"
|
||||
config.db_conf.password = "postgres"
|
||||
|
||||
config.unrar_config.path = "C:\\Program Files\\WinRAR\\unrar.exe"
|
||||
config.folders.temp = "E:\\!TEMP"
|
||||
|
||||
config.basic.logging = True
|
@ -7,6 +7,13 @@ from aore.miscutils.sphinx import SphinxHelper
|
||||
from aore.updater.soapreceiver import SoapReceiver
|
||||
from aore.updater.updater import Updater
|
||||
|
||||
# Load config
|
||||
try:
|
||||
from config import *
|
||||
except ImportError:
|
||||
assert "No config"
|
||||
|
||||
# Initialize logging
|
||||
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
|
||||
|
||||
|
||||
|
@ -1,25 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from aore import phias, config
|
||||
from aore import phias
|
||||
|
||||
# Config section
|
||||
config.sphinx_conf.listen = "192.168.0.37:9312"
|
||||
config.sphinx_conf.var_dir = "C:\\Sphinx"
|
||||
|
||||
config.db_conf.database = "pyfias"
|
||||
config.db_conf.host = "192.168.0.37"
|
||||
config.db_conf.port = 5432
|
||||
config.db_conf.user = "postgres"
|
||||
config.db_conf.password = "intercon"
|
||||
|
||||
config.unrar_config.path = "C:\\Program Files (x86)\\WinRAR\\unrar.exe"
|
||||
config.folders.temp = "E:\\!TEMP"
|
||||
|
||||
config.basic.logging = True
|
||||
# Load config
|
||||
try:
|
||||
from config import *
|
||||
except ImportError:
|
||||
assert "No config"
|
||||
|
||||
# Define main app
|
||||
application = phias.app
|
||||
application = phias.App("pyphias.log")
|
||||
|
||||
# Run bottle WSGI server if no external
|
||||
if __name__ == '__main__':
|
||||
application.run(host='0.0.0.0', port=8087, debug=True)
|
||||
application.start(host='0.0.0.0', port=8087, debug=True)
|
||||
|
Loading…
x
Reference in New Issue
Block a user