feat(search): add legal force catalog and taxonomy draft
This commit is contained in:
@@ -72,6 +72,7 @@ def openapi() -> dict:
|
||||
{"name": "document_type", "in": "query", "schema": {"type": "string"}},
|
||||
{"name": "status", "in": "query", "schema": {"type": "string"}},
|
||||
{"name": "authority", "in": "query", "schema": {"type": "string"}},
|
||||
{"name": "legal_force", "in": "query", "schema": {"type": "string"}},
|
||||
{"name": "date_from", "in": "query", "schema": {"type": "string", "format": "date"}},
|
||||
{"name": "date_to", "in": "query", "schema": {"type": "string", "format": "date"}},
|
||||
{"name": "sort", "in": "query", "schema": {"type": "string", "enum": ["relevance", "date"]}},
|
||||
@@ -121,7 +122,7 @@ class Api:
|
||||
if sort not in {"relevance", "date"}:
|
||||
raise ApiError(400, "sort must be relevance or date")
|
||||
filters: list[dict] = [{"term": {"language": language}}, {"term": {"is_current_edition": True}}]
|
||||
fields = {"document_type": "document_type_code", "status": "status_code", "authority": "authority_codes"}
|
||||
fields = {"document_type": "document_type_code", "status": "status_code", "authority": "authority_codes", "legal_force": "legal_force_code"}
|
||||
for parameter, field in fields.items():
|
||||
value = one(query, parameter)
|
||||
if value:
|
||||
@@ -137,7 +138,7 @@ class Api:
|
||||
body = {
|
||||
"from": (page - 1) * page_size,
|
||||
"size": page_size + 1,
|
||||
"_source": ["document_code", "edition_code", "document_name_ru", "document_name_ky", "document_type_ru", "document_type_ky", "status_ru", "status_ky", "date_adopted", "number"],
|
||||
"_source": ["document_code", "edition_code", "document_name_ru", "document_name_ky", "document_type_ru", "document_type_ky", "status_ru", "status_ky", "legal_force_code", "date_adopted", "number"],
|
||||
"query": {"bool": {"filter": filters, "must": {"multi_match": {"query": text, "fields": [f"document_name_{language}", f"text_{language}"], "type": "cross_fields"}}}},
|
||||
"collapse": {"field": "document_code"},
|
||||
"highlight": {"fields": {f"text_{language}": {"number_of_fragments": 1}}},
|
||||
@@ -161,13 +162,14 @@ class Api:
|
||||
if not isinstance(source, dict) or not source.get("document_code"):
|
||||
raise ApiError(502, "search backend returned an incomplete result")
|
||||
highlight = hit.get("highlight", {}).get(f"text_{language}", [])
|
||||
return {"code": source["document_code"], "edition": source.get("edition_code"), "name": source.get(f"document_name_{language}"), "type": source.get(f"document_type_{language}"), "status": source.get(f"status_{language}"), "date_adopted": source.get("date_adopted"), "number": source.get("number"), "snippet": highlight[0] if highlight else None}
|
||||
legal_force = source.get("legal_force_code")
|
||||
return {"code": source["document_code"], "edition": source.get("edition_code"), "name": source.get(f"document_name_{language}"), "type": source.get(f"document_type_{language}"), "status": source.get(f"status_{language}"), "legal_force": labels("legal_force", legal_force)[language] if legal_force else None, "date_adopted": source.get("date_adopted"), "number": source.get("number"), "snippet": highlight[0] if highlight else None}
|
||||
|
||||
def filters(self, query: dict[str, list[str]]) -> dict:
|
||||
language = one(query, "language") or "ru"
|
||||
if language not in LANGUAGES:
|
||||
raise ApiError(400, "language must be ru or ky")
|
||||
fields = {"document_types": ("document_type", "document_type_code"), "statuses": ("status", "status_code"), "authorities": ("authority", "authority_codes")}
|
||||
fields = {"document_types": ("document_type", "document_type_code"), "statuses": ("status", "status_code"), "authorities": ("authority", "authority_codes"), "legal_forces": ("legal_force", "legal_force_code")}
|
||||
body = {"size": 0, "query": {"term": {"is_current_edition": True}}, "aggs": {name: {"terms": {"field": pair[1], "size": 1000}, "aggs": {"documents": {"cardinality": {"field": "document_code", "precision_threshold": 40000}}}} for name, pair in fields.items()}}
|
||||
response = self.query_opensearch(body)
|
||||
try:
|
||||
|
||||
@@ -5,7 +5,20 @@ DOCUMENT_TYPES = {
|
||||
}
|
||||
STATUSES = {"active": ("Действует", "Күчүндө"), "repealed": ("Утратил силу", "Күчүн жоготту"), "unspecified": ("Не указан", "Көрсөтүлгөн эмес")}
|
||||
AUTHORITIES = {"president": ("Президент", "Президент"), "parliament": ("Органы законодательной власти", "Мыйзам чыгаруу бийлик органдары"), "cabinet": ("Правительство и Кабинет Министров", "Өкмөт жана Министрлер Кабинети"), "ministries_and_committees": ("Министерства и государственные комитеты", "Министрликтер жана мамлекеттик комитеттер"), "administrative_agencies": ("Административные ведомства", "Административдик ведомстволор"), "national_bank": ("Национальный банк", "Улуттук банк"), "other_state_bodies": ("Иные государственные органы", "Башка мамлекеттик органдар"), "local_representative_bodies": ("Представительные органы местного самоуправления", "Жергиликтүү өз алдынча башкаруунун өкүлчүлүктүү органдары"), "other": ("Прочие органы", "Башка органдар")}
|
||||
CATALOGS = {"document_type": DOCUMENT_TYPES, "status": STATUSES, "authority": AUTHORITIES}
|
||||
LEGAL_FORCE_LEVELS = {
|
||||
"constitutional": ("Конституционный уровень", "Конституциялык деңгээл"),
|
||||
"legislative": ("Законодательный уровень", "Мыйзам деңгээли"),
|
||||
"subordinate": ("Подзаконный уровень", "Мыйзам алдындагы деңгээл"),
|
||||
}
|
||||
LEGAL_FORCE_BY_DOCUMENT_TYPE = {
|
||||
"constitution": "constitutional",
|
||||
"constitutional_law": "constitutional",
|
||||
"code": "legislative",
|
||||
"law": "legislative",
|
||||
"decree": "subordinate",
|
||||
"resolution": "subordinate",
|
||||
}
|
||||
CATALOGS = {"document_type": DOCUMENT_TYPES, "status": STATUSES, "authority": AUTHORITIES, "legal_force": LEGAL_FORCE_LEVELS}
|
||||
|
||||
|
||||
def labels(category: str, code: str) -> dict[str, str]:
|
||||
@@ -49,3 +62,7 @@ def authority_codes(paths: list[dict]) -> list[str]:
|
||||
else:
|
||||
codes.add("other")
|
||||
return sorted(codes) or ["other"]
|
||||
|
||||
|
||||
def legal_force_code(document_type: dict | None) -> str | None:
|
||||
return LEGAL_FORCE_BY_DOCUMENT_TYPE.get(source_code("document_type", document_type))
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"document_type_ru": { "type": "keyword" },
|
||||
"document_type_ky": { "type": "keyword" },
|
||||
"document_type_code": { "type": "keyword" },
|
||||
"legal_force_code": { "type": "keyword" },
|
||||
"status_ru": { "type": "keyword" },
|
||||
"status_ky": { "type": "keyword" },
|
||||
"status_code": { "type": "keyword" },
|
||||
|
||||
@@ -15,9 +15,9 @@ import urllib.request
|
||||
from pathlib import Path
|
||||
from typing import Iterator
|
||||
|
||||
from search.catalog import authority_codes, source_code
|
||||
from search.catalog import authority_codes, legal_force_code, source_code
|
||||
|
||||
APP_VERSION = "0.8.3"
|
||||
APP_VERSION = "0.9.0"
|
||||
LANGUAGES = {"ru", "ky"}
|
||||
DEFAULT_MAPPING = Path(__file__).with_name("minjust-fragments-index.json")
|
||||
|
||||
@@ -76,6 +76,7 @@ def search_document(document: dict, fragment: dict, expected: tuple[str, str, st
|
||||
"document_type_ru": localized(document.get("type"), "ru"),
|
||||
"document_type_ky": localized(document.get("type"), "ky"),
|
||||
"document_type_code": source_code("document_type", document.get("type")),
|
||||
"legal_force_code": legal_force_code(document.get("type")),
|
||||
"status_ru": localized(document.get("status"), "ru"),
|
||||
"status_ky": localized(document.get("status"), "ky"),
|
||||
"status_code": source_code("status", document.get("status")),
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="review__actions"><label>Проверяющий <input id="reviewer" maxlength="120" autocomplete="name"></label> <label>Общий комментарий <input id="overall-comment" maxlength="4000"></label> <button class="review__button--primary" id="save" type="button">Сохранить оценку</button></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>
|
||||
<div class="review__status review__status--error" id="error" role="alert" aria-live="assertive"></div>
|
||||
<footer class="review__header">Акылдаш · Backend v0.8.3 · Внутренняя лаборатория релевантности</footer>
|
||||
<footer class="review__header">Акылдаш · Backend v0.9.0 · Внутренняя лаборатория релевантности</footer>
|
||||
<script>
|
||||
const DRAFT_KEY = 'akyldash-search-review-draft';
|
||||
const state = { results: [], token: '', selected: null, query: '' };
|
||||
|
||||
Reference in New Issue
Block a user