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_): 1. Настроим конфиг, для этого необходимо изменить параметры в Вашем wsgi-entrypoint (в моем случае
прописать параметры доступа к базе, демону Sphinx и путь, куда будут сохраняться данные Sphinx; по этому пути [passenger_wsgi.py](passenger_wsgi.py)): в строке `from config import *` измените _config_ на имя Вашего
дополнительно необходимо создать 3 папки: log, run и data. Все доступные настройки можно увидеть в _aore/config/common.py_ конфигурационного файла (создается рядом с wsgi app), пример конфига находится в файле
[config.example.py](config.example.py).
2. Создадим базу: 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.rar`
- из директории `sudo -u phias python manage.py -b create -s /tmp/fias_xml_unpacked` - из директории `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 - - Также, можно указать конкретную версию ФИАС олько_ при http загрузке, с ключом `--update-version <num>`, где num -
номер версии ФИАС, все доступные версии можно получить, выполнив `manage.py -v`. номер версии ФИАС, все доступные версии можно получить, выполнив `manage.py -v`.
**Внимание**! Если Вы инициализируете БД из архива или директории, для последующего корректного обновления необходимо Примечание 1: Если Вы инициализируете БД из архива или директории, при создании или обновлении базы у Вас будет
прописать номер версии ФИАС, которую Вы только что установили, в базе приложения, таблица CONFIG запрошен номер устанавливаемой версии ФИАС.
Примечание 2: У пользователя PostgreSql (postgres, либо созданного Вами) должны быть права на чтение из директории,
указанной в `config.folders.temp`, иначе будет Permission denied при попытке bulk-import.
3. Проиндексируем Sphinx: 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` - Debian: `sudo python manage.py -c -i indexer -o /usr/local/sphinx/etc/sphinx.conf`
4. Затем запустим searchd: 4. Затем запустим searchd:
- Windows: - 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` - и запускаем: `net start sphinxsearch`
- Debian: `sudo searchd --config /usr/local/sphinx/etc/sphinx.conf` - 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): if not os.path.exists(folders.temp):
os.makedirs(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): def configure_indexer(self, indexer_binary, config_filename):
logging.info("Start configuring Sphinx...") logging.info("Start configuring Sphinx...")
self.index_binary = indexer_binary self.index_binary = indexer_binary

View File

