Merge pull request #8 from jar3b/dev

Dev
This commit is contained in:
jar3b 2016-03-15 12:18:18 +03:00
commit 92628d076b
13 changed files with 155 additions and 62 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.log
config.py

View File

@ -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)). Вы можете
использовать любое приемлемое сочетание.

View 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)

View File

@ -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

View File

@ -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):
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
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)
return json.dumps(fias_factory.expand(aoid))
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)
@app.route(r'/normalize/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>')
def normalize(aoid):
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
def __expand(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.expand(aoid))
def __normalize(self, aoid):
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
@app.route(r'/find/<text>')
@app.route(r'/find/<text>/<strong>')
def find(text, strong=False):
strong = (strong == "strong")
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
return json.dumps(self._factory.normalize(aoid))
return json.dumps(fias_factory.find(text, strong))
def __find(self, text, strong=False):
strong = (strong == "strong")
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
return json.dumps(self._factory.find(text, strong))
@app.error(404)
def error404(error):
response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*')
@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))

View File

@ -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

View File

@ -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 = []

View File

@ -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.")

View File

@ -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://"):

View File

@ -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
View 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

View File

@ -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)

View File

@ -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)