Compare commits
2 Commits
feature/im
...
fix/search
| Author | SHA1 | Date | |
|---|---|---|---|
| c2537cf445 | |||
| dffe072ce2 |
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Не выпущено
|
## Не выпущено
|
||||||
|
|
||||||
|
- Backend обновлён до `0.10.2`: действующие документы ранжируются выше, а
|
||||||
|
утратившие силу — после остальных; из лаборатории удалён пустой блок фрагмента.
|
||||||
- Backend обновлён до `0.10.1`: поисковая выдача по регистрации больше не
|
- Backend обновлён до `0.10.1`: поисковая выдача по регистрации больше не
|
||||||
ограничивается только тремя заранее выбранными нормативными актами.
|
ограничивается только тремя заранее выбранными нормативными актами.
|
||||||
- Backend обновлён до `0.10.0`: поисковый API использует общий планировщик
|
- Backend обновлён до `0.10.0`: поисковый API использует общий планировщик
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ Telegram-бот — только часть рабочего окружения
|
|||||||
## Текущее состояние
|
## Текущее состояние
|
||||||
|
|
||||||
Сейчас реализованы Telegram-бот-секретарь версии `0.2.2` и backend версии
|
Сейчас реализованы Telegram-бот-секретарь версии `0.2.2` и backend версии
|
||||||
`0.10.1`. Поиск расширяет аббревиатуры организационно-правовых форм и поднимает
|
`0.10.2`. Поиск расширяет аббревиатуры организационно-правовых форм и поднимает
|
||||||
положение о регистрации 2026 года, закон об обществах и ГК КР для запросов об
|
положение о регистрации 2026 года, закон об обществах и ГК КР для запросов об
|
||||||
открытии ОсОО/ЖЧК.
|
открытии ОсОО/ЖЧК.
|
||||||
|
|
||||||
| Компонент | Версия | Состояние |
|
| Компонент | Версия | Состояние |
|
||||||
|---|---:|---|
|
|---|---:|---|
|
||||||
| Telegram-бот | `0.2.2` | на Synology работает `0.2.1`; обновление после слияния |
|
| Telegram-бот | `0.2.2` | на Synology работает `0.2.1`; обновление после слияния |
|
||||||
| Backend | `0.10.1` | расширение терминов и приоритет действующих актов о регистрации ОсОО/ЖЧК |
|
| Backend | `0.10.2` | расширение терминов и приоритет действующих актов о регистрации ОсОО/ЖЧК |
|
||||||
| Frontend | — | ещё не создан |
|
| Frontend | — | ещё не создан |
|
||||||
| Сбор и обработка правовых данных | `0.8.0` | добавлены relevance set и оценка выдачи |
|
| Сбор и обработка правовых данных | `0.8.0` | добавлены relevance set и оценка выдачи |
|
||||||
| RAG и база знаний | — | ещё не созданы |
|
| RAG и база знаний | — | ещё не созданы |
|
||||||
@@ -60,4 +60,4 @@ python3 -m unittest discover -s tools/telegram-bot -v
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.1 · Frontend — не создан
|
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.2 · Frontend — не создан
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Backend Акылдаш
|
# Backend Акылдаш
|
||||||
|
|
||||||
Версия: `0.10.1`
|
Версия: `0.10.2`
|
||||||
|
|
||||||
Первая backend-область проекта — загрузка правовых документов из официального
|
Первая backend-область проекта — загрузка правовых документов из официального
|
||||||
Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в
|
Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в
|
||||||
@@ -240,4 +240,4 @@ PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --la
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Акылдаш · Backend v0.10.1 · Frontend — не создан
|
Акылдаш · Backend v0.10.2 · Frontend — не создан
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from search.reviews import ReviewSnapshots, ReviewStore
|
|||||||
|
|
||||||
|
|
||||||
API_VERSION = "v1"
|
API_VERSION = "v1"
|
||||||
SEARCH_ALGORITHM_VERSION = "search-3"
|
SEARCH_ALGORITHM_VERSION = "search-4"
|
||||||
LANGUAGES = {"ru", "ky"}
|
LANGUAGES = {"ru", "ky"}
|
||||||
CODE = re.compile(r"^[0-9]+$")
|
CODE = re.compile(r"^[0-9]+$")
|
||||||
MAX_PAGE_SIZE = 100
|
MAX_PAGE_SIZE = 100
|
||||||
@@ -144,8 +144,14 @@ class Api:
|
|||||||
"collapse": {"field": "document_code"},
|
"collapse": {"field": "document_code"},
|
||||||
"highlight": {"fields": {f"text_{language}": {"number_of_fragments": 1}}},
|
"highlight": {"fields": {f"text_{language}": {"number_of_fragments": 1}}},
|
||||||
}
|
}
|
||||||
|
body["sort"] = [{"_script": {
|
||||||
|
"type": "number",
|
||||||
|
"order": "asc",
|
||||||
|
"script": {"lang": "painless", "source": "doc['status_code'].size() == 0 ? 1 : (doc['status_code'].value == 'active' ? 0 : (doc['status_code'].value == 'repealed' ? 2 : 1))"},
|
||||||
|
}}]
|
||||||
if sort == "date":
|
if sort == "date":
|
||||||
body["sort"] = [{"date_adopted": "desc"}, {"_score": "desc"}]
|
body["sort"].append({"date_adopted": "desc"})
|
||||||
|
body["sort"].append({"_score": "desc"})
|
||||||
response = self.query_opensearch(body)
|
response = self.query_opensearch(body)
|
||||||
try:
|
try:
|
||||||
hits = response["hits"]["hits"]
|
hits = response["hits"]["hits"]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from typing import Iterator
|
|||||||
|
|
||||||
from search.catalog import authority_codes, legal_force_code, source_code
|
from search.catalog import authority_codes, legal_force_code, source_code
|
||||||
|
|
||||||
APP_VERSION = "0.10.1"
|
APP_VERSION = "0.10.2"
|
||||||
LANGUAGES = {"ru", "ky"}
|
LANGUAGES = {"ru", "ky"}
|
||||||
DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json")
|
DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json")
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,7 @@
|
|||||||
.review__result-title { display: flex; gap: .65rem; align-items: baseline; width: 100%; text-align: start; font-weight: 800; border: 0; padding: 0; background: none; color: var(--brand-dark); }
|
.review__result-title { display: flex; gap: .65rem; align-items: baseline; width: 100%; text-align: start; font-weight: 800; border: 0; padding: 0; background: none; color: var(--brand-dark); }
|
||||||
.review__result-title:hover { background: none; border-color: transparent; color: var(--brand); }
|
.review__result-title:hover { background: none; border-color: transparent; color: var(--brand); }
|
||||||
.review__rank { flex: 0 0 auto; color: var(--brand); font-variant-numeric: tabular-nums; }
|
.review__rank { flex: 0 0 auto; color: var(--brand); font-variant-numeric: tabular-nums; }
|
||||||
.review__meta, .review__snippet { margin-block: .5rem; color: var(--muted); }
|
.review__meta { margin-block: .5rem; color: var(--muted); font-size: .8rem; }
|
||||||
.review__meta { font-size: .8rem; }
|
|
||||||
.review__snippet { overflow-wrap: anywhere; }
|
|
||||||
.review__rating { display: grid; grid-template-columns: repeat(4, 1fr); gap: .4rem; margin-block: 1rem .75rem; padding: 0; border: 0; }
|
.review__rating { display: grid; grid-template-columns: repeat(4, 1fr); gap: .4rem; margin-block: 1rem .75rem; padding: 0; border: 0; }
|
||||||
.review__rating legend { width: 100%; margin-block-end: .35rem; font-size: .8rem; font-weight: 700; color: var(--muted); grid-column: 1 / -1; }
|
.review__rating legend { width: 100%; margin-block-end: .35rem; font-size: .8rem; font-weight: 700; color: var(--muted); grid-column: 1 / -1; }
|
||||||
.review__rating label { min-width: 0; padding: .45rem .25rem; border: 1px solid #c5d0d8; border-radius: .5rem; color: var(--muted); text-align: center; font-size: .8rem; cursor: pointer; }
|
.review__rating label { min-width: 0; padding: .45rem .25rem; border: 1px solid #c5d0d8; border-radius: .5rem; color: var(--muted); text-align: center; font-size: .8rem; cursor: pointer; }
|
||||||
@@ -119,7 +117,7 @@
|
|||||||
<div class="review__actions"><div class="review__actions-inner"><label>Ваше имя или псевдоним<input id="reviewer" maxlength="120" autocomplete="name" placeholder="Например, Айжан К."></label> <label>Комментарий к выдаче <input id="overall-comment" maxlength="4000" placeholder="Что важно учесть? (необязательно)"></label> <button class="review__button--primary" id="save" type="button">Сохранить разметку</button></div></div>
|
<div class="review__actions"><div class="review__actions-inner"><label>Ваше имя или псевдоним<input id="reviewer" maxlength="120" autocomplete="name" placeholder="Например, Айжан К."></label> <label>Комментарий к выдаче <input id="overall-comment" maxlength="4000" placeholder="Что важно учесть? (необязательно)"></label> <button class="review__button--primary" id="save" type="button">Сохранить разметку</button></div></div>
|
||||||
<dialog id="document-dialog"><button class="review__dialog-close" id="dialog-close" type="button">Закрыть</button><h2 id="dialog-heading">Документ</h2><div class="review__document-body" id="dialog-body"></div></dialog>
|
<dialog id="document-dialog"><button class="review__dialog-close" id="dialog-close" type="button">Закрыть</button><h2 id="dialog-heading">Документ</h2><div class="review__document-body" id="dialog-body"></div></dialog>
|
||||||
<div class="review__tour-spotlight" id="tutorial-spotlight" hidden aria-hidden="true"></div><dialog class="review__tour" id="tutorial-dialog" aria-labelledby="tutorial-heading" aria-describedby="tutorial-text"><div class="review__tour-content"><p class="review__tour-kicker">Короткое знакомство</p><h2 id="tutorial-heading"></h2><p id="tutorial-text"></p><div class="review__tour-footer"><span class="review__tour-progress" id="tutorial-progress"></span><div class="review__tour-actions"><button id="tutorial-skip" type="button">Пропустить</button><button class="review__button--primary" id="tutorial-next" type="button">Далее</button></div></div></div></dialog>
|
<div class="review__tour-spotlight" id="tutorial-spotlight" hidden aria-hidden="true"></div><dialog class="review__tour" id="tutorial-dialog" aria-labelledby="tutorial-heading" aria-describedby="tutorial-text"><div class="review__tour-content"><p class="review__tour-kicker">Короткое знакомство</p><h2 id="tutorial-heading"></h2><p id="tutorial-text"></p><div class="review__tour-footer"><span class="review__tour-progress" id="tutorial-progress"></span><div class="review__tour-actions"><button id="tutorial-skip" type="button">Пропустить</button><button class="review__button--primary" id="tutorial-next" type="button">Далее</button></div></div></div></dialog>
|
||||||
<footer class="review__header">Акылдаш · Backend v0.10.1 · Внутренняя лаборатория релевантности</footer>
|
<footer class="review__header">Акылдаш · Backend v0.10.2 · Внутренняя лаборатория релевантности</footer>
|
||||||
<script>
|
<script>
|
||||||
const DRAFT_KEY = 'akyldash-search-review-draft';
|
const DRAFT_KEY = 'akyldash-search-review-draft';
|
||||||
const TOUR_KEY = 'akyldash-search-review-tour-seen';
|
const TOUR_KEY = 'akyldash-search-review-tour-seen';
|
||||||
@@ -146,11 +144,10 @@
|
|||||||
const item = document.createElement('li'); item.className = 'review__result'; item.dataset.rank = index + 1; item.dataset.code = result.code;
|
const item = document.createElement('li'); item.className = 'review__result'; item.dataset.rank = index + 1; item.dataset.code = result.code;
|
||||||
const title = document.createElement('button'); title.type = 'button'; title.className = 'review__result-title'; title.innerHTML = `<span class="review__rank">#${index + 1}</span><span></span>`; title.lastElementChild.textContent = escapeText(result.name || 'Название не указано'); title.addEventListener('click', () => openDocument(index, title));
|
const title = document.createElement('button'); title.type = 'button'; title.className = 'review__result-title'; title.innerHTML = `<span class="review__rank">#${index + 1}</span><span></span>`; title.lastElementChild.textContent = escapeText(result.name || 'Название не указано'); title.addEventListener('click', () => openDocument(index, title));
|
||||||
const meta = document.createElement('div'); meta.className = 'review__meta'; meta.textContent = [result.type, result.status, result.date_adopted, result.number].filter(Boolean).join(' · ');
|
const meta = document.createElement('div'); meta.className = 'review__meta'; meta.textContent = [result.type, result.status, result.date_adopted, result.number].filter(Boolean).join(' · ');
|
||||||
const snippet = document.createElement('div'); snippet.className = 'review__snippet'; snippet.textContent = result.snippet || 'Фрагмент не найден.';
|
|
||||||
const rating = document.createElement('fieldset'); rating.className = 'review__rating'; rating.innerHTML = `<legend>Оценка результата</legend>`;
|
const rating = document.createElement('fieldset'); rating.className = 'review__rating'; rating.innerHTML = `<legend>Оценка результата</legend>`;
|
||||||
[['0', 'нерелевантен'], ['1', 'косвенно полезен'], ['2', 'частично полезен'], ['3', 'прямо отвечает']].forEach(([value, label]) => { const id = `rating-${index}-${value}`; const wrapper = document.createElement('label'); wrapper.htmlFor = id; wrapper.textContent = `${value} — ${label}`; const input = document.createElement('input'); input.type = 'radio'; input.name = `rating-${index}`; input.id = id; input.value = value; wrapper.prepend(input); rating.append(wrapper); });
|
[['0', 'нерелевантен'], ['1', 'косвенно полезен'], ['2', 'частично полезен'], ['3', 'прямо отвечает']].forEach(([value, label]) => { const id = `rating-${index}-${value}`; const wrapper = document.createElement('label'); wrapper.htmlFor = id; wrapper.textContent = `${value} — ${label}`; const input = document.createElement('input'); input.type = 'radio'; input.name = `rating-${index}`; input.id = id; input.value = value; wrapper.prepend(input); rating.append(wrapper); });
|
||||||
const comment = document.createElement('textarea'); comment.className = 'review__comment'; comment.maxLength = 4000; comment.placeholder = 'Комментарий к результату (необязательно)'; comment.setAttribute('aria-label', `Комментарий к результату #${index + 1}`);
|
const comment = document.createElement('textarea'); comment.className = 'review__comment'; comment.maxLength = 4000; comment.placeholder = 'Комментарий к результату (необязательно)'; comment.setAttribute('aria-label', `Комментарий к результату #${index + 1}`);
|
||||||
item.append(title, meta, snippet, rating, comment); $('results').append(item);
|
item.append(title, meta, rating, comment); $('results').append(item);
|
||||||
});
|
});
|
||||||
applyDraft();
|
applyDraft();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,21 @@ class SearchApiTest(unittest.TestCase):
|
|||||||
self.assertTrue(any("constant_score" in clause for clause in planned["should"]))
|
self.assertTrue(any("constant_score" in clause for clause in planned["should"]))
|
||||||
self.assertTrue(any("общество с ограниченной ответственностью" in clause.get("multi_match", {}).get("query", "") for clause in planned["should"]))
|
self.assertTrue(any("общество с ограниченной ответственностью" in clause.get("multi_match", {}).get("query", "") for clause in planned["should"]))
|
||||||
|
|
||||||
|
def test_search_sorts_repealed_documents_after_other_statuses(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
|
api = self.make_api(Path(temporary))
|
||||||
|
with patch("search.api.request_json", return_value={"hits": {"hits": []}}) as request:
|
||||||
|
api.handle("GET", "/search?q=НДС+на+импорт&language=ru")
|
||||||
|
body = json.loads(request.call_args.args[2])
|
||||||
|
source = body["sort"][0]["_script"]["script"]["source"]
|
||||||
|
self.assertIn("'active' ? 0", source)
|
||||||
|
self.assertIn("'repealed' ? 2", source)
|
||||||
|
self.assertEqual(body["sort"][1], {"_score": "desc"})
|
||||||
|
|
||||||
|
api.handle("GET", "/search?q=НДС+на+импорт&language=ru&sort=date")
|
||||||
|
body = json.loads(request.call_args.args[2])
|
||||||
|
self.assertEqual(body["sort"][1], {"date_adopted": "desc"})
|
||||||
|
|
||||||
def test_document_editions_openapi_and_validation(self):
|
def test_document_editions_openapi_and_validation(self):
|
||||||
with tempfile.TemporaryDirectory() as temporary:
|
with tempfile.TemporaryDirectory() as temporary:
|
||||||
api = self.make_api(Path(temporary))
|
api = self.make_api(Path(temporary))
|
||||||
|
|||||||
@@ -39,4 +39,4 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.1 · Frontend — не создан
|
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.2 · Frontend — не создан
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
- Telegram-бот: `0.2.2`
|
- Telegram-бот: `0.2.2`
|
||||||
- Telegram-бот на Synology: `0.2.1`
|
- Telegram-бот на Synology: `0.2.1`
|
||||||
- Backend: `0.10.1`
|
- Backend: `0.10.2`
|
||||||
- Frontend: не создан
|
- Frontend: не создан
|
||||||
|
|
||||||
## Краткий итог
|
## Краткий итог
|
||||||
@@ -262,4 +262,4 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.1 · Frontend — не создан
|
Акылдаш · Telegram-бот v0.2.2 · Backend v0.10.2 · Frontend — не создан
|
||||||
|
|||||||
Reference in New Issue
Block a user