@ -2,44 +2,49 @@
import json import json
import logging import logging
from bottle import Bottle, response from bottle import response
from aore.search.fiasfactory import FiasFactory from aore.search.fiasfactory import FiasFactory
from miscutils.bottlecl import BottleCL
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
app = Bottle()
fias_factory = FiasFactory()
@app.route(r'/expand/<aoid:re:[\w]{8}(-[\w]{4}){3}-[\w]{12}>') class App(BottleCL):
def expand(aoid): def __init__(self, log_filename):
response.content_type = 'application/json' super(App, self).__init__()
response.set_header('Access-Control-Allow-Origin', '*') 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 __expand(self, aoid):
def normalize(aoid): response.content_type = 'application/json'
response.content_type = 'application/json' response.set_header('Access-Control-Allow-Origin', '*')
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>') return json.dumps(self._factory.normalize(aoid))
@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(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) @staticmethod
def error404(error): def basic_error_handler(error):
response.content_type = 'application/json' response.content_type = 'application/json'
response.set_header('Access-Control-Allow-Origin', '*') 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 -*- # -*- coding: utf-8 -*-
import logging
import re import re
import urllib import urllib
from uuid import UUID from uuid import UUID
import psycopg2 import psycopg2
import traceback
from bottle import template 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.dbutils.dbimpl import DBImpl
from aore.search.search import SphinxSearch from search import SphinxSearch
class FiasFactory: class FiasFactory:
@ -19,7 +21,8 @@ class FiasFactory:
self.normalize_templ = template('aore/templates/postgre/normalize_query.sql', aoid="//aoid") self.normalize_templ = template('aore/templates/postgre/normalize_query.sql', aoid="//aoid")
# Проверка, что строка является действительым UUID v4 # Проверка, что строка является действительым UUID v4
def __check_uuid(self, guid): @staticmethod
def __check_uuid(guid):
try: try:
UUID(guid) UUID(guid)
except ValueError: except ValueError:
@ -54,6 +57,8 @@ class FiasFactory:
results = self.searcher.find(text, strong) results = self.searcher.find(text, strong)
except Exception, err: except Exception, err:
if basic.logging:
logging.error(traceback.format_exc(err))
return dict(error=err.args[0]) return dict(error=err.args[0])
return results return results
@ -66,6 +71,8 @@ class FiasFactory:
sql_query = self.normalize_templ.replace("//aoid", aoid_guid) sql_query = self.normalize_templ.replace("//aoid", aoid_guid)
rows = self.db.get_rows(sql_query, True) rows = self.db.get_rows(sql_query, True)
except Exception, err: except Exception, err:
if basic.logging:
logging.error(traceback.format_exc(err))
return dict(error=err.args[0]) return dict(error=err.args[0])
if len(rows) == 0: if len(rows) == 0:
@ -85,6 +92,8 @@ class FiasFactory:
sql_query = self.expand_templ.replace("//aoid", normalized_id) sql_query = self.expand_templ.replace("//aoid", normalized_id)
rows = self.db.get_rows(sql_query, True) rows = self.db.get_rows(sql_query, True)
except Exception, err: except Exception, err:
if basic.logging:
logging.error(traceback.format_exc(err))
return dict(error=err.args[0]) return dict(error=err.args[0])
return rows return rows

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import logging
import re import re
import time import time
@ -7,9 +8,10 @@ import sphinxapi
from aore.config import basic from aore.config import basic
from aore.config import sphinx_conf from aore.config import sphinx_conf
from aore.search.wordentry import WordEntry from aore.miscutils.exceptions import FiasException
from aore.search.wordvariation import VariationType
from aore.miscutils.trigram import trigram from aore.miscutils.trigram import trigram
from wordentry import WordEntry
from wordvariation import VariationType
class SphinxSearch: class SphinxSearch:
@ -28,15 +30,19 @@ class SphinxSearch:
sphinx_host = sphinx_conf.listen sphinx_host = sphinx_conf.listen
sphinx_port = None sphinx_port = None
# Получаем строку подключения для Sphinx
if ":" in sphinx_conf.listen and "unix:/" not in sphinx_conf.listen: if ":" in sphinx_conf.listen and "unix:/" not in sphinx_conf.listen:
sphinx_host, sphinx_port = sphinx_conf.listen.split(":") sphinx_host, sphinx_port = sphinx_conf.listen.split(":")
sphinx_port = int(sphinx_port) sphinx_port = int(sphinx_port)
# Настраиваем подключение для подсказок
self.client_sugg = sphinxapi.SphinxClient() self.client_sugg = sphinxapi.SphinxClient()
self.client_sugg.SetServer(sphinx_host, sphinx_port) self.client_sugg.SetServer(sphinx_host, sphinx_port)
self.client_sugg.SetLimits(0, self.max_result) self.client_sugg.SetLimits(0, self.max_result)
self.client_sugg.SetConnectTimeout(3.0) self.client_sugg.SetConnectTimeout(3.0)
# Настраиваем подключение для поиска адреса
self.client_show = sphinxapi.SphinxClient() self.client_show = sphinxapi.SphinxClient()
self.client_show.SetServer(sphinx_host, sphinx_port) self.client_show.SetServer(sphinx_host, sphinx_port)
self.client_show.SetLimits(0, self.max_result) self.client_show.SetLimits(0, self.max_result)
@ -139,8 +145,11 @@ class SphinxSearch:
rs = self.client_show.RunQueries() rs = self.client_show.RunQueries()
elapsed_t = time.time() - start_t elapsed_t = time.time() - start_t
if rs is None:
raise FiasException("Cannot find sentence.")
if basic.logging: if basic.logging:
print(elapsed_t) logging.info("Sphinx time for {} = {}".format(text, elapsed_t))
results = [] results = []
parsed_ids = [] parsed_ids = []

View File

@ -61,3 +61,4 @@ class AoRar:
logging.warning("Cannot delete %s, do it manually", self.fname) logging.warning("Cannot delete %s, do it manually", self.fname)
else: else:
logging.error("No file specified or not exists") logging.error("No file specified or not exists")
raise FiasException("No DB archive specified.")

View File

@ -55,11 +55,27 @@ class Updater:
finally: finally:
db.close() 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): def __get_updates_from_folder(self, foldername):
# TODO: Вычислять версию, если берем данные из каталога # 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() aorar = AoRar()
if url.startswith("http://") or url.startswith("https://"): if url.startswith("http://") or url.startswith("https://"):

View File

@ -15,7 +15,7 @@ class XMLParser:
elem.clear() elem.clear()
# Also eliminate now-empty references from the root node to elem # Also eliminate now-empty references from the root node to elem
for ancestor in elem.xpath('ancestor-or-self::*'): for ancestor in elem.xpath('ancestor-or-self::*'):
while ancestor.getprevious(): while ancestor.getprevious() is not None:
del ancestor.getparent()[0] del ancestor.getparent()[0]
del context 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.soapreceiver import SoapReceiver
from aore.updater.updater import Updater 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) logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)

View File

@ -1,25 +1,16 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from aore import phias, config from aore import phias
# Config section # Load config
config.sphinx_conf.listen = "192.168.0.37:9312" try:
config.sphinx_conf.var_dir = "C:\\Sphinx" from config import *
except ImportError:
config.db_conf.database = "pyfias" assert "No config"
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
# Define main app # Define main app
application = phias.app application = phias.App("pyphias.log")
# Run bottle WSGI server if no external # Run bottle WSGI server if no external
if __name__ == '__main__': 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)